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:
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user