Fixed serious race condition: If the thread waiting for a request was
interrupted, another thread closing the other end of the pipe could invoke thread_unblock() while the first thread already entered mutex_lock(). This would make the first thread think it successfully locked the mutex, without removing its (on-stack) wait entry from the mutex queue, thus leading to crashes. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@28195 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
9f3fce1eac
commit
d52718a5f8
@ -76,23 +76,34 @@ class RingBuffer {
|
|||||||
class ReadRequest : public DoublyLinkedListLinkImpl<ReadRequest> {
|
class ReadRequest : public DoublyLinkedListLinkImpl<ReadRequest> {
|
||||||
public:
|
public:
|
||||||
ReadRequest()
|
ReadRequest()
|
||||||
: fThread(find_thread(NULL))
|
:
|
||||||
|
fThread(thread_get_current_thread()),
|
||||||
|
fNotified(true)
|
||||||
{
|
{
|
||||||
|
B_INITIALIZE_SPINLOCK(&fLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetUnnotified() { fNotified = false; }
|
void SetNotified(bool notified)
|
||||||
|
{
|
||||||
|
InterruptsSpinLocker _(fLock);
|
||||||
|
fNotified = notified;
|
||||||
|
}
|
||||||
|
|
||||||
void Notify()
|
void Notify()
|
||||||
{
|
{
|
||||||
|
InterruptsSpinLocker _(fLock);
|
||||||
|
|
||||||
if (!fNotified) {
|
if (!fNotified) {
|
||||||
thread_unblock(fThread, B_OK);
|
SpinLocker threadLocker(gThreadSpinlock);
|
||||||
|
thread_unblock_locked(fThread, B_OK);
|
||||||
fNotified = true;
|
fNotified = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
thread_id fThread;
|
spinlock fLock;
|
||||||
bool fNotified;
|
struct thread* fThread;
|
||||||
|
volatile bool fNotified;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -464,15 +475,20 @@ Inode::RemoveReadRequest(ReadRequest &request)
|
|||||||
status_t
|
status_t
|
||||||
Inode::WaitForReadRequest(ReadRequest &request)
|
Inode::WaitForReadRequest(ReadRequest &request)
|
||||||
{
|
{
|
||||||
request.SetUnnotified();
|
|
||||||
|
|
||||||
// add the entry to wait on
|
// add the entry to wait on
|
||||||
thread_prepare_to_block(thread_get_current_thread(), B_CAN_INTERRUPT,
|
thread_prepare_to_block(thread_get_current_thread(), B_CAN_INTERRUPT,
|
||||||
THREAD_BLOCK_TYPE_OTHER, "fifo read request");
|
THREAD_BLOCK_TYPE_OTHER, "fifo read request");
|
||||||
|
|
||||||
|
request.SetNotified(false);
|
||||||
|
|
||||||
// wait
|
// wait
|
||||||
mutex_unlock(&fRequestLock);
|
mutex_unlock(&fRequestLock);
|
||||||
status_t status = thread_block();
|
status_t status = thread_block();
|
||||||
|
|
||||||
|
// Before going to lock again, we need to make sure no one tries to
|
||||||
|
// unblock us. Otherwise that would screw with mutex_lock().
|
||||||
|
request.SetNotified(true);
|
||||||
|
|
||||||
mutex_lock(&fRequestLock);
|
mutex_lock(&fRequestLock);
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
|
Loading…
Reference in New Issue
Block a user