timers.c
上传用户:pycemail
上传日期:2007-01-04
资源大小:329k
文件大小:8k
源码类别:

Ftp客户端

开发平台:

Unix_Linux

  1. /*
  2.  * ProFTPD - FTP server daemon
  3.  * Copyright (c) 1997, 1998 Public Flood Software
  4.  *
  5.  * This program is free software; you can redistribute it and/or modify
  6.  * it under the terms of the GNU General Public License as published by
  7.  * the Free Software Foundation; either version 2 of the License, or
  8.  * (at your option) any later version.
  9.  *
  10.  * This program is distributed in the hope that it will be useful,
  11.  * BUT witHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License
  16.  * along with this program; if not, write to the Free Software
  17.  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
  18.  */
  19. /* 
  20.  * Timer system, based on alarm() and SIGALRM
  21.  * $Id: timers.c,v 1.3 1999/03/05 17:55:43 flood Exp $
  22.  */
  23. #include <signal.h>
  24. #include "conf.h"
  25. static int _current_timeout = 0;
  26. static int _total_time = 0;
  27. static int _sleep_sem = 0;
  28. static int alarms_blocked = 0,alarm_pending = 0;
  29. static xaset_t *timers = NULL;
  30. static xaset_t *recycled = NULL;
  31. static int _indispatch = 0;
  32. static int dynamic_timerno = 1024;
  33. static int _alarm_received = 0;
  34. xaset_t *free_timers = NULL;
  35. static int _compare_timer(timer_t *t1, timer_t *t2)
  36. {
  37.   if(t1->count < t2->count)
  38.     return -1;
  39.   if(t1->count > t2->count)
  40.     return 1;
  41.   return 0;
  42. }
  43. static int _reset_timers(int elapsed)
  44. {
  45.   timer_t *t,*next;
  46.   if(!recycled)
  47.     recycled = xaset_create(NULL,NULL);
  48.   if(!elapsed && !recycled->xas_list)
  49.     return (timers->xas_list ? ((timer_t*)timers->xas_list)->count :
  50.             0);
  51.   /* Critical code, no interruptions please */
  52.   if(_indispatch)
  53.     return 0;
  54.   _indispatch++;
  55.   block_alarms();
  56.   if(elapsed) {
  57.     for(t = (timer_t*)timers->xas_list; t; t=next) {
  58.       /* If this timer has already been handled, skip */
  59.       next = t->next;
  60.       if( (t->count -= elapsed) <= 0) {
  61.         if(t->remove ||
  62.            t->callback(0,t->timerno,t->interval - t->count,NULL) == 0) {
  63.           /* Move the timer onto the free_timers chain, for later
  64.            * reuse
  65.            */
  66.           /*
  67.            log_debug(DEBUG5,"moving timer %d to free_timers list.",
  68.                      t->timerno);
  69.            */
  70.            xaset_remove(timers,(xasetmember_t*)t);
  71.            xaset_insert(free_timers,(xasetmember_t*)t);
  72.         } else {
  73.          /*
  74.           log_debug(DEBUG5,"moving timer %d to recycled list.",
  75.                     t->timerno);
  76.           */
  77.           /* Restart the timer */
  78.           xaset_remove(timers,(xasetmember_t*)t);
  79.           t->count = t->interval;
  80.           xaset_insert(recycled,(xasetmember_t*)t);
  81.         }
  82.       }
  83.     }
  84.   }
  85.   /* Put the recycled timers back into the main timer list */
  86.   while((t = (timer_t*)recycled->xas_list) != NULL) {
  87.     /*log_debug(DEBUG5,"moving timer %d off recycled list.",t->timerno);*/
  88.     xaset_remove(recycled,(xasetmember_t*)t);
  89.     xaset_insert_sort(timers,(xasetmember_t*)t,TRUE);
  90.   }
  91.   unblock_alarms();
  92.   _indispatch--;
  93.   /* If no active timers remain in the list, there is no reason
  94.      to set the alarm */
  95.   return (timers->xas_list ? ((timer_t*)timers->xas_list)->count : 0);
  96. }
  97. void sig_alarm(int signum)
  98. {
  99.   struct sigaction act;
  100.   _alarm_received++;
  101.   act.sa_handler = sig_alarm;
  102.   sigemptyset(&act.sa_mask);
  103.   act.sa_flags = 0;
  104. #ifdef SA_INTERRUPT
  105.   act.sa_flags |= SA_INTERRUPT;
  106. #endif
  107.   sigaction(SIGALRM,&act,NULL);
  108. #ifdef HAVE_SIGINTERRUPT
  109.   siginterrupt(SIGALRM,1);
  110. #endif
  111.   /* Reset the alarm */
  112.   _total_time += _current_timeout;
  113.   if(_current_timeout)
  114.     alarm(_current_timeout);
  115. }
  116. void set_sig_alarm()
  117. {
  118.   struct sigaction act;
  119.   act.sa_handler = sig_alarm;
  120.   sigemptyset(&act.sa_mask);
  121.   act.sa_flags = 0;
  122. #ifdef SA_INTERRUPT
  123.   act.sa_flags |= SA_INTERRUPT;
  124. #endif
  125.   sigaction(SIGALRM,&act,NULL);
  126. #ifdef HAVE_SIGINTERRUPT
  127.   siginterrupt(SIGALRM,1);
  128. #endif
  129. }
  130. void handle_sig_alarm()
  131. {
  132.   int new_timeout;
  133.   /* We need to adjust for any time that might be remaining on the alarm,
  134.    * in case we were called in order change alarm durations.  Note
  135.    * that rapid-fire calling of this function will probably screw
  136.    * up the already poor resolution of alarm() _horribly_.  Oh well,
  137.    * this shouldn't be used for any precise work anyway, it's only
  138.    * for modules to perform approximate timing.
  139.    */
  140.   /* It's possible that alarms are blocked when this function is
  141.    * called, if so, increment alarm_pending and exit swiftly
  142.    */
  143.   while(_alarm_received) {
  144.     _alarm_received = 0;
  145.     if(!alarms_blocked) {
  146.       new_timeout = _total_time + (_current_timeout - alarm(0));
  147.       _total_time = 0;
  148.       new_timeout = _reset_timers(new_timeout);
  149.       /*log_debug(DEBUG5,"alarm(%d)",new_timeout);*/
  150.       alarm(_current_timeout = new_timeout);
  151.     } else
  152.       alarm_pending++;
  153.   }
  154. }
  155. int reset_timer(int timerno, module *mod)
  156. {
  157.   timer_t *t;
  158.   if(_indispatch)
  159.     return -1;
  160.   block_alarms();
  161.   if(!recycled)
  162.     recycled = xaset_create(NULL,NULL);
  163.   for(t = (timer_t*)timers->xas_list; t; t=t->next)
  164.     if(t->timerno == timerno && (t->mod == mod || mod == ANY_MODULE)) {
  165.       t->count = t->interval;
  166.       xaset_remove(timers,(xasetmember_t*)t);
  167.       xaset_insert(recycled,(xasetmember_t*)t);
  168.       _alarm_received++;
  169.       handle_sig_alarm();
  170.       break;
  171.     }
  172.   unblock_alarms();
  173.   return (t ? t->timerno : 0);
  174. }
  175. int remove_timer(int timerno, module *mod)
  176. {
  177.   timer_t *t;
  178.   block_alarms();
  179.   for(t = (timer_t*)timers->xas_list; t; t=t->next)
  180.     if(t->timerno == timerno && (t->mod == mod || mod == ANY_MODULE)) {
  181.       if(_indispatch)
  182.         t->remove++;
  183.       else {
  184.         xaset_remove(timers,(xasetmember_t*)t);
  185.         xaset_insert(free_timers,(xasetmember_t*)t);
  186. _alarm_received++;
  187.         handle_sig_alarm();
  188.       }      
  189.       break;
  190.     }
  191.   unblock_alarms();
  192.   return (t ? t->timerno : 0);
  193. }
  194. int add_timer(int seconds, int timerno, module *mod, callback_t cb)
  195. {
  196.   timer_t *t;
  197.   if(!timers)
  198.     timers = xaset_create(NULL,(XASET_COMPARE)_compare_timer);
  199.   if(!free_timers)
  200.     free_timers = xaset_create(NULL,NULL);
  201.   /* Try to use an old timer first */
  202.   block_alarms();
  203.   if((t = (timer_t*)free_timers->xas_list) != NULL)
  204.     xaset_remove(free_timers,(xasetmember_t*)t);
  205.   else
  206.     /* Must allocate a new one */
  207.     t = palloc(permanent_pool,sizeof(timer_t));
  208.   if(timerno == -1) { 
  209.     /* Dynamic timer */
  210.     if(dynamic_timerno < 1024)
  211.       dynamic_timerno = 1024;
  212.     timerno = dynamic_timerno++;
  213.   }
  214.   t->timerno = timerno;
  215.   t->count = t->interval = seconds;
  216.   t->callback = cb;
  217.   t->mod = mod;
  218.   t->remove = 0;
  219.   /* If called while _indispatch, add to the recycled list to prevent
  220.    * list corruption
  221.    */
  222.   if(_indispatch) {
  223.     if(!recycled)
  224.       recycled = xaset_create(NULL,NULL);
  225.     xaset_insert(recycled,(xasetmember_t*)t);
  226.   } else {
  227.     xaset_insert_sort(timers,(xasetmember_t*)t,TRUE);
  228.     _alarm_received++;
  229.     set_sig_alarm();
  230.     handle_sig_alarm();
  231.   }
  232.   unblock_alarms();
  233.   return timerno;
  234. }
  235. /* Alarm blocking.  This is done manually rather than with syscalls,
  236.  * so as to allow for easier signal handling, portability and
  237.  * detecting the number of blocked alarms, as well as nesting the
  238.  * block/unblock functions.
  239.  */
  240. void block_alarms()
  241. {
  242.   ++alarms_blocked;
  243. }
  244. void unblock_alarms()
  245. {
  246.   --alarms_blocked;
  247.   if(alarms_blocked == 0 && alarm_pending) {
  248.     alarm_pending = 0;
  249.     _alarm_received++;
  250.     handle_sig_alarm();
  251.   }
  252. }
  253. static int _sleep_callback(CALLBACK_FRAME)
  254. {
  255.   _sleep_sem++;
  256.   return 0;
  257. }
  258. int timer_sleep(int seconds)
  259. {
  260.   int timerno;
  261.   sigset_t oset;
  262.   _sleep_sem = 0;
  263.   if(alarms_blocked || _indispatch)
  264.     return -1;
  265.   timerno = add_timer(seconds,-1,NULL,_sleep_callback);
  266.   if(timerno == -1)
  267.     return -1;
  268.   sigemptyset(&oset);
  269.   while(!_sleep_sem) {
  270.     sigsuspend(&oset);
  271.     handle_sig_alarm();
  272.   }
  273.   
  274.   return 0;  
  275. }