At 8:09 PM -0400 5/24/04, Uri Guttman wrote:
 >>>>> "DS" == Dan Sugalski <[EMAIL PROTECTED]> writes:

  DS> Parrot has a unified event and IO system--indeed events are nothing
  DS> but satisfied IO requests. (Or, alternately, IO requests are nothing
  DS> but solicited events with external data)

that is a very nice way to word what i have assumed for a long time.
mind if i steal it? :)

Sure, go ahead.

  DS> Basic Architecture
  DS> ==================

  DS> The event and IO system revolves around streams, requests, and
  DS> events. Requests travel through streams and out the other end as
  DS> events.

  DS> Each stream in the event and IO system has the same structure. There
  DS> are zero or more pre-processing layers, a single "anchor" layer which
  DS> ultimately satisfies the request, one or more post-processing layers,
  DS> then the IO request becomes a notification that the request has been
  DS> satisfied and goes through the event handling layers.

s/anchor/execution/g ?
s/anchor/blocking/g ?   (since this is the layer that should block until
                         some event occurs).
s/anchor/event loop/g ? (since this is where the core event loop runs)

Nope, anchor. It's not the event loop--that's elsewhere. The anchor layer (as well as the pre and post processing layers) may execute asynchronously, in their own thread.


It's the one unchangeable layer in a stream, the one between the pre and post processing layers. It anchors things, hence the name. (Or so my thinking goes, at least)

  DS> The preprocessing, anchor, and post-processing layers are isolated
  DS> from the requesting thread, and layer code shouldn't count on access
  DS> to any information external to it. The event handling layers execute
  DS> in the context of the thread processing the event, and may count on
  DS> that thread's resources.

  DS> The system is simple--you make a request to a stream. The request
  DS> runs through the preprocessing layers which mangle it as they see
  DS> fit. The request then hits the anchor layer, which actually satisfies
  DS> the request. The anchor layer then passes the satisfied requests
  DS> through the post processing layers. When post-processing is done the
  DS> completed request is posted as an event, at which point a thread
  DS> watching the event queue will eventually process it by calling the
  DS> event layers. At this point the event is completed.

  DS> Non-IO things such as signals and OS events (assuming we can get to
  DS> them safely) behave the same way. The only difference is that the IO
  DS> request is originated by the anchor layer (which monitors the
  DS> external event source) and flows through the postprocessing and event
  DS> handling layers, but not the preprocessing layers.

there may be some preprocessing when the initial request is sent
it. there is no need to watch SIGUSR until the user code requests
it. and that request could come from different sources so there could be
some munging needed then.

When the code requests a signal we put a stream in place for that signal, which becomes the event source for it. You're not requesting a single IO action--you're creating a new event source. Different things. I need to make that a lot clearer.


  DS> At the moment each filehandle has a single set of IO layers which
  DS> handle all read, write, and command requests. This may change if it
  DS> turns out to be untenable, but split streams are a pain to keep
  DS> synchronized.

what if the handles themselves are split as with a a pseudo-tty? but you
could just do an event for the master and slave sides in that case.

Each side is a separate thing in that case, if I'm understanding you correctly.

  DS> Note that any layer along the way may defer the IO request. A
  DS> deferred request stays deferred until something resumes it, at which
  DS> point it continues through the IO layers. Deferring is normally done
  DS> by the anchor layer, if it is done at all. A deferred IO request is
  DS> not considered completed and will block processing of any more events
  DS> through an IO stream if that stream guarantees ordered request
  DS> processing.

and what happens if the order is not guaranteed?

Then the stream may take requests in whatever order it wants. If someone wanted to implement an elevator algorithm for a filehandle (which'd be kind of silly, the OS is usually better for that) or pre-scan the stream and satisfy read requests with prior write requests that overlap, well... they could. The IO requests would potentially get satisfied out of order, which can make for really confusing behaviour if you're not expecting it.


 is this a way to get
the disable/enable feature i want?

Nope. For that you pause the stream.

  DS> Request/Event status
  DS> ====================

and of course enabled/disabled. if i say it often enough, maybe you get
dizzy and patch it in. :)

Nope, not going to happen. :) Events aren't disabled--streams are disabled.

  DS> Event/Request classes
  DS> =====================

why would a signal event be delivered if i didn't request it?

You've requested that a class of signal events, in general, be delivered when they occur.


similarly
for gui events. you would need to request hooks into the gui system in
order to get mouse events. cli programs don't need gui stuff but a gui
program solicits those events by linking in the gui lib. so parrot will
need to either wrap those libs or come up with some other way to handle
a mix of i/o and gui events. the gui libs already do that so wrapping
makes sense.

Right, all of which set up event sources.

The difference is this. With a solicited request you've said "Do this one very specific thing. When you're done, run this very specific function with this specific piece of data". You can use the request object to see where it is in the stream, what state it's in, and potentially kill it if you've decided you don't want that one specific thing to happen.

An unsolicited request, on the other hand, is one where you've said "When the general class of events X happens, send me an event" and the system has. You don't know in advance that one specific thing is going to happen, and only get the event after the fact. You can't associate one specific sub and specific piece of data with it before it happens because you don't know that it's going to happen until after it does.

Solicited events happen because of something your program asks to have happen, while unsolicited events are essentially interrupts.

  DS> 3) A solicited event may have a callback and user data associated
  DS>     with it, an unsolicited event may not.

then how does the user code handle a signal? i would want a
callback.

Each event stream has a set of event handlers associated with it. If you want a callback for SIGUSR, for example, you'd push that layer onto the SIGUSR stream's event layer list.


and even user data could be wanted.

Then either a postprocessing layer or an event layer on the signal's stream would have to add it .


 some signals (sigio?) can
have attached (in some wierd way) data such as the async i/o
results. the user callback should get that passed to it.

Yeah, but just sigio, and only in a few systems. Doesn't matter, though--you can't get access to SIGIO and SIGALRM from user code. Parrot steals those. :) (Well, OK, unless the embedding program steals 'em first)


  DS> 4) Any exception associated with a solicited event will be thrown
  DS>     when the event is waited on. An unsolicited event's exception is
  DS>     thrown as soon as the event is drained from the event queue.

then the exception is the implicit way to do a callback.

But a very bad one. Exceptions aren't resumable, and you may find you've walked much too far out. You also can't be sure where the exception's thrown, either.


i think some
wording to that effect would be good. is there any way to pass event
data in an exception?

Only if the code that throws the exception embeds the event in it.

  DS> The following methods are required of any event source that
  DS> implements the EventSource protocol:

  DS>    pause

DS> Stop the event source from emitting events. Any event that would be
DS> triggered (signals, for example) are instead discarded. It is
DS> generally considered unwise to pause event sources attached to IO devices.


would this be an enable/disable thing?

Yes.

  DS>    hold

  DS> Like pause, except the events are held rather than discarded.

not sure how you would get this done. seems to need a queue layer around
the actual event loop.

Which we need anyway, for event levels.

why is this needed?

In the cases where you don't want to deal with the events but don't want them thrown out either.


  DS>    cancel

  DS> Permanently stop the event source.

  DS>    unpause

DS> Undo a pause or hold on the event source. If any events are pending
DS> because of a prior hold, those events are then posted to the even tqueue.


are those on a per event basis or on a whole event loop system?

Per event source.

  DS> IO Ops
  DS> ======

  DS>     newhandle Pfilehandle, Panchorsub

  DS> Create a new filehandle. C<anchorsub> is the subroutine that anchors
  DS> the filehandle chain.

could that be a no-op sub? or is that just a simple pass-through?

It could be a noop, but in that case nothing would happen if you read or wrote to the stream inless you've


  DS>     pushhandler Pfilehandle, Phandlersub, Ireadwritecontrol

  DS>     pophandler Pfilehandle

  DS>     addhandler Pfilehandle, Phandlersub, Ioffset, Ireadwritecontrol

  DS>     removehandler Pfilehandle, Phandlersub, Ioffset, Ireadwritecontrol

so those two are splicing a handler into the list and push/pop can be
written using them (at least internally)?

These actually are editing errors. I cut-n-pasted from the IO doc I was working on without taking into account the split into pre/post/event layers. I'll fix that in V2.


  DS> Synchronously write the data to the filehandle

  DS>   seek Pevent, Pfilehandle, Iwhere[, Pcallback, Puserdata]

why is this needed?

It isn't, strictly speaking--it could be a single generic "IOCommand" op.

and what would a seek callback do?

Callback? It's mostly in there for orthogonality, as all requests can have a callback and user data associated with them.


 seek isn't
blocking in general

Well... it is if you've queued up a dozen or more write or read requests.

  DS> Submit a request to seek to the specified position in the file.

  DS>   tell Pevent, Pfilehandle[, Pcallback, Puserdata]

this is even odder. why would tell need a callback?

Orthogonality. That and every time I think "Why the *hell* would someone need that?" someone comes along with a reason that's both good and scares me. Since the system supports it, it's exposed.


  DS>   un_read Pfilehandle, Pdata

  DS> Undo a read request, pushing Pdata back onto the filehandle. (Note
  DS> the _ in the op name)

any max size of that call or total amounts you can push back?

Nope.

 if the
data was from a file can it be just discarded and a seek pointer moved?

Nope. It gets queued up for pending read requests.

that fails if you read data and the file was modified, and then you
un_read data.

Sorta. I didn't put the details of unreading in this draft. I'll fix that for v2.
--
Dan


--------------------------------------"it's like this"-------------------
Dan Sugalski                          even samurai
[EMAIL PROTECTED]                         have teddy bears and even
                                      teddy bears get drunk

Reply via email to