REBOL Technologies

How to Think About Async Ports

Carl Sassenrath, CTO
REBOL Technologies
16-Nov-2004

Article #0050
Main page || Index || Prior Article [0049] || Next Article [0051] || 1 Comments || Send feedback

The best way to think about the async port model to consider it similar to the model used for REBOL/View. It is event driven. This approach takes advantage of the fact that most computer programs are waiting most of the time for user interaction or IO to happen (of course there are exceptions, see below).

For example, in /View you write:

window: layout [
    h1 "My Window"
    btn "Button" [print "button1"]
    btn "Quit" [quit]
]

view window

But, that last line is actually the same as:

view/new window  ; open the window, do not wait
do-events        ; wait for events

DO-EVENTS is simply a call to the WAIT function that will not return until the last window is closed. As window events happen, the view system calls the VID system and events are sent to the interface objects defined above.

This is an event driven method of programming. Like Hypertalk (Apple Hypercard) or Lingo (Macromedia) languages, the "flow" depends on the events that occur. In REBOL you have the choice of writing either "linear" code that executes in sequence like most programming languages or event driven code such as that shown above.

Note that when you write code like the above, it begins in linear mode as you define all the functions and actions of the program. This can be thought of as your setup or initialization phase. During this time, the sequence of execution will not be interrupted. Only when you finally call WAIT (directly or indirectly) the system becomes event driven (asynchronous).

In the new REBOL kernel, the event driven model also works with network ports. So, in addition to your view code, you can easily create network connections during your linear setup code (or actually at any time) and they will also be processed once you call WAIT.

window: layout [
    h1 "My Window"
    btn "Button" [print "button1"]
    btn "Quit" [quit]
]

view/new window  ; open the window

handler: func [port action arg] [
    ... your handler code ...
]
server-port: open/direct/binary/async url :handler

... other setup ...

do-events

At that point your code becomes event driven and both the GUI and the async ports will be processed.

Here are some important things to note about this mode of operation:

  1. This is not preemptive multitasking (PMT), but it is very similar to cooperative multitasking. View code (feel methods) and port handlers will never be interrupted. You do not need to worry about classical critical sections, semaphores, or mutex most of the time (unless you are trying to coordinate between different ports or faces).
  2. The above note also means that if you do a lot of heavy processing (like image processing), call a synchronous protocol (like the current HTTP, FTP, SMTP, and other protocols), or call a synchronous OS function (like reading a disk file) events will not occur until the action is complete. GUI events and port events will be held off. If this is a big problem for your code you can launch one or more additional copies of REBOL and use those for time consuming operations.
  3. Beware testing async code from the console. The console itself will imply a WAIT while waiting for user input. For example, if you type the above OPEN/async from the console, it will enter the handler with its first event almost immediately. This can become confusing. To avoid this problem, write your test code in a file and use DO to execute it.

Also note that your program may return from the WAIT or DO-EVENTS that is shown above. Errors will cause it to return and closing the main window will cause it to return. A well written program needs to handle those cases. For example, you can deal with errors like this:

... above setup code ...

if error? err: try [do-events][
    show-error disarm :err
]

... additional cleanup code ...

Here the SHOW-ERROR function may open a new window to report the error to the user. If you want the program to continue, even after the error occurred, you can put a loop around this part. If you do that it is wise to add an error counter for the case where continuing from the error situation is not possible:

loop 3 [
    either error? err: try [do-events][
        show-error disarm :err
    ][
        break ; normal exit
    ]
]

Keep in mind that an error can be thrown from a point in your code where state changes (e.g. setting variables, modifying lists) related to your functions has not been concluded. Everything that follows the error point has been left undone.

AltME and other products serve as a good example of this kind of error "capture" and processing. In AltME's case the error is displayed and the user has the option of sending it directly to SafeWorlds bug database.

1 Comments

Updated 6-Mar-2024   -   Copyright Carl Sassenrath   -   WWW.REBOL.COM   -   Edit   -   Blogger Source Code