Re: Why can signals only be sent from within locks?

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view

Re: Why can signals only be sent from within locks?

Brian Goetz
> For example, a producer-consumer can use a concurrent collection instead
> of locks. If you then want to signal the consumer from the producer
> once something has been produced, a lock must be obtained before
> signalling. What is the reason for needing this lock?

Invariably, you are signalling the other thread of a change in some
shared state.  Shared mutable state must be guarded by a lock.  Without
requiring the calling thread to acquire the lock that guards this shared
state, it opens the door to race conditions.

Consider a work queue.  You cannot take an element from an empty work
queue.  So if you arrive at the queue, and find it empty, you wait --
and you never wait if the queue is not empty.

How do you KNOW that the queue is empty?  You can only know this for
sure if you hold the lock.  Otherwise its state could change between the
time you checked and the time you waited.  Since all wait/notify
scenarios share this element, the lock and the condition queue work
together to make sure you don't make this mistake.

Concurrency-interest mailing list
[hidden email]
Reply | Threaded
Open this post in threaded view

Re: Why can signals only be sent from within locks?

David Holmes
Notwithstanding Brian's response as to why the shared state must be protected
by the lock, it isn't semantically essential to actually perform a signal
while holding the lock. In Java you must own the monitor before doing a
notify/notifyAll and ReentrantLock and its Condition emulate this behaviour.
This has some advantages: from the programming perspective it ensures you
don't forget to protect access to the shared state; from the implementation
perspective it means that the lock/monitor can be used to protect access to
the condition-queue/wait-set.

POSIX doesn't require this and allows you to signal without the lock being
held. Some people say this is an optimization, but that's generally only true
for implementations that don't do what is termed "wait-morphing" - this is
where a signal just transfers a thread from the condition queue to the
lock/mutex queue (rather than have a signal cause a thread to resume
execution and immediately attempt to acquire a mutex that is locked by the
thread that did the signal.)

If the signal is done after the lock is released there is more chance that
the signalling thread will be switched out between releasing the lock and
doing the signal (particularly in real-time systems). This makes the signals
somewhat "asynchronous" in nature. But provided you always wait in a loop,
testing the right condition, and you always use signal or signalAll
correctly, then this will only impact performance not correctness.

David Holmes
Concurrency-interest mailing list
[hidden email]