The comments on the first draft that still apply have been incorporated in this draft. I fully expect to make a second one as there's quite a bit of IO that's still unspecified, but I wanted to get this out for folks to think about.
++++++++++++++++++++++++++Snip Here++++++++++++++++++
Events and IO =============
Parrot has a unified event and IO system--indeed events are nothing but satisfied IO requests. (Or, alternately, IO requests are nothing but solicited events with external data)
Basic Architecture ==================
The event and IO system revolves around streams, requests, and events. Requests travel through streams and out the other end as events.
Each stream in the event and IO system has the same structure. There are zero or more pre-processing layers, a single "anchor" layer which ultimately satisfies the request, one or more post-processing layers, then the IO request becomes a notification that the request has been satisfied and goes through the event handling layers.
The preprocessing, anchor, and post-processing layers are isolated from the requesting thread, and layer code shouldn't count on access to any information external to it. The event handling layers execute in the context of the thread processing the event, and may count on that thread's resources.
The system is simple--you make a request to a stream. The request runs through the preprocessing layers which mangle it as they see fit. The request then hits the anchor layer, which actually satisfies the request. The anchor layer then passes the satisfied requests through the post processing layers. When post-processing is done the completed request is posted as an event, at which point a thread watching the event queue will eventually process it by calling the event layers. At this point the event is completed.
Non-IO things such as signals and OS events (assuming we can get to them safely) behave the same way. The only difference is that the IO request is originated by the anchor layer (which monitors the external event source) and flows through the postprocessing and event handling layers, but not the preprocessing layers.
At the moment each filehandle has a single set of IO layers which handle all read, write, and command requests. This may change if it turns out to be untenable, but split streams are a pain to keep synchronized.
Note that any layer along the way may defer the IO request. A deferred request stays deferred until something resumes it, at which point it continues through the IO layers. Deferring is normally done by the anchor layer, if it is done at all. A deferred IO request is not considered completed and will block processing of any more events through an IO stream if that stream guarantees ordered request processing.
Layer and Callback functions ============================
Since IO layers and event callbacks are identical, they have the same signature. That signature is:
(Sreturndata, Icommand) = layersub(Pevent, Slayerdata, Scallbackdata, Icommand)
The returndata from a layer is passed into the next layer as its layerdata parameter, and the returned command status is passed onto the next layer as its command parameter. This way a layer can alter what the next layer in the chain sees.
The commands are:
* 1 - Event * 2 - Read request * 3 - Write request * 4 - Command request. The layerdata holds the command itself * 5 - Reset stream * 6 - Close stream
Negative command arguments indicate that the command request has had an error or exception of some kind. Layers may pull out the error or exception status to determine what they should do. A layer may change the error status of the request by just setting it, while it may change the exception status by throwing an exception of its own. Requests have a single exception associated with them, the last exception thrown by a layer in the stream.
Request/Event status ====================
Requests and events have one of the following statuses:
* Unprocessed - The request has been issued but not yet touched * Preprocess - The request is in the preprocessing layers * Handling - The request is being dealt with by the anchor layer * Postprocess - The request is in the postprocessing layers * Completing - The request is done and waiting for event handling * Done - All the event handlers have run on the event * Historical - The event has been waited for so any pending exceptions have been delivered
Event/Request classes =====================
There are two classses of events and IO requests: solicited and unsolicited.
A solicited event or request is one that a user program has explicitly asked for. They are unique, and are generated as a result of user code making a request for something to happen, for example a read from a disk file or a one-shot timer.
An unsolicited event, on the other hand, is one that parrot generates as the result of something happening external to itself, or as the result of some recurring event happening. Signals and GUI events, for example, are unsolicted as are recurring timer events.
There are four big differences between solicited and unsolicited events:
1) A solicited event goes through a stream's preprocessing layers. Unsolicited events and requests do not.
2) A solicted event may be cancelled from user code, as the user code will have the request object as soon as the request is made. Unsolicited events are only available to user code after the fact--that is, user code only knows they exist after they have happened.
3) A solicited event may have a callback and user data associated with it, an unsolicited event may not.
4) Any exception associated with a solicited event will be thrown when the event is waited on. An unsolicited event's exception is thrown as soon as the event is drained from the event queue.
Interfacing with System Events ==============================
It is possible, given the nature of fighting event loops, that parrot may not be able to integrate its event system with the system event loop.
Event Source Methods ====================
The following methods are required of any event source that implements the EventSource protocol:
pause
Stop the event source from emitting events. Any event that would be triggered (signals, for example) are instead discarded. It is generally considered unwise to pause event sources attached to IO devices.
hold
Like pause, except the events are held rather than discarded.
cancel
Permanently stop the event source.
unpause
Undo a pause or hold on the event source. If any events are pending because of a prior hold, those events are then posted to the even tqueue.
Event Ops =========
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 request, 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.
IO Ops ======
These are the ops that control IO. Note that the synchronous versions of the ops are just simple wrappers around the asynchronous versions. For example, this:
readw mydata, Filehandle, 500
is equivalent to the sequence
read tempevent, Filehandle, 500 wait tempevent getdata mydata, tempevent
but is provided for convenience for those languages which are so averse to asynchronous IO that they don't want to even acknowledge its existence.
newhandle Pfilehandle, Panchorsub
Create a new filehandle. C<anchorsub> is the subroutine that anchors the filehandle chain.
pushhandler Pfilehandle, Phandlersub, Ireadwritecontrol
pophandler Pfilehandle
addhandler Pfilehandle, Phandlersub, Ioffset, Ireadwritecontrol
removehandler Pfilehandle, Phandlersub, Ioffset, Ireadwritecontrol
read Pevent, Pfilehandle, Ihowmuch[, Pcallback, Puserdata]
Request a read of data from the filehandle.
readw Pdata, Pfilehandle, Ihowmuch
Synchronously read C<howmuch> data from the filehandle.
write Pevent, Pfilehandle, Pdata[, Pcallback, Puserdata]
Write the data to the filehandle.
writew Pfilehandle, Pdata
Synchronously write the data to the filehandle
seek Pevent, Pfilehandle, Iwhere[, Pcallback, Puserdata]
Submit a request to seek to the specified position in the file.
tell Pevent, Pfilehandle[, Pcallback, Puserdata]
Submit a request for the current position in the filehandle.
un_read Pfilehandle, Pdata
Undo a read request, pushing Pdata back onto the filehandle. (Note the _ in the op name)
-- Dan
--------------------------------------"it's like this"------------------- Dan Sugalski even samurai [EMAIL PROTECTED] have teddy bears and even teddy bears get drunk