None of this is set in stone, but take a look and see how everyone feels about this.
I'll get to IO after, since the two are pretty tightly intertwined, and changes to this will affect the IO stuff too. ------------>Snip here<--------------- Events, another design sketch Overview ======== Events are a terrific idea, as evidenced by the vast array of event handling libraries there are. (There are at least a half dozen on CPAN) All of which are the same but different, and incompatible. Of course. Proper event handling's the only way to manage asynchronous IO, and since parrot needs event handling it only makes sense to expose it in general, so we can avoid the problems of having multiple event handling loops. Event basics ============ An event is an indication to your program that something has happened. It may be that a timer has fired, a packet has been received (or sent) on a socket, a disk read has completed, or that the user has moved the mouse. Events are asynchronous--that is they can happen at any time, and your program has no real control over when they are generated. It does, however, have control over when they are processed. More on that later. Event Classes ============= There are two main classes of events, which we'll call expected and unexpected. An expected event is one that your program is specifically expecting to get as a result of a request for asynchronous action, and each expected event has a corresponding event handle. User code can wait for an expected event to complete, cancel an expected event, and query the status of an expected event. Expected events may also be either complete or incomplete. An incomplete event is one whose requested action hasn't yet happened, while a complete event Unexpected events, on the other hand, happen for reasons outside of your program. Signals, GUI events, and exceptions for asynchronous files (which may not be delivered as signals on many platforms) are examples of unexpected events. Unexpected events, by their nature, are always complete, since they aren't generated until an action actually occurs. Some event sources may be either expected or unexpected. Network connections are an example of this--you may have a network filehandle set either as an expected event source, where your program must explicitly ask for data, or set as an unexpected event source, where a monitoring thread captures data as it is received and generates events for the captured data. Callbacks and User Data ======================= Each expected event can have a callback routine and piece of user data associated with it. When the event request is satisfied (The IO operation completes or the timer fires, for example) the callback associated with it is invoked. The user data, as well as the event data, if there is any, is passed into the callback. Callback signatures are fixed, and of the form: (Preturndata, Icommandstatus) = callbacksub(Pevent, Peventdata, Puserdata, Icommand) The callback is passed in the event PMC, the PMC for the event data, and the PMC for the user data, if any. (Either or both of these can be the Null PMC if there is no user or event data) Command is always 0 for callback handling. (Callbacks and IO layer functions are identical. More detail in the IO section) The callback must then return the data and a command status. The status should be 0 for correct completion, a non-zero negative number for an error, and a non-zero positive number for successful completion with a change in status for the next handler in the chain, if there is one. If a callback function throws an exception the exception itself is deferred. Expected events defer the exception until the event is waited on, while unexpected events throw their excecption when their event functions are done processing. (This does mean that expected events may have their exceptions effectively ignored if the event is never waited on) When there are multiple event handler in a chain, each handler can alter the data and command passed to the next handler in the chain. The return data is passed in as the user data to the next command, and the return status is, if a positive non-zero number, passed to the next handler in the chain. If the return status is 0, then the same command is passed into the next handler in the chain. This allows event handlers to preprocess the event if need be. Generic Handlers ================ Each event source can have a generic handler associated with it. This handler is called before the event's specific callback is invoked, if it has one. Generic handlers have the same function signature as callback functions do, and behave the same way--if they return 0 the event is passed to the next handler in the chain, and if they return non-zero then the event stops passing through and is marked as failed. Note that the handler table is scoped, however due to the inherently unpredictable nature of asynchronous events it isn't possible to guarantee that changes in the handler table due to scope changes will take place immediately so far as event sources are concerned--that is, the program may leave a scope and change the handler table yet have an event generated that uses the old handler table. Events and Exceptions ===================== Event handlers are generally discouraged from throwing exceptions, however it is sometimes necessary. Exceptions are handled differently by expected and unexpected events. With an expected event, if an event handler is terminated by an unhandled exception, that exception is attached to the event itself, and when user-level code C<wait>s on the event, the exception is thrown then. Unexpected events whose handlers throw exceptions are handled somewhat differently. In that case, the exception is thrown as soon as the event is drained from the event queue. This may or may not happen immediately, depending on how the event in question is currently being handled. If the event is processed in a separate event handling thread then the event is put in the main thread's event queue and the exception will be thrown when the main thread drains the queue and hits the event. If the event is currently being processed in the main thread (because there is no event-handling thread, or this event isn't being handled by one) then as soon as the event is drained from the event queue the exception is thrown. How events are handled ====================== There are three steps in event handling. First, an event is triggered. This puts the event into the event queue, but otherwise does nothing. (This operation is interrupt-safe, though there issues in obtaining event PMCs at interrupt level in some cases) Next, a queue runner pulls an event out of the event queue and calls its handlers. These are a combination of any event class handlers as well as an event-specific callback, if there is one. This operation happens as normal user-level code, though it may be done significantly after the event itself is put in the queue, and may not happen in the same thread as the event was requested from. [NB: I'm not sure what to do about event handling threads and shared data yet] Finally, for expected events, something can wait on the event to get the event's data and status. This can be done before the event has completed, in which case the operation will wait (draining the event queue of pending events) Note that the queue runner will automatically wait on any unexpected event that it completes. This puts the event in the done state and will throw any exception that the event handlers may have generated. Expected events, however, will be left alone until user-level code explicitly waits on it. Event States ============ Events have five states. They are: * Quiescent - The event object has been newly allocated but hasn't been associated with anything yet and can't be triggered * Waiting - The operation the event triggers on hasn't yet happened * Triggered - The operation the event triggers on has happened, but the event's handlers haven't been called yet * Ready - The event has been processed by its handlers but hasn't yet been waited on to flush out any exception it might have. * Done - The event has flushed any pending exceptions and can now only be queried for its status and data Creating Events =============== Events are just PMCs, and there are no ops specifically to create them. To create an event, allocate a PMC of the class Event. These are array/hash combination PMCs and may be accessed as follows: 0 (CLASS) - The class of the event 1 (EVENT_DATA) - The event data associated with this event 2 (USER_DATA) - The user data associated with the event 3 (CALLBACK) - The callback sub for this event 4 (EXCEPTION) - The pending exception for this event, if any 5 (STATUS) - The status from the exception handlers 6 (STATE) - What state the event is in 7 (LEVEL) - The event level this event is at Event Opcodes ============= The opcodes in this section are a combination of event requests and event handling ops. It doesn't include the IO ops--those are separate. Most of the event request ops have two forms, one of which takes a callback PMC and user data PMC. checkevent Explicitly check to see if there are any events pending in the event queue and, if so process one. drainqueue Wait forever, draining the event queue as it fills. This op only exits if it encounters a QUEUE_DONE event in the queue. wait Pevent Wait for the specified event to complete. If the event has already completed the op doesn't wait. If there is a pending exception it is thrown. This opcode will drain the pending event queue. getstat Istatus, Pevent Returns the status of the event, noting whether it has been completed yet and, if so, what happened. getdata Pdata, Pevent getdata Sdata, Pevent Get the data associated with the event, if there is any. What the data is depends on what generated the event. (For filehandle reads this will be the data read from the filehandle) getuser Puserdata, Pevent Get the user data that was passed into this event. cancel Pevent Cancel the event, if possible. post Pevent Post an event to the event queue for later handling sethandler Ieventclass, Phandlersub Set the default handler for the specified class of events. seteventlevel Ieventlevel Set the current event level. Events of a lower level than this won't be processed. settimer Pevent, Nseconds[, Pcallback, Puserdata] settimer Pevent, Iseconds[, Pcallback, Puserdata] Set a timer to go off in C<seconds> seconds. setalarm Pevent, Nabs_time[, Pcallback, Puserdata] setalarm Pevent, Iabs_time[, Pcallback, Puserdata] Set a timer to go off at the absolute time specified. setinterval Ninterval_seconds[, Pcallback, Puserdata] setinterval Iinterval_seconds[, Pcallback, Puserdata] Set an interval timer that goes off every interval_second seconds. When are events handled? ======================== Parrot does C<not> guarantee immediate delivery of events. That, unfortunately, is untenable for us, as much as we would like it. The overhead involved in checking for pending events every op is excessive for at least some of the run cores. Instead, parrot guarantees that events get checked with some regularity. All ops that appear to block are actually waiting for a wakeup event, and so drain the queue as they wait. Other ops may check the event queue, and are tagged with the "check_event" property in the ops files. It's the compiler's job to ensure that at least some event-checking ops get executed, unfortunately. C Interface =========== Because many events are actually generated from within C code, the following API is exposed for use: INTVAL Parrot_ping_event(Parrot_Interp Interpreter, INTVAL type); Note that an event of type C<type> has just occurred. Returns either 0 on success or 1 on failure. This function may fail if the target interpreter is unable to post an event to its event queue. This normally happens because there are no event PMCs available to allocate for the event. INTVAL Parrot_post_event(Parrot_Interp Interpreter, PMC *event); Post the event PMC to the target interpreter's event queue. Returns success status, 0 on success, 1 on failure. This function may fail if the event can't be posted to the target interpreter's event queue for some reason. -- Dan --------------------------------------"it's like this"------------------- Dan Sugalski even samurai [EMAIL PROTECTED] have teddy bears and even teddy bears get drunk