missionary.core/sem
Usage
(sem)
- define a new binary semaphore.(sem n)
- define a new counting semaphore withn
initial permits.s
- used as a task, acquire a permit from semaphores
.(s)
- release a permit to semaphores
, returnnil
.
Description
A port constructor defining a semaphore. A semaphore maintains a set of permits which can be acquired and released. Acquisition is an asynchronous effect completing with nil
when the permit is made available.
Note : holding
is a simple wrapper around ?
and try
/finally
that can be used to make sure every acquired permit is eventually released.
Examples
Dining philosophers with forks represented as binary semaphores and philosophers as sp
blocks. This is the resource hierarchy solution, nested semaphores are acquired in a consistent order to prevent deadlocks.
(require '[missionary.core :as m]) (defn phil [id f1 f2] (m/sp (loop [] (prn id :thinking) (m/? (m/sleep 50)) (m/holding f1 (m/holding f2 (prn id :eating) (m/? (m/sleep 70)))) (recur)))) (def forks (vec (repeatedly 5 m/sem))) (def dinner (m/join (constantly nil) (phil 'descartes (forks 0) (forks 1)) (phil 'hume (forks 1) (forks 2)) (phil 'plato (forks 2) (forks 3)) (phil 'nietzsche (forks 3) (forks 4)) (phil 'kant (forks 0) (forks 4)))) (def main (m/timeout dinner 300)) (def ps (main #(prn :success %) #(prn :failure %))) descartes :thinking hume :thinking plato :thinking nietzsche :thinking kant :thinking descartes :eating plato :eating kant :eating descartes :thinking hume :eating plato :thinking hume :thinking nietzsche :eating descartes :eating kant :thinking descartes :thinking kant :eating plato :eating nietzsche :thinking :success nil
Synchronicity
- semaphore definition is a synchronous effect.
- semaphore permit releasing is a synchronous effect.
- when a semaphore has available permits, acquisition completion is synchronous with acquisition spawn.
- when a semaphore has no more permits, acquisition completion is synchronous with the releasing of delivered permit.