On this page:
Evt
Evt.always
Evt.never
Evt.sync
Evt.wrap
Evt.Wrap  Return
Evt.Wrap  Return.no_  break
Evt.Wrap  Return.tail
Evt.replace
Evt.guard
Evt.nack_  guard
Evt.poll_  guard
Evt.choice
Evt.alarm
Evt.system_  idle
Evt.handle
Evt.from_  handle
Progress  Evt
Commit  Evt
Synchronizable
Synchronizable.as_  evt
0.45+9.2.0.2

15.4 Synchronizable Events🔗ℹ

A synchronizable event is an object that can be used with Evt.sync to wait until it is ready for synchronization. Synchronizing a ready event may have a side effect and an associated value. For example, synchronizing on a Semaphore is the same as using Semaphore.wait, so it decrements the semaphore’s count.

annotation

Evt

An annotation that recognizes synchronizable events, which include threads, semaphores, objects returned by methods like TCPListener.accept_evt, and objects that implement the Synchronizable interface.

The Evt annotation interface-like in the sense that every Evt supports the Evt.sync method.

value

def Evt.always :: Evt

 

value

def Evt.never :: Evt

The Evt.always synchronizable event is always ready for synchronization, and its synchronization result is itself.

The Evt.never synchronizable event is never ready for synchronization.

method

method (evt :: Evt).sync(

  ~timeout: timeout_secs :: maybe(NonnegReal) = #false,

  ~enable_break: enable_break :: Any.to_boolean = #false,

  evt :: Evt,

  ...

) :: Any

Blocks until at least one of the evts is ready for synchronization, and returns the ready result. If multiple evts become ready before one is selected, one of the ready evts is selected at random.

> Evt.always.sync()

Evt.always

> Evt.never.sync(~timeout: 0)

#false

> Evt.never.sync(Evt.always)

Evt.always

method

method (evt :: Evt).wrap(

  ~return: return :: Evt.WrapReturn = #'no_break,

  wrapf :: Function

) :: Evt

 

enumeration

enum Evt.WrapReturn

| no_break

| tail

Creates an Evt that is ready for synchronization when evt is ready for synchronization, but whose synchronization result is determined by applying wrapf to the synchronization result of evt. The number of arguments accepted by wrapf must match the number of values for the synchronization result of evt.

> Evt.always.wrap(fun (_): "done").sync()

"done"

> Evt.always.wrap(fun (_): values("also", "done")).sync()

"also"

"done"

> Evt.always.wrap(fun (_): 1).wrap(fun (v): v+1).sync()

2

> Evt.always.wrap(fun (_): values(1, 2)).wrap(fun (u, v): u+v).sync()

3

If return is #'no_break, then wrapf is called with breaks (in the sense of Thread.break) disabled.

If return is #'tail, then wrapf is called in tail position with respect to a synchronization request via Evt.sync. When the Evt produced by Evt.wrap is wrapped by another Evt.wrap with #'no_break, however, this tail-call behavior is disabled.

> // loop runs in contsant space

  recur loop(i = 0):

    if i == 10_000

    | "done"

    | Evt.always.wrap(fun (_): loop(i+1), ~return: #'tail).sync()

"done"

method

method (evt :: Evt).replace(

  replacef :: Function

) :: Evt

Creates an Evt evt_result that is calls replacef when evt is ready for synchronization. If replacef returns an Evt evt_new, then evt_result is ready for synchronization when evt_new is ready, and evt_new provides the synchronization result; if replacef results any other result, then evt_result becomes ready immediately, and evt_result itself is the synchronization result.

The number of arguments accepted by replacef must match the number of values for the synchronization result of evt.

> Evt.always.replace(fun (_): Evt.always.wrap(fun (_): "replaced")).sync()

"replaced"

> Evt.always.replace(fun (_): "done").sync()

ReplaceEvt(...)

function

fun Evt.guard(make :: () -> Evt) :: Evt

Creates a value that behaves as an Evt, but that is more precisely characterized as an event generator.

The result guard of Evt.guard generates an event when guard is used with Evt.sync (or whenever it is part of a Evt.choice event used with Evt.sync, etc.), where the generated event is the result of calling make. The make function may be called by Evt.sync at most once for a given call to Evt.sync, but make may not be called if a ready event is chosen before guard is even considered.

> def mutable counter = 0

> def g = Evt.guard(fun ():

                      let c = counter

                      counter := counter + 1

                      Evt.always.wrap(fun (_): c))

> g.sync()

0

> g.sync()

1

function

fun Evt.nack_guard(make :: (Evt) -> Evt) :: Evt

Like Evt.guard, but make is call with an event nack that becomes ready for synchronization that the point where the result or Evt.nack_guard is not chosen in a synchronization.

> def mutable state = 0

> def g = Evt.nack_guard(fun (nack :: Evt):

                           state := state + 1

                           thread:

                             nack.sync()

                             state := state - 1

                           Evt.never)

> g.sync(~timeout: 1)

#false

> Evt.system_idle.sync()

Evt.system_idle

> state

0

> // might try `g`, or might immediately succeed with `Evt.always`

  g.sync(Evt.always)

Evt.always

> Evt.system_idle.sync()

Evt.system_idle

> state

0

function

fun Evt.poll_guard(make :: (Boolean) -> Evt) :: Evt

Like Evt.guard, but make is call with a boolean indicating whether the resulting Evt will be used in a polling synchronization (i.e., with a zero timeout), as opposed to a blocking synchronization.

> def g = Evt.poll_guard(fun (is_poll):

                           Evt.always.wrap(fun (_): to_string(is_poll)))

> g.sync(~timeout: 0)

"#true"

> g.sync()

"#false"

function

fun Evt.choice(evt :: Evt, ...) :: Evt

Creates an Evt that is ready whenever one of the given evts is ready, and with that evt’s synchronization result.

In other words, supplying the result of Evt.choice to Evt.sync is the same as supplying all of the individual evts to the same Evt.sync.

> // result could be either:

  Evt.choice(Evt.always.wrap(fun (_): "left"),

             Evt.always.wrap(fun (_): "right"))

    .sync()

"right"

function

fun Evt.alarm(

  msecs :: Real,

  ~monotonic: monotonic :: Any.to_boolean = #false

) :: Evt

Creates a synchronizable event is ready when system.milliseconds() would return a value of msecs or more if monotonic is #false, or when measure.real_milliseconds() would return a value of msecs or more if monotonic is true. The returned event’s synchronization result is itself.

> Evt.alarm(system.milliseconds() + 1).sync()

Evt.alarm(...)

> Evt.alarm(measure.real_milliseconds() + 1, ~monotonic: #true).sync()

Evt.alarm(...)

The Evt.system_idle synchronizable event becomes ready when no thread can run otherwise. In other words, all threads must be suspended or blocked on events with timeouts that have not yet expired. Its synchronization result is #void.

The Evt.system_idle event is intended primarily for use in tests where all concurrency is known.

property

property (evt :: Evt).handle

 

function

fun Evt.from_handle(handle) :: Evt

The Evt.handle property accesses a synchronizable event’s underlying Racket representation. The Evt.from_handle function constructs a Rhombus synchronizable event from a Racket synchronizable event.

annotation

ProgressEvt

 

annotation

CommitEvt

A ProgressEvt is produced by Port.Input.Progress.evt to detect and synchronize reads from input ports that support progress events.

A CommitEvt is used in combination with a ProgressEvt for Port.Input.Progress.commit. A CommitEvt is either a Semaphore, channel-put event, channel, semaphore-peek event, Evt.always, or Evt.never.

An interface that a class can implement to make instances of the class usable as an Evt. When a class that implements Synchronizable is used with Evt.sync, the Synchronizable.as_evt method is called, and the result is used in the synchronization.

The interface has a single abstract method:

method

method (obj :: Synchronizable).as_evt() :: Evt

Obtains a synchronizable event for a Synchronizable object.