version 1.18, 2010/04/06 20:07:01 |
version 1.19, 2010/05/02 10:43:30 |
|
|
* the module also provides trivial timeout implementation, |
* the module also provides trivial timeout implementation, |
* derived from: |
* derived from: |
* |
* |
* anoncvs@moule.caoua.org:/cvs/midish/timo.c rev 1.16 |
* anoncvs@moule.caoua.org:/cvs |
* |
* |
|
* midish/timo.c rev 1.16 |
|
* midish/mdep.c rev 1.69 |
|
* |
* A timeout is used to schedule the call of a routine (the callback) |
* A timeout is used to schedule the call of a routine (the callback) |
* there is a global list of timeouts that is processed inside the |
* there is a global list of timeouts that is processed inside the |
* event loop. Timeouts work as follows: |
* event loop. Timeouts work as follows: |
|
|
#endif |
#endif |
|
|
#define MAXFDS 100 |
#define MAXFDS 100 |
|
#define TIMER_USEC 10000 |
|
|
struct timeval file_tv; |
struct timespec file_ts; |
struct filelist file_list; |
struct filelist file_list; |
struct timo *timo_queue; |
struct timo *timo_queue; |
unsigned timo_abstime; |
unsigned timo_abstime; |
|
|
|
|
#ifdef DEBUG |
#ifdef DEBUG |
if (o->set) { |
if (o->set) { |
dbg_puts("timo_set: already set\n"); |
dbg_puts("timo_add: already set\n"); |
dbg_panic(); |
dbg_panic(); |
} |
} |
if (delta == 0) { |
if (delta == 0) { |
dbg_puts("timo_set: zero timeout is evil\n"); |
dbg_puts("timo_add: zero timeout is evil\n"); |
dbg_panic(); |
dbg_panic(); |
} |
} |
#endif |
#endif |
|
|
struct pollfd pfds[MAXFDS]; |
struct pollfd pfds[MAXFDS]; |
struct file *f, *fnext; |
struct file *f, *fnext; |
struct aproc *p; |
struct aproc *p; |
struct timeval tv; |
struct timespec ts; |
long delta_usec; |
long delta_nsec; |
int timo; |
|
|
|
|
if (LIST_EMPTY(&file_list)) { |
|
#ifdef DEBUG |
|
if (debug_level >= 3) |
|
dbg_puts("nothing to do...\n"); |
|
#endif |
|
return 0; |
|
} |
/* |
/* |
* Fill the pfds[] array with files that are blocked on reading |
* Fill the pfds[] array with files that are blocked on reading |
* and/or writing, skipping those that are just waiting. |
* and/or writing, skipping those that are just waiting. |
|
|
dbg_puts("\n"); |
dbg_puts("\n"); |
} |
} |
#endif |
#endif |
if (LIST_EMPTY(&file_list)) { |
|
#ifdef DEBUG |
|
if (debug_level >= 3) |
|
dbg_puts("nothing to do...\n"); |
|
#endif |
|
return 0; |
|
} |
|
if (nfds > 0) { |
if (nfds > 0) { |
if (timo_queue) { |
if (poll(pfds, nfds, -1) < 0) { |
timo = (timo_queue->val - timo_abstime) / (2 * 1000); |
|
if (timo == 0) |
|
timo = 1; |
|
} else |
|
timo = -1; |
|
if (poll(pfds, nfds, timo) < 0) { |
|
if (errno == EINTR) |
if (errno == EINTR) |
return 1; |
return 1; |
err(1, "file_poll: poll failed"); |
err(1, "file_poll: poll failed"); |
} |
} |
gettimeofday(&tv, NULL); |
clock_gettime(CLOCK_MONOTONIC, &ts); |
delta_usec = 1000000L * (tv.tv_sec - file_tv.tv_sec); |
delta_nsec = 1000000000L * (ts.tv_sec - file_ts.tv_sec); |
delta_usec += tv.tv_usec - file_tv.tv_usec; |
delta_nsec += ts.tv_nsec - file_ts.tv_nsec; |
if (delta_usec > 0) { |
if (delta_nsec > 0) { |
file_tv = tv; |
file_ts = ts; |
timo_update(delta_usec); |
timo_update(delta_nsec / 1000); |
} |
} |
} |
} |
f = LIST_FIRST(&file_list); |
f = LIST_FIRST(&file_list); |
|
|
return 1; |
return 1; |
} |
} |
|
|
|
/* |
|
* handler for SIGALRM, invoked periodically |
|
*/ |
void |
void |
|
file_sigalrm(int i) |
|
{ |
|
/* nothing to do, we only want poll() to return EINTR */ |
|
} |
|
|
|
|
|
void |
filelist_init(void) |
filelist_init(void) |
{ |
{ |
|
static struct sigaction sa; |
|
struct itimerval it; |
sigset_t set; |
sigset_t set; |
|
|
sigemptyset(&set); |
sigemptyset(&set); |
|
|
if (sigprocmask(SIG_BLOCK, &set, NULL)) |
if (sigprocmask(SIG_BLOCK, &set, NULL)) |
err(1, "sigprocmask"); |
err(1, "sigprocmask"); |
LIST_INIT(&file_list); |
LIST_INIT(&file_list); |
|
if (clock_gettime(CLOCK_MONOTONIC, &file_ts) < 0) { |
|
perror("clock_gettime"); |
|
exit(1); |
|
} |
|
sa.sa_flags = SA_RESTART; |
|
sa.sa_handler = file_sigalrm; |
|
sigfillset(&sa.sa_mask); |
|
if (sigaction(SIGALRM, &sa, NULL) < 0) { |
|
perror("sigaction"); |
|
exit(1); |
|
} |
|
it.it_interval.tv_sec = 0; |
|
it.it_interval.tv_usec = TIMER_USEC; |
|
it.it_value.tv_sec = 0; |
|
it.it_value.tv_usec = TIMER_USEC; |
|
if (setitimer(ITIMER_REAL, &it, NULL) < 0) { |
|
perror("setitimer"); |
|
exit(1); |
|
} |
timo_init(); |
timo_init(); |
gettimeofday(&file_tv, NULL); |
|
#ifdef DEBUG |
#ifdef DEBUG |
dbg_sync = 0; |
dbg_sync = 0; |
#endif |
#endif |
|
|
void |
void |
filelist_done(void) |
filelist_done(void) |
{ |
{ |
|
struct itimerval it; |
#ifdef DEBUG |
#ifdef DEBUG |
struct file *f; |
struct file *f; |
|
|
if (!LIST_EMPTY(&file_list)) { |
if (!LIST_EMPTY(&file_list)) { |
LIST_FOREACH(f, &file_list, entry) { |
LIST_FOREACH(f, &file_list, entry) { |
dbg_puts("\t"); |
|
file_dbg(f); |
file_dbg(f); |
dbg_puts("\n"); |
dbg_puts(" not closed\n"); |
} |
} |
dbg_panic(); |
dbg_panic(); |
} |
} |
dbg_sync = 1; |
dbg_sync = 1; |
dbg_flush(); |
dbg_flush(); |
#endif |
#endif |
|
it.it_value.tv_sec = 0; |
|
it.it_value.tv_usec = 0; |
|
it.it_interval.tv_sec = 0; |
|
it.it_interval.tv_usec = 0; |
|
if (setitimer(ITIMER_REAL, &it, NULL) < 0) { |
|
perror("setitimer"); |
|
exit(1); |
|
} |
timo_done(); |
timo_done(); |
} |
} |
|
|
|
|
{ |
{ |
unsigned n; |
unsigned n; |
#ifdef DEBUG |
#ifdef DEBUG |
struct timeval tv0, tv1, dtv; |
struct timespec ts0, ts1; |
unsigned us; |
long us; |
|
|
gettimeofday(&tv0, NULL); |
|
if (!(f->state & FILE_ROK)) { |
if (!(f->state & FILE_ROK)) { |
file_dbg(f); |
file_dbg(f); |
dbg_puts(": read: bad state\n"); |
dbg_puts(": read: bad state\n"); |
dbg_panic(); |
dbg_panic(); |
} |
} |
|
clock_gettime(CLOCK_MONOTONIC, &ts0); |
#endif |
#endif |
n = f->ops->read(f, data, count); |
n = f->ops->read(f, data, count); |
#ifdef DEBUG |
#ifdef DEBUG |
gettimeofday(&tv1, NULL); |
clock_gettime(CLOCK_MONOTONIC, &ts1); |
timersub(&tv1, &tv0, &dtv); |
us = 1000000L * (ts1.tv_sec - ts0.tv_sec); |
us = dtv.tv_sec * 1000000 + dtv.tv_usec; |
us += (ts1.tv_nsec - ts0.tv_nsec) / 1000; |
if (debug_level >= 4 || (debug_level >= 1 && us >= 5000)) { |
if (debug_level >= 4 || (debug_level >= 2 && us >= 5000)) { |
dbg_puts(f->name); |
dbg_puts(f->name); |
dbg_puts(": read "); |
dbg_puts(": read "); |
dbg_putu(n); |
dbg_putu(n); |
|
|
{ |
{ |
unsigned n; |
unsigned n; |
#ifdef DEBUG |
#ifdef DEBUG |
struct timeval tv0, tv1, dtv; |
struct timespec ts0, ts1; |
unsigned us; |
long us; |
|
|
if (!(f->state & FILE_WOK)) { |
if (!(f->state & FILE_WOK)) { |
file_dbg(f); |
file_dbg(f); |
dbg_puts(": write: bad state\n"); |
dbg_puts(": write: bad state\n"); |
dbg_panic(); |
dbg_panic(); |
} |
} |
gettimeofday(&tv0, NULL); |
clock_gettime(CLOCK_MONOTONIC, &ts0); |
#endif |
#endif |
n = f->ops->write(f, data, count); |
n = f->ops->write(f, data, count); |
#ifdef DEBUG |
#ifdef DEBUG |
gettimeofday(&tv1, NULL); |
clock_gettime(CLOCK_MONOTONIC, &ts1); |
timersub(&tv1, &tv0, &dtv); |
us = 1000000L * (ts1.tv_sec - ts0.tv_sec); |
us = dtv.tv_sec * 1000000 + dtv.tv_usec; |
us += (ts1.tv_nsec - ts0.tv_nsec) / 1000; |
if (debug_level >= 4 || (debug_level >= 1 && us >= 5000)) { |
if (debug_level >= 4 || (debug_level >= 2 && us >= 5000)) { |
dbg_puts(f->name); |
dbg_puts(f->name); |
dbg_puts(": wrote "); |
dbg_puts(": wrote "); |
dbg_putu(n); |
dbg_putu(n); |