From 72addc62e042d2a39fba1f04e2cc8dadc155adfe Mon Sep 17 00:00:00 2001 From: Pawel Dziepak Date: Thu, 7 Nov 2013 22:16:36 +0100 Subject: [PATCH] kernel: Introduce Thread::time_lock and Team::time_lock --- headers/private/kernel/thread_types.h | 11 +-- src/system/kernel/UserTimer.cpp | 74 ++++++++++--------- .../kernel/scheduler/scheduler_common.h | 12 +++ src/system/kernel/team.cpp | 13 ++-- src/system/kernel/thread.cpp | 20 +++-- 5 files changed, 78 insertions(+), 52 deletions(-) diff --git a/headers/private/kernel/thread_types.h b/headers/private/kernel/thread_types.h index 96014a22fd..c0448213e3 100644 --- a/headers/private/kernel/thread_types.h +++ b/headers/private/kernel/thread_types.h @@ -264,10 +264,11 @@ struct Team : TeamThreadIteratorEntry, 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, 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; diff --git a/src/system/kernel/UserTimer.cpp b/src/system/kernel/UserTimer.cpp index 6c04125774..b3da871849 100644 --- a/src/system/kernel/UserTimer.cpp +++ b/src/system/kernel/UserTimer.cpp @@ -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 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 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; diff --git a/src/system/kernel/scheduler/scheduler_common.h b/src/system/kernel/scheduler/scheduler_common.h index 0de57db2b6..266f7d878a 100644 --- a/src/system/kernel/scheduler/scheduler_common.h +++ b/src/system/kernel/scheduler/scheduler_common.h @@ -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); } diff --git a/src/system/kernel/team.cpp b/src/system/kernel/team.cpp index 906ca0b64e..6b651e58cf 100644 --- a/src/system/kernel/team.cpp +++ b/src/system/kernel/team.cpp @@ -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; diff --git a/src/system/kernel/thread.cpp b/src/system/kernel/thread.cpp index fa63e16d01..185da50376 100644 --- a/src/system/kernel/thread.cpp +++ b/src/system/kernel/thread.cpp @@ -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); }