Triton Repository branch, master, updated. 074f9e76d35a042e476ece68bcb0487b6eb1bad0
This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "Triton Repository". The branch, master has been updated via 074f9e76d35a042e476ece68bcb0487b6eb1bad0 (commit) from e5d0a0d2cb2f2d148913b41b63906c03d1371b16 (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit 074f9e76d35a042e476ece68bcb0487b6eb1bad0 Author: Phil Carns <[email protected]> Date: Fri Feb 25 11:59:27 2011 -0500 rework timer resource to only use one posix timer ----------------------------------------------------------------------- Summary of changes: code/src/common/resources/timer/timer.c | 236 +++++++++++++++++------------- 1 files changed, 134 insertions(+), 102 deletions(-) Diff of changes: diff --git a/code/src/common/resources/timer/timer.c b/code/src/common/resources/timer/timer.c index aefba7a..18fb1e2 100644 --- a/code/src/common/resources/timer/timer.c +++ b/code/src/common/resources/timer/timer.c @@ -18,29 +18,20 @@ * resource. The aesop callbacks are driven directly from the timer * notfication function. * - * This design seems to work ok, but for future reference: an - * alternative design would be to put the timer ops in a sorted queue and - * then have the posix timer notification functions simply trigger - * ae_resource_request_poll() so that the poll function can scan the timer - * queue. That would eliminate race conditions because superfluous timer - * notifications would simply trigger harmless additional poll calls. - * - * It would also be better to simply set one timer at a time (for the - * earliest timer that needs to be triggered). - * - * Also, if we continue to use libev, we could use its timer functionality - * instead of the posix timers. + * TODO: optional support for libev */ #define TIMER_DEFAULT_SIZE 1024 static ae_opcache_t timer_opcache; - static int triton_timer_resource_id; +static timer_t timer_id; +static struct sigevent evp; +static void timer_notify(union sigval sv); struct timer_op { - timer_t timer_id; + struct timeval timer; ae_op_id_t op_id; struct ae_op op; triton_ret_t tret; @@ -49,15 +40,15 @@ static triton_mutex_t timer_mutex = TRITON_MUTEX_INITIALIZER; static ae_ops_t timer_oplist; static ae_ops_t cancel_oplist; -static void timer_notify(union sigval sv); - ae_define_post(triton_ret_t, triton_timer, int millisecs) { + struct timeval adjust, now; int ret; - struct itimerspec tspec; - struct sigevent evp; struct timer_op *top; struct ae_op *op; + struct ae_op *iter, *safe, *holder; + struct timer_op *timer_op_iter; + struct itimerspec tspec; op = ae_opcache_get(timer_opcache); ae_op_fill(op); @@ -66,40 +57,54 @@ ae_define_post(triton_ret_t, triton_timer, int millisecs) top->op_id = ae_id_gen(triton_timer_resource_id, (uint64_t)(op->cache_id)); top->tret = TRITON_SUCCESS; - memset(&tspec, 0, sizeof(tspec)); - tspec.it_value.tv_sec = (int)(millisecs/1000); - tspec.it_value.tv_nsec = (millisecs % 1000) * 1e6; + adjust.tv_sec = (int)(millisecs / 1e3); + adjust.tv_usec = (millisecs % 1000) * 1e3; + gettimeofday(&now, NULL); + timeradd(&adjust, &now, &(top->timer)); - memset(&evp, 0, sizeof(evp)); - evp.sigev_notify = SIGEV_THREAD; - evp.sigev_value.sival_ptr = op; - evp.sigev_notify_function = timer_notify; + triton_mutex_lock(&timer_mutex); + //holder = ae_ops_peek(&timer_oplist); + holder = NULL; + /* NOTE: we search the timer list from the back, on the assumption that + * the newest timers added are most likely to be the furthest away from + * completion. We will often see the same millisecs value over and + * over. + */ + ae_ops_for_each_reverse(iter, safe, &timer_oplist) + { + timer_op_iter = ae_op_entry(iter, struct timer_op, op); + if(timercmp(&(top->timer), &(timer_op_iter->timer), >)) + { + break; + } + holder = iter; + } + if(holder) + { + ae_ops_insert_before(op, holder, &timer_oplist); + } + else + { + ae_ops_enqueue(op, &timer_oplist); + } - ret = timer_create(CLOCK_REALTIME, &evp, &top->timer_id); - if(ret != 0) + /* Did this end up at the head of the queue? */ + holder = ae_ops_peek(&timer_oplist); + if(holder == op) { - fprintf(stderr, "errno: %d\n", errno); - triton_err(triton_log_default, "Error: failed to create timer."); - return(TRITON_ERR_NOMEM); + /* new head of queue; arm a new timer or modify the existing one */ + memset(&tspec, 0, sizeof(tspec)); + tspec.it_value.tv_sec = (int)(millisecs/1000); + tspec.it_value.tv_nsec = (millisecs % 1000) * 1e6; + ret = timer_settime(timer_id, 0, &tspec, NULL); + /* TODO: error handling (cancel all remaining timers?) */ + assert(ret == 0); } *__ae_op_id = top->op_id; - triton_mutex_lock(&timer_mutex); - ae_ops_enqueue(op, &timer_oplist); triton_mutex_unlock(&timer_mutex); - ret = timer_settime(top->timer_id, 0, &tspec, NULL); - if(ret != 0) - { - timer_delete(top->timer_id); - triton_mutex_lock(&timer_mutex); - ae_ops_del(op); - ae_opcache_put(timer_opcache, op); - triton_mutex_unlock(&timer_mutex); - return(TRITON_ERR_INVAL); - } - return TRITON_SUCCESS; } @@ -107,9 +112,7 @@ static triton_ret_t triton_timer_poll(ae_context_t context) { struct ae_op *op; struct timer_op *top; -#if 0 - /* nothing to do here; this resource is purely thread driven */ -#else + /* harvest any timers that have been cancelled */ triton_mutex_lock(&timer_mutex); while((op = ae_ops_dequeue(&cancel_oplist))) @@ -118,13 +121,11 @@ static triton_ret_t triton_timer_poll(ae_context_t context) top = ae_op_entry(op, struct timer_op, op); assert(top->tret == TRITON_ERR_CANCELED); - - timer_delete(top->timer_id); ae_opcache_complete_op(timer_opcache, op, triton_ret_t, top->tret); triton_mutex_lock(&timer_mutex); } triton_mutex_unlock(&timer_mutex); -#endif + return TRITON_SUCCESS; } @@ -161,42 +162,18 @@ static triton_ret_t triton_timer_cancel(ae_context_t triton_ctx, ae_op_id_t op_i top = ae_op_entry(op, struct timer_op, op); top->tret = TRITON_ERR_CANCELED; - /* NOTE: although we hold a mutex lock here, the timer could still have - * completed and fired the callback function (which is now waiting on - * the mutex). We can detect that scenario by checking the output - * argument of timer_settime(). - * + /* NOTE: don't bother modifying the posix timer when we cancel + * something. In the worst case it will fire one time more than is + * strictly necessary and reset itself. */ - /* disarm timer */ - memset(&tspec, 0, sizeof(tspec)); - timer_settime(top->timer_id, 0, &tspec, &old_tspec); - if(old_tspec.it_value.tv_sec != 0 || old_tspec.it_value.tv_nsec != 0) - { -#if 0 - /* timer had not fired yet; re-arm it for 1 nsec in the future so - * that the normal completion function executes and propigates the - * ERR_CANCELED return code - */ - tspec.it_value.tv_sec = 0; - tspec.it_value.tv_nsec = 1; - timer_settime(top->timer_id, 0, &tspec, NULL); -#else - /* move to a special queue of cancelled timers */ - assert(ae_ops_exists(&timer_oplist, &op->link)); - ae_ops_del(op); - ctx = op->ctx; - ae_ops_enqueue(op, &cancel_oplist); - /* request a poll for aesop to harvest the cancelled timer */ - ae_resource_request_poll(ctx, triton_timer_resource_id); -#endif - } - else - { - /* Timer already fired, but has not yet acquired the timer_mutex. - * We can let it complete naturally from here. - */ - } + /* move to a special queue of cancelled timers */ + assert(ae_ops_exists(&timer_oplist, &op->link)); + ae_ops_del(op); + ctx = op->ctx; + ae_ops_enqueue(op, &cancel_oplist); + /* request a poll for aesop to harvest the cancelled timer */ + ae_resource_request_poll(ctx, triton_timer_resource_id); triton_mutex_unlock(&timer_mutex); @@ -219,15 +196,28 @@ __attribute__((constructor)) void triton_timer_init_register(void) triton_ret_t triton_timer_init(void) { - triton_ret_t ret; + triton_ret_t tret; + int ret; + memset(&evp, 0, sizeof(evp)); + evp.sigev_notify = SIGEV_THREAD; + evp.sigev_value.sival_ptr = NULL; + evp.sigev_notify_function = timer_notify; + + ret = timer_create(CLOCK_REALTIME, &evp, &timer_id); + if(ret < 0) + { + triton_err(triton_log_default, "Error: failed to create timer."); + return(TRITON_ERR_NOMEM); + } + ae_ops_init(&timer_oplist); ae_ops_init(&cancel_oplist); - ret = AE_OPCACHE_INIT(struct timer_op, op, TIMER_DEFAULT_SIZE, &timer_opcache); - if(ret != TRITON_SUCCESS) + tret = AE_OPCACHE_INIT(struct timer_op, op, TIMER_DEFAULT_SIZE, &timer_opcache); + if(tret != TRITON_SUCCESS) { - return ret; + return tret; } return ae_resource_register(&triton_timer_resource, &triton_timer_resource_id); @@ -237,32 +227,74 @@ void triton_timer_finalize(void) { ae_resource_unregister(triton_timer_resource_id); + timer_delete(timer_id); + ae_opcache_destroy(timer_opcache); } static void timer_notify(union sigval sv) { - struct ae_op *op; + struct ae_op *gop; struct timer_op *top; + struct timeval now; + struct timeval diff; + int did_something = 0; ae_context_t ctx; + struct itimerspec tspec; + int ret; + + triton_mutex_lock(&timer_mutex); - op = sv.sival_ptr; - top = ae_op_entry(op, struct timer_op, op); + gop = ae_ops_peek(&timer_oplist); + top = ae_op_entry(gop, struct timer_op, op); - triton_mutex_lock(&timer_mutex); - assert(ae_ops_exists(&timer_oplist, &op->link)); - ae_ops_del(op); - triton_mutex_unlock(&timer_mutex); + gettimeofday(&now, NULL); - ctx = op->ctx; - timer_delete(top->timer_id); - ae_opcache_complete_op(timer_opcache, op, triton_ret_t, top->tret); + /* complete the timers that have hit (t < now) */ + while(top && timercmp(&(top->timer), &now, <)) + { + /* this timer has hit, so we pop and call callback */ + gop = ae_ops_dequeue(&timer_oplist); + top = ae_op_entry(gop, struct timer_op, op); + triton_mutex_unlock(&timer_mutex); - /* request poll; not because ae_poll() needs to drive any work, but - * simply because we want it to break out if any callers are blocking on - * it - */ - ae_resource_request_poll(ctx, triton_timer_resource_id); + ctx = gop->ctx; + ae_opcache_complete_op(timer_opcache, gop, triton_ret_t, TRITON_SUCCESS); + /* request poll; not because ae_poll() needs to drive any work, but + * simply because we want it to break out if any callers are blocking on + * it + */ + ae_resource_request_poll(ctx, triton_timer_resource_id); + + /* setup for next iteration */ + triton_mutex_lock(&timer_mutex); + gop = ae_ops_peek(&timer_oplist); + top = ae_op_entry(gop, struct timer_op, op); + } + + /* is there anything left in the queue? If so, arm the timer again */ + gop = ae_ops_peek(&timer_oplist); + top = ae_op_entry(gop, struct timer_op, op); + if(top) + { + gettimeofday(&now, NULL); + timersub(&top->timer, &now, &diff); + if(diff.tv_sec < 0 || diff.tv_usec < 0) + { + /* whoops, we already need to harvest this one */ + diff.tv_sec = 0; + diff.tv_usec = 1; + } + + memset(&tspec, 0, sizeof(tspec)); + tspec.it_value.tv_sec = diff.tv_sec; + tspec.it_value.tv_nsec = diff.tv_usec * 1e3; + ret = timer_settime(timer_id, 0, &tspec, NULL); + /* TODO: error handling (cancel all remaining timers?) */ + assert(ret == 0); + } + + triton_mutex_unlock(&timer_mutex); return; } hooks/post-receive -- Triton Repository
participants (1)
-
noreply@mcs.anl.gov