newLisp’s Cilk API simplifies the task for forking new processes and retrieving the results of child processes’ calculations a breeze. Some patterns remain complex, particularly when dealing with shared state or managing access to resources. To formalize some of the more common patterns of usage with the Cilk API, semaphores, and shared memory, I have written a multiprocessing library for newLisp.
The module came out of my original locks module, which contained only the Lock and RLock classes. The new library contains the following classes:
- Semaphore: simplifies use of semaphores
- Shared: simplifies use of shared memory
- Synchronized: shared memory made safe for use between processes
- Lock: a binary semaphore, or mutex
- RLock: a recursive Lock
- Event: a simple mechanism to signal multiple processes
- Pipe: simplifies use of pipes
- Channel: a two-way communications channel made with pipes
- Queue: a synchronized first in, first out class
MP additionally includes various utilities to deal with common tasks:
- get-pid: gets the current processes’ pid
- with-lock-held: a macro that evaluates its body while holding a lock
- wait: waits for a condition with variable length polling to reduce processor load
- map and iter: versions of map and dolist evaluated asynchronously using the Cilk API
Perhaps the most useful function in the module is with-lock-held, which simplifies the most common locking case: assuring that a block of code is executed atomically.
(setf lock (RLock)) (with-lock-held lock (access-restricted-resource))
In this case, no more than one process can evaluate
(access-restricted-resource) at a time.