cl_status_t cl_timer_start(IN cl_timer_t * const p_timer, IN const uint32_t time_ms) { struct timeval curtime; cl_list_item_t *p_list_item; uint32_t delta_time = time_ms; CL_ASSERT(p_timer); CL_ASSERT(p_timer->state == CL_INITIALIZED); pthread_mutex_lock(&gp_timer_prov->mutex); /* Signal the timer provider thread to wake up. */ pthread_cond_signal(&gp_timer_prov->cond); /* Remove the timer from the queue if currently queued. */ if (p_timer->timer_state == CL_TIMER_QUEUED) cl_qlist_remove_item(&gp_timer_prov->queue, &p_timer->list_item); /* Get the current time */ #ifndef timerclear #define timerclear(tvp) (tvp)->tv_sec = (time_t)0, (tvp)->tv_usec = 0L #endif timerclear(&curtime); gettimeofday(&curtime, NULL); /* do not do 0 wait ! */ /* if (delta_time < 1000.0) {delta_time = 1000;} */ /* Calculate the timeout. */ p_timer->timeout.tv_sec = curtime.tv_sec + (delta_time / 1000); p_timer->timeout.tv_nsec = (curtime.tv_usec + ((delta_time % 1000) * 1000)) * 1000; /* Add the timer to the queue. */ if (cl_is_qlist_empty(&gp_timer_prov->queue)) { /* The timer list is empty. Add to the head. */ cl_qlist_insert_head(&gp_timer_prov->queue, &p_timer->list_item); } else { /* Find the correct insertion place in the list for the timer. */ p_list_item = cl_qlist_find_from_tail(&gp_timer_prov->queue, __cl_timer_find, p_timer); /* Insert the timer. */ cl_qlist_insert_next(&gp_timer_prov->queue, p_list_item, &p_timer->list_item); } /* Set the state. */ p_timer->timer_state = CL_TIMER_QUEUED; pthread_mutex_unlock(&gp_timer_prov->mutex); return (CL_SUCCESS); }
static void __cl_event_wheel_callback(IN void *context) { cl_event_wheel_t *p_event_wheel = (cl_event_wheel_t *) context; cl_list_item_t *p_list_item, *p_prev_event_list_item; cl_list_item_t *p_list_next_item; cl_event_wheel_reg_info_t *p_event; uint64_t current_time; uint64_t next_aging_time; uint32_t new_timeout; cl_status_t cl_status; /* might be during closing ... */ if (p_event_wheel->closing) return; current_time = cl_get_time_stamp(); if (NULL != p_event_wheel->p_external_lock) /* Take care of the order of acquiring locks to avoid the deadlock! * The external lock goes first. */ cl_spinlock_acquire(p_event_wheel->p_external_lock); cl_spinlock_acquire(&p_event_wheel->lock); p_list_item = cl_qlist_head(&p_event_wheel->events_wheel); if (p_list_item == cl_qlist_end(&p_event_wheel->events_wheel)) /* the list is empty - nothing to do */ goto Exit; /* we found such an item. get the p_event */ p_event = PARENT_STRUCT(p_list_item, cl_event_wheel_reg_info_t, list_item); while (p_event->aging_time <= current_time) { /* this object has aged - invoke it's callback */ if (p_event->pfn_aged_callback) next_aging_time = p_event->pfn_aged_callback(p_event->key, p_event->num_regs, p_event->context); else next_aging_time = 0; /* point to the next object in the wheel */ p_list_next_item = cl_qlist_next(p_list_item); /* We need to retire the event if the next aging time passed */ if (next_aging_time < current_time) { /* remove it from the map */ cl_qmap_remove_item(&p_event_wheel->events_map, &(p_event->map_item)); /* pop p_event from the wheel */ cl_qlist_remove_head(&p_event_wheel->events_wheel); /* delete the event info object - allocated by cl_event_wheel_reg */ free(p_event); } else { /* update the required aging time */ p_event->aging_time = next_aging_time; p_event->num_regs++; /* do not remove from the map - but remove from the list head and place in the correct position */ /* pop p_event from the wheel */ cl_qlist_remove_head(&p_event_wheel->events_wheel); /* find the event that ages just before */ p_prev_event_list_item = cl_qlist_find_from_tail(&p_event_wheel-> events_wheel, __event_will_age_before, &p_event->aging_time); /* insert just after */ cl_qlist_insert_next(&p_event_wheel->events_wheel, p_prev_event_list_item, &p_event->list_item); /* as we have modified the list - restart from first item: */ p_list_next_item = cl_qlist_head(&p_event_wheel->events_wheel); } /* advance to next event */ p_list_item = p_list_next_item; if (p_list_item == cl_qlist_end(&p_event_wheel->events_wheel)) /* the list is empty - nothing to do */ break; /* get the p_event */ p_event = PARENT_STRUCT(p_list_item, cl_event_wheel_reg_info_t, list_item); } /* We need to restart the timer only if the list is not empty now */ if (p_list_item != cl_qlist_end(&p_event_wheel->events_wheel)) { /* get the p_event */ p_event = PARENT_STRUCT(p_list_item, cl_event_wheel_reg_info_t, list_item); /* start the timer to the timeout [msec] */ new_timeout = (uint32_t) ((p_event->aging_time - current_time + 500) / 1000); CL_DBG("__cl_event_wheel_callback: Restart timer in: " "%u [msec]\n", new_timeout); cl_status = cl_timer_start(&p_event_wheel->timer, new_timeout); if (cl_status != CL_SUCCESS) { CL_DBG("__cl_event_wheel_callback : ERR 6200: " "Failed to start timer\n"); } } /* release the lock */ Exit: cl_spinlock_release(&p_event_wheel->lock); if (NULL != p_event_wheel->p_external_lock) cl_spinlock_release(p_event_wheel->p_external_lock); }
cl_status_t cl_event_wheel_reg(IN cl_event_wheel_t * const p_event_wheel, IN const uint64_t key, IN const uint64_t aging_time_usec, IN cl_pfn_event_aged_cb_t pfn_callback, IN void *const context) { cl_event_wheel_reg_info_t *p_event; uint64_t timeout; uint32_t to; cl_status_t cl_status = CL_SUCCESS; cl_list_item_t *prev_event_list_item; cl_map_item_t *p_map_item; /* Get the lock on the manager */ cl_spinlock_acquire(&(p_event_wheel->lock)); cl_event_wheel_dump(p_event_wheel); /* Make sure such a key does not exists */ p_map_item = cl_qmap_get(&p_event_wheel->events_map, key); if (p_map_item != cl_qmap_end(&p_event_wheel->events_map)) { CL_DBG("cl_event_wheel_reg: Already exists key:0x%" PRIx64 "\n", key); /* already there - remove it from the list as it is getting a new time */ p_event = PARENT_STRUCT(p_map_item, cl_event_wheel_reg_info_t, map_item); /* remove the item from the qlist */ cl_qlist_remove_item(&p_event_wheel->events_wheel, &p_event->list_item); /* and the qmap */ cl_qmap_remove_item(&p_event_wheel->events_map, &p_event->map_item); } else { /* make a new one */ p_event = (cl_event_wheel_reg_info_t *) malloc(sizeof(cl_event_wheel_reg_info_t)); p_event->num_regs = 0; } p_event->key = key; p_event->aging_time = aging_time_usec; p_event->pfn_aged_callback = pfn_callback; p_event->context = context; p_event->num_regs++; CL_DBG("cl_event_wheel_reg: Registering event key:0x%" PRIx64 " aging in %u [msec]\n", p_event->key, (uint32_t) ((p_event->aging_time - cl_get_time_stamp()) / 1000)); /* If the list is empty - need to start the timer */ if (cl_is_qlist_empty(&p_event_wheel->events_wheel)) { /* Edward Bortnikov 03/29/2003 * ++TBD Consider moving the timer manipulation behind the list manipulation. */ /* calculate the new timeout */ timeout = (p_event->aging_time - cl_get_time_stamp() + 500) / 1000; /* stop the timer if it is running */ /* Edward Bortnikov 03/29/2003 * Don't call cl_timer_stop() because it spins forever. * cl_timer_start() will invoke cl_timer_stop() by itself. * * The problematic scenario is when __cl_event_wheel_callback() * is in race condition with this code. It sets timer.in_timer_cb * to TRUE and then blocks on p_event_wheel->lock. Following this, * the call to cl_timer_stop() hangs. Following this, the whole system * enters into a deadlock. * * cl_timer_stop(&p_event_wheel->timer); */ /* The timeout for the cl_timer_start should be given as uint32_t. if there is an overflow - warn about it. */ to = (uint32_t) timeout; if (timeout > (uint32_t) timeout) { to = 0xffffffff; /* max 32 bit timer */ CL_DBG("cl_event_wheel_reg: timeout requested is " "too large. Using timeout: %u\n", to); } /* start the timer to the timeout [msec] */ cl_status = cl_timer_start(&p_event_wheel->timer, to); if (cl_status != CL_SUCCESS) { CL_DBG("cl_event_wheel_reg : ERR 6203: " "Failed to start timer\n"); goto Exit; } } /* insert the object to the qlist and the qmap */ /* BUT WE MUST INSERT IT IN A SORTED MANNER */ prev_event_list_item = cl_qlist_find_from_tail(&p_event_wheel->events_wheel, __event_will_age_before, &p_event->aging_time); cl_qlist_insert_next(&p_event_wheel->events_wheel, prev_event_list_item, &p_event->list_item); cl_qmap_insert(&p_event_wheel->events_map, key, &(p_event->map_item)); Exit: cl_spinlock_release(&p_event_wheel->lock); return cl_status; }