Please wait until the page is fully downloaded and then press the "Expand" button or the blue line numbers.

0004001 /*
0004002 clock.c
0004003 
0004004 Copyright 1995 Philip Homburg
0004005 */
synchronous alarm task

The network service listens for messages from 3 sources: the file system service, the synchronous alarm task, and the ethernet task. The function of the synchronous alarm task is described in Operating Systems, Design and Implementation by Tanenbaum and Woodhull, page 229:

"The synchronous alarm mechanism was added to MINIX to support the network server, which like the memory manager and the file server, runs as a separate process. Frequently there is a need to set a limit on the time a process may be blocked while waiting for input. For instance in a network, failure to receive an acknowledgement of a data packet within a definite period is probably due to a failure of transmission. A network server can set a synchronous alarm before it tries to receive a message and blocks. Since the synchronous alarm is delivered as a message, it will unblock the server eventually if no message is received from the network. Upon receiving any message the server must first reset the alarm. Then by examining the type or origin of the message, it can determine a packet has arrived or if it has been unblocked by a timeout. If it is the latter, then the server can try to recover, usually by resending the last unacknowledged packet."

In other words, the synchronous alarm task prevents client connections (specifically, tcp and arp connections) from hanging.

The synchronous alarm works as follows:

If a client wishes to set a synchronous alarm, clck_timer() is called and a timer is added to the existing timers. For example, the arp client calls clck_timer() in order to set a limit on the time it's willing to wait for another system on the network to respond to an arp request. If the new timer expires before any of the other timers are due to expire, a message is sent to the clock task requesting an alarm set for the appropriate time. (Note that the clock task is only aware of at most one alarm at any given time.)

When the alarm message is eventually received by the network service (this process), set_timer() sets the global variable clck_call_expire and returns. The next time around its endless while loop, main() processes the received alarm message by calling clck_expire_timers(). This function calls the client-specific function (e.g., arp_timeout()) that handles alarms, passing in the necessary information so that the client can identify the connection associated with the alarm.

After handling the alarm, set_timer() again sends a message to the clock task requesting an alarm message be sent for the next timer due to expire (assuming that there are other timers in timer_chain).


0004006 
0004007 #include "inet.h"
0004008 #include "proto.h"
0004009 #include "generic/assert.h"
0004010 #include "generic/buf.h"
0004011 #include "generic/clock.h"
0004012 #include "generic/type.h"
0004013 
0004014 THIS_FILE
0004015 
0004016 PUBLIC int clck_call_expire;
clck_call_expire

clck_call_expire is a global variable that is set by set_timer() if a timer has expired. If clck_call_expire is set, clck_expire_timers() is called the next time around main()'s endless loop. clck_expire_timers(), in turn, calls the client-specific code that handles an alarm (e.g., arp_timeout().


0004017 
0004018 PRIVATE time_t curr_time;
curr_time

curr_time, if non-zero, is the number of clock ticks since boot. This value is set either by get_time() or set_time().

Clients use get_time() when setting a timer. For example, the arp client calls get_time() when setting a timer to limit the amount of time it will wait for a system to respond to its arp packet.

set_time() is called by eth_rec(), which is called by the endless loop in main(). eth_rec() is called when a message is received from the ethernet task and the current time is extracted from this message.

The network service does not try to keep track of the current time. curr_time is set to zero (i.e., made invalid) each time around main()'s endless loop. (Note that reset_time() simply sets curr_time to zero.) If the current time is needed (and a message from the ethernet task wasn't just received), it is recalculated (by calling get_time()).


0004019 PRIVATE timer_t *timer_chain;
timer_chain

timer_chain points to the first (head) timer in a linked list of timers. The timers are arranged according to their expiration times, with the first timer scheduled to expire at the head of the linked list. timer_chain will be NULL if no timers are scheduled.

Each timer in the linked list is a struct timer:

typedef struct timer

{
struct timer *tim_next; /* Points to next timer in linked list. */
timer_func_t tim_func; /* Function called when timer expires (
int tim_ref;
time_t tim_time;
int tim_active;
} timer_t;
tim_next: Points to next timer in linked list.

tim_func: Function that is called when timer expires (e.g., arp_timeout()).

tim_ref: Field used by clients (arp and tcp) to identify the connection associated with the timer. This field is an argument to tim_func (see tim_func above).

tim_time: Expiration time for timer (in clock ticks since boot).

tim_active: 1 if active, 0 if inactive.



0004020 PRIVATE time_t next_timeout;
next_timeout

next_timeout is the expiration time of the next synchronous alarm (i.e., the time at which the next message from the synchronous alarm is scheduled to arrive). Like curr_time, next_timeout is in clock ticks.


0004021 
0004022 FORWARD _PROTOTYPE( void clck_fast_release, (timer_t *timer) );
0004023 FORWARD _PROTOTYPE( void set_timer, (void) );
0004024 
0004025 PUBLIC void clck_init()
clck_init()

clck_init() is called during the initialization of the network service to initialize various clock related global variables (e.g., clck_call_expire).


0004026 {
0004027 #if ZERO
0004028          clck_call_expire= 0;
clck_call_expire

clck_call_expire is a global variable that is set by set_timer() if a timer has expired. If clck_call_expire is set, clck_expire_timers() is called the next time around main()'s endless loop. clck_expire_timers(), in turn, calls the client-specific code that handles an alarm (e.g., arp_timeout().


0004029          curr_time= 0;
curr_time

curr_time, if non-zero, is the number of clock ticks since boot. This value is set either by get_time() or set_time().

Clients use get_time() when setting a timer. For example, the arp client calls get_time() when setting a timer to limit the amount of time it will wait for a system to respond to its arp packet.

set_time() is called by eth_rec(), which is called by the endless loop in main(). eth_rec() is called when a message is received from the ethernet task and the current time is extracted from this message.

The network service does not try to keep track of the current time. curr_time is set to zero (i.e., made invalid) each time around main()'s endless loop. (Note that reset_time() simply sets curr_time to zero.) If the current time is needed (and a message from the ethernet task wasn't just received), it is recalculated (by calling get_time()).


0004030          next_timeout= 0;
next_timeout

next_timeout is the expiration time of the next synchronous alarm (i.e., the time at which the next message from the synchronous alarm is scheduled to arrive). Like curr_time, next_timeout is in clock ticks.


0004031          timer_chain= 0;
timer_chain

timer_chain points to the first (head) timer in a linked list of timers. The timers are arranged according to their expiration times, with the first timer scheduled to expire at the head of the linked list. timer_chain will be NULL if no timers are scheduled.

Each timer in the linked list is a struct timer:

typedef struct timer

{
struct timer *tim_next; /* Points to next timer in linked list. */
timer_func_t tim_func; /* Function called when timer expires (
int tim_ref;
time_t tim_time;
int tim_active;
} timer_t;
tim_next: Points to next timer in linked list.

tim_func: Function that is called when timer expires (e.g., arp_timeout()).

tim_ref: Field used by clients (arp and tcp) to identify the connection associated with the timer. This field is an argument to tim_func (see tim_func above).

tim_time: Expiration time for timer (in clock ticks since boot).

tim_active: 1 if active, 0 if inactive.



0004032 #endif
0004033 }
0004034 
0004035 PUBLIC time_t get_time()
get_time()

get_time() returns the number of clock ticks since reboot.

Several of the clients (eth, arp, ip, tcp, and udp) use get_time() to determine an appropriate timeout value for a given operation. For example, the arp code calls get_time() to determine an appropriate amount of time to wait for a response from an arp request before giving up.


0004036 {
0004037          if (!curr_time)
curr_time

curr_time, if non-zero, is the number of clock ticks since boot. This value is set either by get_time() or set_time().

Clients use get_time() when setting a timer. For example, the arp client calls get_time() when setting a timer to limit the amount of time it will wait for a system to respond to its arp packet.

set_time() is called by eth_rec(), which is called by the endless loop in main(). eth_rec() is called when a message is received from the ethernet task and the current time is extracted from this message.

The network service does not try to keep track of the current time. curr_time is set to zero (i.e., made invalid) each time around main()'s endless loop. (Note that reset_time() simply sets curr_time to zero.) If the current time is needed (and a message from the ethernet task wasn't just received), it is recalculated (by calling get_time()).


0004038          {
0004039                   static message mess;
0004040 
In the general case, curr_time will not be set since it is cleared each time around main()'s endless loop (reset_time() clears curr_time). Therefore, get_time() must send a message to the CLOCK task asking for the number of clock ticks since boot. Note that the network service considers this to be the "current time".

CLOCK, GET_UPTIME, and NEW_TIME are all #define'd in include/minix/com.h.


0004041                   mess.m_type= GET_UPTIME;
0004042                   if (sendrec (CLOCK, &mess) < 0 || mess.m_type != OK)
0004043                            ip_panic(("can't read clock"));
sendrec() sends the GET_UPTIME message to the CLOCK task and receives the reply.
IMPORTANT: The reason that multiple calls to get_time() result to the same time, when we are in the same loop of inet, which results for different packets to have the same arrival time, is to minimize the interaction between inet and the clock task having as few message passing as possible.


0004044                   curr_time= mess.NEW_TIME;
the NEW_TIME field of the reply is assigned to curr_time


0004045          }
0004046          return curr_time;
0004047 }
0004048 
0004049 PUBLIC void set_time (tim)
0004050 time_t tim;
set_time()

set_time() is called only from eth_rec(). eth_rec() takes advantage of the fact that the current time is included in messages from the ethernet task. (Specifically, the current time is in the field DL_CLCK of the message.)

Do not confuse set_time() with set_timer(), which does something completely different.


0004051 {
0004052          if (!curr_time)
As described in the comments on line 4057, curr_time is set to zero each time around main()'s endless loop.


0004053          {
0004054                   /* Some code assumes that no time elapses while it is
0004055                    * running.
"Some code" refers to the ethernet code (the only code that calls set_time()).


0004056                    */
0004057                   curr_time= tim;
curr_time

curr_time, if non-zero, is the number of clock ticks since boot. This value is set either by get_time() or set_time().

Clients use get_time() when setting a timer. For example, the arp client calls get_time() when setting a timer to limit the amount of time it will wait for a system to respond to its arp packet.

set_time() is called by eth_rec(), which is called by the endless loop in main(). eth_rec() is called when a message is received from the ethernet task and the current time is extracted from this message.

The network service does not try to keep track of the current time. curr_time is set to zero (i.e., made invalid) each time around main()'s endless loop. (Note that reset_time() simply sets curr_time to zero.) If the current time is needed (and a message from the ethernet task wasn't just received), it is recalculated (by calling get_time()).


0004058          }
0004059 }
0004060 
0004061 PUBLIC void reset_time()
reset_time()

In the interest of conserving processor time, the time (as seen by the network service) is "frozen" for certain intervals. In this way, the current time, if needed, does not need to be retrieved from the system (which is fairly costly in terms of processor time).

reset_time() simply sets the global variable curr_time to zero (i.e., makes curr_time invalid). If curr_time is zero, the next time that get_time() is called, get_time() must retrieve the time by requesting the time from the clock task.

reset_time() is called each time around main()'s endless loop.


0004062 {
0004063          curr_time= 0;
curr_time

curr_time, if non-zero, is the number of clock ticks since boot. This value is set either by get_time() or set_time().

Clients use get_time() when setting a timer. For example, the arp client calls get_time() when setting a timer to limit the amount of time it will wait for a system to respond to its arp packet.

set_time() is called by eth_rec(), which is called by the endless loop in main(). eth_rec() is called when a message is received from the ethernet task and the current time is extracted from this message.

The network service does not try to keep track of the current time. curr_time is set to zero (i.e., made invalid) each time around main()'s endless loop. (Note that reset_time() simply sets curr_time to zero.) If the current time is needed (and a message from the ethernet task wasn't just received), it is recalculated (by calling get_time()).


0004064 }
0004065 
0004066 PUBLIC void clck_timer(timer, timeout, func, fd)
0004067 timer_t *timer;
0004068 time_t timeout;
0004069 timer_func_t func;
0004070 int fd;
clck_timer()

clck_timer(timer, timeout, func, fd) configures timer, clck_timer()'s first parameter, based on timeout, func, and fd (clck_timer()'s second, third, and fourth parameters) and places the timer in its appropriate position in timer_chain. In other words, if the new timer is scheduled to expire before any other timer, it is inserted at the head of the linked list and if the new timer is scheduled to expire after all the other timers, it is inserted at the tail of the linked list. If the new timer is scheduled to expire before any other timers, clck_timer() calls set_timer(), which sends a message to the clock task with a revised expiration time for the synchronous alarm.

An example of a client calling clck_timer() to insert a timer in timer_chain is the arp client.


0004071 {
0004072          timer_t *timer_index;
timer_chain

timer_chain points to the first (head) timer in a linked list of timers. The timers are arranged according to their expiration times, with the first timer scheduled to expire at the head of the linked list. timer_chain will be NULL if no timers are scheduled.

Each timer in the linked list is a struct timer:

typedef struct timer

{
struct timer *tim_next; /* Points to next timer in linked list. */
timer_func_t tim_func; /* Function called when timer expires (
int tim_ref;
time_t tim_time;
int tim_active;
} timer_t;
tim_next: Points to next timer in linked list.

tim_func: Function that is called when timer expires (e.g., arp_timeout()).

tim_ref: Field used by clients (arp and tcp) to identify the connection associated with the timer. This field is an argument to tim_func (see tim_func above).

tim_time: Expiration time for timer (in clock ticks since boot).

tim_active: 1 if active, 0 if inactive.



0004073 
0004074          if (timer->tim_active)
0004075                   clck_fast_release(timer);
clck_fast_release()

clck_fast_release(timer) deactivates timer, clck_fast_release()'s only parameter, and removes it from timer_chain.


0004076          assert(!timer->tim_active);
0004077 
0004078          timer->tim_next= 0;
0004079          timer->tim_func= func;
0004080          timer->tim_ref= fd;
0004081          timer->tim_time= timeout;
0004082          timer->tim_active= 1;
0004083 
Lines 4084-4099 insert the newly configured timer into its appropriate position in timer_chain, based on its expiration time (tim_time).


0004084          if (!timer_chain)
If the linked list is empty, the timer becomes the new head of the timer linked list.


0004085                   timer_chain= timer;
0004086          else if (timeout < timer_chain->tim_time)
If the expiration time of the new timer is less than the head of the linked list, make the new timer the head. Recall that the head of the linked list has the earliest expiration time.


0004087          {
0004088                   timer->tim_next= timer_chain;
0004089                   timer_chain= timer;
0004090          }
0004091          else
0004092          {
Find the proper position of the timer within the linked list.


0004093                   timer_index= timer_chain;
0004094                   while (timer_index->tim_next &&
0004095                            timer_index->tim_next->tim_time < timeout)
0004096                            timer_index= timer_index->tim_next;
0004097                   timer->tim_next= timer_index->tim_next;
0004098                   timer_index->tim_next= timer;
0004099          }
0004100          if (next_timeout == 0 || timer_chain->tim_time < next_timeout)
next_timeout

next_timeout is the expiration time of the next synchronous alarm (i.e., the time at which the next message from the synchronous alarm is scheduled to arrive). Like curr_time, next_timeout is in clock ticks.


0004101                   set_timer();
If the synchronous alarm is not set or if the newly created timer is scheduled to expire before all other timers, call set_timer() to set (or reset) the alarm (item 2 below).


set_timer()


If there are any timers in timer_chain, set_timer() (not to be mistaken with set_time()) does one (and only one) of two things:

1) clck_call_expire is set so that clck_expire_timers() is called the next time around main()'s endless loop. This occurs if the timer at the head of timer_chain (i.e., the timer scheduled to expired first) has expired.

2) A message is sent to the clock task requesting a new synchronous alarm. This occurs if the synchronous alarm has not been set or if a new timer expires before the other timers.

If there are no timers in timer_chain, set_timer() simply returns without doing anything.


0004102 }
0004103 
0004104 PUBLIC void clck_tick (mess)
0004105 message *mess;
clck_tick()

clck_tick() is called upon receipt of a message from the synchronous alarm task in the endless loop within main(). clck_tick() simply sets next_timeout to zero (0) and then calls set_timer(), which sets clck_call_expire if a timer has expired, causing clck_expire_timers() to be called. clck_expire_timers()'s most important task is to call the client-specific timeout function (e.g., arp_timeout()) to handle the time-out. clck_expire_timer() also calls set_timer() to set another timer with the synchronous alarm task.


0004106 {
0004107          next_timeout= 0;
next_timeout

next_timeout is the expiration time of the next synchronous alarm (i.e., the time at which the next message from the synchronous alarm is scheduled to arrive). Like curr_time, next_timeout is in clock ticks.


0004108          set_timer();
set_timer()

If there are any timers in timer_chain, set_timer() (not to be mistaken with set_time()) does one (and only one) of two things:

1) clck_call_expire is set so that clck_expire_timers() is called the next time around main()'s endless loop. This occurs if the timer at the head of timer_chain (i.e., the timer scheduled to expired first) has expired.

2) A message is sent to the clock task requesting a new synchronous alarm. This occurs if the synchronous alarm has not been set or if a new timer expires before the other timers.

If there are no timers in timer_chain, set_timer() simply returns without doing anything.


0004109 }
0004110 
0004111 PRIVATE void clck_fast_release (timer)
0004112 timer_t *timer;
clck_fast_release()

clck_fast_release(timer) deactivates timer, clck_fast_release()'s only parameter, and removes it from timer_chain.


0004113 {
0004114          timer_t *timer_index;
0004115 
0004116          if (!timer->tim_active)
If the timer is inactive, there's no need to do anything.


0004117                   return;
0004118 
0004119          if (timer == timer_chain)
The timer is at the head of timer_chain.


timer_chain


timer_chain points to the first (head) timer in a linked list of timers. The timers are arranged according to their expiration times, with the first timer scheduled to expire at the head of the linked list. timer_chain will be NULL if no timers are scheduled.

Each timer in the linked list is a struct timer:

typedef struct timer

{
struct timer *tim_next; /* Points to next timer in linked list. */
timer_func_t tim_func; /* Function called when timer expires (
int tim_ref;
time_t tim_time;
int tim_active;
} timer_t;
tim_next: Points to next timer in linked list.

tim_func: Function that is called when timer expires (e.g., arp_timeout()).

tim_ref: Field used by clients (arp and tcp) to identify the connection associated with the timer. This field is an argument to tim_func (see tim_func above).

tim_time: Expiration time for timer (in clock ticks since boot).

tim_active: 1 if active, 0 if inactive.



0004120                   timer_chain= timer_chain->tim_next;
0004121          else
The timer is in the middle or the end (tail) of timer_chain.


0004122          {
0004123                   timer_index= timer_chain;
0004124                   while (timer_index && timer_index->tim_next != timer)
0004125                            timer_index= timer_index->tim_next;
0004126                   assert(timer_index);
0004127                   timer_index->tim_next= timer->tim_next;
0004128          }
0004129          timer->tim_active= 0;
Mark the timer as inactive.


0004130 }
0004131 
0004132 PRIVATE void set_timer()
set_timer()

If there are any timers in timer_chain, set_timer() (not to be mistaken with set_time()) does one (and only one) of two things:

1) clck_call_expire is set so that clck_expire_timers() is called the next time around main()'s endless loop. This occurs if the timer at the head of timer_chain (i.e., the timer scheduled to expired first) has expired.

2) A message is sent to the clock task requesting a new synchronous alarm. This occurs if the synchronous alarm has not been set or if a new timer expires before the other timers.

If there are no timers in timer_chain, set_timer() simply returns without doing anything.


0004133 {
0004134          time_t new_time;
0004135          time_t curr_time;
0004136 
0004137          if (!timer_chain)
timer_chain

timer_chain points to the first (head) timer in a linked list of timers. The timers are arranged according to their expiration times, with the first timer scheduled to expire at the head of the linked list. timer_chain will be NULL if no timers are scheduled.

Each timer in the linked list is a struct timer:

typedef struct timer

{
struct timer *tim_next; /* Points to next timer in linked list. */
timer_func_t tim_func; /* Function called when timer expires (
int tim_ref;
time_t tim_time;
int tim_active;
} timer_t;
tim_next: Points to next timer in linked list.

tim_func: Function that is called when timer expires (e.g., arp_timeout()).

tim_ref: Field used by clients (arp and tcp) to identify the connection associated with the timer. This field is an argument to tim_func (see tim_func above).

tim_time: Expiration time for timer (in clock ticks since boot).

tim_active: 1 if active, 0 if inactive.



0004138                   return;
0004139 
0004140          curr_time= get_time();
get_time()

get_time() returns the number of clock ticks since reboot.

Several of the clients (eth, arp, ip, tcp, and udp) use get_time() to determine an appropriate timeout value for a given operation. For example, the arp code calls get_time() to determine an appropriate amount of time to wait for a response from an arp request before giving up.


0004141          new_time= timer_chain->tim_time;
0004142          if (new_time <= curr_time)
The first timer in timer_chain has expired.


0004143          {
0004144                   clck_call_expire= 1;
clck_call_expire

clck_call_expire is a global variable that is set by set_timer() if a timer has expired. If clck_call_expire is set, clck_expire_timers() is called the next time around main()'s endless loop. clck_expire_timers(), in turn, calls the client-specific code that handles an alarm (e.g., arp_timeout().


0004145                   return;
0004146          }
0004147 
0004148          if (next_timeout == 0 || new_time < next_timeout)
If the synchronous alarm is not set or if new timer expires before all other timers, set (or reset) the synchronous alarm.


next_timeout


next_timeout is the expiration time of the next synchronous alarm (i.e., the time at which the next message from the synchronous alarm is scheduled to arrive). Like curr_time, next_timeout is in clock ticks.


0004149          {
0004150                   static message mess;
0004151 
0004152                   next_timeout= new_time;
0004153 
0004154                   new_time -= curr_time;
0004155 
0004156                   mess.m_type= SET_SYNC_AL;
Send a message to the clock task requesting a synchronous alarm.

CLOCK, DELTA_TICKS, SET_SYNC_AL, and CLOCK_PROC_NR are all #define'd in include/minix/com.h.


0004157                   mess.CLOCK_PROC_NR= this_proc;
this_proc is a global variable that is set to the process number of the network service in nw_init().


0004158                   mess.DELTA_TICKS= new_time;
0004159                   if (sendrec (CLOCK, &mess) < 0 || mess.m_type != OK)
0004160                            ip_panic(("can't set timer"));
0004161          }
0004162 }
0004163 
0004164 PUBLIC void clck_untimer (timer)
clck_untimer()

clck_untimer(timer) releases timer, clck_untimer()'s only parameter. If necessary, clck_untimer() sends a message to the clock task requesting a synchronous alarm with a revised expiration time (actually, clck_untimer() calls set_timer(), which would send the message). Remember that the synchronous alarm task is aware of only a single alarm at any given time.


0004165 timer_t *timer;
0004166 {
0004167          clck_fast_release (timer);
clck_fast_release()

clck_fast_release(timer) deactivates timer, clck_fast_release()'s only parameter, and removes it from timer_chain.


0004168          set_timer();
set_timer()

If there are any timers in timer_chain, set_timer() (not to be mistaken with set_time()) does one (and only one) of two things:

1) clck_call_expire is set so that clck_expire_timers() is called the next time around main()'s endless loop. This occurs if the timer at the head of timer_chain (i.e., the timer scheduled to expired first) has expired.

2) A message is sent to the clock task requesting a new synchronous alarm. This occurs if the synchronous alarm has not been set or if a new timer expires before the other timers.

If there are no timers in timer_chain, set_timer() simply returns without doing anything.


0004169 }
0004170 
0004171 PUBLIC void clck_expire_timers()
clck_expire_timers()

clck_expire_timers() is called in the endless loop in main() if one or more timers have expired. clck_expire_timers() calls the client-specific timeout function (e.g., arp_timeout()) to handle the time-out and then calls set_timer() to request another alarm from the synchronous alarm task (if there are other timers in timer_chain).


0004172 {
0004173          time_t curr_time;
0004174          timer_t *timer_index;
0004175 
0004176          clck_call_expire= 0;
clck_call_expire

clck_call_expire is a global variable that is set by set_timer() if a timer has expired. If clck_call_expire is set, clck_expire_timers() is called the next time around main()'s endless loop. clck_expire_timers(), in turn, calls the client-specific code that handles an alarm (e.g., arp_timeout().


0004177 
0004178          if (timer_chain == NULL)
timer_chain

timer_chain points to the first (head) timer in a linked list of timers. The timers are arranged according to their expiration times, with the first timer scheduled to expire at the head of the linked list. timer_chain will be NULL if no timers are scheduled.

Each timer in the linked list is a struct timer:

typedef struct timer

{
struct timer *tim_next; /* Points to next timer in linked list. */
timer_func_t tim_func; /* Function called when timer expires (
int tim_ref;
time_t tim_time;
int tim_active;
} timer_t;
tim_next: Points to next timer in linked list.

tim_func: Function that is called when timer expires (e.g., arp_timeout()).

tim_ref: Field used by clients (arp and tcp) to identify the connection associated with the timer. This field is an argument to tim_func (see tim_func above).

tim_time: Expiration time for timer (in clock ticks since boot).

tim_active: 1 if active, 0 if inactive.



0004179                   return;
0004180 
0004181          curr_time= get_time();
get_time()

get_time() returns the number of clock ticks since reboot.

Several of the clients (eth, arp, ip, tcp, and udp) use get_time() to determine an appropriate timeout value for a given operation. For example, the arp code calls get_time() to determine an appropriate amount of time to wait for a response from an arp request before giving up.


0004182          while (timer_chain && timer_chain->tim_time<=curr_time)
Handle all the expired timers. First, mark the timer as inactive and then call the client-specific timeout function.


0004183          {
0004184                   assert(timer_chain->tim_active);
0004185                   timer_chain->tim_active= 0;
0004186                   timer_index= timer_chain;
0004187                   timer_chain= timer_chain->tim_next;
0004188                   (*timer_index->tim_func)(timer_index->tim_ref, timer_index);
Call the client-specific timeout function (e.g., arp_timeout()).


0004189          }
0004190          set_timer();
Call set_timer() to request a new alarm from the synchronous alarm task for the first of the remaining (i.e., not expired) timers (item 2 below).


set_timer()


If there are any timers in timer_chain, set_timer() (not to be mistaken with set_time()) does one (and only one) of two things:

1) clck_call_expire is set so that clck_expire_timers() is called the next time around main()'s endless loop. This occurs if the timer at the head of timer_chain (i.e., the timer scheduled to expired first) has expired.

2) A message is sent to the clock task requesting a new synchronous alarm. This occurs if the synchronous alarm has not been set or if a new timer expires before the other timers.

If there are no timers in timer_chain, set_timer() simply returns without doing anything.


0004191 }
0004192 
0004193 /*
0004194  * $PchId: clock.c,v 1.6 1995/11/21 06:54:39 philip Exp $
0004195  */