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 Update(Thread* unscheduledThread);
void Update(Thread* unscheduledThread,
Thread* lockedThread = NULL);
void TimeWarped(bigtime_t changedBy);
protected:
virtual void HandleTimer();
private:
void _Update(bool unscheduling);
void _Update(bool unscheduling,
Thread* lockedThread = NULL);
private:
team_id fTeamID;
+2 -1
View File
@@ -388,7 +388,8 @@ public:
inline TeamUserTimeUserTimerList::ConstIterator
UserTimeUserTimerIterator() const;
bigtime_t CPUTime(bool ignoreCurrentRun) const;
bigtime_t CPUTime(bool ignoreCurrentRun,
Thread* lockedThread = NULL) const;
bigtime_t UserCPUTime() const;
private:
+6 -6
View File
@@ -625,7 +625,7 @@ TeamTimeUserTimer::Deactivate()
currently running and which is in the process of being unscheduled.
*/
void
TeamTimeUserTimer::Update(Thread* unscheduledThread)
TeamTimeUserTimer::Update(Thread* unscheduledThread, Thread* lockedThread)
{
if (fTeam == NULL)
return;
@@ -639,7 +639,7 @@ TeamTimeUserTimer::Update(Thread* unscheduledThread)
fRunningThreads++;
}
_Update(unscheduledThread != NULL);
_Update(unscheduledThread != NULL, lockedThread);
}
@@ -695,7 +695,7 @@ TeamTimeUserTimer::HandleTimer()
being unscheduled.
*/
void
TeamTimeUserTimer::_Update(bool unscheduling)
TeamTimeUserTimer::_Update(bool unscheduling, Thread* lockedThread)
{
// unschedule the kernel timer, if scheduled
if (fScheduled)
@@ -708,7 +708,7 @@ TeamTimeUserTimer::_Update(bool unscheduling)
}
// 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 (fInterval > 0)
@@ -1560,7 +1560,7 @@ user_timer_stop_cpu_timers(Thread* thread, Thread* nextThread)
for (TeamTimeUserTimerList::ConstIterator it
= thread->team->CPUTimeUserTimerIterator();
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
= thread->team->CPUTimeUserTimerIterator();
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.
*/
bigtime_t
Team::CPUTime(bool ignoreCurrentRun) const
Team::CPUTime(bool ignoreCurrentRun, Thread* lockedThread) const
{
bigtime_t time = cpu_clock_offset + dead_threads_kernel_time
+ dead_threads_user_time;
@@ -934,13 +934,17 @@ Team::CPUTime(bool ignoreCurrentRun) const
for (Thread* thread = thread_list; thread != NULL;
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;
if (thread->last_time != 0) {
if (!ignoreCurrentRun || thread != currentThread)
time += now - thread->last_time;
}
if (alreadyLocked)
threadTimeLocker.Detach();
}
return time;