I was writing some code using semaphores and realized that I wasn't sure I completely understand them.
Suppose I have something like this:
(define sema (make-semaphore 1)) (define user->id (make-hash (list (cons 'alice 1) (cons 'bill 2)))) (define id->user (make-hash (list (cons 1 'alice) (cons 2 'bill)))) (define (get-name id) (hash-ref id->user id)) (define (get-id name) (hash-ref user->id name)) (define (add-user! name id) (call-with-semaphore sema (thunk (hash-set! user->id name id) (hash-set! id->user id name)))) (add-user! 'charlie 3) (get-name 2) (get-id 'alice)
My understanding is that:
A. Racket is single-threaded and implements concurrency by periodically pausing the current thread and running a different one.
B. The semaphore ensures that if multiple threads want to call
add-user! then they will take turns, thereby ensuring that the two hashes are always consistent.
The things I'm not sure about:
- Do I need to worry about
get-nameseeing an inconsistent state if it's running in thread A and the
add-user!is happening in thread B?
- How exactly does this work? Does
call-with-semaphorepause all other threads (or, rather, disable thread switching) while it finishes the protected block?
- If the answer to 1 is yes then I believe the solution is to use the semaphore in both the accessors and mutators, while being careful to avoid deadlock. Is this right?
- If the answer to 2 is yes then does the time spent in the protected block count against the duration of a
sync/timeoutin another thread?