Skip to content

Add SEMAPHORE-COUNT #70

@phoe

Description

@phoe

In the Lisp Koans, we have a threading koan that, when solved, is supposed to look similar to the following:

(defvar *semaphore* (bt:make-semaphore))

(defun signal-our-semaphore ()
  (bt:signal-semaphore *semaphore*))

(defun wait-on-our-semaphore ()
  (bt:wait-on-semaphore *semaphore* :timeout 0.1))

(define-test semaphore
  (bt:join-thread (bt:make-thread #'signal-our-semaphore))
  (bt:join-thread (bt:make-thread #'signal-our-semaphore))
  (bt:join-thread (bt:make-thread #'signal-our-semaphore))
  (assert-equal 2 (bt:join-thread (bt:make-thread #'wait-on-our-semaphore)))
  (assert-equal 1 (bt:join-thread (bt:make-thread #'wait-on-our-semaphore)))
  (assert-equal 0 (bt:join-thread (bt:make-thread #'wait-on-our-semaphore)))
  (assert-equal nil (bt:join-thread (bt:make-thread #'wait-on-our-semaphore)))
  (assert-equal nil (bt:join-thread (bt:make-thread #'wait-on-our-semaphore))))

The previous version of koan also had semaphore-count calls to assert the correct behaviour of the threading system. It looked similar to the following:

(define-test semaphore
  (assert-equal 0 (sb-thread:semaphore-count *semaphore*))
  (bt:join-thread (bt:make-thread #'signal-our-semaphore))
  (assert-equal 1 (sb-thread:semaphore-count *semaphore*))
  (bt:join-thread (bt:make-thread #'signal-our-semaphore))
  (bt:join-thread (bt:make-thread #'signal-our-semaphore))
  (assert-equal 3 (sb-thread:semaphore-count *semaphore*))
  (assert-equal 2 (bt:join-thread (bt:make-thread #'wait-on-our-semaphore)))
  (assert-equal 1 (bt:join-thread (bt:make-thread #'wait-on-our-semaphore)))
  (assert-equal 1 (sb-thread:semaphore-count *semaphore*))
  (assert-equal 0 (bt:join-thread (bt:make-thread #'wait-on-our-semaphore)))
  (assert-equal 0 (sb-thread:semaphore-count *semaphore*))
  (assert-equal nil (bt:join-thread (bt:make-thread #'wait-on-our-semaphore)))
  (assert-equal 0 (sb-thread:semaphore-count *semaphore*)))

The obvious missing thing is the lack of bt:semaphore-count in Bordeaux Threads. The Lisp Koans rolled out its own semaphore object based on the bordeaux-threads provided one to work around this issue, which I have removed in google/lisp-koans#111 to clean up the code.


A comment from @Slids at Clozure/ccl#308 says:

Stellian doesn't want a semaphore-count function, already asked.
Arguably it's not very informative, the count could change immediately after or
before retrieved and and since it's a condition variable you have no great
use of it for a CAS utility...

I agree that such a function is nigh useless in actual production code, since the simple act of returning a semaphore value causes it to immediately become stale. Therefore depending on the value of this function in multithreaded code single-handedly enables race conditions to happen.

At the same time, the Lisp Koans have come up with, IMO, a somewhat surprising valid use case for that function - which is, introspecting the semaphore state in a controlled, single-threaded unit test case, where the semaphore is only available to the thread executing the test case. (The Koans spawn other threads via bt:make-thread and then wait for them to bt:join-thread, which is single-threaded enough, since the spawning thread is effectively paused at the time.) I imagine that other unit tests that single-threadedly run chunks of a multithreaded code to verify its functioning might also want to benefit from numeric assertions on the semaphore count during any given moment in the test execution.

Technically speaking, SBCL exports a function for accessing the count of a semaphore, the not-yet-merged ECL 20.4.24 semaphores also export one; it is also trivial to extract it from the current portable implementation that BT has for other CL implementations. The only missing implementation is CCL with a ticket at Clozure/ccl#308, but it seems that adding a ccl:semaphore-count that works similarly should be easy and therefore wouldn't be a blocker for adding this function to BT. Therefore, I think it can be said that there is a semaphore-count API that a portability library can wrap over.

Therefore, my question is: since, IMO, a valid use case for it has appeared, would you consider adding BT:SEMAPHORE-COUNT with the aforementioned warning for people who dare use it in production code?

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions