common lisp - How to utilize the SBCL provided semaphore against race conditions -
as far knowledge semaphores goes, semaphore used protect resources can counted , vulnerable race conditions. while reading sbcl documentation of semaphores not figure out, how use provided semaphore implementation protect resource.
a usual work flow, recall be:
a process wants retrieve of semaphore protected data (which sake of example trivial queue). semaphore counter 0, process waits
another process puts in queue , semaphore incremented, signal sent waiting processes
given possibility of interleaving, 1 has protect of resource accesses might not in order, or linear order @ all. therefore e.g. java interprets each class implicit monitor , provides syncronized
keyword programmer can define protected area can accessed 1 process @ time.
how emulate functionality in common-lisp, pretty sure current code thread safe without semaphore, semaphore has no clue code protect.
;;the package (defpackage :tests (:use :cl :sb-thread)) (in-package :tests) (defclass thread-queue () ((semaphore :initform (make-semaphore :name "thread-queue-semaphore")) (in-stack :initform nil) (out-stack :initform nil))) (defgeneric enqueue-* (queue element) (:documentation "adds element queue")) (defgeneric dequeue-* (queue &key timeout) (:documentation "removes , returns first element out")) (defmethod enqueue-* ((queue thread-queue) element) (signal-semaphore (slot-value queue 'semaphore)) (setf (slot-value queue 'in-stack) (push element (slot-value queue 'in-stack)))) (defmethod dequeue-* ((queue thread-queue) &key timeout) (wait-on-semaphore (slot-value queue 'semaphore) :timeout timeout) (when (= (length (slot-value queue 'out-stack)) 0) (setf (slot-value queue 'out-stack) (reverse (slot-value queue 'in-stack))) (setf (slot-value queue 'in-stack) nil)) (let ((first (car (slot-value queue 'out-stack)))) (setf (slot-value queue 'out-stack) (cdr (slot-value queue 'out-stack))) first)) (defparameter *test* (make-instance 'thread-queue)) (dequeue-* *test* :timeout 5) (enqueue-* *test* 42) (enqueue-* *test* 41) (enqueue-* *test* 40) (dequeue-* *test* :timeout 5) (dequeue-* *test* :timeout 5) (dequeue-* *test* :timeout 5) (dequeue-* *test* :timeout 5)
what have semaphore of count = 0, on consumers wait.
what need exclusive lock around access stacks (perhaps 1 each), or alternatively lock-free queue. if want/must use semaphores, binary semaphore can serve exclusive lock.
edit: in sbcl, have lock-free queues, might want use 1 of these instead of 2 stacks. possibility use atomic operations.
finally, if still doesn't suit you, use mutex, wrapping code acesses , updates stacks inside with-mutex
or with-recursive-lock
.
be sure use lock/mutex after waking semaphore, not around waiting semaphore, otherwise lose advantage semaphores give you, possibility of waking multiple waiters in row, instead of 1 @ time.
you can read these things in sbcl manual.
also, think work has been done rename every lock-like thing in sbcl lock
, according this blog post, don't know status of , states old names supported while.
you'll surely need semaphore of count = limit producers, not exceed queue limit.
in enqueue-*
, should signal semaphore after updating queue. setf
not needed, push
stores new head of list in place.
in dequeue-*
, length
lengthy function when applied lists, checking if list empty cheap null
or endp
. instead of taking car
, store cdr
, can use pop
, that.
Comments
Post a Comment