#include <UserTimer.h>
#include <algorithm>
#include <AutoDeleter.h>
#include <debug.h>
#include <kernel.h>
#include <real_time_clock.h>
#include <syscall_clock_info.h>
#include <team.h>
#include <thread_types.h>
#include <UserEvent.h>
#include <util/AutoLock.h>
#define CPUCLOCK_TEAM 0x00000000
#define CPUCLOCK_THREAD 0x80000000
#define CPUCLOCK_SPECIAL 0xc0000000
#define CPUCLOCK_ID_MASK (~(CPUCLOCK_SPECIAL))
static const bigtime_t kMinPeriodicTimerInterval = 100;
static RealTimeUserTimerList sAbsoluteRealTimeTimers;
static spinlock sAbsoluteRealTimeTimersLock = B_SPINLOCK_INITIALIZER;
namespace {
struct TimerLocker {
Team* team;
Thread* thread;
TimerLocker()
:
team(NULL),
thread(NULL)
{
}
~TimerLocker()
{
Unlock();
}
void Lock(Team* team, Thread* thread)
{
this->team = team;
team->Lock();
this->thread = thread;
if (thread != NULL) {
thread->AcquireReference();
thread->Lock();
}
}
status_t LockAndGetTimer(thread_id threadID, int32 timerID,
UserTimer*& _timer)
{
team = thread_get_current_thread()->team;
team->Lock();
if (threadID >= 0) {
thread = Thread::GetAndLock(threadID);
if (thread == NULL)
return B_BAD_THREAD_ID;
if (thread->team != team)
return B_NOT_ALLOWED;
}
UserTimer* timer = thread != NULL
? thread->UserTimerFor(timerID) : team->UserTimerFor(timerID);
if (timer == NULL)
return B_BAD_VALUE;
_timer = timer;
return B_OK;
}
void Unlock()
{
if (thread != NULL) {
thread->UnlockAndReleaseReference();
thread = NULL;
}
if (team != NULL) {
team->Unlock();
team = NULL;
}
}
};
}
UserTimer::UserTimer()
:
fID(-1),
fEvent(NULL),
fNextTime(0),
fInterval(0),
fOverrunCount(0),
fScheduled(false)
{
fTimer.user_data = this;
}
UserTimer::~UserTimer()
{
if (fEvent != NULL)
fEvent->ReleaseReference();
}
void
UserTimer::Cancel()
{
bigtime_t oldNextTime;
bigtime_t oldInterval;
return Schedule(B_INFINITE_TIMEOUT, 0, 0, oldNextTime, oldInterval);
}
int32
UserTimer::HandleTimerHook(struct timer* timer)
{
UserTimer* userTimer = reinterpret_cast<UserTimer*>(timer->user_data);
userTimer->HandleTimer();
return B_HANDLED_INTERRUPT;
}
void
UserTimer::HandleTimer()
{
if (fEvent != NULL) {
status_t error = fEvent->Fire();
if (error == B_BUSY) {
if (fOverrunCount < MAX_USER_TIMER_OVERRUN_COUNT)
fOverrunCount++;
}
}
fScheduled = false;
}
void
UserTimer::UpdatePeriodicStartTime()
{
if (fInterval < kMinPeriodicTimerInterval) {
bigtime_t skip = (kMinPeriodicTimerInterval + fInterval - 1) / fInterval;
fNextTime += skip * fInterval;
skip--;
if (skip + fOverrunCount > MAX_USER_TIMER_OVERRUN_COUNT)
fOverrunCount = MAX_USER_TIMER_OVERRUN_COUNT;
else
fOverrunCount += skip;
} else
fNextTime += fInterval;
}
void
UserTimer::CheckPeriodicOverrun(bigtime_t now)
{
if (fNextTime + fInterval > now)
return;
bigtime_t skip = (now - fNextTime) / fInterval;
fNextTime += skip * fInterval;
if (skip + fOverrunCount > MAX_USER_TIMER_OVERRUN_COUNT)
fOverrunCount = MAX_USER_TIMER_OVERRUN_COUNT;
else
fOverrunCount += skip;
}
SystemTimeUserTimer::SystemTimeUserTimer()
:
fLock(B_SEQLOCK_INITIALIZER)
{
}
void
SystemTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval,
uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval)
{
InterruptsWriteSequentialLocker locker(fLock);
bigtime_t now = system_time();
if (fScheduled) {
fScheduled = false;
cancel_timer(&fTimer);
_oldRemainingTime = fNextTime - now;
_oldInterval = fInterval;
} else {
_oldRemainingTime = B_INFINITE_TIMEOUT;
_oldInterval = 0;
}
fNextTime = nextTime;
fInterval = interval;
fOverrunCount = 0;
if (nextTime == B_INFINITE_TIMEOUT)
return;
if ((flags & B_RELATIVE_TIMEOUT) != 0)
fNextTime += now;
ScheduleKernelTimer(now, fInterval > 0);
}
void
SystemTimeUserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval,
uint32& _overrunCount)
{
uint32 count;
do {
count = acquire_read_seqlock(&fLock);
if (fScheduled) {
_remainingTime = fNextTime - system_time();
_interval = fInterval;
} else {
_remainingTime = B_INFINITE_TIMEOUT;
_interval = 0;
}
_overrunCount = fOverrunCount;
} while (!release_read_seqlock(&fLock, count));
}
void
SystemTimeUserTimer::HandleTimer()
{
while (!try_acquire_write_seqlock(&fLock)) {
if (!fScheduled)
return;
cpu_pause();
}
WriteSequentialLocker locker(fLock, true);
_HandleTimerLocked();
}
void
SystemTimeUserTimer::_HandleTimerLocked()
{
UserTimer::HandleTimer();
if (fInterval > 0) {
UpdatePeriodicStartTime();
ScheduleKernelTimer(system_time(), true);
}
}
void
SystemTimeUserTimer::ScheduleKernelTimer(bigtime_t now,
bool checkPeriodicOverrun)
{
if (checkPeriodicOverrun)
CheckPeriodicOverrun(now);
uint32 timerFlags = B_ONE_SHOT_ABSOLUTE_TIMER
| B_TIMER_USE_TIMER_STRUCT_TIMES;
fTimer.schedule_time = std::max(fNextTime, (bigtime_t)0);
fTimer.period = 0;
add_timer(&fTimer, &HandleTimerHook, fTimer.schedule_time, timerFlags);
fScheduled = true;
}
void
RealTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval,
uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval)
{
InterruptsWriteSequentialLocker locker(fLock);
bigtime_t now = system_time();
if (fScheduled) {
fScheduled = false;
cancel_timer(&fTimer);
_oldRemainingTime = fNextTime - now;
_oldInterval = fInterval;
if (fAbsolute) {
SpinLocker globalListLocker(sAbsoluteRealTimeTimersLock);
sAbsoluteRealTimeTimers.Remove(this);
}
} else {
_oldRemainingTime = B_INFINITE_TIMEOUT;
_oldInterval = 0;
}
fNextTime = nextTime;
fInterval = interval;
fOverrunCount = 0;
if (nextTime == B_INFINITE_TIMEOUT)
return;
fAbsolute = (flags & B_RELATIVE_TIMEOUT) == 0;
if (fAbsolute) {
fRealTimeOffset = rtc_boot_time();
fNextTime -= fRealTimeOffset;
if (fInterval > 0)
CheckPeriodicOverrun(now);
SpinLocker globalListLocker(sAbsoluteRealTimeTimersLock);
sAbsoluteRealTimeTimers.Insert(this);
} else
fNextTime += now;
ScheduleKernelTimer(now, false);
}
void
RealTimeUserTimer::TimeWarped()
{
ASSERT(fScheduled && fAbsolute);
InterruptsWriteSequentialLocker locker(fLock);
bigtime_t oldRealTimeOffset = fRealTimeOffset;
fRealTimeOffset = rtc_boot_time();
if (fRealTimeOffset == oldRealTimeOffset)
return;
fScheduled = false;
cancel_timer(&fTimer);
fNextTime += oldRealTimeOffset - fRealTimeOffset;
ScheduleKernelTimer(system_time(), fInterval > 0);
}
void
RealTimeUserTimer::HandleTimer()
{
while (!try_acquire_write_seqlock(&fLock)) {
if (!fScheduled)
return;
cpu_pause();
}
WriteSequentialLocker locker(fLock, true);
SystemTimeUserTimer::_HandleTimerLocked();
if (!fScheduled && fAbsolute) {
SpinLocker globalListLocker(sAbsoluteRealTimeTimersLock);
sAbsoluteRealTimeTimers.Remove(this);
}
}
TeamTimeUserTimer::TeamTimeUserTimer(Team* team)
:
fTeam(team)
{
if (fTeam != NULL)
fTeam->AcquireReference();
}
TeamTimeUserTimer::~TeamTimeUserTimer()
{
ASSERT(!fScheduled);
if (fTeam != NULL)
fTeam->ReleaseReference();
}
void
TeamTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval,
uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval)
{
if (fTeam == NULL) {
_oldRemainingTime = B_INFINITE_TIMEOUT;
_oldInterval = 0;
return;
}
InterruptsSpinLocker timeLocker(fTeam->time_lock);
bigtime_t now = fTeam->CPUTime(false);
if (fScheduled) {
fScheduled = false;
cancel_timer(&fTimer);
fTeam->UserTimerDeactivated(this);
_oldRemainingTime = fNextTime - now;
_oldInterval = fInterval;
} else {
_oldRemainingTime = B_INFINITE_TIMEOUT;
_oldInterval = 0;
}
fNextTime = nextTime;
fInterval = interval;
fOverrunCount = 0;
if (fNextTime == B_INFINITE_TIMEOUT)
return;
if (fTeam->state >= TEAM_STATE_SHUTDOWN) {
timeLocker.Unlock();
fTeam->ReleaseReference();
fTeam = NULL;
return;
}
fAbsolute = (flags & B_RELATIVE_TIMEOUT) == 0;
if (!fAbsolute)
fNextTime += now;
Update(NULL);
}
void
TeamTimeUserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval,
uint32& _overrunCount)
{
if (fTeam != NULL) {
InterruptsSpinLocker timeLocker(fTeam->time_lock);
_remainingTime = fNextTime - fTeam->CPUTime(false);
_interval = fInterval;
_overrunCount = fOverrunCount;
} else {
_remainingTime = B_INFINITE_TIMEOUT;
_interval = 0;
_overrunCount = fOverrunCount;
}
}
void
TeamTimeUserTimer::Deactivate()
{
if (fTeam == NULL)
return;
if (fScheduled) {
fScheduled = false;
cancel_timer(&fTimer);
fTeam->UserTimerDeactivated(this);
}
}
void
TeamTimeUserTimer::Update(Thread* unscheduledThread, Thread* lockedThread)
{
if (fTeam == NULL)
return;
_Update(unscheduledThread, true, lockedThread);
}
void
TeamTimeUserTimer::TimeWarped(bigtime_t changedBy)
{
if (fTeam == NULL || changedBy == 0)
return;
if (!fAbsolute)
fNextTime += changedBy;
_Update(NULL, false);
}
void
TeamTimeUserTimer::HandleTimer()
{
while (!try_acquire_spinlock(&fTeam->time_lock)) {
if (!fScheduled)
return;
cpu_pause();
}
SpinLocker timeLocker(fTeam->time_lock, true);
UserTimer::HandleTimer();
fTeam->UserTimerDeactivated(this);
if (fInterval != 0) {
UpdatePeriodicStartTime();
_Update(NULL, false);
}
}
void
TeamTimeUserTimer::_Update(Thread* unscheduledThread, bool checkRunning, Thread* lockedThread)
{
if (checkRunning) {
fRunningThreads = 0;
int32 cpuCount = smp_get_num_cpus();
for (int32 i = 0; i < cpuCount; i++) {
Thread* thread = gCPU[i].running_thread;
if (thread != unscheduledThread && thread->team == fTeam)
fRunningThreads++;
}
}
if (fScheduled) {
fScheduled = false;
cancel_timer(&fTimer);
fTeam->UserTimerDeactivated(this);
}
if (fRunningThreads == 0)
return;
bigtime_t now = fTeam->CPUTime(unscheduledThread != NULL, lockedThread);
if (fInterval > 0)
CheckPeriodicOverrun(now);
if (fNextTime > now) {
fTimer.schedule_time = system_time()
+ (fNextTime - now + fRunningThreads - 1) / fRunningThreads;
if (fTimer.schedule_time < 0)
fTimer.schedule_time = B_INFINITE_TIMEOUT;
} else
fTimer.schedule_time = 0;
fTimer.period = 0;
add_timer(&fTimer, &HandleTimerHook, fTimer.schedule_time,
B_ONE_SHOT_ABSOLUTE_TIMER | B_TIMER_USE_TIMER_STRUCT_TIMES);
fTeam->UserTimerActivated(this);
fScheduled = true;
}
TeamUserTimeUserTimer::TeamUserTimeUserTimer(Team* team)
:
fTeam(team)
{
if (fTeam != NULL)
fTeam->AcquireReference();
}
TeamUserTimeUserTimer::~TeamUserTimeUserTimer()
{
ASSERT(!fScheduled);
if (fTeam != NULL)
fTeam->ReleaseReference();
}
void
TeamUserTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval,
uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval)
{
if (fTeam == NULL){
_oldRemainingTime = B_INFINITE_TIMEOUT;
_oldInterval = 0;
return;
}
InterruptsSpinLocker timeLocker(fTeam->time_lock);
bigtime_t now = fTeam->UserCPUTime();
if (fScheduled) {
fScheduled = false;
cancel_timer(&fTimer);
fTeam->UserTimerDeactivated(this);
_oldRemainingTime = fNextTime - now;
_oldInterval = fInterval;
} else {
_oldRemainingTime = B_INFINITE_TIMEOUT;
_oldInterval = 0;
}
fNextTime = nextTime;
fInterval = interval;
fOverrunCount = 0;
if (fNextTime == B_INFINITE_TIMEOUT)
return;
if (fTeam->state >= TEAM_STATE_SHUTDOWN) {
timeLocker.Unlock();
fTeam->ReleaseReference();
fTeam = NULL;
return;
}
if ((flags & B_RELATIVE_TIMEOUT) != 0)
fNextTime += now;
Check();
}
void
TeamUserTimeUserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval,
uint32& _overrunCount)
{
if (fTeam != NULL) {
InterruptsSpinLocker timeLocker(fTeam->time_lock);
_remainingTime = fNextTime - fTeam->UserCPUTime();
_interval = fInterval;
_overrunCount = fOverrunCount;
} else {
_remainingTime = B_INFINITE_TIMEOUT;
_interval = 0;
_overrunCount = fOverrunCount;
}
}
void
TeamUserTimeUserTimer::Deactivate()
{
if (fTeam == NULL)
return;
if (fScheduled) {
fTeam->UserTimerDeactivated(this);
fScheduled = false;
}
}
void
TeamUserTimeUserTimer::Check()
{
if (fTeam == NULL)
return;
bigtime_t now = fTeam->UserCPUTime();
if (now < fNextTime)
return;
HandleTimer();
if (fInterval == 0) {
fTeam->UserTimerDeactivated(this);
return;
}
CheckPeriodicOverrun(now);
fNextTime += fInterval;
fTeam->UserTimerActivated(this);
fScheduled = true;
}
ThreadTimeUserTimer::ThreadTimeUserTimer(Thread* thread)
:
fThread(thread)
{
if (fThread != NULL)
fThread->AcquireReference();
}
ThreadTimeUserTimer::~ThreadTimeUserTimer()
{
ASSERT(!fScheduled);
if (fThread != NULL)
fThread->ReleaseReference();
}
void
ThreadTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval,
uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval)
{
if (fThread == NULL) {
_oldRemainingTime = B_INFINITE_TIMEOUT;
_oldInterval = 0;
return;
}
InterruptsSpinLocker timeLocker(fThread->time_lock);
bigtime_t now = fThread->CPUTime(false);
if (fScheduled) {
fScheduled = false;
cancel_timer(&fTimer);
fThread->UserTimerDeactivated(this);
_oldRemainingTime = fNextTime - now;
_oldInterval = fInterval;
} else {
_oldRemainingTime = B_INFINITE_TIMEOUT;
_oldInterval = 0;
}
fNextTime = nextTime;
fInterval = interval;
fOverrunCount = 0;
if (fNextTime == B_INFINITE_TIMEOUT)
return;
if (fThread->state >= THREAD_STATE_FREE_ON_RESCHED) {
timeLocker.Unlock();
fThread->ReleaseReference();
fThread = NULL;
return;
}
fAbsolute = (flags & B_RELATIVE_TIMEOUT) == 0;
if (!fAbsolute)
fNextTime += now;
if (fThread->cpu != NULL)
Start();
}
void
ThreadTimeUserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval,
uint32& _overrunCount)
{
if (fThread != NULL) {
InterruptsSpinLocker timeLocker(fThread->time_lock);
_remainingTime = fNextTime - fThread->CPUTime(false);
_interval = fInterval;
_overrunCount = fOverrunCount;
} else {
_remainingTime = B_INFINITE_TIMEOUT;
_interval = 0;
_overrunCount = fOverrunCount;
}
}
void
ThreadTimeUserTimer::Deactivate()
{
if (fThread == NULL)
return;
if (fScheduled) {
fScheduled = false;
cancel_timer(&fTimer);
fThread->UserTimerDeactivated(this);
}
}
void
ThreadTimeUserTimer::Start()
{
if (fThread == NULL)
return;
ASSERT(!fScheduled);
bigtime_t now = fThread->CPUTime(false);
if (fInterval > 0)
CheckPeriodicOverrun(now);
if (fNextTime > now) {
fTimer.schedule_time = system_time() + fNextTime - now;
if (fTimer.schedule_time < 0)
fTimer.schedule_time = B_INFINITE_TIMEOUT;
} else
fTimer.schedule_time = 0;
fTimer.period = 0;
uint32 flags = B_ONE_SHOT_ABSOLUTE_TIMER | B_TIMER_USE_TIMER_STRUCT_TIMES;
add_timer(&fTimer, &HandleTimerHook, fTimer.schedule_time, flags);
fThread->UserTimerActivated(this);
fScheduled = true;
}
void
ThreadTimeUserTimer::Stop()
{
if (fThread == NULL)
return;
ASSERT(fScheduled);
fScheduled = false;
cancel_timer(&fTimer);
fThread->UserTimerDeactivated(this);
}
void
ThreadTimeUserTimer::TimeWarped(bigtime_t changedBy)
{
if (fThread == NULL || changedBy == 0)
return;
if (!fAbsolute)
fNextTime += changedBy;
if (fScheduled) {
Stop();
Start();
}
}
void
ThreadTimeUserTimer::HandleTimer()
{
while (!try_acquire_spinlock(&fThread->time_lock)) {
if (!fScheduled)
return;
cpu_pause();
}
SpinLocker timeLocker(fThread->time_lock, true);
UserTimer::HandleTimer();
fThread->UserTimerDeactivated(this);
if (fInterval > 0) {
UpdatePeriodicStartTime();
Start();
}
}
UserTimerList::UserTimerList()
{
}
UserTimerList::~UserTimerList()
{
ASSERT(fTimers.IsEmpty());
}
UserTimer*
UserTimerList::TimerFor(int32 id) const
{
for (TimerList::ConstIterator it = fTimers.GetIterator();
UserTimer* timer = it.Next();) {
if (timer->ID() == id)
return timer;
}
return NULL;
}
void
UserTimerList::AddTimer(UserTimer* timer)
{
int32 id = timer->ID();
if (id < 0) {
id = USER_TIMER_FIRST_USER_DEFINED_ID;
UserTimer* insertAfter = NULL;
for (TimerList::Iterator it = fTimers.GetIterator();
UserTimer* other = it.Next();) {
if (other->ID() > id)
break;
if (other->ID() == id)
id++;
insertAfter = other;
}
timer->SetID(id);
fTimers.InsertAfter(insertAfter, timer);
} else {
UserTimer* insertAfter = NULL;
for (TimerList::Iterator it = fTimers.GetIterator();
UserTimer* other = it.Next();) {
if (other->ID() > id)
break;
if (other->ID() == id) {
panic("UserTimerList::AddTimer(): timer with ID %" B_PRId32
" already exists!", id);
}
insertAfter = other;
}
fTimers.InsertAfter(insertAfter, timer);
}
}
int32
UserTimerList::DeleteTimers(bool userDefinedOnly)
{
int32 userDefinedCount = 0;
for (TimerList::Iterator it = fTimers.GetIterator();
UserTimer* timer = it.Next();) {
if (timer->ID() < USER_TIMER_FIRST_USER_DEFINED_ID) {
if (userDefinedOnly)
continue;
} else
userDefinedCount++;
it.Remove();
timer->Cancel();
delete timer;
}
return userDefinedCount;
}
static int32
create_timer(clockid_t clockID, int32 timerID, Team* team, Thread* thread,
uint32 flags, const struct sigevent& event,
ThreadCreationAttributes* threadAttributes, bool isDefaultEvent)
{
UserTimer* timer;
switch (clockID) {
case CLOCK_MONOTONIC:
timer = new(std::nothrow) SystemTimeUserTimer;
break;
case CLOCK_REALTIME:
timer = new(std::nothrow) RealTimeUserTimer;
break;
case CLOCK_THREAD_CPUTIME_ID:
timer = new(std::nothrow) ThreadTimeUserTimer(
thread_get_current_thread());
break;
case CLOCK_PROCESS_CPUTIME_ID:
if (team == NULL)
return B_BAD_VALUE;
timer = new(std::nothrow) TeamTimeUserTimer(team);
break;
case CLOCK_PROCESS_USER_CPUTIME_ID:
if (team == NULL)
return B_BAD_VALUE;
timer = new(std::nothrow) TeamUserTimeUserTimer(team);
break;
default:
{
if (clockID <= 0)
return B_BAD_VALUE;
if (clockID == team_get_kernel_team_id())
return B_NOT_ALLOWED;
Team* timedTeam = Team::GetAndLock(clockID);
if (timedTeam == NULL)
return B_BAD_VALUE;
uid_t uid = geteuid();
uid_t teamUID = timedTeam->effective_uid;
timedTeam->Unlock();
if (uid != 0 && uid != teamUID)
return B_NOT_ALLOWED;
timer = new(std::nothrow) TeamTimeUserTimer(timedTeam);
timedTeam->ReleaseReference();
break;
}
}
if (timer == NULL)
return B_NO_MEMORY;
ObjectDeleter<UserTimer> timerDeleter(timer);
if (timerID >= 0)
timer->SetID(timerID);
SignalEvent* signalEvent = NULL;
switch (event.sigev_notify) {
case SIGEV_NONE:
break;
case SIGEV_SIGNAL:
{
if (event.sigev_signo <= 0 || event.sigev_signo > MAX_SIGNAL_NUMBER)
return B_BAD_VALUE;
if (thread != NULL && (flags & USER_TIMER_SIGNAL_THREAD) != 0) {
signalEvent = ThreadSignalEvent::Create(thread,
event.sigev_signo, SI_TIMER, 0, team->id);
} else {
signalEvent = TeamSignalEvent::Create(team, event.sigev_signo,
SI_TIMER, 0);
}
if (signalEvent == NULL)
return B_NO_MEMORY;
timer->SetEvent(signalEvent);
break;
}
case SIGEV_THREAD:
{
if (threadAttributes == NULL)
return B_BAD_VALUE;
CreateThreadEvent* event
= CreateThreadEvent::Create(*threadAttributes);
if (event == NULL)
return B_NO_MEMORY;
timer->SetEvent(event);
break;
}
default:
return B_BAD_VALUE;
}
TimerLocker timerLocker;
timerLocker.Lock(team, thread);
status_t error = thread != NULL
? thread->AddUserTimer(timer) : team->AddUserTimer(timer);
if (error != B_OK)
return error;
if (signalEvent != NULL) {
union sigval signalValue = event.sigev_value;
if (isDefaultEvent)
signalValue.sival_int = timer->ID();
signalEvent->SetUserValue(signalValue);
}
return timerDeleter.Detach()->ID();
}
static void
thread_clock_changed(Thread* thread, bigtime_t changedBy)
{
for (ThreadTimeUserTimerList::ConstIterator it
= thread->CPUTimeUserTimerIterator();
ThreadTimeUserTimer* timer = it.Next();) {
timer->TimeWarped(changedBy);
}
}
static void
team_clock_changed(Team* team, bigtime_t changedBy)
{
for (TeamTimeUserTimerList::ConstIterator it
= team->CPUTimeUserTimerIterator();
TeamTimeUserTimer* timer = it.Next();) {
timer->TimeWarped(changedBy);
}
}
status_t
user_timer_create_thread_timers(Team* team, Thread* thread)
{
struct sigevent event = {0};
event.sigev_notify = SIGEV_SIGNAL;
event.sigev_signo = SIGALRM;
int32 timerID = create_timer(CLOCK_MONOTONIC, USER_TIMER_REAL_TIME_ID,
team, thread, USER_TIMER_SIGNAL_THREAD, event, NULL, true);
if (timerID < 0)
return timerID;
return B_OK;
}
status_t
user_timer_create_team_timers(Team* team)
{
struct sigevent event = {0};
event.sigev_notify = SIGEV_SIGNAL;
event.sigev_signo = SIGALRM;
int32 timerID = create_timer(CLOCK_MONOTONIC, USER_TIMER_REAL_TIME_ID,
team, NULL, 0, event, NULL, true);
if (timerID < 0)
return timerID;
event.sigev_notify = SIGEV_SIGNAL;
event.sigev_signo = SIGPROF;
timerID = create_timer(CLOCK_PROCESS_CPUTIME_ID,
USER_TIMER_TEAM_TOTAL_TIME_ID, team, NULL, 0, event, NULL, true);
if (timerID < 0)
return timerID;
event.sigev_notify = SIGEV_SIGNAL;
event.sigev_signo = SIGVTALRM;
timerID = create_timer(CLOCK_PROCESS_USER_CPUTIME_ID,
USER_TIMER_TEAM_USER_TIME_ID, team, NULL, 0, event, NULL, true);
if (timerID < 0)
return timerID;
return B_OK;
}
status_t
user_timer_get_clock(clockid_t clockID, bigtime_t& _time)
{
switch (clockID) {
case CLOCK_MONOTONIC:
_time = system_time();
return B_OK;
case CLOCK_REALTIME:
_time = real_time_clock_usecs();
return B_OK;
case CLOCK_THREAD_CPUTIME_ID:
{
Thread* thread = thread_get_current_thread();
InterruptsSpinLocker timeLocker(thread->time_lock);
_time = thread->CPUTime(false);
return B_OK;
}
case CLOCK_PROCESS_USER_CPUTIME_ID:
{
Team* team = thread_get_current_thread()->team;
InterruptsSpinLocker timeLocker(team->time_lock);
_time = team->UserCPUTime();
return B_OK;
}
case CLOCK_PROCESS_CPUTIME_ID:
default:
{
team_id teamID = 0;
if (clockID == CLOCK_PROCESS_CPUTIME_ID) {
teamID = B_CURRENT_TEAM;
} else if ((clockID & CPUCLOCK_SPECIAL) == CPUCLOCK_THREAD) {
thread_id threadID = clockID & CPUCLOCK_ID_MASK;
Thread* thread = Thread::Get(threadID);
if (thread == NULL)
return B_BAD_VALUE;
BReference<Thread> threadReference(thread, true);
if (thread->team == team_get_kernel_team())
return B_NOT_ALLOWED;
InterruptsSpinLocker timeLocker(thread->time_lock);
_time = thread->CPUTime(false);
return B_OK;
} else if ((clockID & CPUCLOCK_SPECIAL) == CPUCLOCK_TEAM) {
teamID = clockID & CPUCLOCK_ID_MASK;
if (teamID == team_get_kernel_team_id())
return B_NOT_ALLOWED;
} else {
return B_BAD_VALUE;
}
Team* team = Team::Get(teamID);
if (team == NULL)
return B_BAD_VALUE;
BReference<Team> teamReference(team, true);
InterruptsSpinLocker timeLocker(team->time_lock);
_time = team->CPUTime(false);
return B_OK;
}
}
}
void
user_timer_real_time_clock_changed()
{
InterruptsSpinLocker globalListLocker(sAbsoluteRealTimeTimersLock);
for (RealTimeUserTimerList::Iterator it
= sAbsoluteRealTimeTimers.GetIterator();
RealTimeUserTimer* timer = it.Next();) {
timer->TimeWarped();
}
}
void
user_timer_stop_cpu_timers(Thread* thread, Thread* nextThread)
{
for (ThreadTimeUserTimerList::ConstIterator it
= thread->CPUTimeUserTimerIterator();
ThreadTimeUserTimer* timer = it.Next();) {
timer->Stop();
}
if (nextThread == NULL || nextThread->team != thread->team) {
for (TeamTimeUserTimerList::ConstIterator it
= thread->team->CPUTimeUserTimerIterator();
TeamTimeUserTimer* timer = it.Next();) {
timer->Update(thread, thread);
}
}
}
void
user_timer_continue_cpu_timers(Thread* thread, Thread* previousThread)
{
if (previousThread == NULL || previousThread->team != thread->team) {
for (TeamTimeUserTimerList::ConstIterator it
= thread->team->CPUTimeUserTimerIterator();
TeamTimeUserTimer* timer = it.Next();) {
timer->Update(NULL, thread);
}
}
for (ThreadTimeUserTimerList::ConstIterator it
= thread->CPUTimeUserTimerIterator();
ThreadTimeUserTimer* timer = it.Next();) {
timer->Start();
}
}
void
user_timer_check_team_user_timers(Team* team)
{
for (TeamUserTimeUserTimerList::ConstIterator it
= team->UserTimeUserTimerIterator();
TeamUserTimeUserTimer* timer = it.Next();) {
timer->Check();
}
}
status_t
_user_get_clock(clockid_t clockID, bigtime_t* userTime)
{
bigtime_t time;
status_t error = user_timer_get_clock(clockID, time);
if (error != B_OK)
return error;
if (userTime == NULL || !IS_USER_ADDRESS(userTime)
|| user_memcpy(userTime, &time, sizeof(time)) != B_OK) {
return B_BAD_ADDRESS;
}
return B_OK;
}
status_t
_user_set_clock(clockid_t clockID, bigtime_t time)
{
switch (clockID) {
case CLOCK_MONOTONIC:
return B_BAD_VALUE;
case CLOCK_REALTIME:
if (geteuid() != 0)
return B_NOT_ALLOWED;
set_real_time_clock_usecs(time);
return B_OK;
case CLOCK_THREAD_CPUTIME_ID:
{
Thread* thread = thread_get_current_thread();
InterruptsSpinLocker timeLocker(thread->time_lock);
bigtime_t diff = time - thread->CPUTime(false);
thread->cpu_clock_offset += diff;
thread_clock_changed(thread, diff);
return B_OK;
}
case CLOCK_PROCESS_USER_CPUTIME_ID:
return B_BAD_VALUE;
case CLOCK_PROCESS_CPUTIME_ID:
default:
{
team_id teamID;
if (clockID == CLOCK_PROCESS_CPUTIME_ID) {
teamID = B_CURRENT_TEAM;
} else if ((clockID & CPUCLOCK_THREAD) != 0) {
thread_id threadID = clockID & CPUCLOCK_ID_MASK;
if (threadID < 0)
return B_BAD_VALUE;
Thread* thread = Thread::Get(threadID);
if (thread == NULL)
return B_BAD_VALUE;
BReference<Thread> threadReference(thread, true);
if (thread->team == team_get_kernel_team())
return B_NOT_ALLOWED;
InterruptsSpinLocker timeLocker(thread->time_lock);
bigtime_t diff = time - thread->CPUTime(false);
thread->cpu_clock_offset += diff;
thread_clock_changed(thread, diff);
return B_OK;
} else {
teamID = clockID & CPUCLOCK_ID_MASK;
if (teamID < 0)
return B_BAD_VALUE;
if (teamID == team_get_kernel_team_id())
return B_NOT_ALLOWED;
}
Team* team = Team::Get(teamID);
if (team == NULL)
return B_BAD_VALUE;
BReference<Team> teamReference(team, true);
InterruptsSpinLocker timeLocker(team->time_lock);
bigtime_t diff = time - team->CPUTime(false);
team->cpu_clock_offset += diff;
team_clock_changed(team, diff);
return B_OK;
}
}
return B_OK;
}
status_t
_user_get_cpuclockid(thread_id id, int32 which, clockid_t* userclockID)
{
clockid_t clockID;
if (which != TEAM_ID && which != THREAD_ID)
return B_BAD_VALUE;
if (which == TEAM_ID) {
Team* team = Team::Get(id);
if (team == NULL)
return B_BAD_VALUE;
clockID = id | CPUCLOCK_TEAM;
} else if (which == THREAD_ID) {
Thread* thread = Thread::Get(id);
if (thread == NULL)
return B_BAD_VALUE;
clockID = id | CPUCLOCK_THREAD;
}
if (userclockID != NULL
&& (!IS_USER_ADDRESS(userclockID)
|| user_memcpy(userclockID, &clockID, sizeof(clockID)) != B_OK)) {
return B_BAD_ADDRESS;
}
return B_OK;
}
int32
_user_create_timer(clockid_t clockID, thread_id threadID, uint32 flags,
const struct sigevent* userEvent,
const thread_creation_attributes* userThreadAttributes)
{
struct sigevent event = {0};
if (userEvent != NULL) {
if (!IS_USER_ADDRESS(userEvent)
|| user_memcpy(&event, userEvent, sizeof(event)) != B_OK) {
return B_BAD_ADDRESS;
}
} else {
event.sigev_notify = SIGEV_SIGNAL;
event.sigev_signo = SIGALRM;
}
char nameBuffer[B_OS_NAME_LENGTH];
ThreadCreationAttributes threadAttributes;
if (event.sigev_notify == SIGEV_THREAD) {
status_t error = threadAttributes.InitFromUserAttributes(
userThreadAttributes, nameBuffer);
if (error != B_OK)
return error;
}
Team* team = thread_get_current_thread()->team;
Thread* thread = NULL;
if (threadID >= 0) {
thread = Thread::Get(threadID);
if (thread == NULL)
return B_BAD_THREAD_ID;
}
BReference<Thread> threadReference(thread, true);
if (thread != NULL && thread->team != team)
return B_BAD_THREAD_ID;
return create_timer(clockID, -1, team, thread, flags, event,
userThreadAttributes != NULL ? &threadAttributes : NULL,
userEvent == NULL);
}
status_t
_user_delete_timer(int32 timerID, thread_id threadID)
{
if (timerID < USER_TIMER_FIRST_USER_DEFINED_ID)
return B_BAD_VALUE;
TimerLocker timerLocker;
UserTimer* timer;
status_t error = timerLocker.LockAndGetTimer(threadID, timerID, timer);
if (error != B_OK)
return error;
timer->Cancel();
if (threadID >= 0)
timerLocker.thread->RemoveUserTimer(timer);
else
timerLocker.team->RemoveUserTimer(timer);
delete timer;
return B_OK;
}
status_t
_user_get_timer(int32 timerID, thread_id threadID,
struct user_timer_info* userInfo)
{
TimerLocker timerLocker;
UserTimer* timer;
status_t error = timerLocker.LockAndGetTimer(threadID, timerID, timer);
if (error != B_OK)
return error;
user_timer_info info;
timer->GetInfo(info.remaining_time, info.interval, info.overrun_count);
if (info.remaining_time <= 0)
info.remaining_time = 1;
timerLocker.Unlock();
if (userInfo != NULL
&& (!IS_USER_ADDRESS(userInfo)
|| user_memcpy(userInfo, &info, sizeof(info)) != B_OK)) {
return B_BAD_ADDRESS;
}
return B_OK;
}
status_t
_user_set_timer(int32 timerID, thread_id threadID, bigtime_t startTime,
bigtime_t interval, uint32 flags, struct user_timer_info* userOldInfo)
{
if (startTime < 0 || interval < 0)
return B_BAD_VALUE;
TimerLocker timerLocker;
UserTimer* timer;
status_t error = timerLocker.LockAndGetTimer(threadID, timerID, timer);
if (error != B_OK)
return error;
user_timer_info oldInfo;
timer->Schedule(startTime, interval, flags, oldInfo.remaining_time,
oldInfo.interval);
if (oldInfo.remaining_time <= 0)
oldInfo.remaining_time = 1;
timerLocker.Unlock();
if (userOldInfo != NULL
&& (!IS_USER_ADDRESS(userOldInfo)
|| user_memcpy(userOldInfo, &oldInfo, sizeof(oldInfo)) != B_OK)) {
return B_BAD_ADDRESS;
}
return B_OK;
}