kernel: Workaround for double lock of spinlock in user timers.

The thread that is being [un]scheduled already has its time_lock locked
in {stop|continue}_cpu_timers(). When updating the TeamTimeUserTimer,
the team is asked for its cpu time. Team::CPUTime() then iterates the
threads of the team and locks the time_lock of the thread again.

This workaround passes a possibly locked thread through the relevant
functions so Team::CPUTime() can decide whether or not a thread it
iterates needs to be locked or not.

This works around #11032 and its duplicates #11314 and #11344.
This commit is contained in:
Michael Lotz
2014-10-29 00:25:37 +01:00
parent 4ed39e6a62
commit 52d500e5b4
4 changed files with 18 additions and 11 deletions
+4 -2
View File
@@ -117,14 +117,16 @@ struct TeamTimeUserTimer : public UserTimer {
void Deactivate(); void Deactivate();
void Update(Thread* unscheduledThread); void Update(Thread* unscheduledThread,
Thread* lockedThread = NULL);
void TimeWarped(bigtime_t changedBy); void TimeWarped(bigtime_t changedBy);
protected: protected:
virtual void HandleTimer(); virtual void HandleTimer();
private: private:
void _Update(bool unscheduling); void _Update(bool unscheduling,
Thread* lockedThread = NULL);
private: private:
team_id fTeamID; team_id fTeamID;
+2 -1
View File
@@ -388,7 +388,8 @@ public:
inline TeamUserTimeUserTimerList::ConstIterator inline TeamUserTimeUserTimerList::ConstIterator
UserTimeUserTimerIterator() const; UserTimeUserTimerIterator() const;
bigtime_t CPUTime(bool ignoreCurrentRun) const; bigtime_t CPUTime(bool ignoreCurrentRun,
Thread* lockedThread = NULL) const;
bigtime_t UserCPUTime() const; bigtime_t UserCPUTime() const;
private: private:
+6 -6
View File
@@ -625,7 +625,7 @@ TeamTimeUserTimer::Deactivate()
currently running and which is in the process of being unscheduled. currently running and which is in the process of being unscheduled.
*/ */
void void
TeamTimeUserTimer::Update(Thread* unscheduledThread) TeamTimeUserTimer::Update(Thread* unscheduledThread, Thread* lockedThread)
{ {
if (fTeam == NULL) if (fTeam == NULL)
return; return;
@@ -639,7 +639,7 @@ TeamTimeUserTimer::Update(Thread* unscheduledThread)
fRunningThreads++; fRunningThreads++;
} }
_Update(unscheduledThread != NULL); _Update(unscheduledThread != NULL, lockedThread);
} }
@@ -695,7 +695,7 @@ TeamTimeUserTimer::HandleTimer()
being unscheduled. being unscheduled.
*/ */
void void
TeamTimeUserTimer::_Update(bool unscheduling) TeamTimeUserTimer::_Update(bool unscheduling, Thread* lockedThread)
{ {
// unschedule the kernel timer, if scheduled // unschedule the kernel timer, if scheduled
if (fScheduled) if (fScheduled)
@@ -708,7 +708,7 @@ TeamTimeUserTimer::_Update(bool unscheduling)
} }
// There are still threads running. Reschedule the kernel timer. // There are still threads running. Reschedule the kernel timer.
bigtime_t now = fTeam->CPUTime(unscheduling); bigtime_t now = fTeam->CPUTime(unscheduling, lockedThread);
// If periodic, check whether the start time is too far in the past. // If periodic, check whether the start time is too far in the past.
if (fInterval > 0) if (fInterval > 0)
@@ -1560,7 +1560,7 @@ user_timer_stop_cpu_timers(Thread* thread, Thread* nextThread)
for (TeamTimeUserTimerList::ConstIterator it for (TeamTimeUserTimerList::ConstIterator it
= thread->team->CPUTimeUserTimerIterator(); = thread->team->CPUTimeUserTimerIterator();
TeamTimeUserTimer* timer = it.Next();) { TeamTimeUserTimer* timer = it.Next();) {
timer->Update(thread); timer->Update(thread, thread);
} }
} }
} }
@@ -1574,7 +1574,7 @@ user_timer_continue_cpu_timers(Thread* thread, Thread* previousThread)
for (TeamTimeUserTimerList::ConstIterator it for (TeamTimeUserTimerList::ConstIterator it
= thread->team->CPUTimeUserTimerIterator(); = thread->team->CPUTimeUserTimerIterator();
TeamTimeUserTimer* timer = it.Next();) { TeamTimeUserTimer* timer = it.Next();) {
timer->Update(NULL); timer->Update(NULL, thread);
} }
} }
+6 -2
View File
@@ -924,7 +924,7 @@ Team::DeactivateCPUTimeUserTimers()
\return The team's current total CPU time. \return The team's current total CPU time.
*/ */
bigtime_t bigtime_t
Team::CPUTime(bool ignoreCurrentRun) const Team::CPUTime(bool ignoreCurrentRun, Thread* lockedThread) const
{ {
bigtime_t time = cpu_clock_offset + dead_threads_kernel_time bigtime_t time = cpu_clock_offset + dead_threads_kernel_time
+ dead_threads_user_time; + dead_threads_user_time;
@@ -934,13 +934,17 @@ Team::CPUTime(bool ignoreCurrentRun) const
for (Thread* thread = thread_list; thread != NULL; for (Thread* thread = thread_list; thread != NULL;
thread = thread->team_next) { thread = thread->team_next) {
SpinLocker threadTimeLocker(thread->time_lock); bool alreadyLocked = thread == lockedThread;
SpinLocker threadTimeLocker(thread->time_lock, alreadyLocked);
time += thread->kernel_time + thread->user_time; time += thread->kernel_time + thread->user_time;
if (thread->last_time != 0) { if (thread->last_time != 0) {
if (!ignoreCurrentRun || thread != currentThread) if (!ignoreCurrentRun || thread != currentThread)
time += now - thread->last_time; time += now - thread->last_time;
} }
if (alreadyLocked)
threadTimeLocker.Detach();
} }
return time; return time;