Don't use a condition variable for blocking the system profiler thread, since

that causes a locking order reversal (condition variable lock <-> system
profiler lock) and thus a potential deadlock. Instead we use the thread
blocking API directly. Fixes #5229.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@35003 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2010-01-11 02:33:10 +00:00
parent 83de0ccdec
commit 8fc761bd7a
+20 -20
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright 2009, Ingo Weinhold, [email protected]. * Copyright 2009-2010, Ingo Weinhold, [email protected].
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
*/ */
@@ -200,8 +200,7 @@ private:
bool fIONotificationsEnabled; bool fIONotificationsEnabled;
bool fSchedulerNotificationsRequested; bool fSchedulerNotificationsRequested;
bool fWaitObjectNotificationsRequested; bool fWaitObjectNotificationsRequested;
ConditionVariable fProfilerWaitCondition; struct thread* volatile fWaitingProfilerThread;
bool fProfilerWaiting;
bool fProfilingActive; bool fProfilingActive;
bool fReentered[B_MAX_CPU_COUNT]; bool fReentered[B_MAX_CPU_COUNT];
CPUProfileData fCPUData[B_MAX_CPU_COUNT]; CPUProfileData fCPUData[B_MAX_CPU_COUNT];
@@ -218,11 +217,11 @@ inline void
SystemProfiler::_MaybeNotifyProfilerThreadLocked() SystemProfiler::_MaybeNotifyProfilerThreadLocked()
{ {
// If the buffer is full enough, notify the profiler. // If the buffer is full enough, notify the profiler.
if (fProfilerWaiting && fBufferSize > fBufferCapacity / 2) { if (fWaitingProfilerThread != NULL && fBufferSize > fBufferCapacity / 2) {
fProfilerWaiting = false;
int cpu = smp_get_current_cpu(); int cpu = smp_get_current_cpu();
fReentered[cpu] = true; fReentered[cpu] = true;
fProfilerWaitCondition.NotifyOne(true); thread_unblock_locked(fWaitingProfilerThread, B_OK);
fWaitingProfilerThread = NULL;
fReentered[cpu] = false; fReentered[cpu] = false;
} }
} }
@@ -231,7 +230,7 @@ SystemProfiler::_MaybeNotifyProfilerThreadLocked()
inline void inline void
SystemProfiler::_MaybeNotifyProfilerThread() SystemProfiler::_MaybeNotifyProfilerThread()
{ {
if (!fProfilerWaiting) if (fWaitingProfilerThread == NULL)
return; return;
InterruptsSpinLocker threadsLocker(gThreadSpinlock); InterruptsSpinLocker threadsLocker(gThreadSpinlock);
@@ -267,7 +266,7 @@ SystemProfiler::SystemProfiler(team_id team, const area_info& userAreaInfo,
fIONotificationsEnabled(false), fIONotificationsEnabled(false),
fSchedulerNotificationsRequested(false), fSchedulerNotificationsRequested(false),
fWaitObjectNotificationsRequested(false), fWaitObjectNotificationsRequested(false),
fProfilerWaiting(false), fWaitingProfilerThread(NULL),
fWaitObjectBuffer(NULL), fWaitObjectBuffer(NULL),
fWaitObjectCount(0), fWaitObjectCount(0),
fUsedWaitObjects(), fUsedWaitObjects(),
@@ -275,7 +274,6 @@ SystemProfiler::SystemProfiler(team_id team, const area_info& userAreaInfo,
fWaitObjectTable() fWaitObjectTable()
{ {
B_INITIALIZE_SPINLOCK(&fLock); B_INITIALIZE_SPINLOCK(&fLock);
fProfilerWaitCondition.Init(this, "system profiler");
memset(fReentered, 0, sizeof(fReentered)); memset(fReentered, 0, sizeof(fReentered));
@@ -296,9 +294,9 @@ SystemProfiler::~SystemProfiler()
// Wake up the user thread, if it is waiting, and mark profiling // Wake up the user thread, if it is waiting, and mark profiling
// inactive. // inactive.
InterruptsSpinLocker locker(fLock); InterruptsSpinLocker locker(fLock);
if (fProfilerWaiting) { if (fWaitingProfilerThread != NULL) {
fProfilerWaiting = false; thread_unblock_locked(fWaitingProfilerThread, B_OK);
fProfilerWaitCondition.NotifyAll(); fWaitingProfilerThread = NULL;
} }
fProfilingActive = false; fProfilingActive = false;
locker.Unlock(); locker.Unlock();
@@ -527,8 +525,10 @@ SystemProfiler::NextBuffer(size_t bytesRead, uint64* _droppedEvents)
{ {
InterruptsSpinLocker locker(fLock); InterruptsSpinLocker locker(fLock);
if (fProfilerWaiting || !fProfilingActive || bytesRead > fBufferSize) if (fWaitingProfilerThread != NULL || !fProfilingActive
|| bytesRead > fBufferSize) {
return B_BAD_VALUE; return B_BAD_VALUE;
}
fBufferSize -= bytesRead; fBufferSize -= bytesRead;
fBufferStart += bytesRead; fBufferStart += bytesRead;
@@ -543,24 +543,24 @@ SystemProfiler::NextBuffer(size_t bytesRead, uint64* _droppedEvents)
// Wait until the buffer gets too full or an error or a timeout occurs. // Wait until the buffer gets too full or an error or a timeout occurs.
while (true) { while (true) {
ConditionVariableEntry waitEntry; struct thread* thread = thread_get_current_thread();
fProfilerWaitCondition.Add(&waitEntry); fWaitingProfilerThread = thread;
fProfilerWaiting = true; thread_prepare_to_block(thread, B_CAN_INTERRUPT,
THREAD_BLOCK_TYPE_OTHER, "system profiler buffer");
locker.Unlock(); locker.Unlock();
status_t error = waitEntry.Wait( status_t error = thread_block_with_timeout(B_RELATIVE_TIMEOUT, 1000000);
B_CAN_INTERRUPT | B_RELATIVE_TIMEOUT, 1000000);
locker.Lock(); locker.Lock();
if (error == B_OK) { if (error == B_OK) {
// the caller has unset fProfilerWaiting for us // the caller has unset fWaitingProfilerThread for us
break; break;
} }
fProfilerWaiting = false; fWaitingProfilerThread = NULL;
if (error != B_TIMED_OUT) if (error != B_TIMED_OUT)
return error; return error;