/*
 * Copyright 2014, Paweł Dziepak, pdziepak@quarnos.org.
 * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Distributed under the terms of the MIT License.
 */
 
 
#include <UserTimer.h>
 
#include <algorithm>
 
#include <AutoDeleter.h>
 
#include <debug.h>
#include <kernel.h>
#include <real_time_clock.h>
#include <team.h>
#include <thread_types.h>
#include <UserEvent.h>
#include <util/AutoLock.h>
 
 
// Minimum interval length in microseconds for a periodic timer. This is not a
// restriction on the user timer interval length itself, but the minimum time
// span by which we advance the start time for kernel timers. A shorted user
// timer interval will result in the overrun count to be increased every time
// the kernel timer is rescheduled.
static const bigtime_t kMinPeriodicTimerInterval = 100;
 
static RealTimeUserTimerList sAbsoluteRealTimeTimers;
static spinlock sAbsoluteRealTimeTimersLock = B_SPINLOCK_INITIALIZER;
 
static seqlock sUserTimerLock = B_SEQLOCK_INITIALIZER;
 
 
// #pragma mark - TimerLocker
 
 
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();
		}
 
		// We don't check thread->team != team here, since this method can be
		// called for new threads not added to the team yet.
	}
 
	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;
		}
	}
};
 
}	// unnamed namespace
 
 
// #pragma mark - UserTimer
 
 
UserTimer::UserTimer()
	:
	fID(-1),
	fEvent(NULL),
	fNextTime(0),
	fInterval(0),
	fOverrunCount(0),
	fScheduled(false),
	fSkip(0)
{
	// mark the timer unused
	fTimer.user_data = this;
}
 
 
UserTimer::~UserTimer()
{
	if (fEvent != NULL)
		fEvent->ReleaseReference();
}
 
 
/*!	\fn UserTimer::Schedule(bigtime_t nextTime, bigtime_t interval,
		bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval)
	Cancels the timer, if it is already scheduled, and optionally schedules it
	with new parameters.
 
	\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.
	\param interval If <tt> >0 </tt>, the timer will be scheduled to fire
		periodically every \a interval microseconds. Otherwise it will fire
		only once at \a nextTime. If \a nextTime is \c B_INFINITE_TIMEOUT, it
		will fire never in either case.
	\param flags Bitwise OR of flags. Currently \c B_ABSOLUTE_TIMEOUT and
		\c B_RELATIVE_TIMEOUT are supported, indicating whether \a nextTime is
		an absolute or relative time.
	\param _oldRemainingTime 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
		\c B_INFINITE_TIMEOUT.
	\param _oldInterval Return variable that will be set to the interval in
		microseconds the timer was to be scheduled periodically. If the timer
		wasn't periodic, the variable is set to \c 0.
*/
 
 
/*!	Cancels the timer, if it is scheduled.
*/
void
UserTimer::Cancel()
{
	bigtime_t oldNextTime;
	bigtime_t oldInterval;
	return Schedule(B_INFINITE_TIMEOUT, 0, 0, oldNextTime, oldInterval);
}
 
 
/*!	\fn UserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval,
		uint32& _overrunCount)
	Return information on the current timer.
 
	\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
		\c B_INFINITE_TIMEOUT.
	\param _interval Return variable that will be set to the interval in
		microseconds the timer is to be scheduled periodically. If the timer
		isn't periodic, the variable is set to \c 0.
	\param _overrunCount Return variable that will be set to the number of times
		the timer went off, but its event couldn't be delivered, since it's
		previous delivery hasn't been handled yet.
*/
 
 
/*static*/ int32
UserTimer::HandleTimerHook(struct timer* timer)
{
	UserTimer* userTimer = reinterpret_cast<UserTimer*>(timer->user_data);
 
	InterruptsLocker _;
 
	bool locked = false;
	while (!locked && atomic_get(&userTimer->fSkip) == 0) {
		locked = try_acquire_write_seqlock(&sUserTimerLock);
		if (!locked)
			cpu_pause();
	}
 
	if (locked) {
		userTimer->HandleTimer();
		release_write_seqlock(&sUserTimerLock);
	}
 
	return B_HANDLED_INTERRUPT;
}
 
 
void
UserTimer::HandleTimer()
{
	if (fEvent != NULL) {
		// fire the event and update the overrun count, if necessary
		status_t error = fEvent->Fire();
		if (error == B_BUSY) {
			if (fOverrunCount < MAX_USER_TIMER_OVERRUN_COUNT)
				fOverrunCount++;
		}
	}
 
	// Since we don't use periodic kernel timers, it isn't scheduled anymore.
	// If the timer is periodic, the derived class' version will schedule it
	// again.
	fScheduled = false;
}
 
 
/*!	Updates the start time for a periodic timer after it expired, enforcing
	sanity limits and updating \c fOverrunCount, if necessary.
 
	The caller must not hold \c sUserTimerLock.
*/
void
UserTimer::UpdatePeriodicStartTime()
{
	if (fInterval < kMinPeriodicTimerInterval) {
		bigtime_t skip = (kMinPeriodicTimerInterval + fInterval - 1) / fInterval;
		fNextTime += skip * fInterval;
 
		// One interval is the normal advance, so don't consider it skipped.
		skip--;
 
		if (skip + fOverrunCount > MAX_USER_TIMER_OVERRUN_COUNT)
			fOverrunCount = MAX_USER_TIMER_OVERRUN_COUNT;
		else
			fOverrunCount += skip;
	} else
		fNextTime += fInterval;
}
 
 
/*!	Checks whether the timer start time lies too much in the past and, if so,
	adjusts it and updates \c fOverrunCount.
 
	The caller must not hold \c sUserTimerLock.
 
	\param now The current time.
*/
void
UserTimer::CheckPeriodicOverrun(bigtime_t now)
{
	if (fNextTime + fInterval > now)
		return;
 
	// The start time is a full interval or more in the past. Skip those
	// intervals.
	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;
}
 
 
void
UserTimer::CancelTimer()
{
	ASSERT(fScheduled);
 
	atomic_set(&fSkip, 1);
	cancel_timer(&fTimer);
	atomic_set(&fSkip, 0);
}
 
 
// #pragma mark - SystemTimeUserTimer
 
 
void
SystemTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval,
	uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval)
{
	InterruptsWriteSequentialLocker locker(sUserTimerLock);
 
	// get the current time
	bigtime_t now = system_time();
 
	// Cancel the old timer, if still scheduled, and get the previous values.
	if (fScheduled) {
		CancelTimer();
 
		_oldRemainingTime = fNextTime - now;
		_oldInterval = fInterval;
 
		fScheduled = false;
	} else {
		_oldRemainingTime = B_INFINITE_TIMEOUT;
		_oldInterval = 0;
	}
 
	// schedule the new timer
	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(&sUserTimerLock);
 
		if (fScheduled) {
			_remainingTime = fNextTime - system_time();
			_interval = fInterval;
		} else {
			_remainingTime = B_INFINITE_TIMEOUT;
			_interval = 0;
		}
 
		_overrunCount = fOverrunCount;
	} while (!release_read_seqlock(&sUserTimerLock, count));
}
 
 
void
SystemTimeUserTimer::HandleTimer()
{
	UserTimer::HandleTimer();
 
	// if periodic, reschedule the kernel timer
	if (fInterval > 0) {
		UpdatePeriodicStartTime();
		ScheduleKernelTimer(system_time(), true);
	}
}
 
 
/*!	Schedules the kernel timer.
 
	The caller must hold \c sUserTimerLock.
 
	\param now The current system time to be used.
	\param checkPeriodicOverrun If \c true, calls CheckPeriodicOverrun() first,
		i.e. the start time will be adjusted to not lie too much in the past.
*/
void
SystemTimeUserTimer::ScheduleKernelTimer(bigtime_t now,
	bool checkPeriodicOverrun)
{
	// If periodic, check whether the start time is too far in the past.
	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;
}
 
 
// #pragma mark - RealTimeUserTimer
 
 
void
RealTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval,
	uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval)
{
	InterruptsWriteSequentialLocker locker(sUserTimerLock);
 
	// get the current time
	bigtime_t now = system_time();
 
	// Cancel the old timer, if still scheduled, and get the previous values.
	if (fScheduled) {
		CancelTimer();
 
		_oldRemainingTime = fNextTime - now;
		_oldInterval = fInterval;
 
		if (fAbsolute) {
			SpinLocker globalListLocker(sAbsoluteRealTimeTimersLock);
			sAbsoluteRealTimeTimers.Remove(this);
		}
 
		fScheduled = false;
	} else {
		_oldRemainingTime = B_INFINITE_TIMEOUT;
		_oldInterval = 0;
	}
 
	// schedule the new timer
	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 periodic, check whether the start time is too far in the past.
		if (fInterval > 0)
			CheckPeriodicOverrun(now);
 
		// add the absolute timer to the global list
		SpinLocker globalListLocker(sAbsoluteRealTimeTimersLock);
		sAbsoluteRealTimeTimers.Insert(this);
	} else
		fNextTime += now;
 
	ScheduleKernelTimer(now, false);
}
 
 
/*!	Called when the real-time clock has been changed.
 
	The caller must hold \c sUserTimerLock. Optionally the caller may also
	hold \c sAbsoluteRealTimeTimersLock.
*/
void
RealTimeUserTimer::TimeWarped()
{
	ASSERT(fScheduled && fAbsolute);
 
	// get the new real-time offset
	bigtime_t oldRealTimeOffset = fRealTimeOffset;
	fRealTimeOffset = rtc_boot_time();
	if (fRealTimeOffset == oldRealTimeOffset)
		return;
 
	// cancel the kernel timer and reschedule it
	CancelTimer();
 
	fNextTime += oldRealTimeOffset - fRealTimeOffset;
 
	ScheduleKernelTimer(system_time(), fInterval > 0);
}
 
 
void
RealTimeUserTimer::HandleTimer()
{
	SystemTimeUserTimer::HandleTimer();
 
	// remove from global list, if no longer scheduled
	if (!fScheduled && fAbsolute) {
		SpinLocker globalListLocker(sAbsoluteRealTimeTimersLock);
		sAbsoluteRealTimeTimers.Remove(this);
	}
}
 
 
// #pragma mark - TeamTimeUserTimer
 
 
TeamTimeUserTimer::TeamTimeUserTimer(team_id teamID)
	:
	fTeamID(teamID),
	fTeam(NULL)
{
}
 
 
TeamTimeUserTimer::~TeamTimeUserTimer()
{
	ASSERT(fTeam == NULL);
}
 
 
void
TeamTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval,
	uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval)
{
	InterruptsWriteSequentialLocker locker(sUserTimerLock);
	SpinLocker timeLocker(fTeam != NULL ? &fTeam->time_lock : NULL);
 
	// get the current time, but only if needed
	bool nowValid = fTeam != NULL;
	bigtime_t now = nowValid ? fTeam->CPUTime(false) : 0;
 
	// Cancel the old timer, if still scheduled, and get the previous values.
	if (fTeam != NULL) {
		if (fScheduled) {
			CancelTimer();
			fScheduled = false;
		}
 
		_oldRemainingTime = fNextTime - now;
		_oldInterval = fInterval;
 
		fTeam->UserTimerDeactivated(this);
		fTeam->ReleaseReference();
		fTeam = NULL;
	} else {
		_oldRemainingTime = B_INFINITE_TIMEOUT;
		_oldInterval = 0;
	}
 
	// schedule the new timer
	fNextTime = nextTime;
	fInterval = interval;
	fOverrunCount = 0;
 
	if (fNextTime == B_INFINITE_TIMEOUT)
		return;
 
	// Get the team. If it doesn't exist anymore, just don't schedule the
	// timer anymore.
	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;
 
	// convert relative to absolute timeouts
	if (!fAbsolute) {
		if (!nowValid)
			now = fTeam->CPUTime(false);
		fNextTime += now;
	}
 
	fTeam->UserTimerActivated(this);
 
	// schedule/udpate the kernel timer
	Update(NULL);
}
 
 
void
TeamTimeUserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval,
	uint32& _overrunCount)
{
	uint32 count;
	do {
		count = acquire_read_seqlock(&sUserTimerLock);
 
		if (fTeam != NULL) {
			InterruptsSpinLocker timeLocker(fTeam->time_lock);
			_remainingTime = fNextTime - fTeam->CPUTime(false);
			_interval = fInterval;
		} else {
			_remainingTime = B_INFINITE_TIMEOUT;
			_interval = 0;
		}
 
		_overrunCount = fOverrunCount;
	} while (!release_read_seqlock(&sUserTimerLock, count));
}
 
 
/*!	Deactivates the timer, if it is activated.
 
	The caller must hold \c time_lock and \c sUserTimerLock.
*/
void
TeamTimeUserTimer::Deactivate()
{
	if (fTeam == NULL)
		return;
 
	// unschedule, if scheduled
	if (fScheduled) {
		CancelTimer();
		fScheduled = false;
	}
 
	// deactivate
	fTeam->UserTimerDeactivated(this);
	fTeam->ReleaseReference();
	fTeam = NULL;
}
 
 
/*!	Starts/stops the timer as necessary, if it is active.
 
	Called whenever threads of the team whose CPU time is referred to by the
	timer are scheduled or unscheduled (or leave the team), or when the timer
	was just set. Schedules a kernel timer for the remaining time, respectively
	cancels it.
 
	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.
*/
void
TeamTimeUserTimer::Update(Thread* unscheduledThread, Thread* lockedThread)
{
	if (fTeam == NULL)
		return;
 
	// determine how many of the team's threads are currently running
	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++;
	}
 
	_Update(unscheduledThread != NULL, lockedThread);
}
 
 
/*!	Called when the team's CPU time clock which this timer refers to has been
	set.
 
	The caller must hold \c time_lock and \c sUserTimerLock.
 
	\param changedBy The value by which the clock has changed.
*/
void
TeamTimeUserTimer::TimeWarped(bigtime_t changedBy)
{
	if (fTeam == NULL || changedBy == 0)
		return;
 
	// If this is a relative timer, adjust fNextTime by the value the clock has
	// changed.
	if (!fAbsolute)
		fNextTime += changedBy;
 
	// reschedule the kernel timer
	_Update(false);
}
 
 
void
TeamTimeUserTimer::HandleTimer()
{
	UserTimer::HandleTimer();
 
	// If the timer is not periodic, it is no longer active. Otherwise
	// reschedule the kernel timer.
	if (fTeam != NULL) {
		if (fInterval == 0) {
			fTeam->UserTimerDeactivated(this);
			fTeam->ReleaseReference();
			fTeam = NULL;
		} else {
			UpdatePeriodicStartTime();
			_Update(false);
		}
	}
}
 
 
/*!	Schedules/cancels the kernel timer as necessary.
 
	\c fRunningThreads must be up-to-date.
	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.
*/
void
TeamTimeUserTimer::_Update(bool unscheduling, Thread* lockedThread)
{
	// unschedule the kernel timer, if scheduled
	if (fScheduled)
		CancelTimer();
 
	// if no more threads are running, we're done
	if (fRunningThreads == 0) {
		fScheduled = false;
		return;
	}
 
	// There are still threads running. Reschedule the kernel timer.
	bigtime_t now = fTeam->CPUTime(unscheduling, lockedThread);
 
	// If periodic, check whether the start time is too far in the past.
	if (fInterval > 0)
		CheckPeriodicOverrun(now);
 
	if (fNextTime > now) {
		fTimer.schedule_time = system_time()
			+ (fNextTime - now + fRunningThreads - 1) / fRunningThreads;
		// check for overflow
		if (fTimer.schedule_time < 0)
			fTimer.schedule_time = B_INFINITE_TIMEOUT;
	} else
		fTimer.schedule_time = 0;
	fTimer.period = 0;
		// We reschedule periodic timers manually in HandleTimer() to avoid
		// rounding errors.
 
	add_timer(&fTimer, &HandleTimerHook, fTimer.schedule_time,
		B_ONE_SHOT_ABSOLUTE_TIMER | B_TIMER_USE_TIMER_STRUCT_TIMES);
		// We use B_TIMER_USE_TIMER_STRUCT_TIMES, so period remains 0, which
		// our base class expects.
 
	fScheduled = true;
}
 
 
// #pragma mark - TeamUserTimeUserTimer
 
 
TeamUserTimeUserTimer::TeamUserTimeUserTimer(team_id teamID)
	:
	fTeamID(teamID),
	fTeam(NULL)
{
}
 
 
TeamUserTimeUserTimer::~TeamUserTimeUserTimer()
{
	ASSERT(fTeam == NULL);
}
 
 
void
TeamUserTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval,
	uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval)
{
	InterruptsWriteSequentialLocker locker(sUserTimerLock);
	SpinLocker timeLocker(fTeam != NULL ? &fTeam->time_lock : NULL);
 
	// get the current time, but only if needed
	bool nowValid = fTeam != NULL;
	bigtime_t now = nowValid ? fTeam->UserCPUTime() : 0;
 
	// Cancel the old timer, if still active, and get the previous values.
	if (fTeam != NULL) {
		_oldRemainingTime = fNextTime - now;
		_oldInterval = fInterval;
 
		fTeam->UserTimerDeactivated(this);
		fTeam->ReleaseReference();
		fTeam = NULL;
	} else {
		_oldRemainingTime = B_INFINITE_TIMEOUT;
		_oldInterval = 0;
	}
 
	// schedule the new timer
	fNextTime = nextTime;
	fInterval = interval;
	fOverrunCount = 0;
 
	if (fNextTime == B_INFINITE_TIMEOUT)
		return;
 
	// Get the team. If it doesn't exist anymore, just don't schedule the
	// timer anymore.
	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) {
		if (!nowValid)
			now = fTeam->CPUTime(false);
		fNextTime += now;
	}
 
	fTeam->UserTimerActivated(this);
 
	// fire the event, if already timed out
	Check();
}
 
 
void
TeamUserTimeUserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval,
	uint32& _overrunCount)
{
	uint32 count;
	do {
		count = acquire_read_seqlock(&sUserTimerLock);
 
		if (fTeam != NULL) {
			InterruptsSpinLocker timeLocker(fTeam->time_lock);
			_remainingTime = fNextTime - fTeam->UserCPUTime();
			_interval = fInterval;
		} else {
			_remainingTime = B_INFINITE_TIMEOUT;
			_interval = 0;
		}
 
		_overrunCount = fOverrunCount;
	} while (!release_read_seqlock(&sUserTimerLock, count));
}
 
 
/*!	Deactivates the timer, if it is activated.
 
	The caller must hold \c time_lock and \c sUserTimerLock.
*/
void
TeamUserTimeUserTimer::Deactivate()
{
	if (fTeam == NULL)
		return;
 
	// deactivate
	fTeam->UserTimerDeactivated(this);
	fTeam->ReleaseReference();
	fTeam = NULL;
}
 
 
/*!	Checks whether the timer is up, firing an event, if so.
 
	The caller must hold \c time_lock and \c sUserTimerLock.
*/
void
TeamUserTimeUserTimer::Check()
{
	if (fTeam == NULL)
		return;
 
	// check whether we need to fire the event yet
	bigtime_t now = fTeam->UserCPUTime();
	if (now < fNextTime)
		return;
 
	HandleTimer();
 
	// If the timer is not periodic, it is no longer active. Otherwise compute
	// the event time.
	if (fInterval == 0) {
		fTeam->UserTimerDeactivated(this);
		fTeam->ReleaseReference();
		fTeam = NULL;
		return;
	}
 
	// First validate fNextTime, then increment it, so that fNextTime is > now
	// (CheckPeriodicOverrun() only makes it > now - fInterval).
	CheckPeriodicOverrun(now);
	fNextTime += fInterval;
	fScheduled = true;
}
 
 
// #pragma mark - ThreadTimeUserTimer
 
 
ThreadTimeUserTimer::ThreadTimeUserTimer(thread_id threadID)
	:
	fThreadID(threadID),
	fThread(NULL)
{
}
 
 
ThreadTimeUserTimer::~ThreadTimeUserTimer()
{
	ASSERT(fThread == NULL);
}
 
 
void
ThreadTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval,
	uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval)
{
	InterruptsWriteSequentialLocker locker(sUserTimerLock);
	SpinLocker timeLocker(fThread != NULL ? &fThread->time_lock : NULL);
 
	// get the current time, but only if needed
	bool nowValid = fThread != NULL;
	bigtime_t now = nowValid ? fThread->CPUTime(false) : 0;
 
	// Cancel the old timer, if still scheduled, and get the previous values.
	if (fThread != NULL) {
		if (fScheduled) {
			CancelTimer();
			fScheduled = false;
		}
 
		_oldRemainingTime = fNextTime - now;
		_oldInterval = fInterval;
 
		fThread->UserTimerDeactivated(this);
		fThread->ReleaseReference();
		fThread = NULL;
	} else {
		_oldRemainingTime = B_INFINITE_TIMEOUT;
		_oldInterval = 0;
	}
 
	// schedule the new timer
	fNextTime = nextTime;
	fInterval = interval;
	fOverrunCount = 0;
 
	if (fNextTime == B_INFINITE_TIMEOUT)
		return;
 
	// Get the thread. If it doesn't exist anymore, just don't schedule the
	// timer anymore.
	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;
 
	// convert relative to absolute timeouts
	if (!fAbsolute) {
		if (!nowValid)
			now = fThread->CPUTime(false);
		fNextTime += now;
	}
 
	fThread->UserTimerActivated(this);
 
	// If the thread is currently running, also schedule a kernel timer.
	if (fThread->cpu != NULL)
		Start();
}
 
 
void
ThreadTimeUserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval,
	uint32& _overrunCount)
{
	uint32 count;
	do {
		count = acquire_read_seqlock(&sUserTimerLock);
 
		if (fThread != NULL) {
			SpinLocker timeLocker(fThread->time_lock);
			_remainingTime = fNextTime - fThread->CPUTime(false);
			_interval = fInterval;
		} else {
			_remainingTime = B_INFINITE_TIMEOUT;
			_interval = 0;
		}
 
		_overrunCount = fOverrunCount;
	} while (!release_read_seqlock(&sUserTimerLock, count));
}
 
 
/*!	Deactivates the timer, if it is activated.
 
	The caller must hold \c time_lock and \c sUserTimerLock.
*/
void
ThreadTimeUserTimer::Deactivate()
{
	if (fThread == NULL)
		return;
 
	// unschedule, if scheduled
	if (fScheduled) {
		CancelTimer();
		fScheduled = false;
	}
 
	// deactivate
	fThread->UserTimerDeactivated(this);
	fThread->ReleaseReference();
	fThread = NULL;
}
 
 
/*!	Starts the timer, if it is active.
 
	Called when the thread whose CPU time is referred to by the timer is
	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 \c time_lock and \c sUserTimerLock.
*/
void
ThreadTimeUserTimer::Start()
{
	if (fThread == NULL)
		return;
 
	ASSERT(!fScheduled);
 
	// add the kernel timer
	bigtime_t now = fThread->CPUTime(false);
 
	// If periodic, check whether the start time is too far in the past.
	if (fInterval > 0)
		CheckPeriodicOverrun(now);
 
	if (fNextTime > now) {
		fTimer.schedule_time = system_time() + fNextTime - now;
		// check for overflow
		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);
 
	fScheduled = true;
}
 
 
/*!	Stops the timer, if it is active.
 
	Called when the thread whose CPU time is referred to by the timer is
	unscheduled, or, when the timer is canceled.
 
	The caller must hold \c sUserTimerLock.
*/
void
ThreadTimeUserTimer::Stop()
{
	if (fThread == NULL)
		return;
 
	ASSERT(fScheduled);
 
	// cancel the kernel timer
	CancelTimer();
	fScheduled = false;
 
	// TODO: To avoid odd race conditions, we should check the current time of
	// the thread (ignoring the time since last_time) and manually fire the
	// user event, if necessary.
}
 
 
/*!	Called when the team's CPU time clock which this timer refers to has been
	set.
 
	The caller must hold \c time_lock and \c sUserTimerLock.
 
	\param changedBy The value by which the clock has changed.
*/
void
ThreadTimeUserTimer::TimeWarped(bigtime_t changedBy)
{
	if (fThread == NULL || changedBy == 0)
		return;
 
	// If this is a relative timer, adjust fNextTime by the value the clock has
	// changed.
	if (!fAbsolute)
		fNextTime += changedBy;
 
	// reschedule the kernel timer
	if (fScheduled) {
		Stop();
		Start();
	}
}
 
 
void
ThreadTimeUserTimer::HandleTimer()
{
	UserTimer::HandleTimer();
 
	if (fThread != NULL) {
		// If the timer is periodic, reschedule the kernel timer. Otherwise it
		// is no longer active.
		if (fInterval > 0) {
			UpdatePeriodicStartTime();
			Start();
		} else {
			fThread->UserTimerDeactivated(this);
			fThread->ReleaseReference();
			fThread = NULL;
		}
	}
}
 
 
// #pragma mark - UserTimerList
 
 
UserTimerList::UserTimerList()
{
}
 
 
UserTimerList::~UserTimerList()
{
	ASSERT(fTimers.IsEmpty());
}
 
 
/*!	Returns the user timer with the given ID.
 
	\param id The timer's ID
	\return The user timer with the given ID or \c NULL, if there is no such
		timer.
*/
UserTimer*
UserTimerList::TimerFor(int32 id) const
{
	// TODO: Use a more efficient data structure. E.g. a sorted array.
	for (TimerList::ConstIterator it = fTimers.GetIterator();
			UserTimer* timer = it.Next();) {
		if (timer->ID() == id)
			return timer;
	}
 
	return NULL;
}
 
 
/*!	Adds the given user timer and assigns it an ID.
 
	\param timer The timer to be added.
*/
void
UserTimerList::AddTimer(UserTimer* timer)
{
	int32 id = timer->ID();
	if (id < 0) {
		// user-defined timer -- find an usused ID
		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;
		}
 
		// insert the timer
		timer->SetID(id);
		fTimers.InsertAfter(insertAfter, timer);
	} else {
		// default timer -- find the insertion point
		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;
		}
 
		// insert the timer
		fTimers.InsertAfter(insertAfter, timer);
	}
}
 
 
/*!	Deletes all (or all user-defined) user timers.
 
	\param userDefinedOnly If \c true, only the user-defined timers are deleted,
		otherwise all timers are deleted.
	\return The number of user-defined timers that were removed and deleted.
*/
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++;
 
		// remove, cancel, and delete the timer
		it.Remove();
		timer->Cancel();
		delete timer;
	}
 
	return userDefinedCount;
}
 
 
// #pragma mark - private
 
 
static int32
create_timer(clockid_t clockID, int32 timerID, Team* team, Thread* thread,
	uint32 flags, const struct sigevent& event,
	ThreadCreationAttributes* threadAttributes, bool isDefaultEvent)
{
	// create the timer object
	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()->id);
			break;
 
		case CLOCK_PROCESS_CPUTIME_ID:
			if (team == NULL)
				return B_BAD_VALUE;
			timer = new(std::nothrow) TeamTimeUserTimer(team->id);
			break;
 
		case CLOCK_PROCESS_USER_CPUTIME_ID:
			if (team == NULL)
				return B_BAD_VALUE;
			timer = new(std::nothrow) TeamUserTimeUserTimer(team->id);
			break;
 
		default:
		{
			// The clock ID is a ID of the team whose CPU time the clock refers
			// to. Check whether the team exists and we have permission to
			// access its clock.
			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->UnlockAndReleaseReference();
 
			if (uid != 0 && uid != teamUID)
				return B_NOT_ALLOWED;
 
			timer = new(std::nothrow) TeamTimeUserTimer(clockID);
			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:
			// the timer's event remains NULL
			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) {
				// The signal shall be sent to the thread.
				signalEvent = ThreadSignalEvent::Create(thread,
					event.sigev_signo, SI_TIMER, 0, team->id);
			} else {
				// The signal shall be sent to the team.
				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;
	}
 
	// add it to the team/thread
	TimerLocker timerLocker;
	timerLocker.Lock(team, thread);
 
	status_t error = thread != NULL
		? thread->AddUserTimer(timer) : team->AddUserTimer(timer);
	if (error != B_OK)
		return error;
 
	// set a signal event's user value
	if (signalEvent != NULL) {
		// If no sigevent structure was given, use the timer ID.
		union sigval signalValue = event.sigev_value;
		if (isDefaultEvent)
			signalValue.sival_int = timer->ID();
 
		signalEvent->SetUserValue(signalValue);
	}
 
	return timerDeleter.Detach()->ID();
}
 
 
/*!	Called when the CPU time clock of the given thread has been set.
 
	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
		(new = old + changedBy).
*/
static void
thread_clock_changed(Thread* thread, bigtime_t changedBy)
{
	for (ThreadTimeUserTimerList::ConstIterator it
				= thread->CPUTimeUserTimerIterator();
			ThreadTimeUserTimer* timer = it.Next();) {
		timer->TimeWarped(changedBy);
	}
}
 
 
/*!	Called when the CPU time clock of the given team has been set.
 
	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
		(new = old + changedBy).
*/
static void
team_clock_changed(Team* team, bigtime_t changedBy)
{
	for (TeamTimeUserTimerList::ConstIterator it
				= team->CPUTimeUserTimerIterator();
			TeamTimeUserTimer* timer = it.Next();) {
		timer->TimeWarped(changedBy);
	}
}
 
 
// #pragma mark - kernel private
 
 
/*!	Creates the pre-defined user timers for the given thread.
	The thread may not have been added to its team yet, hence the team must be
	passed
 
	\param team The thread's (future) team.
	\param thread The thread whose pre-defined timers shall be created.
	\return \c B_OK, when everything when fine, another error code otherwise.
*/
status_t
user_timer_create_thread_timers(Team* team, Thread* thread)
{
	// create a real time user timer
	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;
}
 
 
/*!	Creates the pre-defined user timers for the given team.
 
	\param team The team whose pre-defined timers shall be created.
	\return \c B_OK, when everything when fine, another error code otherwise.
*/
status_t
user_timer_create_team_timers(Team* team)
{
	// create a real time user timer
	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;
 
	// create a total CPU time user timer
	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;
 
	// create a user CPU time user timer
	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:
		{
			// get the ID of the target team (or the respective placeholder)
			team_id teamID;
			if (clockID == CLOCK_PROCESS_CPUTIME_ID) {
				teamID = B_CURRENT_TEAM;
			} else {
				if (clockID < 0)
					return B_BAD_VALUE;
				if (clockID == team_get_kernel_team_id())
					return B_NOT_ALLOWED;
 
				teamID = clockID;
			}
 
			// get the team
			Team* team = Team::Get(teamID);
			if (team == NULL)
				return B_BAD_VALUE;
			BReference<Team> teamReference(team, true);
 
			// get the time
			InterruptsSpinLocker timeLocker(team->time_lock);
			_time = team->CPUTime(false);
 
			return B_OK;
		}
	}
}
 
 
void
user_timer_real_time_clock_changed()
{
	// we need to update all absolute real-time timers
	InterruptsWriteSequentialLocker locker(sUserTimerLock);
	SpinLocker globalListLocker(sAbsoluteRealTimeTimersLock);
 
	for (RealTimeUserTimerList::Iterator it
				= sAbsoluteRealTimeTimers.GetIterator();
			RealTimeUserTimer* timer = it.Next();) {
		timer->TimeWarped();
	}
}
 
 
void
user_timer_stop_cpu_timers(Thread* thread, Thread* nextThread)
{
	// stop thread timers
	for (ThreadTimeUserTimerList::ConstIterator it
				= thread->CPUTimeUserTimerIterator();
			ThreadTimeUserTimer* timer = it.Next();) {
		timer->Stop();
	}
 
	// update team timers
	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)
{
	// update team timers
	if (previousThread == NULL || previousThread->team != thread->team) {
		for (TeamTimeUserTimerList::ConstIterator it
					= thread->team->CPUTimeUserTimerIterator();
				TeamTimeUserTimer* timer = it.Next();) {
			timer->Update(NULL, thread);
		}
	}
 
	// start thread timers
	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();
	}
}
 
 
// #pragma mark - syscalls
 
 
status_t
_user_get_clock(clockid_t clockID, bigtime_t* userTime)
{
	// get the time
	bigtime_t time;
	status_t error = user_timer_get_clock(clockID, time);
	if (error != B_OK)
		return error;
 
	// copy the value back to userland
	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:
			// only root may set the time
			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:
			// not supported -- this clock is an Haiku-internal extension
			return B_BAD_VALUE;
 
		case CLOCK_PROCESS_CPUTIME_ID:
		default:
		{
			// get the ID of the target team (or the respective placeholder)
			team_id teamID;
			if (clockID == CLOCK_PROCESS_CPUTIME_ID) {
				teamID = B_CURRENT_TEAM;
			} else {
				if (clockID < 0)
					return B_BAD_VALUE;
				if (clockID == team_get_kernel_team_id())
					return B_NOT_ALLOWED;
 
				teamID = clockID;
			}
 
			// get the team
			Team* team = Team::Get(teamID);
			if (team == NULL)
				return B_BAD_VALUE;
			BReference<Team> teamReference(team, true);
 
			// set the time offset
			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;
}
 
 
int32
_user_create_timer(clockid_t clockID, thread_id threadID, uint32 flags,
	const struct sigevent* userEvent,
	const thread_creation_attributes* userThreadAttributes)
{
	// copy the sigevent structure from userland
	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 {
		// none given -- use defaults
		event.sigev_notify = SIGEV_SIGNAL;
		event.sigev_signo = SIGALRM;
	}
 
	// copy thread creation attributes from userland, if specified
	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;
	}
 
	// get team and thread
	Team* team = thread_get_current_thread()->team;
	Thread* thread = NULL;
	if (threadID >= 0) {
		thread = Thread::GetAndLock(threadID);
		if (thread == NULL)
			return B_BAD_THREAD_ID;
 
		thread->Unlock();
	}
	BReference<Thread> threadReference(thread, true);
 
	// create the timer
	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)
{
	// can only delete user-defined timers
	if (timerID < USER_TIMER_FIRST_USER_DEFINED_ID)
		return B_BAD_VALUE;
 
	// get the timer
	TimerLocker timerLocker;
	UserTimer* timer;
	status_t error = timerLocker.LockAndGetTimer(threadID, timerID, timer);
	if (error != B_OK)
		return error;
 
	// cancel, remove, and delete it
	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)
{
	// get the timer
	TimerLocker timerLocker;
	UserTimer* timer;
	status_t error = timerLocker.LockAndGetTimer(threadID, timerID, timer);
	if (error != B_OK)
		return error;
 
	// get the info
	user_timer_info info;
	timer->GetInfo(info.remaining_time, info.interval, info.overrun_count);
 
	// Sanitize remaining_time. If it's <= 0, we set it to 1, the least valid
	// value.
	if (info.remaining_time <= 0)
		info.remaining_time = 1;
 
	timerLocker.Unlock();
 
	// copy it back to userland
	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)
{
	// check the values
	if (startTime < 0 || interval < 0)
		return B_BAD_VALUE;
 
	// get the timer
	TimerLocker timerLocker;
	UserTimer* timer;
	status_t error = timerLocker.LockAndGetTimer(threadID, timerID, timer);
	if (error != B_OK)
		return error;
 
	// schedule the timer
	user_timer_info oldInfo;
	timer->Schedule(startTime, interval, flags, oldInfo.remaining_time,
		oldInfo.interval);
 
	// Sanitize remaining_time. If it's <= 0, we set it to 1, the least valid
	// value.
	if (oldInfo.remaining_time <= 0)
		oldInfo.remaining_time = 1;
 
	timerLocker.Unlock();
 
	// copy back the old info
	if (userOldInfo != NULL
		&& (!IS_USER_ADDRESS(userOldInfo)
			|| user_memcpy(userOldInfo, &oldInfo, sizeof(oldInfo)) != B_OK)) {
		return B_BAD_ADDRESS;
	}
 
	return B_OK;
}

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fRunningThreads, fAbsolute.

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fAbsolute.