The timer server includes a timer allocator to allocate timers and delivers virtual clock interrupts as timers expire.
<Off timer server. >= (U->)
// A timer server.
//
class off_TmrSrv : public off_HWCompResource {
public:
// Returns a pointer to the timer allocator.
virtual off_TmrAllocator *get_allocator(void);
<Other public methods of off_TmrSrv. >
protected:
<Other protected methods of off_TmrSrv. >
private:
off_TmrAllocator t_alloc;
<Other private members of off_TmrSrv. >
};
Definesoff_TmrSrv(links are to index).
<off_TmrSrv::get_allocator implementation. >= (U->)
inline off_TmrAllocator *off_TmrSrv::get_allocator(void) {
assert(valid()); return &t_alloc;
}
Being a subclass of CompResource we must implement
make_available. This method does not have sense for timers thus it
simply returns an error code4.7.
<Other public methods ofoff_TmrSrv. >= (<-U) [D->] // Is this identifier a local one? inline boolean_t is_local(const off_tmr_id_t &tmr); <Implementation ofoff_CompResource::make_available. >
Such routine uses is_local.
<off_TmrSrv::is_local implementation. >= (U->)
// Is this identifier a local one?
inline boolean_t off_TmrSrv::is_local(const off_tmr_id_t &tmr)
{
return ( tmr.i_slot <= t_alloc.get_length() &&
(t_alloc+(natural_t)tmr.i_slot)->l_tmr.get_id() == tmr );
}
<Off timer server dependencies. >= (U->) [D->] #include <flux/types.h> // for boolean_t natural_t et al. #include <klib/ids.h> // for natural_t et al. #include <klib/HWCompResource.h> // for off_HWCompResource #include <klib/err.h> // for err_t #include <klib/prot.h> // for off_Protection #include <klib/Magic.h> // for magic numbers #include <hw/Tmr.h> // for off_Tmr #include <hw/TmrAllocator.h> // for off_TmrAllocator
This implementation uses is_local.
Each timer server is created by its processor. Initially, it is
statically allocated with default parameters for its members. Besides,
the desired hz is set.
<Other public methods of off_TmrSrv. >+= (<-U) [<-D->]
// Creates a Timer server.
off_TmrSrv(void);
<off_TmrSrv::off_TmrSrvimplementation. >= (U->) // Creates a Timer server. off_TmrSrv::off_TmrSrv(void) : off_HWCompResource(off_Protection(),OFF_PRTL_NULL, OFF_ID_NULL,OFF_MAGIC_TMRSRV), <Initializet_alloc. >, t_hz(OFF_TMR_HZ), <Initialize other aggregate members ofoff_TmrSrv. > { do_debug(putchar('t')); }
The hertz the timer is running at is a system limit.
<Off limits. >= const int OFF_TMR_HZ = 1000; // Hertz each timer is running at.
DefinesOFF_TMR_HZ(links are to index).
<Off timer server implementation dependencies. >= (U->) [D->] #include <klib/limits.h> // for OFF_TMR_HZ
<Other private members of off_TmrSrv. >= (<-U) [D->]
natural_t t_hz; // HZs for this timer server.
When the node is started, the timer server is started too.
<Other public methods of off_TmrSrv. >+= (<-U) [<-D->]
// Starts normal operation for this timer server.
err_t start(const off_mdepTmrSrv &mdep, const off_id_t &id);
<off_TmrSrv::startimplementation. >= (U->) // Starts normal operation for this timer server. err_t off_TmrSrv::start( const off_mdepTmrSrv &mdep, const off_id_t &id) { assert(valid()); set_id(id); <Start other members ofoff_TmrSrvaccording tomdep. > <Start the timer hardware att_hz. > kcout<< "Tmr #"<<id<< fmt(" (%d timers) ",get_ntmrs()); kcout<< "started."<< nl; return EOK; }
Here, get_ntmrs returns the number of existing timers.
<Other public methods of off_TmrSrv. >+= (<-U) [<-D->]
natural_t get_ntmrs(void) const { return off_mdepTmrSrv::get_ntmrs(); }
<Off timer server implementation dependencies. >+= (U->) [<-D->] #include <assert.h> // for assert #include <klib/str.h> // for kcout.
<Off timer server dependencies. >+= (U->) [<-D->] #include <hw/mdep/mTmrSrv.h> // for off_mdepTmrSrv
A new member, m_mdep will store the boot time information, which
is needed because it provides common information useful to timer server
users.
<Other private members of off_TmrSrv. >+= (<-U) [<-D->]
off_mdepTmrSrv t_mdep; // Mach. dep. information
<Start other members ofoff_TmrSrvaccording tomdep. >= (<-U) [D->] t_mdep=mdep;
We must define the new magic number used before.
<Off magic numbers. >= [D->] OFF_MAGIC_TMRSRV, // TmrSrv magic nb.
We can now add another case to the implementation of
Magic::nameof.
<off_Magic::nameofcase form_numbers. >= [D->] case OFF_MAGIC_TMRSRV: return "off_TmrSrv";
Timer server users can check their references to processors using this method.
<Other public methods of off_TmrSrv. >+= (<-U) [<-D->]
// Does this look like a TmrSrv?
boolean_t valid(void) const { return get_magic() == OFF_MAGIC_TMRSRV; }
The interesting part of start is setting the hardware timer. We
simply start the machine dependent server and then allocate the clock
interrupt (which is initially disabled).
<Start the timer hardware at t_hz. >= (<-U)
mdep.start(t_hz);
t_clkirq=new(nd.get_proc(0).get_irqs(),
nd.get_proc(0).get_irqs()->get_clk()) off_Irq(get_protection(),
get_domain(),NULL);
Note how we set a \emph{null} handler. The event table knows that the
clock interrupt is part of the system inner cycle and delivers it
directly to clock_handler.
<Other private members of off_TmrSrv. >+= (<-U) [<-D->]
off_Irq *t_clkirq; // HW clock interrupt.
<Initialize other aggregate members of off_TmrSrv. >= (<-U) [D->]
t_clkirq(NULL),
<Off timer server dependencies. >+= (U->) [<-D->] #include <hw/Event.h> // for off_Irq
A timer is simply a hardware resource unit which has an associated period of time, might repeat (i.e. reinstall itself when expired), and has an associated virtual clock interrupt (which is raised when the timer expires).
<Off timer. >= (U->)
// A timer.
//
class off_Tmr : public off_HWResUnit {
public:
// Is this timer periodic?
inline boolean_t is_periodic(void) const;
// Which interrupt line is used by this timer?
inline const off_ev_id_t &get_irqline(void) const;
<Other public methods of off_Tmr. >
protected:
<Other protected members of off_Tmr. >
<Other protected methods of off_Tmr. >
inline natural_t get_ticks(void);
private:
natural_t t_ticks; // # of ticks left for this timer.
off_ev_id_t t_irq; // Interrupt line.
<Other private members of off_Tmr. >
};
Definesoff_Tmr(links are to index).
<Off timer dependencies. >= (U->) [D->] #include <klib/HWResUnit.h> // for off_HWResUnit
<off_Tmr::get_ticks implementation. >= (U->)
inline natural_t off_Tmr::get_ticks(void)
{
return t_ticks;
}
<off_Tmr::get_irqline implementation. >= (U->)
// Which interrupt line is used by this timer?
inline const off_ev_id_t &off_Tmr::get_irqline(void) const { return t_irq; }
To know if a timer is periodic we use a resource flag.
<off_Tmr::is_periodic implementation. >= (U->)
// Is this timer periodic?
inline boolean_t off_Tmr::is_periodic(void) const
{
return r_flags & OFF_TF_PRD;
}
<Set timer periodic flag. >= (U->)
if (periodic)
r_flags |= OFF_TF_PRD;
else
r_flags &= ~OFF_TF_PRD;
Where we have to define the flag used.
<Off timer flags. >= (U->) // flag bits for off_Tmrs. // They are compatible with resource flag bits. const unsigned8_t OFF_TF_PRD = 0x10; // Is it a periodic timer?
DefinesOFF_TF_PRD(links are to index).
A timer is created with a prespecified protection and domain identifier. The interrupt line, number of ticks, and the periodic flag must be specified too.
<Other public methods of off_Tmr. >= (<-U) [D->]
// Creates a timer.
off_Tmr(const off_Protection &p,
const off_prtl_id_t &domain,
const off_tmr_id_t &id,
off_TmrSrv *srv,
natural_t nticks,
const off_ev_id_t &irqline,
boolean_t periodic );
Users use a constructor receiving only the protection, domain, number of ticks, and the periodic flag.
<Other public methods of off_Tmr. >+= (<-U) [<-D->]
// Creates a timer.
off_Tmr(const off_Protection &p,
const off_prtl_id_t &domain,
natural_t nticks,
boolean_t periodic );
<off_Tmr::off_Tmrimplementation. >= (U->) [D->] // Creates a timer. off_Tmr::off_Tmr(const off_Protection &p, const off_prtl_id_t &domain, natural_t nticks, boolean_t periodic ) : off_HWResUnit(p,domain), t_ticks(nticks), t_irq(OFF_EU_ID_NULL) { <Set timerperiodicflag. > <Initialize other private members ofoff_Tmr. > }
As you may suspect, each timer delivers a fixed virtual clock interrupt. Users may not choose which clock interrupt is delivered by each timer.
Remaining timer data is pre-initialized. A zero-argument (trap and interrupt) constructor permits declaration of uninitialized events.
<Other public methods of off_Tmr. >+= (<-U) [<-D->]
// Allows uninitialized timers.
off_Tmr(void) :
off_HWResUnit(off_Protection(), OFF_PRTL_NULL, OFF_EU_ID_NULL,
OFF_MAGIC_TMR, NULL)
{;}
When the timer server is created, another constructor is used to preinitialize fields which will remain fixed.
<Other public methods of off_Tmr. >+= (<-U) [<-D->]
// Preinitializes a timer.
off_Tmr(const off_tmr_id_t &id, const off_ev_id_t &irq, off_TmrSrv *c);
<off_Tmr::off_Tmr implementation. >+= (U->) [<-D]
// Preinitializes a timer.
off_Tmr::off_Tmr(const off_tmr_id_t &id,
const off_ev_id_t &irq, off_TmrSrv *c) :
off_HWResUnit(id,OFF_MAGIC_TMR,(off_HWCompResource*)c),
t_ticks(0),
t_irq(irq)
{;}
<Off timer dependencies. >+= (U->) [<-D->] #include <sys/types.h> #include <klib/ids.h> #include <klib/err.h> // for err_t #include <klib/prot.h> // for off_Protection #include <klib/Magic.h> // for magic numbers class off_TmrSrv;
We have to define the new magic number as well as add another case to
the implementation of Magic::nameof.
<Off magic numbers. >+= [<-D] OFF_MAGIC_TMR, // Tmr magic nb.
<off_Magic::nameofcase form_numbers. >+= [<-D] case OFF_MAGIC_TMR: return "off_Tmr";
Timer users can check their references to processors using this method.
<Other public methods of off_Tmr. >+= (<-U) [<-D->]
// Does this look like a Tmr?
boolean_t valid(void) const { return get_magic() == OFF_MAGIC_TMR; }
The new container can also implement its get_umagic method.
<Other protected methods of off_TmrSrv. >= (<-U) [D->]
virtual off_magic_t get_umagic(void) { return OFF_MAGIC_TMR; }
A timer allocator is a FixedAllocator instantiated at timer server
creation time to allocate Tmrs.
<Off timer allocator. >= (U->)
// A timer allocator.
//
class off_TmrAllocator : public off_TFixedAllocator<off_LTmr> {
public:
// Creates a timer allocator.
off_TmrAllocator(off_Exhausted *r);
<Other public methods of off_TmrAllocator. >
const off_TmrAllocator &operator=(const off_TmrAllocator &other) {
return (*this=other);
}
private:
<Other private members of off_TmrAllocator. >
};
Definesoff_TmrAllocator(links are to index).
The timer server only needs to supply the appropriate revocation trigger (i.e. himself) to create the timer allocator.
<Initialize t_alloc. >= (<-U)
t_alloc((off_Exhausted*)this)
<Off timer allocator dependencies. >= (U->) [D->] #include <klib/FixedAllocator.h> // for off_TFixedAllocator et al.
To support the linked lists used in the timer allocator, nodes
allocated are actually of type LTmr.
<Off linkable timer. >= (U->)
// A linkable timer.
//
class off_LTmr : public DLinkedNode {
public:
off_Tmr l_tmr;
// To allow uninitialized timers.
off_LTmr(void) : l_tmr() {;}
<Other public methods of off_LTmr. >
};
<Off timer allocator dependencies. >+= (U->) [<-D->] #include <hw/Tmr.h> // for off_Tmr
The fixed allocator uses a fixed array of LTmrs. There are as many
timers as virtual clock interrupts.
<Other private members of off_TmrAllocator. >= (<-U) [D->]
off_LTmr t_tmr[OFF_MDEP_NVINT_MAX]; // Timer slots.
<Off timer allocator dependencies. >+= (U->) [<-D->] #include <klib/limits.h> // for OFF_MDEP_NVINT_MAX
<Other private members of off_TmrAllocator. >+= (<-U) [<-D]
static off_Indexer<off_LTmr> t_idx; // Linked timer indexer.
<Off timer allocator static members. >= (U->) off_Indexer<off_LTmr> off_TmrAllocator::t_idx; // Linked timer indexer.
<Off timer allocator dependencies. >+= (U->) [<-D] #include <klib/Indexer.h> // for off_Indexer
Timer allocator creation is pretty simple.
<off_TmrAllocator::off_TmrAllocator implementation. >= (U->)
// Creates a timer allocator.
off_TmrAllocator::off_TmrAllocator(off_Exhausted *r)
{;}
Using the allocator, the timer server can allocate and deallocate timers.
<Other public methods of off_TmrSrv. >+= (<-U) [<-D->]
// Allocates a timer.
off_Tmr *alloc(boolean_t use_revocation=FALSE);
off_Tmr *alloc(const off_tmr_id_t &at, boolean_t use_revocation=FALSE);
// Deallocates a timer..
void free(const off_Tmr *rq);
<off_TmrSrv::allocandfreeimplementation. >= (U->) // Allocates a timer off_Tmr *off_TmrSrv::alloc(boolean_t use_revocation) { off_LTmr *lt; assert(valid()); w_lock(); lt=t_alloc.allocate(use_revocation); w_unlock(); return (lt)?(<->l_tmr):(off_Tmr*)NULL; } off_Tmr *off_TmrSrv::alloc(const off_tmr_id_t &at, boolean_t use_revocation) { off_LTmr *lt; assert(valid()); w_lock(); lt=t_alloc.allocate(at,use_revocation); w_unlock(); return (lt)?(<->l_tmr):(off_Tmr*)NULL; } // Deallocates a timer void off_TmrSrv::free(const off_Tmr *t) { off_LTmr *lt=(off_LTmr*)(((char*)t)-(int)&off_LTmr::l_tmr); assert(valid() && t); w_lock(); t_alloc.deallocate(lt); <Deactivate HW clock interrupt if necessary. > w_unlock(); }
Timers have their own new and delete operators. The operator
new receives the where the slot should be allocated. Operator
new uses the timer server alloc method to find an available
timer.
<Other public methods of off_Tmr. >+= (<-U) [<-D->]
// Allocate a timer.
void *operator new(size_t s, boolean_t use_revocation=FALSE);
void *operator new(size_t s, const off_tmr_id_t &at,
boolean_t use_revocation=FALSE);
// Deallocates this timer.
void operator delete(void *p);
<off_Tmr::operator new implementation. >= (U->)
// Allocate a timer.
void *off_Tmr::operator new(size_t s, boolean_t use_revocation)
{
off_Tmr *t=nd.get_proc(0).get_tmr().alloc(use_revocation);
(void)s;
assert(s && nd.get_proc(0).get_tmr().valid());
assert(!t || t->valid());
return (void*)t;
}
void *off_Tmr::operator new(size_t s, const off_tmr_id_t &at,
boolean_t use_revocation)
{
off_Tmr *t=nd.get_proc(0).get_tmr().alloc(at,use_revocation);
(void)s;
assert(s && nd.get_proc(0).get_tmr().valid());
assert(!t || t->valid());
return (void*)t;
}
<Off timer implementation dependencies. >= (U->) [D->] #include <node/Node.h> // for off_Node, nd, etc.
The operator delete does what could be expected.
<off_Tmr::operator delete implementation. >= (U->)
// Deallocates this run queue slot.
void off_Tmr::operator delete(void *p)
{
off_Tmr *t=(off_Tmr*)p;
assert(t && t->valid());
((off_TmrSrv*)(t->get_container()))->free(t);
}
Timers are pre-initialized when the timer server is started.
<Start other members ofoff_TmrSrvaccording tomdep. >+= (<-U) [<-D] for (natural_t t=0; t< get_ntmrs(); t++){ off_tmr_id_t tid(get_id(),t); off_ev_id_t irq(nd.get_proc(0).get_irqs()->get_vint0(), t + nd.get_proc(0).get_irqs()->get_vint0()); (void) new(t_alloc+t) off_LTmr(tid,irq,this); }
<Off timer server implementation dependencies. >+= (U->) [<-D->] #include <flux/types.h> // for boolean_t natural_t et al.
Again, we use a tricky new operator.
<Other public methods of off_LTmr. >= (<-U) [D->]
void *operator new(size_t s, off_LTmr *t) { (void)s; return (void*)t; }
The LTmr constructor pre-initializes each timer in turn using the
pre-initialization constructor for Tmrs.
<Other public methods of off_LTmr. >+= (<-U) [<-D]
off_LTmr(const off_tmr_id_t &id, const off_ev_id_t &irq, off_TmrSrv *c) :
l_tmr(id,irq,c) {;}
Using the timer allocator, the timer server implements these operators to access existing timers.
<Other public methods of off_TmrSrv. >+= (<-U) [<-D->]
//Gets a timer from its distributed or physical address.
inline off_Tmr *operator+(off_offset_t t);
<off_TmrSrv::operator+ implementation. >= (U->)
//Gets a timer from its distributed or physical address.
inline off_Tmr *off_TmrSrv::operator+(off_offset_t t)
{
return &(t_alloc+t)->l_tmr;
}
The implementation uses a redefinition of the operator in the allocator to do it more efficiently.
<Other public methods of off_TmrAllocator. >= (<-U)
//Gets a timer from its distributed or physical address.
inline off_LTmr *operator+(natural_t t);
<off_TmrAllocator::operator + implementation. >= (U->)
//Gets a timer from its distributed or physical address.
inline off_LTmr *off_TmrAllocator::operator+(natural_t t)
{
return t_tmr+t;
}
The timer server must implement the notify method used to behave
as a resource revocation trigger.
As of today, there is no revocation for timers.
<Other public methods of off_TmrSrv. >+= (<-U) [<-D->]
// Notifies that run queue is exhausted.
void notify(void) { return; }
The implementation of timers is the one typically used in OSes. We maintain a list of timers in increasing expiration order where each timer keeps the number of ticks to expiration relative to the previous one (which could also be zero).
<Other private members of off_TmrSrv. >+= (<-U) [<-D->]
DLinkedQueue t_events; // List of pending events.
<Off timer server dependencies. >+= (U->) [<-D->] #include <klib/dqueue.h> // for DLinkedQueue
This relative count is handled not by the timer, but by the timer
server activate method.
<Other protected members of off_Tmr. >= (<-U)
friend class off_TmrSrv; // for activate()
natural_t t_rticks; // Relative number of ticks.
<Other public methods of off_Tmr. >+= (<-U) [<-D]
// Returns the number of ticks for expiration relative to the previous
// event.
inline natural_t get_rticks(void);
<off_Tmr::get_rticks implementation. >= (U->)
// Returns the number of ticks for expiration relative to the previous
// event.
extern inline natural_t off_Tmr::get_rticks(void)
{
return t_rticks;
}
The number of ticks until the first event in the queue happens is cached in the timer server.
<Other private members of off_TmrSrv. >+= (<-U) [<-D->]
natural_t t_remaining; // # of ticks until next event.
<Initialize other aggregate members of off_TmrSrv. >+= (<-U) [<-D->]
t_remaining(1),
After the clock interrupt has been allocated, clock_handler is
called every time the clock ticks. Such routine calls a single timer
server on non SMP systems and dispatches the event to the current
processor timer server on SMP systems.
<off_clock_handler implementation. >= (U->)
extern "C" {
void off_clock_handler(void)
{
off_Processor::self()->get_tmr().clock_handler();
}
}
Definesoff_clock_handler(links are to index).
<Off timer server implementation dependencies. >+= (U->) [<-D->] #include <hw/Processor.h> // for self
<Other public methods of off_TmrSrv. >+= (<-U) [<-D->]
// Another clock tick in the wall.
inline void clock_handler(void);
It decrements the number of ticks until the first event in the queue
and then calls expired when it becomes zero.
<off_TmrSrv::clock_handler implementation. >= (U->)
// Another clock tick in the wall.
inline void off_TmrSrv::clock_handler(void)
{
if (--t_remaining)
expired();
}
expired is used both to handle the event and to reset
t_remaining to the number of ticks until further events.
<Other public methods of off_TmrSrv. >+= (<-U) [<-D->]
// It is called when the period until next event expires.
// The number of ticks until next event is returned.
volatile void expired(void);
<off_TmrSrv::expiredimplementation. >= (U->) // It is called when the period until next event expires. // The number of ticks until next event is returned. volatile void off_TmrSrv::expired(void) { off_LTmr *ev; <off_TmrSrv::expiredlocal variables. > <Pick up first pendingevand resett_remaining. > <Handle expired eventev. > }
When the first pending event expires, we get a reference to it (to
handle it later) and reset the clock counter with the relative count
for the next timer. The queue is also handled from interrupts, thus
we clear interrupts and lock the queue for enqueue and dequeue
operations.
<Pick up first pendingevand resett_remaining. >= (<-U) clrd=off_Processor::has_clri(); off_Processor::clri(); w_lock(); ev = (off_LTmr*)t_events.dequeue(); t_remaining=((off_LTmr*)t_events.get_first())->l_tmr.get_rticks(); w_unlock(); if (!clrd) off_Processor::seti();
<off_TmrSrv::expired local variables. >= (<-U)
boolean_t clrd;
To handle a timer we usually raise its virtual interrupt; but for a special timer which will be discussed later.
<Handle expired eventev. >= (<-U) [D->] if (<It is a specialevto be handled. >){ <Handle specialev. > } else off_Processor::self()->get_irq(ev->l_tmr.get_irqline())->raise();
We still have to show how are timers created. When a timer is
created, we must find the place in the list of active timers where it
should be placed and adjust both its relative number of ticks and the
relative number of ticks of the following timers. Both tasks are done
by the timer protected method activate.
<Other protected methods of off_Tmr. >= (<-U) [D->]
// Activates the timer by linking it in the active timer queue.
void activate(void);
which is called by the constructor when the timer is created.
<Initialize other private members of off_Tmr. >= (<-U)
activate();
Note that when the timer is periodic, it is activated when expired too.
<Handle expired event ev. >+= (<-U) [<-D]
if (ev->l_tmr.is_periodic())
ev->l_tmr.activate();
The timer activate method simply calls a counterpart in the timer
server.
<off_Tmr::activate implementation. >= (U->)
// Activates the timer by linking it in the active timer queue.
void off_Tmr::activate(void)
{
((off_TmrSrv*)get_container())->activate(this);
}
<Other public methods of off_TmrSrv. >+= (<-U) [<-D->]
// Activates the timer by linking it in the active timer queue.
void activate(off_Tmr *t);
The timer server method is used to maintain (to link timers in) the
active timer queue. To do so, we need a pointer to the LTmr the
timer is in so we could reuse the pointers used by the timer
allocator; then we walk the queue maintaining in ticks the
relative number of ticks (with respect the queue node being visited)
for timer expiration.
<off_TmrSrv::activateimplementation. >= (U->) // Activates the timer by linking it in the active timer queue. void off_TmrSrv::activate(off_Tmr *t) { boolean_t clrd; off_LTmr *lt=(off_LTmr*)(((char*)t)-(int)&off_LTmr::l_tmr); natural_t &ticks=t->t_rticks; ticks=t->get_ticks(); clrd=off_Processor::has_clri(); off_Processor::clri(); w_lock(); if (t_events.is_empty()){ <Place timertto expire inticksat head oft_events. > } else { <Find place int_eventsfor timertto expire inticks> } <Activate HW clock interrupt if necessary. > w_unlock(); if (!clrd) off_Processor::seti(); }
If there is no timer we simply put the timer in the head of the queue.
<Place timertto expire inticksat head oft_events. >= (<-U) t->t_rticks=ticks; t_events.enqueue(lt);
If there are timers, we start scanning the queue from its head
using a queue iterator itr. If the iterator was not placed
after the last node was visited, we link the node at the end of the
queue.
Every time a new node is visited we maintain ticks as a relative
count starting at it. When the node is finally placed, ticks
contains a relative count and we adjust the relative count of the
following node to discount ticks.
<Find place int_eventsfor timertto expire inticks>= (<-U) DLinkedQueueItr itr,head; boolean_t placed=FALSE; head=itr=t_events.get_head(); do { natural_t &nticks= ((off_LTmr*)itr.get_element())->l_tmr.t_rticks; if (nticks >= ticks){ ticks -= nticks; } else { placed=TRUE; nticks -= ticks; t_events.link_before(lt,itr); } ++itr; } while (!placed && itr != head); if (!placed){ DLinkedQueueItr i=t_events.get_tail(); t_events.link_after(lt,i); }
Finally, we do not want to maintain the hardware clock interrupt always enabled. It is enabled only when there are timers to be serviced. The timer server enables the hardware clock interrupt when the first timer is activated
<Activate HW clock interrupt if necessary. >= (<-U) t_clkirq->unmask();
and disables it again when the last active timer is disabled.
<Deactivate HW clock interrupt if necessary. >= (<-U) if (!t_alloc.get_nalloc()) t_clkirq->mask();
\subsubsection{Helping processors with their quanta}
Every processor with a run queue will always want to be interrupted to implement quanta. Those interrupts take part in the critical path for the context switch, hence they are handled apart.
The timer server includes a alloc_pclk method intended to start
the ``scheduling'' clock.
<Other public methods of off_TmrSrv. >+= (<-U) [<-D->]
// Starts the scheduling clock timer.
void alloc_pclk(natural_t nticks);
It programs a special timer which is handled by the timer server itself.
<off_TmrSrv::alloc_pclk implementation. >= (U->)
// Starts the scheduling clock timer.
void off_TmrSrv::alloc_pclk(natural_t nticks)
{
if (!t_pclk){
off_Tmr *t=new off_Tmr(get_protection(),get_domain(),nticks,TRUE);
t_pclk=(off_LTmr*)(((char*)t)-(int)&off_LTmr::l_tmr);
}
}
What makes it so special is that when time has come to handle it the
timer server checks the timer address to see if it is the same of
t_pclk. If it matches, the current processor new_quantum
method is called instead of raising a virtual interrupt. Thus,
<It is a special ev to be handled. >= (<-U)
ev == t_pclk
<Initialize other aggregate members of off_TmrSrv. >+= (<-U) [<-D->]
t_pclk(NULL),
<Other private members of off_TmrSrv. >+= (<-U) [<-D->]
off_LTmr *t_pclk; // Special processor clock timer.
Now, to handle such event we call new_quantum. But before doing
that we reactivate the timer.
<Handle special ev. >= (<-U)
ev->l_tmr.activate();
off_Processor::self()->new_quantum();
The new_quantum routine will (perhaps) leave us in the context for
a new shuttle before it returns.