0004001 /*
0004002 clock.c
0004003
0004004 Copyright 1995 Philip Homburg
0004005 */
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;
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.
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)
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 {
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)
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
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;
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)
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);
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 */
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).