/** * check_nanosleep: * * Check if nanosleep() is accurate enough for sub-millisecond sleeping. If * it is not good enough, we're skipping the minor sleeps in LogReader to * balance the cost of returning to the main loop (e.g. we're always going * to return to the main loop, instead of trying to wait for the writer). **/ gboolean check_nanosleep(void) { #ifdef HAVE_CLOCK_GETTIME struct timespec start, stop, sleep; glong diff; gint attempts; for (attempts = 0; attempts < 3; attempts++) { clock_gettime(CLOCK_MONOTONIC, &start); sleep.tv_sec = 0; /* 0.1 msec */ sleep.tv_nsec = 1e5; while (nanosleep(&sleep, &sleep) < 0) ; clock_gettime(CLOCK_MONOTONIC, &stop); diff = timespec_diff_nsec(&stop, &start); if (diff < 5e5) return TRUE; } #endif return FALSE; }
static void _flow_control_rate_adjust(LogSource *self) { guint32 cur_ack_count, last_ack_count; /* NOTE: this is racy. msg_ack may be executing in different writer * threads. I don't want to lock, all we need is an approximate value of * the ACK rate of the last couple of seconds. */ #ifdef HAVE_CLOCK_GETTIME if (accurate_nanosleep && self->threaded) { cur_ack_count = ++self->ack_count; if ((cur_ack_count & 0x3FFF) == 0) { struct timespec now; glong diff; /* do this every once in a while, once in 16k messages should be fine */ last_ack_count = self->last_ack_count; /* make sure that we have at least 16k messages to measure the rate * for. Because of the race we may have last_ack_count == * cur_ack_count if another thread already measured the same span */ if (last_ack_count < cur_ack_count - 16383) { clock_gettime(CLOCK_MONOTONIC, &now); if (now.tv_sec > self->last_ack_rate_time.tv_sec + 6) { /* last check was too far apart, this means the rate is quite slow. turn off sleeping. */ self->window_full_sleep_nsec = 0; self->last_ack_rate_time = now; } else { /* ok, we seem to have a close enough measurement, this means * we do have a high rate. Calculate how much we should sleep * in case the window gets full */ diff = timespec_diff_nsec(&now, &self->last_ack_rate_time); self->window_full_sleep_nsec = (diff / (cur_ack_count - last_ack_count)); if (self->window_full_sleep_nsec > 1e6) { /* in case we'd be waiting for 1msec for another free slot in the window, let's go to background instead */ self->window_full_sleep_nsec = 0; } else { /* otherwise let's wait for about 8 message to be emptied before going back to the loop, but clamp the maximum time to 0.1msec */ self->window_full_sleep_nsec <<= 3; if (self->window_full_sleep_nsec > 1e5) self->window_full_sleep_nsec = 1e5; } self->last_ack_count = cur_ack_count; self->last_ack_rate_time = now; } } } } #endif }