This semaphore is based on a condition-variable
and two monotonically increasing counters acquired
and released
.
This way, it can support a performance counter and interacts well with threads.
This package offers optional integration with emacs-promise in semaphore-promise
, that can function as a kind of bounded thread-pool, but for promises.
(require 'semaphore)
defun semaphore-create (count &optional name)
Create a semaphore with count initially available slots (can be negative)
Use semaphore-acquire, to borrow a slot, and semaphore-release, to return it.
cl-defmethod semaphore-acquire ((this semaphore))
Borrow a slot from the semaphore
If there are no slots available, semaphore-acquire will block your thread, until there are. Don't do this on the main thread, or risk emacs freezing dead.
cl-defmethod semaphore-release ((this semaphore))
Return a slot to the semaphore
This will unblock one blocked acquirer, if any.
cl-defmethod semaphore-name ((this semaphore))
Given name of the semaphore
cl-defmethod semaphore-times-acquired ((this semaphore))
An absolute count of times acquired over lifetime of the semaphore
cl-defmethod semaphore-mutex ((this semaphore))
Access to the semaphore's mutex. Be careful.
cl-defmethod semaphore-available ((this semaphore))
Get a current count of available slots
Mostly for logging purposes.
If you want to base any decision in the program on available count, you need to have the semaphore-available call and following action synchronized on the semaphore mutex.
Not recommended, unless you have a pressing need and a convincing story about preventing deadlocks.
(require 'semaphore-promise)
defun semaphore-promise-gated (semaphore handler)
Create a promise based on handler, like promise-new, but acquire and release the semaphore around resolving the handler (+ result promises).
This is useful for controlling parallelism in asynchronous work flows: e.g. if you want to fetch 10000 files, but only 7 at a time:
(let ((s (semaphore-create 7)))
(promise-all
(mapcar
(lambda (url)
(semaphore-promise-gated s
(lambda (resolve _)
(funcall resolve (http-get url))))) ;; assuming http-get returns another promise
url-list))
cask install
cask exec ert-runner
When trying to run multithreaded elisp in batch mode, run emacs as
foreground daemon, with --fg-daemon=<name>
. Call (kill-emacs 0)
at
the end of the script. This way, emacs will keep running your threads,
until you terminate it explicitly.
(defun main ()
(make-thread
(lambda ()
(message "Finished, exiting")
(kill-emacs 0))))
#!/bin/sh
exec emacs --fg-daemon=worker --quick -l worker.el -f main "$@"