/** * Trampoline for dispatching periodic events. */ static void cq_periodic_trampoline(cqueue_t *cq, void *data) { cperiodic_t *cp = data; bool reschedule; cqueue_check(cq); cperiodic_check(cp); cp->ev = NULL; /* * As long as the periodic event returns TRUE, keep scheduling it. * * To handle synchronous calls to cq_periodic_remove(), freeing of the * periodic event is deferred until we come back from the user call. */ reschedule = (*cp->event)(cp->arg); if (cp->to_free || !reschedule) { cq_periodic_free(cp, TRUE); } else { cp->ev = cq_insert(cq, cp->period, cq_periodic_trampoline, cp); } }
/** * Retry publishing after some delay. * * @param pe the entry to publish * @param delay delay in seconds * @param msg if non-NULL, logging message explaining the delay */ static void publisher_retry(struct publisher_entry *pe, int delay, const char *msg) { struct pubdata *pd; publisher_check(pe); g_assert(NULL == pe->publish_ev); g_assert(delay > 0); pd = get_pubdata(pe->sha1); if (pd != NULL) { pd->next_enqueue = time_advance(tm_time(), UNSIGNED(delay)); dbmw_write(db_pubdata, pe->sha1, pd, sizeof *pd); } pe->publish_ev = cq_insert(publish_cq, delay * 1000, handle_entry, pe); pe->last_delayed = tm_time(); if (GNET_PROPERTY(publisher_debug) > 3) { shared_file_t *sf = shared_file_by_sha1(pe->sha1); g_debug("PUBLISHER will retry SHA-1 %s %s\"%s\" in %s: %s", sha1_to_string(pe->sha1), (sf && sf != SHARE_REBUILDING && shared_file_is_partial(sf)) ? "partial " : "", (sf && sf != SHARE_REBUILDING) ? shared_file_name_nfc(sf) : "", compact_time(delay), msg != NULL ? msg : "<no reason>"); shared_file_unref(&sf); } }
/** * Called from the callout queue when the Nagle timer expires. * * If we can send the buffer, flush it and send it. Otherwise, reschedule. */ static void deflate_nagle_timeout(cqueue_t *cq, void *arg) { txdrv_t *tx = arg; struct attr *attr = tx->opaque; cq_zero(cq, &attr->tm_ev); if (-1 != attr->send_idx) { /* Send buffer still incompletely sent */ if (tx_deflate_debugging(9)) { g_debug("TX %s: (%s) buffer #%d unsent, exiting [%c%c]", G_STRFUNC, gnet_host_to_string(&tx->host), attr->send_idx, (attr->flags & DF_FLOWC) ? 'C' : '-', (attr->flags & DF_FLUSH) ? 'f' : '-'); } attr->tm_ev = cq_insert(attr->cq, BUFFER_NAGLE, deflate_nagle_timeout, tx); return; } attr->flags &= ~DF_NAGLE; if (tx_deflate_debugging(9)) { struct buffer *b = &attr->buf[attr->fill_idx]; /* Buffer to send */ g_debug("TX %s: (%s) flushing %zu bytes (buffer #%d) [%c%c]", G_STRFUNC, gnet_host_to_string(&tx->host), b->wptr - b->rptr, attr->fill_idx, (attr->flags & DF_FLOWC) ? 'C' : '-', (attr->flags & DF_FLUSH) ? 'f' : '-'); } deflate_flush_send(tx); }
/** * Add value to the table. * * If it was already present, its lifetime is augmented by the aging delay. * * The key argument is freed immediately if there is a free routine for * keys and the key was present in the table. * * The previous value is freed and replaced by the new one if there is * an insertion conflict and the value pointers are different. */ void aging_insert(aging_table_t *ag, const void *key, void *value) { gboolean found; void *okey, *ovalue; time_t now = tm_time(); struct aging_value *aval; g_assert(ag->magic == AGING_MAGIC); found = g_hash_table_lookup_extended(ag->table, key, &okey, &ovalue); if (found) { aval = ovalue; g_assert(aval->key == okey); if (aval->key != key && ag->kvfree != NULL) { /* * We discard the new and keep the old key instead. * That way, we don't have to update the hash table. */ (*ag->kvfree)(deconstify_gpointer(key), aval->value); } g_assert(aval->cq_ev != NULL); /* * Value existed for this key, prolonge its life. */ aval->value = value; aval->ttl -= delta_time(now, aval->last_insert); aval->ttl += ag->delay; aval->ttl = MAX(aval->ttl, 1); aval->ttl = MIN(aval->ttl, INT_MAX / 1000); aval->last_insert = now; cq_resched(aval->cq_ev, 1000 * aval->ttl); } else { WALLOC(aval); aval->value = value; aval->key = deconstify_gpointer(key); aval->ttl = ag->delay; aval->ttl = MAX(aval->ttl, 1); aval->ttl = MIN(aval->ttl, INT_MAX / 1000); aval->last_insert = now; aval->ag = ag; aval->cq_ev = cq_insert(aging_cq, 1000 * aval->ttl, aging_expire, aval); gm_hash_table_insert_const(ag->table, key, aval); } }
/** * Start the nagle timer. */ static void deflate_nagle_start(txdrv_t *tx) { struct attr *attr = tx->opaque; g_assert(!(attr->flags & DF_NAGLE)); g_assert(NULL == attr->tm_ev); if (!attr->nagle) /* Nagle not allowed */ return; attr->tm_ev = cq_insert(attr->cq, BUFFER_NAGLE, deflate_nagle_timeout, tx); attr->flags |= DF_NAGLE; attr->nagle_start = tm_time(); }
/** * Create a new periodic event, invoked every ``period'' milliseconds with * the supplied argument. * * When the callout queue is freed, registered periodic events are * automatically reclaimed as well, so they need not be removed explicitly. * * @param cq the callout queue in which the periodic event should be put * @param period firing period, in milliseconds * @param event the callback to invoke each period * @param arg additional callback argument to supply * * @return a new periodic event descriptor, which can be discarded if there * is no need to explicitly remove it between firing periods. */ cperiodic_t * cq_periodic_add(cqueue_t *cq, int period, cq_invoke_t event, void *arg) { cperiodic_t *cp; cqueue_check(cq); WALLOC0(cp); cp->magic = CPERIODIC_MAGIC; cp->event = event; cp->arg = arg; cp->period = period; cp->cq = cq; cp->ev = cq_insert(cq, period, cq_periodic_trampoline, cp); cq_register_object(&cq->cq_periodic, cp); return cp; }
/** * Watchdog timer has expired. */ static void wd_expired(cqueue_t *cq, void *arg) { watchdog_t *wd = arg; watchdog_check(wd); wd->ev = NULL; /* * If no kicks have happened, fire the registered callback. Otherwise, * reset the callout queue event, so that the sliding window is starting * when the last tick happened. */ if (0 == wd->last_kick) { wd_trigger(wd); } else { time_t now = tm_time(); time_delta_t elapsed = delta_time(now, wd->last_kick); /* * If for some reason the callout queue heartbeat got delayed, more * than ``period'' seconds may have elapsed since the last kick, in * which case we also need to trigger the callback. * * Note that watchdog ``period'' is expressed in seconds. */ if (elapsed >= wd->period) { wd_trigger(wd); } else { time_delta_t delay = wd->period - elapsed; wd->ev = cq_insert(cq, delay * 1000, wd_expired, wd); } } }
/** * Convenience routine: insert event in the main callout queue. * * Same as calling: * * cq_insert(cq_main(), delay, fn, arg); * * only it is shorter. */ cevent_t * cq_main_insert(int delay, cq_service_t fn, void *arg) { cq_main_init(); return cq_insert(callout_queue, delay, fn, arg); }