# /* timer.c */ /* EMACS_MODES: c !fill */ /* This file contains all the routines for handling the timeout queue, * including processing timeouts, enqueueing and dequeueing data, etc. * The following routines are included: * do_timeouts Check for any timeouts, and process them * enq_timeout Enqueue a timeout request * deq_timeout Dequeue a timeout request, call completion routine * add_to_tail Add an element to the end of a queue * dequeue Dequeue an element from the queue * */ #include "params.h" #include "icmp_codes.h" #include "timer.h" #include "extern.h" #include "ip.h" static struct q_head retry_queue; /* the retry queue */ do_timeouts () /* Check the timeout queue for requests which have timed out. The * queue is maintained in timeout order, so if the first request * has not timed out, none have. For each timeout: * if the retry count has reached 0, call the completion routine * with stat = TIMEOUT, then abandon the request * otherwise, resend the queued packet; if error, call the * completion routine with stat = the error status and abandon * the request, otherwise requeue the timeout. * The routine is responsible for freeing the storage occupied by * the queue entries and the outgoing packets to which the queue * entries point when a request times out or network I/O error. * Return the time to sleep until the next wakeup. */ { reg struct q_ent *qe; /* current queue entry */ reg struct q_ent *qn; /* next queue entry */ reg int alarmtime; /* time for next wakeup */ int stat; /* transmission status */ for (qe = retry_queue.qh_head; qe != NULL && qe->q_timeout <= curtime; qe = qn) { qn = qe->q_next; dequeue (qe); if (--(qe->q_retry) <= 0) { if (log & LOG_LOC) { logdate (); printf ("request timeout, trans = %d\n", qe->q_uid); } (*qe->q_comp)(NULL, qe->q_arg1, qe->q_arg2, TTIMEOUT); in_free (qe->q_pkt); cfree ((char *)qe); } else { if (log & LOG_LOC) { logdate (); printf ("retransmitting packet, trans = %d\n", qe->q_uid); } if (in_write (qe->q_fd, qe->q_pkt, qe->q_size, 0, 0, NOFILL) <= 0) { stat = errno; if (log & LOG_ERR) { logdate (); printf ("net write error %d, trans =\ %d\n", stat, qe->q_uid); } (*qe->q_comp)(NULL, qe->q_arg1,qe->q_arg2, stat); in_free (qe->q_pkt); cfree ((char *)qe); } else { if (log & LOG_XMT) in_logpkt(qe->q_pkt,qe->q_size,OUTPKT); qe->q_timeout = curtime + qe->q_time; add_to_tail (qe); } } } alarmtime = 0; if (retry_queue.qh_head != NULL) alarmtime = retry_queue.qh_head->q_timeout - curtime; return (alarmtime); } enq_timeout (timeout, retries, ppkt, size, fd, uid, comp_routine, arg1, arg2) /* Put the specified timeout on the retry_queue. First allocate * and set up a queue entry block, then add it to the tail of the * retry_queue. The queue is maintained in timeout order, so * that in general only the first element need be checked for timeouts. * * Arguments: */ reg int timeout; /* timeout time */ int retries; /* max. retries */ caddr_t ppkt; /* ptr. to pkt. for retransmit */ unshort size; /* packet size for retransmit */ int fd; /* file descriptor for retransmit */ unshort uid; /* unique request id */ int (*comp_routine)(); /* completion routine addr */ char *arg1; /* arg. for completion routine */ char *arg2; /* another completion routine arg */ { reg struct q_ent *qe; /* this queue entry */ if ((qe = calloc (1, sizeof (struct q_ent))) == NULL) { printf ("can't alloc queue entry; aborting!\n"); abort (); } qe->q_timeout = curtime + timeout; qe->q_time = timeout; qe->q_retry = retries; qe->q_pkt = ppkt; qe->q_size = size; qe->q_fd = fd; qe->q_uid = uid; qe->q_comp = comp_routine; qe->q_arg1 = arg1; qe->q_arg2 = arg2; add_to_tail (qe); } deq_timeout (uid, ppkt) /* Dequeue the timeout request with the specified unique id from the * timeout/retry queue, and call its completion routine, passing * the specified packet as the first argument. If no such timeout * exists, returns FALSE; otherwise, TRUE. * This routine is responsible for freeing the storage occupied * by the timeout queue entry and by the packet stored in the * timeout queue entry for retransmission. * * Arguments: */ reg unshort uid; /* unique id of this request */ reg caddr_t ppkt; /* received response packet */ { reg struct q_ent *qe; /* this request's queue entry */ for (qe = retry_queue.qh_head; qe != NULL; qe = qe->q_next) if (qe->q_uid == uid) break; if (qe == NULL) return (FALSE); dequeue (qe); (*qe->q_comp)(ppkt, qe->q_arg1, qe->q_arg2, TSUCCESS); in_free (qe->q_pkt); cfree ((char *)qe); return (TRUE); } add_to_tail (qe) /* Add the specified queue entry to the tail of the retry_queue. * NOTE: THIS ROUTINE IS NOT ATOMIC. * * Arguments: */ reg struct q_ent *qe; /* the entry */ { qe->q_next = NULL; if (retry_queue.qh_count == 0) retry_queue.qh_head = qe; else retry_queue.qh_tail->q_next = qe; retry_queue.qh_tail = qe; retry_queue.qh_count++; } dequeue (qe) /* Dequeue the specified queue entry from the retry_queue. * NOTE: THIS ROUTINE IS NOT ATOMIC. * * Arguments: */ reg struct q_ent *qe; /* element to dequeue */ { reg struct q_ent *qn; /* previous entry in queue */ if (retry_queue.qh_head == NULL) { printf ("deletion from empty queue, aborting!\n"); abort (); } if (retry_queue.qh_head == qe) { retry_queue.qh_head = qe->q_next; if (retry_queue.qh_tail == qe) retry_queue.qh_tail = NULL; } else { for (qn = retry_queue.qh_head; qn->q_next != NULL && qn->q_next != qe; qn = qn->q_next) ; if (qn->q_next == NULL) { printf ("element not found in queue; aborting!\n"); abort (); } qn->q_next = qe->q_next; if (retry_queue.qh_tail == qe) retry_queue.qh_tail = qn; } retry_queue.qh_count--; }