[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Chicken-users] Reading from TCP sockets and time outs
From: |
Kim |
Subject: |
Re: [Chicken-users] Reading from TCP sockets and time outs |
Date: |
Tue, 13 Apr 2004 10:08:01 -0700 |
User-agent: |
Mutt/1.5.6i |
Hmmm... actually, after some brain storming with Category 5, here is the
current idea for an event handler API.
Provide a way to create an 'event monitor' (this would be similiar to
the PLT concept of a 'waitable', but I would prefer using some other
term to avoid confusion.) An event monitor would be something similiar
to a mutex in terms of having state and exclusivity. Then also have
a "(event-case ...) construct to wait on events. The rough idea
would be to have something that looked like this:
(define readable? (make-event-monitor an-input-port ))
(define writable? (make-event-monitor an-output-port ))
(define messages? (make-event-monitor a-mailbox ))
(define got-mutex? (make-event-monitor a-mutex ))
(event-case
((readable?) (read-line (event-monitor-source readable?)))
((writable?) (display "Output" (event-monitor-source writable?)))
((messages?) (get-message-from a-mailbox) )
((got-mutex?) (do-protected-stuff ... ) )
(num-or-time-spec (do-stuff-on-timeout ... ) )
)
What (event-case ... ) would do is block until one of the events happened,
then execute the code for that case *after* locking the resource
involved, and then unlocking the resource when exiting the (event-case).
So the 'event-monitor' API would look something like:
(make-event-monitor [ port | mutex | thread | signal | ? ] )
Creates a monitor that indicates a state change on the object/situation
being monitored. (Note that calling:
(define a (make-event-monitor (current-input)))
(define b (make-event-monitor (current-input)))
may result in 'a' and 'b' have the exact same value. Having multiple
monitors on a single event source may still boil down,
implementation-wise, to a single internal one.)
(event-monitor-source a-monitor)
Returns the instance of the object being monitored. If the current
thread does not have a lock/exclusive access to the event-monitor,
this function might need to block -- not sure about that yet.
(event-monitor-lock! a-monitor [timeout [thread]])
Locks the event-monitor such that no other thread has access to
the event-monitor. No other thread will receive any events or
status updates about the event-monitor or the object being
monitored while the event-monitor is locked.
(event-monitor-unlock! a-monitor)
Unlocks the event-monitor, allowing any other threads to receive
event notifications. (*Probably* do not need the mutex-unlock!
syntax of having a condition variable. That is fairly implicit
in the idea of dealing with events.)
(event-monitor-status a-monitor)
Returns status information about the monitor -- locked, unlocked,
events ready to be deal with, or an error state. The error state
could be a result of the event source, the object being monitored
becoming invalid. (i.e. port closed, thread termianted, etc.)
(event-monitor? a-monitor)
Returns #t if a-monitor is an event-monitor
(event-monitor-ready? a-monitor)
Returns #t iff there are events pending.
(event-monitor-number-pending a-monitor)
Returns the number of unhandled events. For some objects being
monitored, this may never be greater than 1. Not entirely sure
if this will be useful.
So, say:
(define a-monitor (make-event-monitor an-input-port))
;; Block forever until a-monitor has an event.
(event-monitor-lock! a-monitor)
(read (event-monitor-source a-monitor))
(event-monitor-unlock! a-monitor)
;; Check to see if there's anything to be read, but don't block
(when (event-monitor-lock! a-monitor 0)
(read (event-monitor-source a-monitor))
(event-monitor-unlock! a-monitor) )
;; Only wait so long for input
(if (event-monitor-lock! a-monitor 0)
(begin
(read (event-monitor-source a-monitor))
(event-monitor-unlock! a-monitor) )
(print "Timed out waiting for input!"))
So, on the face of it, not much more than some syntactic wrapping of
mutexes around current objects, with the mutexes becoming available
or ready to be locked only when there is some pending event.
So, now to:
(event-case [#t | #f ] ;; automatically lock
((a-monitor ... ) (expressions ...))
...
(int-or-timespec (expressions...))
)
What event-case would do is wait until one of the event-monitors
listed had events pending, or until the timeout (if present)
expired.
If the first argument to event-case is a boolean, this determines
whether or not the event-case will automatically use an implicit
(event-monitor-lock!) and (event-monitor-unlock!) around the
executed expressions. That is, the following two would be *almost*
equivalent:
(event-case #t
((a-monitor)
(do-stuff-with a-monitor)))
(event-case #f
((a-monitor)
(event-monitor-lock! a-monitor)
(do-stuff-with a-monitor)
(event-monitor-unlock! a-monitor)))
The difference is in the latter there might (depending on the
implementation) be a window of time where if the thread quantum
expired just so, the (event-monitor-lock!) statement might
block again if another thread locked the event-monitor between
the event-case firing and the event-monitor-lock! locking
the event monitor. (Note that *is* a problem, race condition-wise
because the thread would no longer be blocking on a timeout or
any other event, but solely on acquiring a-monitor, except it
may acquire it only after another thread has processed the event,
leaving the code with no event to process, and potentially missing
any other events that have been happening.)
The non-automatically locking version is needed if the expression
called when an event happens is not going to return, or otherwise
is going to manage the locking itself. The automatically-locking
version basically assures an atomicity between the event occuring
and the event-case statement starting an expression, and the
expression having exclusive access to the monitored resource.
In the case of:
(define a-mutex (make-mutex))
(define a-monitor (make-event-monitor a-mutex))
(event-case ((a-monitor) (do-stuff)))
is pretty much the same as:
(define a-mutex (make-mutex))
(mutex-lock! a-mutex) (do-stuff) (mutex-unlock! a-mutex)
and from an implementation point of view, (event-monitor-lock! a-monitor)
should, if a-monitor is created around a mutex, lock the underlying
mutex as well as an atomic operation. Though combining 'direct'
operations with the monitored objects simultaneously with the
event-monitor wrapped ones is probably not a good idea. The whole
point of developing an API/framework like this is to manage the
processing of events and thread interactions nicely.
A version of (event-case) that is more amiable to a dynamic list of
events to wait on would probably also be needed. Something like
(event-case-list [#t | #f]
(list (list (list a-monitor) (lambda () (do-stuff)))))
That might actually be the underlying implementation, of course.
This could collapse down into compatibility with PLT with something
like:
(letrec ([a-read-monitor (make-event-monitor an-input-port)]
[a-write-monitor (make-event-monitor an-output-port)])
(event-case
((a-read-monitor) (read (event-monitor-source a-read-monitor)))
((a-write-monitor) (event-monitor-source a-write-monitor))
(timeout-value #f)))
would be a reasonable implementation/equivalent of the PLT:
(object-wait-multiple timeout-value an-input-port an-output-port)
Except that the (event-case) style lets the program decide exactly
how to process the data from the input-ports instead of having the
implementation read an arbitrary block of it and passing that back.
Most of this could probably be cobbled together ontop of SRFI-18
at least in prototype form. The only implementation tricky bit is
linking things like i/o readiness the mutex-style locking/unlocking.
So, comments anyone?
Other examples/thoughts:
(event-case
((monitor-1 monitor-2)
(if (did-thread-lock? monitor-1)
(do-stuff monitor-1)
(do-stuff monitor-2) )))
More robust:
(event-case
((a-monitor)
(case (event-monitor-status a-monitor)
(('locked) (do-stuff-with a-monitor))
(('error) (deal-with-error-on a-monitor)))))
--
Kim address@hidden http://solluna.org/
"Nothing in the world is more dangerous than a sincere ignorance
and conscientious stupidity" - Martin Luther King, Jr.