kernel: Introduce Thread::time_lock and Team::time_lock

This commit is contained in:
Pawel Dziepak 2013-11-07 22:16:36 +01:00
parent 3519eb334a
commit 72addc62e0
5 changed files with 78 additions and 52 deletions

View File

@ -264,10 +264,11 @@ struct Team : TeamThreadIteratorEntry<team_id>, KernelReferenceable,
struct team_debug_info debug_info;
// protected by scheduler lock
// protected by time_lock
bigtime_t dead_threads_kernel_time;
bigtime_t dead_threads_user_time;
bigtime_t cpu_clock_offset;
spinlock time_lock;
// user group information; protected by fLock, the *_uid/*_gid fields also
// by the scheduler lock
@ -516,7 +517,7 @@ struct Thread : TeamThreadIteratorEntry<thread_id>, KernelReferenceable,
bigtime_t user_time; // protected by time_lock
bigtime_t kernel_time; // protected by time_lock
bigtime_t last_time; // protected by time_lock
bigtime_t cpu_clock_offset; // protected by scheduler lock
bigtime_t cpu_clock_offset; // protected by time_lock
void (*post_interrupt_callback)(void*);
void* post_interrupt_data;
@ -610,7 +611,7 @@ private:
UserTimerList fUserTimers; // protected by fLock
ThreadTimeUserTimerList fCPUTimeUserTimers;
// protected by scheduler lock
// protected by time_lock
};
@ -749,7 +750,7 @@ Thread::DequeuePendingSignal(sigset_t nonBlocked, Signal& buffer)
/*! Returns the thread's current total CPU time (kernel + user + offset).
The caller must hold the scheduler lock.
The caller must hold \c time_lock.
\param ignoreCurrentRun If \c true and the thread is currently running,
don't add the time since the last time \c last_time was updated. Should
@ -764,7 +765,7 @@ Thread::CPUTime(bool ignoreCurrentRun) const
// If currently running, also add the time since the last check, unless
// requested otherwise.
if (!ignoreCurrentRun && cpu != NULL)
if (!ignoreCurrentRun && last_time != 0)
time += system_time() - last_time;
return time;

View File

@ -137,8 +137,6 @@ UserTimer::~UserTimer()
Cancels the timer, if it is already scheduled, and optionally schedules it
with new parameters.
The caller must not hold the scheduler lock.
\param nextTime The time at which the timer should go off the next time. If
\c B_INFINITE_TIMEOUT, the timer will not be scheduled. Whether the
value is interpreted as absolute or relative time, depends on \c flags.
@ -160,8 +158,6 @@ UserTimer::~UserTimer()
/*! Cancels the timer, if it is scheduled.
The caller must not hold the scheduler lock.
*/
void
UserTimer::Cancel()
@ -176,8 +172,6 @@ UserTimer::Cancel()
uint32& _overrunCount)
Return information on the current timer.
The caller must not hold the scheduler lock.
\param _remainingTime Return variable that will be set to the microseconds
remaining to the time for which the timer was scheduled next before the
call. If it wasn't scheduled, the variable is set to
@ -513,7 +507,7 @@ TeamTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval,
uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval)
{
InterruptsWriteSequentialLocker locker(sUserTimerLock);
InterruptsSpinLocker schedulerLocker(gSchedulerLock);
SpinLocker timeLocker(fTeam != NULL ? &fTeam->time_lock : NULL);
// get the current time, but only if needed
bool nowValid = fTeam != NULL;
@ -547,9 +541,13 @@ TeamTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval,
// Get the team. If it doesn't exist anymore, just don't schedule the
// timer anymore.
fTeam = Team::Get(fTeamID);
if (fTeam == NULL)
Team* newTeam = Team::Get(fTeamID);
if (newTeam == NULL) {
fTeam = NULL;
return;
} else if (fTeam == NULL)
timeLocker.SetTo(newTeam->time_lock, false);
fTeam = newTeam;
fAbsolute = (flags & B_RELATIVE_TIMEOUT) == 0;
@ -576,7 +574,7 @@ TeamTimeUserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval,
count = acquire_read_seqlock(&sUserTimerLock);
if (fTeam != NULL) {
InterruptsSpinLocker schedulerLocker(gSchedulerLock);
InterruptsSpinLocker timeLocker(fTeam->time_lock);
_remainingTime = fNextTime - fTeam->CPUTime(false);
_interval = fInterval;
} else {
@ -591,7 +589,7 @@ TeamTimeUserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval,
/*! Deactivates the timer, if it is activated.
The caller must hold the scheduler lock and \c sUserTimerLock.
The caller must hold \c time_lock and \c sUserTimerLock.
*/
void
TeamTimeUserTimer::Deactivate()
@ -619,7 +617,7 @@ TeamTimeUserTimer::Deactivate()
was just set. Schedules a kernel timer for the remaining time, respectively
cancels it.
The caller must hold the scheduler lock and \c sUserTimerLock.
The caller must hold \c time_lock and \c sUserTimerLock.
\param unscheduledThread If not \c NULL, this is the thread that is
currently running and which is in the process of being unscheduled.
@ -646,7 +644,7 @@ TeamTimeUserTimer::Update(Thread* unscheduledThread)
/*! Called when the team's CPU time clock which this timer refers to has been
set.
The caller must hold the scheduler lock and \c sUserTimerLock.
The caller must hold \c time_lock and \c sUserTimerLock.
\param changedBy The value by which the clock has changed.
*/
@ -689,7 +687,7 @@ TeamTimeUserTimer::HandleTimer()
/*! Schedules/cancels the kernel timer as necessary.
\c fRunningThreads must be up-to-date.
The caller must hold the scheduler lock and \c sUserTimerLock.
The caller must hold \c time_lock and \c sUserTimerLock.
\param unscheduling \c true, when the current thread is in the process of
being unscheduled.
@ -757,7 +755,7 @@ TeamUserTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval,
uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval)
{
InterruptsWriteSequentialLocker locker(sUserTimerLock);
InterruptsSpinLocker schedulerLocker(gSchedulerLock);
SpinLocker timeLocker(fTeam != NULL ? &fTeam->time_lock : NULL);
// get the current time, but only if needed
bool nowValid = fTeam != NULL;
@ -786,9 +784,13 @@ TeamUserTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval,
// Get the team. If it doesn't exist anymore, just don't schedule the
// timer anymore.
fTeam = Team::Get(fTeamID);
if (fTeam == NULL)
Team* newTeam = Team::Get(fTeamID);
if (newTeam == NULL) {
fTeam = NULL;
return;
} else if (fTeam == NULL)
timeLocker.SetTo(newTeam->time_lock, false);
fTeam = newTeam;
// convert relative to absolute timeouts
if ((flags & B_RELATIVE_TIMEOUT) != 0) {
@ -813,7 +815,7 @@ TeamUserTimeUserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval,
count = acquire_read_seqlock(&sUserTimerLock);
if (fTeam != NULL) {
InterruptsSpinLocker schedulerLocker(gSchedulerLock);
InterruptsSpinLocker timeLocker(fTeam->time_lock);
_remainingTime = fNextTime - fTeam->UserCPUTime();
_interval = fInterval;
} else {
@ -828,7 +830,7 @@ TeamUserTimeUserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval,
/*! Deactivates the timer, if it is activated.
The caller must hold the scheduler lock and \c sUserTimerLock.
The caller must hold \c time_lock and \c sUserTimerLock.
*/
void
TeamUserTimeUserTimer::Deactivate()
@ -845,7 +847,7 @@ TeamUserTimeUserTimer::Deactivate()
/*! Checks whether the timer is up, firing an event, if so.
The caller must hold the scheduler lock and \c sUserTimerLock.
The caller must hold \c time_lock and \c sUserTimerLock.
*/
void
TeamUserTimeUserTimer::Check()
@ -899,7 +901,7 @@ ThreadTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval,
uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval)
{
InterruptsWriteSequentialLocker locker(sUserTimerLock);
InterruptsSpinLocker schedulerLocker(gSchedulerLock);
SpinLocker timeLocker(fThread->time_lock);
// get the current time, but only if needed
bool nowValid = fThread != NULL;
@ -933,9 +935,13 @@ ThreadTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval,
// Get the thread. If it doesn't exist anymore, just don't schedule the
// timer anymore.
fThread = Thread::Get(fThreadID);
if (fThread == NULL)
Thread* newThread = Thread::Get(fThreadID);
if (newThread == NULL) {
fThread = NULL;
return;
} else if (fThread == NULL)
timeLocker.SetTo(newThread->time_lock, false);
fThread = newThread;
fAbsolute = (flags & B_RELATIVE_TIMEOUT) == 0;
@ -963,7 +969,7 @@ ThreadTimeUserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval,
count = acquire_read_seqlock(&sUserTimerLock);
if (fThread != NULL) {
InterruptsSpinLocker schedulerLocker(gSchedulerLock);
SpinLocker timeLocker(fThread->time_lock);
_remainingTime = fNextTime - fThread->CPUTime(false);
_interval = fInterval;
} else {
@ -978,7 +984,7 @@ ThreadTimeUserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval,
/*! Deactivates the timer, if it is activated.
The caller must hold the scheduler lock and \c sUserTimerLock.
The caller must hold \c time_lock and \c sUserTimerLock.
*/
void
ThreadTimeUserTimer::Deactivate()
@ -1005,7 +1011,7 @@ ThreadTimeUserTimer::Deactivate()
scheduled, or, when the timer was just set and the thread is already
running. Schedules a kernel timer for the remaining time.
The caller must hold the scheduler lock and \c sUserTimerLock.
The caller must hold \c time_lock and \c sUserTimerLock.
*/
void
ThreadTimeUserTimer::Start()
@ -1066,7 +1072,7 @@ ThreadTimeUserTimer::Stop()
/*! Called when the team's CPU time clock which this timer refers to has been
set.
The caller must hold the scheduler lock and \c sUserTimerLock.
The caller must hold \c time_lock and \c sUserTimerLock.
\param changedBy The value by which the clock has changed.
*/
@ -1358,7 +1364,7 @@ create_timer(clockid_t clockID, int32 timerID, Team* team, Thread* thread,
/*! Called when the CPU time clock of the given thread has been set.
The caller must hold the scheduler lock.
The caller must hold \c time_lock.
\param thread The thread whose CPU time clock has been set.
\param changedBy The value by which the CPU time clock has changed
@ -1377,7 +1383,7 @@ thread_clock_changed(Thread* thread, bigtime_t changedBy)
/*! Called when the CPU time clock of the given team has been set.
The caller must hold the scheduler lock.
The caller must hold \c time_lock.
\param team The team whose CPU time clock has been set.
\param changedBy The value by which the CPU time clock has changed
@ -1477,7 +1483,7 @@ user_timer_get_clock(clockid_t clockID, bigtime_t& _time)
case CLOCK_THREAD_CPUTIME_ID:
{
Thread* thread = thread_get_current_thread();
InterruptsSpinLocker schedulerLocker(gSchedulerLock);
InterruptsSpinLocker timeLocker(thread->time_lock);
_time = thread->CPUTime(false);
return B_OK;
}
@ -1485,7 +1491,7 @@ user_timer_get_clock(clockid_t clockID, bigtime_t& _time)
case CLOCK_PROCESS_USER_CPUTIME_ID:
{
Team* team = thread_get_current_thread()->team;
InterruptsSpinLocker schedulerLocker(gSchedulerLock);
InterruptsSpinLocker timeLocker(team->time_lock);
_time = team->UserCPUTime();
return B_OK;
}
@ -1513,7 +1519,7 @@ user_timer_get_clock(clockid_t clockID, bigtime_t& _time)
BReference<Team> teamReference(team, true);
// get the time
InterruptsSpinLocker schedulerLocker(gSchedulerLock);
InterruptsSpinLocker timeLocker(team->time_lock);
_time = team->CPUTime(false);
return B_OK;
@ -1630,7 +1636,7 @@ _user_set_clock(clockid_t clockID, bigtime_t time)
case CLOCK_THREAD_CPUTIME_ID:
{
Thread* thread = thread_get_current_thread();
InterruptsSpinLocker schedulerLocker(gSchedulerLock);
InterruptsSpinLocker timeLocker(thread->time_lock);
bigtime_t diff = time - thread->CPUTime(false);
thread->cpu_clock_offset += diff;
@ -1665,7 +1671,7 @@ _user_set_clock(clockid_t clockID, bigtime_t time)
BReference<Team> teamReference(team, true);
// set the time offset
InterruptsSpinLocker schedulerLocker(gSchedulerLock);
InterruptsSpinLocker timeLocker(team->time_lock);
bigtime_t diff = time - team->CPUTime(false);
team->cpu_clock_offset += diff;

View File

@ -26,10 +26,14 @@ scheduler_switch_thread(Thread* fromThread, Thread* toThread)
user_debug_thread_unscheduled(fromThread);
// stop CPU time based user timers
acquire_spinlock(&fromThread->team->time_lock);
acquire_spinlock(&fromThread->time_lock);
if (fromThread->HasActiveCPUTimeUserTimers()
|| fromThread->team->HasActiveCPUTimeUserTimers()) {
user_timer_stop_cpu_timers(fromThread, toThread);
}
release_spinlock(&fromThread->time_lock);
release_spinlock(&fromThread->team->time_lock);
// update CPU and Thread structures and perform the context switch
cpu_ent* cpu = fromThread->cpu;
@ -46,10 +50,14 @@ scheduler_switch_thread(Thread* fromThread, Thread* toThread)
// first time the same is done in thread.cpp:common_thread_entry().
// continue CPU time based user timers
acquire_spinlock(&fromThread->team->time_lock);
acquire_spinlock(&fromThread->time_lock);
if (fromThread->HasActiveCPUTimeUserTimers()
|| fromThread->team->HasActiveCPUTimeUserTimers()) {
user_timer_continue_cpu_timers(fromThread, cpu->previous_thread);
}
release_spinlock(&fromThread->time_lock);
release_spinlock(&fromThread->team->time_lock);
// notify the user debugger code
if ((fromThread->flags & THREAD_FLAGS_DEBUGGER_INSTALLED) != 0)
@ -69,6 +77,7 @@ scheduler_update_thread_times(Thread* oldThread, Thread* nextThread)
} else {
acquire_spinlock(&oldThread->time_lock);
oldThread->kernel_time += now - oldThread->last_time;
oldThread->last_time = 0;
release_spinlock(&oldThread->time_lock);
acquire_spinlock(&nextThread->time_lock);
@ -78,8 +87,11 @@ scheduler_update_thread_times(Thread* oldThread, Thread* nextThread)
// If the old thread's team has user time timers, check them now.
Team* team = oldThread->team;
acquire_spinlock(&team->time_lock);
if (team->HasActiveUserTimeUserTimers())
user_timer_check_team_user_timers(team);
release_spinlock(&team->time_lock);
}

View File

@ -490,6 +490,7 @@ Team::Team(team_id id, bool kernel)
// init dead/stopped/continued children condition vars
dead_children.condition_variable.Init(&dead_children, "team children");
B_INITIALIZE_SPINLOCK(&time_lock);
B_INITIALIZE_SPINLOCK(&signal_lock);
fQueuedSignalsCounter = new(std::nothrow) BKernel::QueuedSignalsCounter(
@ -908,7 +909,7 @@ Team::DeactivateCPUTimeUserTimers()
/*! Returns the team's current total CPU time (kernel + user + offset).
The caller must hold the scheduler lock.
The caller must hold \c time_lock.
\param ignoreCurrentRun If \c true and the current thread is one team's
threads, don't add the time since the last time \c last_time was
@ -931,7 +932,7 @@ Team::CPUTime(bool ignoreCurrentRun) const
SpinLocker threadTimeLocker(thread->time_lock);
time += thread->kernel_time + thread->user_time;
if (thread->IsRunning()) {
if (thread->last_time != 0) {
if (!ignoreCurrentRun || thread != currentThread)
time += now - thread->last_time;
}
@ -943,7 +944,7 @@ Team::CPUTime(bool ignoreCurrentRun) const
/*! Returns the team's current user CPU time.
The caller must hold the scheduler lock.
The caller must hold \c time_lock.
\return The team's current user CPU time.
*/
@ -959,7 +960,7 @@ Team::UserCPUTime() const
SpinLocker threadTimeLocker(thread->time_lock);
time += thread->user_time;
if (thread->IsRunning() && !thread->in_kernel)
if (thread->last_time != 0 && !thread->in_kernel)
time += now - thread->last_time;
}
@ -3093,12 +3094,12 @@ team_shutdown_team(Team* team)
team->DeleteUserTimers(false);
// deactivate CPU time user timers for the team
InterruptsSpinLocker schedulerLocker(gSchedulerLock);
InterruptsSpinLocker timeLocker(team->time_lock);
if (team->HasActiveCPUTimeUserTimers())
team->DeactivateCPUTimeUserTimers();
schedulerLocker.Unlock();
timeLocker.Unlock();
// kill all threads but the main thread
team_death_entry deathEntry;

View File

@ -696,18 +696,22 @@ common_thread_entry(void* _args)
// The thread is new and has been scheduled the first time.
// start CPU time based user timers
acquire_spinlock(&thread->team->time_lock);
acquire_spinlock(&thread->time_lock);
if (thread->HasActiveCPUTimeUserTimers()
|| thread->team->HasActiveCPUTimeUserTimers()) {
user_timer_continue_cpu_timers(thread, thread->cpu->previous_thread);
}
// start tracking time
thread->last_time = system_time();
release_spinlock(&thread->time_lock);
release_spinlock(&thread->team->time_lock);
// notify the user debugger code
if ((thread->flags & THREAD_FLAGS_DEBUGGER_INSTALLED) != 0)
user_debug_thread_scheduled(thread);
// start tracking time
thread->last_time = system_time();
// unlock the scheduler lock and enable interrupts
release_spinlock(&gSchedulerLock);
enable_interrupts();
@ -1991,10 +1995,10 @@ thread_exit(void)
// remember how long this thread lasted
bigtime_t now = system_time();
InterruptsSpinLocker threadTimeLocker(thread->time_lock);
InterruptsSpinLocker teamTimeLocker(team->time_lock);
SpinLocker threadTimeLocker(thread->time_lock);
thread->kernel_time += now - thread->last_time;
thread->last_time = now;
threadTimeLocker.Unlock();
team->dead_threads_kernel_time += thread->kernel_time;
team->dead_threads_user_time += thread->user_time;
@ -2009,6 +2013,9 @@ thread_exit(void)
if (thread->HasActiveCPUTimeUserTimers())
thread->DeactivateCPUTimeUserTimers();
threadTimeLocker.Unlock();
teamTimeLocker.Unlock();
// put the thread into the kernel team until it dies
remove_thread_from_team(team, thread);
insert_thread_into_team(kernelTeam, thread);
@ -2334,11 +2341,10 @@ thread_reset_for_exec(void)
thread->user_stack_size = 0;
// reset signals
InterruptsSpinLocker schedulerLocker(gSchedulerLock);
thread->ResetSignalsOnExec();
// reset thread CPU time clock
InterruptsSpinLocker timeLocker(thread->time_lock);
thread->cpu_clock_offset = -thread->CPUTime(false);
}