I think I see what you mean: There's no way to combine the completion
of an event plus saving its value as an atomic operation, except by
putting the synchronization in its own thread. But if you put the
synchronization in its own thread, then there's no way to prevent that
thread's synchronization when a consumer synchronization (i.e., one
that is waiting for the thread's result) picks a different event than
the one represented by the thread.
It's easy to make a complete+save combination atomic if it's built into
the scheduler. So, I can easily imagine adding a simpler primitive,
`once-evt`. The event OE created by `(once-evt E)` could save the first
result produced by E, but not attempt to have only a single wait on E.
That is, if OE1 is synchronized in multiple threads, then it would be
like synchronizing E in multiple threads, but only the first result
from E will be saved.
Unfortunately, `once-evt` isn't enough to implement `memoize-evt`. The
troublesome case is then thread T1 is synchronizing on OE1, T1 gets
suspended, and T2 starts waiting on OE1. In that case, you'd like T2 to
take over the wait, even if it means restarting E. You can detect that
T1 is suspended and have T2 start waiting on E, but there's no way to
cancel the wait of E in T1.
Building `memoize-evt` into the core doesn't the avoid the need to, at
some level, cancel T1's wait on E. I'll keep thinking about it, but it
looks like that would require deep changes to the scheduler.
Would the simpler `once-evt` work in your situation, or do you need the
guarantee that only one wait of E happens at a time?
At Wed, 28 Jan 2015 13:49:51 +0100, Jan Dvořák wrote:
> I would like to ask for another extension of the Racket's event handling
> system. A `memoize-evt` constructor with following semantics:
> Given a base event E_b, memoize-evt will produce a memoizing event E_m.
> Synchronizing on E_m from any number of threads will block until a
> single value is produced by E_b. This value is then stored inside the
> E_m. From that moment on, E_m will always be immediately ready for
> synchronization and produce the stored value in all waiting threads.
> The single-threaded implementation is a trivial guard-evt + replace-evt
> + (wrap-evt always-evt) combo, but for thread-safety a temporary thread
> would be needed to wait for the base event and the solution would have
> different semantics then rest of the event system.
> A lower-level approach would also be possible; create something along
> the lines of a dynamic-wind-evt that would, with some cleverness, allow
> for generic thread-safe events via locking. Or create a locked-wrap-evt
> constructor that will not be ready until it's handler finishes.
> Hoping that I am not being too bothersome,
> from Prague with thanks,
> Jan Dvorak
Racket Developers list: