mirror of
https://gitlab.com/sortix/sortix.git
synced 2023-02-13 20:55:38 -05:00
4daedc31f7
Support zero relative and absolute times in the timer API.
151 lines
3.9 KiB
C++
151 lines
3.9 KiB
C++
/*
|
|
* Copyright (c) 2013, 2016, 2017, 2021 Jonas 'Sortie' Termansen.
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*
|
|
* timer.cpp
|
|
* Clock and timer facility.
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <timespec.h>
|
|
|
|
#include <sortix/kernel/clock.h>
|
|
#include <sortix/kernel/interrupt.h>
|
|
#include <sortix/kernel/kernel.h>
|
|
#include <sortix/kernel/kthread.h>
|
|
#include <sortix/kernel/timer.h>
|
|
|
|
namespace Sortix {
|
|
|
|
// The caller should protect the timer using a lock if multiple threads can
|
|
// access it.
|
|
|
|
Timer::Timer()
|
|
{
|
|
value = { timespec_nul(), timespec_nul() };
|
|
clock = NULL;
|
|
prev_timer = NULL;
|
|
next_timer = NULL;
|
|
next_interrupt_timer = NULL;
|
|
callback = NULL;
|
|
user = NULL;
|
|
num_firings_scheduled = 0;
|
|
num_overrun_events = 0;
|
|
flags = 0;
|
|
}
|
|
|
|
Timer::~Timer()
|
|
{
|
|
// TODO: Is this the right thing?
|
|
// The user of this object should cancel all pending triggers before calling
|
|
// the destructor. We could try to cancel the timer, but if we fail the race
|
|
// the handler function will be called, and that function may access the
|
|
// timer object. We'd then have to wait for the function to finish and then
|
|
// continue the destuction. This is a bit complex and fragile, so we'll just
|
|
// make it the rule that all outstanding requests must be cancelled before
|
|
// our destruction. The caller should know better than we how to cancel.
|
|
assert(!(flags & TIMER_ACTIVE));
|
|
}
|
|
|
|
void Timer::Attach(Clock* the_clock)
|
|
{
|
|
assert(!clock);
|
|
assert(the_clock);
|
|
clock = the_clock;
|
|
}
|
|
|
|
void Timer::Detach()
|
|
{
|
|
assert(!(flags & TIMER_ACTIVE));
|
|
assert(clock);
|
|
clock = NULL;
|
|
}
|
|
|
|
void Timer::Cancel()
|
|
{
|
|
if ( clock )
|
|
clock->Cancel(this);
|
|
}
|
|
|
|
bool Timer::TryCancel()
|
|
{
|
|
if ( clock )
|
|
return clock->TryCancel(this);
|
|
return true;
|
|
}
|
|
|
|
void Timer::GetInternal(struct itimerspec* current)
|
|
{
|
|
if ( !(this->flags & TIMER_ACTIVE ) )
|
|
current->it_value = timespec_nul(),
|
|
current->it_interval = timespec_nul();
|
|
else if ( flags & TIMER_ABSOLUTE )
|
|
current->it_value = timespec_sub(value.it_value, clock->current_time),
|
|
current->it_interval = value.it_interval;
|
|
else
|
|
*current = value;
|
|
}
|
|
|
|
void Timer::Get(struct itimerspec* current)
|
|
{
|
|
assert(clock);
|
|
|
|
clock->LockClock();
|
|
GetInternal(current);
|
|
clock->UnlockClock();
|
|
}
|
|
|
|
void Timer::Set(struct itimerspec* new_value, struct itimerspec* old_value,
|
|
int new_flags, void (*new_callback)(Clock*, Timer*, void*),
|
|
void* new_user)
|
|
{
|
|
assert(clock);
|
|
assert(!(flags & TIMER_FUNC_MAY_DEALLOCATE_TIMER) ||
|
|
timespec_le(new_value->it_interval, timespec_nul()));
|
|
|
|
clock->LockClock();
|
|
|
|
// Dequeue this timer if it is already armed.
|
|
if ( flags & TIMER_ACTIVE )
|
|
{
|
|
// TODO: How does this interplay with concurrently running timer
|
|
// handlers? Maybe the timer should be cancelled instead?
|
|
clock->Unlink(this);
|
|
}
|
|
|
|
// Let the caller know how much time was left on the timer.
|
|
if ( old_value )
|
|
GetInternal(old_value);
|
|
|
|
// Arm the timer if a value was specified.
|
|
if ( !(new_flags & TIMER_DISARM) )
|
|
{
|
|
value = *new_value;
|
|
flags = new_flags;
|
|
callback = new_callback;
|
|
user = new_user;
|
|
if ( !(flags & TIMER_ABSOLUTE) && value.it_value.tv_sec < 0 )
|
|
value.it_value = timespec_nul();
|
|
clock->Register(this);
|
|
}
|
|
else
|
|
{
|
|
value.it_value = timespec_nul();
|
|
value.it_interval = timespec_nul();
|
|
}
|
|
|
|
clock->UnlockClock();
|
|
}
|
|
|
|
} // namespace Sortix
|