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