PGM_GNUC_INTERNAL pgm_time_t pgm_rate_remaining2 ( pgm_rate_t* major_bucket, pgm_rate_t* minor_bucket, const size_t n ) { pgm_time_t remaining = 0; pgm_time_t now; /* pre-conditions */ pgm_assert (NULL != major_bucket); pgm_assert (NULL != minor_bucket); if (PGM_UNLIKELY(0 == major_bucket->rate_per_sec && 0 == minor_bucket->rate_per_sec)) return remaining; if (0 != major_bucket->rate_per_sec) { pgm_spinlock_lock (&major_bucket->spinlock); now = pgm_time_update_now(); const int64_t bucket_bytes = major_bucket->rate_limit + pgm_to_secs (major_bucket->rate_per_sec * (now - major_bucket->last_rate_check)) - n; if (bucket_bytes < 0) { const int64_t outstanding_bytes = -bucket_bytes; const pgm_time_t major_remaining = (1000000UL * outstanding_bytes) / major_bucket->rate_per_sec; remaining = major_remaining; } } else { /* ensure we have a timestamp */ now = pgm_time_update_now(); } if (0 != minor_bucket->rate_per_sec) { const int64_t bucket_bytes = minor_bucket->rate_limit + pgm_to_secs (minor_bucket->rate_per_sec * (now - minor_bucket->last_rate_check)) - n; if (bucket_bytes < 0) { const int64_t outstanding_bytes = -bucket_bytes; const pgm_time_t minor_remaining = (1000000UL * outstanding_bytes) / minor_bucket->rate_per_sec; remaining = remaining > 0 ? MIN(remaining, minor_remaining) : minor_remaining; } } if (0 != major_bucket->rate_per_sec) { pgm_spinlock_unlock (&major_bucket->spinlock); } return remaining; }
PGM_GNUC_INTERNAL void pgm_rand_create ( pgm_rand_t* new_rand ) { /* pre-conditions */ pgm_assert (NULL != new_rand); #ifndef _WIN32 /* attempt to read seed from kernel */ FILE* fp; do { fp = fopen ("/dev/urandom", "rb"); } while (PGM_UNLIKELY(NULL == fp && EINTR == errno)); if (fp) { size_t items_read; do { items_read = fread (&new_rand->seed, sizeof(new_rand->seed), 1, fp); } while (PGM_UNLIKELY(EINTR == errno)); fclose (fp); if (1 == items_read) return; } #endif /* !_WIN32 */ const pgm_time_t now = pgm_time_update_now(); new_rand->seed = (uint32_t)pgm_to_msecs (now); }
PGM_GNUC_INTERNAL void pgm_rate_create ( pgm_rate_t* bucket, const ssize_t rate_per_sec, /* 0 = disable */ const size_t iphdr_len, const uint16_t max_tpdu ) { /* pre-conditions */ pgm_assert (NULL != bucket); pgm_assert (rate_per_sec >= max_tpdu); bucket->rate_per_sec = rate_per_sec; bucket->iphdr_len = iphdr_len; bucket->last_rate_check = pgm_time_update_now (); /* pre-fill bucket */ if ((rate_per_sec / 1000) >= max_tpdu) { bucket->rate_per_msec = bucket->rate_per_sec / 1000; bucket->rate_limit = bucket->rate_per_msec; } else { bucket->rate_limit = bucket->rate_per_sec; } pgm_spinlock_init (&bucket->spinlock); }
PGM_GNUC_INTERNAL pgm_time_t pgm_rate_remaining ( pgm_rate_t* bucket, const size_t n ) { /* pre-conditions */ pgm_assert (NULL != bucket); if (PGM_UNLIKELY(0 == bucket->rate_per_sec)) return 0; pgm_spinlock_lock (&bucket->spinlock); const pgm_time_t now = pgm_time_update_now(); const pgm_time_t time_since_last_rate_check = now - bucket->last_rate_check; const int64_t bucket_bytes = bucket->rate_limit + pgm_to_secs (bucket->rate_per_sec * time_since_last_rate_check) - n; pgm_spinlock_unlock (&bucket->spinlock); if (bucket_bytes >= 0) return 0; const int64_t outstanding_bytes = -bucket_bytes; const pgm_time_t remaining = (1000000UL * outstanding_bytes) / bucket->rate_per_sec; return remaining; }
PGM_GNUC_INTERNAL bool pgm_timer_prepare ( pgm_sock_t* const sock ) { pgm_time_t now, expiration; int32_t msec; /* pre-conditions */ pgm_assert (NULL != sock); pgm_assert (sock->can_send_data || sock->can_recv_data); now = pgm_time_update_now(); if (sock->can_send_data) expiration = sock->next_ambient_spm; else expiration = now + sock->peer_expiry; sock->next_poll = expiration; /* advance time again to adjust for processing time out of the event loop, this * could cause further timers to expire even before checking for new wire data. */ msec = (int32_t)pgm_to_msecs ((int64_t)expiration - (int64_t)now); if (msec < 0) msec = 0; else msec = MIN (INT32_MAX, msec); pgm_trace (PGM_LOG_ROLE_NETWORK,_("Next expiration in %" PRIi32 "ms"), msec); return (msec == 0); }
PGM_GNUC_INTERNAL pgm_time_t pgm_timer_expiration ( pgm_sock_t* const sock ) { const pgm_time_t now = pgm_time_update_now(); pgm_time_t expiration; /* pre-conditions */ pgm_assert (NULL != sock); pgm_timer_lock (sock); expiration = pgm_time_after (sock->next_poll, now) ? pgm_to_usecs (sock->next_poll - now) : 0; pgm_timer_unlock (sock); return expiration; }
PGM_GNUC_INTERNAL bool pgm_timer_check ( pgm_sock_t* const sock ) { const pgm_time_t now = pgm_time_update_now(); bool expired; /* pre-conditions */ pgm_assert (NULL != sock); pgm_timer_lock (sock); expired = pgm_time_after_eq (now, sock->next_poll); pgm_timer_unlock (sock); return expired; }
PGM_GNUC_INTERNAL bool pgm_time_init ( pgm_error_t** error ) { char *pgm_timer; size_t envlen; errno_t err; if (pgm_atomic_exchange_and_add32 (&time_ref_count, 1) > 0) return TRUE; /* user preferred time stamp function */ err = pgm_dupenv_s (&pgm_timer, &envlen, "PGM_TIMER"); if (0 != err || 0 == envlen) { pgm_timer = pgm_strdup ( /* default time stamp function */ #if defined(_WIN32) "MMTIME" #else "GETTIMEOFDAY" #endif ); } pgm_time_since_epoch = pgm_time_conv; switch (pgm_timer[0]) { #ifdef HAVE_FTIME case 'F': pgm_minor (_("Using ftime() timer.")); pgm_time_update_now = pgm_ftime_update; break; #endif #ifdef HAVE_CLOCK_GETTIME case 'C': pgm_minor (_("Using clock_gettime() timer.")); pgm_time_update_now = pgm_clock_update; break; #endif #ifdef HAVE_DEV_RTC case 'R': pgm_minor (_("Using /dev/rtc timer.")); pgm_time_update_now = pgm_rtc_update; pgm_time_since_epoch = pgm_time_conv_from_reset; break; #endif #ifdef HAVE_RDTSC case 'T': pgm_minor (_("Using TSC timer.")); pgm_time_update_now = pgm_tsc_update; pgm_time_since_epoch = pgm_time_conv_from_reset; break; #endif #ifdef HAVE_DEV_HPET case 'H': pgm_minor (_("Using HPET timer.")); pgm_time_update_now = pgm_hpet_update; pgm_time_since_epoch = pgm_time_conv_from_reset; break; #endif #ifdef HAVE_GETTIMEOFDAY case 'G': pgm_minor (_("Using gettimeofday() timer.")); pgm_time_update_now = pgm_gettimeofday_update; break; #endif #ifdef _WIN32 case 'M': pgm_minor (_("Using multi-media timer.")); pgm_time_update_now = pgm_mmtime_update; pgm_time_since_epoch = pgm_time_conv_from_reset; break; case 'Q': pgm_minor (_("Using QueryPerformanceCounter() timer.")); pgm_time_update_now = pgm_queryperformancecounter_update; pgm_time_since_epoch = pgm_time_conv_from_reset; break; #endif default: pgm_set_error (error, PGM_ERROR_DOMAIN_TIME, PGM_ERROR_FAILED, _("Unsupported time stamp function: PGM_TIMER=%s"), pgm_timer); pgm_free (pgm_timer); goto err_cleanup; } /* clean environment copy */ pgm_free (pgm_timer); #ifdef HAVE_DEV_RTC if (pgm_time_update_now == pgm_rtc_update) { pgm_error_t* sub_error = NULL; if (!pgm_rtc_init (&sub_error)) { pgm_propagate_error (error, sub_error); goto err_cleanup; } } #endif #ifdef _WIN32 if (pgm_time_update_now == pgm_queryperformancecounter_update) { /* MSDN statement: The frequency cannot change while the system is running. * http://msdn.microsoft.com/en-us/library/ms644905(v=vs.85).aspx */ LARGE_INTEGER frequency; if (QueryPerformanceFrequency (&frequency)) { tsc_khz = (uint_fast32_t)(frequency.QuadPart / 1000LL); pgm_minor (_("High-resolution performance counter frequency %lld Hz"), frequency.QuadPart); } else { const DWORD save_errno = GetLastError(); char winstr[1024]; pgm_set_error (error, PGM_ERROR_DOMAIN_TIME, PGM_ERROR_FAILED, _("No supported high-resolution performance counter: %s"), pgm_win_strerror (winstr, sizeof (winstr), save_errno)); goto err_cleanup; } set_tsc_mul (tsc_khz); } #endif /* _WIN32 */ #ifdef HAVE_RDTSC if (pgm_time_update_now == pgm_tsc_update) { char *rdtsc_frequency; #ifdef HAVE_PROC_CPUINFO /* attempt to parse clock ticks from kernel */ FILE *fp = fopen ("/proc/cpuinfo", "r"); if (fp) { char buffer[1024]; while (!feof(fp) && fgets (buffer, sizeof(buffer), fp)) { if (strstr (buffer, "cpu MHz")) { const char *p = strchr (buffer, ':'); if (p) tsc_khz = atoi (p + 1) * 1000; break; } } fclose (fp); } #elif defined(_WIN32) /* core frequency HKLM/Hardware/Description/System/CentralProcessor/0/~Mhz */ HKEY hKey; if (ERROR_SUCCESS == RegOpenKeyExA (HKEY_LOCAL_MACHINE, "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 0, KEY_QUERY_VALUE, &hKey)) { DWORD dwData = 0; DWORD dwDataSize = sizeof (dwData); if (ERROR_SUCCESS == RegQueryValueExA (hKey, "~MHz", NULL, NULL, (LPBYTE)&dwData, &dwDataSize)) { tsc_khz = dwData * 1000; pgm_minor (_("Registry reports central processor frequency %u MHz"), (unsigned)dwData); /* dump processor name for comparison aid of obtained frequency */ char szProcessorBrandString[48]; dwDataSize = sizeof (szProcessorBrandString); if (ERROR_SUCCESS == RegQueryValueExA (hKey, "ProcessorNameString", NULL, NULL, (LPBYTE)szProcessorBrandString, &dwDataSize)) { pgm_minor (_("Processor Brand String \"%s\""), szProcessorBrandString); } } else { const DWORD save_errno = GetLastError(); char winstr[1024]; pgm_warn (_("Registry query on HKLM\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0\\~MHz failed: %s"), pgm_win_strerror (winstr, sizeof (winstr), save_errno)); } RegCloseKey (hKey); } #elif defined(__APPLE__) /* nb: RDTSC is non-functional on Darwin */ uint64_t cpufrequency; size_t len; len = sizeof (cpufrequency); if (0 == sysctlbyname ("hw.cpufrequency", &cpufrequency, &len, NULL, 0)) { tsc_khz = (uint_fast32_t)(cpufrequency / 1000); } #elif defined(__FreeBSD__) /* frequency in Mhz */ unsigned long clockrate; size_t len; len = sizeof (clockrate); if (0 == sysctlbyname ("hw.clockrate", &clockrate, &len, NULL, 0)) { tsc_khz = (uint_fast32_t)(clockrate * 1000); } #elif defined(KSTAT_DATA_INT32) /* ref: http://developers.sun.com/solaris/articles/kstatc.html */ kstat_ctl_t* kc; kstat_t* ksp; kstat_named_t* kdata; if (NULL != (kc = kstat_open()) && NULL != (ksp = kstat_lookup (kc, "cpu_info", -1, NULL)) && KSTAT_TYPE_NAMED == ksp->ks_type && -1 != kstat_read (kc, ksp, NULL) && NULL != (kdata = kstat_data_lookup (ksp, "clock_MHz")) && KSTAT_DATA_INT32 == kdata->data_type) { tsc_khz = (uint_fast32_t)(kdata->value.i32 * 1000); kstat_close (kc); } #endif /* !_WIN32 */ /* e.g. export RDTSC_FREQUENCY=3200.000000 * * Value can be used to override kernel tick rate as well as internal calibration */ err = pgm_dupenv_s (&rdtsc_frequency, &envlen, "RDTSC_FREQUENCY"); if (0 == err && envlen > 0) { tsc_khz = atoi (rdtsc_frequency) * 1000; pgm_free (rdtsc_frequency); } #ifndef _WIN32 /* calibrate */ if (0 >= tsc_khz) { pgm_error_t* sub_error = NULL; if (!pgm_tsc_init (&sub_error)) { pgm_propagate_error (error, sub_error); goto err_cleanup; } } #endif pgm_minor (_("TSC frequency set at %u KHz"), (unsigned)(tsc_khz)); set_tsc_mul (tsc_khz); } #endif /* HAVE_RDTSC */ #ifdef HAVE_DEV_HPET if (pgm_time_update_now == pgm_hpet_update) { pgm_error_t* sub_error = NULL; if (!pgm_hpet_init (&sub_error)) { pgm_propagate_error (error, sub_error); goto err_cleanup; } } #endif pgm_time_update_now(); /* calculate relative time offset */ #if defined(HAVE_DEV_RTC) || defined(HAVE_RDTSC) || defined(HAVE_DEV_HPET) || defined(_WIN32) if ( 0 # ifdef HAVE_DEV_RTC || pgm_time_update_now == pgm_rtc_update # endif # ifdef HAVE_RDTSC || pgm_time_update_now == pgm_tsc_update # endif # ifdef HAVE_DEV_HPET || pgm_time_update_now == pgm_hpet_update # endif # ifdef _WIN32 || pgm_time_update_now == pgm_mmtime_update || pgm_time_update_now == pgm_queryperformancecounter_update # endif ) { # if defined( HAVE_GETTIMEOFDAY ) rel_offset = pgm_gettimeofday_update() - pgm_time_update_now(); # elif defined( HAVE_FTIME ) rel_offset = pgm_ftime_update() - pgm_time_update_now(); # else # error "gettimeofday() or ftime() required to calculate counter offset" # endif } #else rel_offset = 0; #endif /* update Windows timer resolution to 1ms */ #ifdef _WIN32 TIMECAPS tc; if (TIMERR_NOERROR == timeGetDevCaps (&tc, sizeof (TIMECAPS))) { wTimerRes = min (max (tc.wPeriodMin, 1 /* ms */), tc.wPeriodMax); timeBeginPeriod (wTimerRes); pgm_debug ("Set timer resolution to %ums.", wTimerRes); } else { pgm_warn (_("Unable to determine timer device resolution.")); } #endif return TRUE; err_cleanup: pgm_atomic_dec32 (&time_ref_count); return FALSE; }
int main ( int argc, char *argv[] ) { puts ("test_rs2"); /* parse program arguments */ const char* binary_name = strrchr (argv[0], '/'); int c; while ((c = getopt (argc, argv, "h")) != -1) { switch (c) { case 'h': case '?': usage (binary_name); } } /* setup signal handlers */ signal(SIGHUP, SIG_IGN); pgm_time_init(); /* Reed-Solomon code */ int n = 255; /* [ k+1 .. 255 ] */ int k = 64; /* [ 2 .. 128 ] */ const gchar source[] = "Ayumi Lee (Hangul: 이 아유미, Japanese name: Ito Ayumi 伊藤亜由美, born August 25, 1984, Tottori Prefecture, Japan[1]), from former Korean girl-group Sugar, was raised in Japan for much of her younger life and is the reason for her Japanese accent, although she is now fluent in both languages. Although Ayumi's name is commonly believed to be Japanese in origin, she has explained that her name is hanja-based."; int source_len = strlen (source); /* chars: g_utf8_strlen() */ int block_len = 0; int source_offset = 0; guint8* packet_block[n]; for (int i = 0; i < k; i++) { packet_block[i] = g_malloc0 (g_max_tpdu); /* fill with source text */ int packet_offset = 0; do { int copy_len = MIN( source_len - source_offset, g_max_tpdu - packet_offset ); /* adjust for unicode borders */ gchar* p = (gchar*)source + source_offset + copy_len; if (*p) { int new_copy_len = g_utf8_find_prev_char (source + source_offset, p + 1) - (source + source_offset); if (new_copy_len != copy_len) { printf ("ERROR: shuffle on packet border not implemented.\n"); } } memcpy (packet_block[i] + packet_offset, source + source_offset, copy_len); packet_offset += copy_len; source_offset += copy_len; if (source_offset >= source_len) source_offset = 0; block_len += copy_len; } while (packet_offset < g_max_tpdu); } /* parity packet */ guint8* parity = g_slice_alloc0 (g_max_tpdu); /* Start Reed-Solomon engine * * symbol size: 8 bits * primitive polynomial: 1 + x^2 + x^3 + x^4 + x^8 * * poly = (1*x^0) + (0*x^1) + (1*x^2) + (1*x^3) + (1*x^4) + (0*x^5) + (0*x^6) + (0*x^7) + (1*x^8) * = 1 0 1 1 1 0 0 0 1 * = 101110001 * = 0x171 / 0x11D (reversed) */ void* rs; pgm_rs_create (&rs, n, k); /* select random packet to erase: * erasure_index is offset in FEC block of parity packet [k .. n-1] * erasure is offset of erased packet [0 .. k-1] */ int erasure_index = k; int erasure = g_random_int_range (0, k); pgm_time_t start, end, elapsed; printf ("\n" "encoding\n" "--------\n" "\n" "GF(2⁸), RS (%i,%i)\n", n, k ); /* test encoding */ start = pgm_time_update_now(); pgm_rs_encode (rs, (const void**)(void*)packet_block, erasure_index, parity, g_max_tpdu); end = pgm_time_update_now(); elapsed = end - start; printf ("encoding time %" G_GUINT64_FORMAT " us\n", elapsed); /* test erasure decoding (no errors) */ puts ( "\n" "decoding\n" "--------\n" ); printf ("erased %i packet at %i\n", 1, erasure); int offsets[ k ]; for (int i = 0; i < k; i++) offsets[i] = i; offsets[erasure] = erasure_index; memset (packet_block[erasure], 0, g_max_tpdu); packet_block[k] = parity; /* place parity packet after original data block */ /* test decoding */ start = pgm_time_update_now(); int retval = pgm_rs_decode_parity_appended (rs, (void**)(void*)packet_block, offsets, g_max_tpdu); end = pgm_time_update_now(); elapsed = end - start; printf ("decoding time %" G_GUINT64_FORMAT " us\n", elapsed); /* display final string */ gchar* final = g_malloc ( (k * g_max_tpdu) + 1 ); final[0] = 0;
PGM_GNUC_INTERNAL bool pgm_rate_check2 ( pgm_rate_t* major_bucket, pgm_rate_t* minor_bucket, const size_t data_size, const bool is_nonblocking ) { int64_t new_major_limit, new_minor_limit; pgm_time_t now; /* pre-conditions */ pgm_assert (NULL != major_bucket); pgm_assert (NULL != minor_bucket); pgm_assert (data_size > 0); if (0 == major_bucket->rate_per_sec && 0 == minor_bucket->rate_per_sec) return TRUE; if (0 != major_bucket->rate_per_sec) { pgm_spinlock_lock (&major_bucket->spinlock); now = pgm_time_update_now(); if (major_bucket->rate_per_msec) { const pgm_time_t time_since_last_rate_check = now - major_bucket->last_rate_check; if (time_since_last_rate_check > pgm_msecs(1)) new_major_limit = major_bucket->rate_per_msec; else { new_major_limit = major_bucket->rate_limit + ((major_bucket->rate_per_msec * time_since_last_rate_check) / 1000UL); if (new_major_limit > major_bucket->rate_per_msec) new_major_limit = major_bucket->rate_per_msec; } } else { const pgm_time_t time_since_last_rate_check = now - major_bucket->last_rate_check; if (time_since_last_rate_check > pgm_secs(1)) new_major_limit = major_bucket->rate_per_sec; else { new_major_limit = major_bucket->rate_limit + ((major_bucket->rate_per_sec * time_since_last_rate_check) / 1000000UL); if (new_major_limit > major_bucket->rate_per_sec) new_major_limit = major_bucket->rate_per_sec; } } new_major_limit -= ( major_bucket->iphdr_len + data_size ); if (is_nonblocking && new_major_limit < 0) { pgm_spinlock_unlock (&major_bucket->spinlock); return FALSE; } if (new_major_limit < 0) { const pgm_time_t wait_start = now; ssize_t sleep_amount; do { pgm_thread_yield(); now = pgm_time_update_now(); sleep_amount = (ssize_t)pgm_to_secs (major_bucket->rate_per_sec * (now - wait_start)); } while (sleep_amount + new_major_limit < 0); new_major_limit += sleep_amount; } } else { /* ensure we have a timestamp */ now = pgm_time_update_now(); } if (0 != minor_bucket->rate_per_sec) { if (minor_bucket->rate_per_msec) { const pgm_time_t time_since_last_rate_check = now - minor_bucket->last_rate_check; if (time_since_last_rate_check > pgm_msecs(1)) new_minor_limit = minor_bucket->rate_per_msec; else { new_minor_limit = minor_bucket->rate_limit + ((minor_bucket->rate_per_msec * time_since_last_rate_check) / 1000UL); if (new_minor_limit > minor_bucket->rate_per_msec) new_minor_limit = minor_bucket->rate_per_msec; } } else { const pgm_time_t time_since_last_rate_check = now - minor_bucket->last_rate_check; if (time_since_last_rate_check > pgm_secs(1)) new_minor_limit = minor_bucket->rate_per_sec; else { new_minor_limit = minor_bucket->rate_limit + ((minor_bucket->rate_per_sec * time_since_last_rate_check) / 1000000UL); if (new_minor_limit > minor_bucket->rate_per_sec) new_minor_limit = minor_bucket->rate_per_sec; } } new_minor_limit -= ( minor_bucket->iphdr_len + data_size ); if (is_nonblocking && new_minor_limit < 0) { if (0 != major_bucket->rate_per_sec) pgm_spinlock_unlock (&major_bucket->spinlock); return FALSE; } /* commit new rate limit */ minor_bucket->rate_limit = new_minor_limit; minor_bucket->last_rate_check = now; } if (0 != major_bucket->rate_per_sec) { major_bucket->rate_limit = new_major_limit; major_bucket->last_rate_check = now; pgm_spinlock_unlock (&major_bucket->spinlock); } /* sleep on minor bucket outside of lock */ if (minor_bucket->rate_limit < 0) { ssize_t sleep_amount; do { pgm_thread_yield(); now = pgm_time_update_now(); sleep_amount = (ssize_t)pgm_to_secs (minor_bucket->rate_per_sec * (now - minor_bucket->last_rate_check)); } while (sleep_amount + minor_bucket->rate_limit < 0); minor_bucket->rate_limit += sleep_amount; minor_bucket->last_rate_check = now; } return TRUE; }
PGM_GNUC_INTERNAL bool pgm_rate_check ( pgm_rate_t* bucket, const size_t data_size, const bool is_nonblocking ) { int64_t new_rate_limit; /* pre-conditions */ pgm_assert (NULL != bucket); pgm_assert (data_size > 0); if (0 == bucket->rate_per_sec) return TRUE; pgm_spinlock_lock (&bucket->spinlock); pgm_time_t now = pgm_time_update_now(); if (bucket->rate_per_msec) { const pgm_time_t time_since_last_rate_check = now - bucket->last_rate_check; if (time_since_last_rate_check > pgm_msecs(1)) new_rate_limit = bucket->rate_per_msec; else { new_rate_limit = bucket->rate_limit + ((bucket->rate_per_msec * time_since_last_rate_check) / 1000UL); if (new_rate_limit > bucket->rate_per_msec) new_rate_limit = bucket->rate_per_msec; } } else { const pgm_time_t time_since_last_rate_check = now - bucket->last_rate_check; if (time_since_last_rate_check > pgm_secs(1)) new_rate_limit = bucket->rate_per_sec; else { new_rate_limit = bucket->rate_limit + ((bucket->rate_per_sec * time_since_last_rate_check) / 1000000UL); if (new_rate_limit > bucket->rate_per_sec) new_rate_limit = bucket->rate_per_sec; } } new_rate_limit -= ( bucket->iphdr_len + data_size ); if (is_nonblocking && new_rate_limit < 0) { pgm_spinlock_unlock (&bucket->spinlock); return FALSE; } bucket->rate_limit = new_rate_limit; bucket->last_rate_check = now; if (bucket->rate_limit < 0) { ssize_t sleep_amount; do { pgm_thread_yield(); now = pgm_time_update_now(); sleep_amount = (ssize_t)pgm_to_secs (bucket->rate_per_sec * (now - bucket->last_rate_check)); } while (sleep_amount + bucket->rate_limit < 0); bucket->rate_limit += sleep_amount; bucket->last_rate_check = now; } pgm_spinlock_unlock (&bucket->spinlock); return TRUE; }
PGM_GNUC_INTERNAL bool pgm_timer_dispatch ( pgm_sock_t* const sock ) { const pgm_time_t now = pgm_time_update_now(); pgm_time_t next_expiration = 0; /* pre-conditions */ pgm_assert (NULL != sock); pgm_debug ("pgm_timer_dispatch (sock:%p)", (const void*)sock); /* find which timers have expired and call each */ if (sock->can_recv_data) { if (!pgm_check_peer_state (sock, now)) return FALSE; next_expiration = pgm_min_receiver_expiry (sock, now + sock->peer_expiry); } if (sock->can_send_data) { /* reset congestion control on ACK timeout */ if (sock->use_pgmcc && sock->tokens < pgm_fp8 (1) && 0 != sock->ack_expiry) { if (pgm_time_after_eq (now, sock->ack_expiry)) { #ifdef DEBUG_PGMCC char nows[1024]; time_t t = time (NULL); struct tm* tmp = localtime (&t); strftime (nows, sizeof(nows), "%Y-%m-%d %H:%M:%S", tmp); printf ("ACK timeout, T:%u W:%u\n", pgm_fp8tou(sock->tokens), pgm_fp8tou(sock->cwnd_size)); #endif sock->tokens = sock->cwnd_size = pgm_fp8 (1); sock->ack_bitmap = 0xffffffff; sock->ack_expiry = 0; /* notify blocking tx thread that transmission time is now available */ pgm_notify_send (&sock->ack_notify); } next_expiration = next_expiration > 0 ? MIN(next_expiration, sock->ack_expiry) : sock->ack_expiry; } /* SPM broadcast */ pgm_mutex_lock (&sock->timer_mutex); const unsigned spm_heartbeat_state = sock->spm_heartbeat_state; const pgm_time_t next_heartbeat_spm = sock->next_heartbeat_spm; pgm_mutex_unlock (&sock->timer_mutex); /* no lock needed on ambient */ const pgm_time_t next_ambient_spm = sock->next_ambient_spm; pgm_time_t next_spm = spm_heartbeat_state ? MIN(next_heartbeat_spm, next_ambient_spm) : next_ambient_spm; if (pgm_time_after_eq (now, next_spm) && !pgm_send_spm (sock, 0)) return FALSE; /* ambient timing not so important so base next event off current time */ if (pgm_time_after_eq (now, next_ambient_spm)) { sock->next_ambient_spm = now + sock->spm_ambient_interval; next_spm = spm_heartbeat_state ? MIN(next_heartbeat_spm, sock->next_ambient_spm) : sock->next_ambient_spm; } /* heartbeat timing is often high resolution so base times to last event */ if (spm_heartbeat_state && pgm_time_after_eq (now, next_heartbeat_spm)) { unsigned new_heartbeat_state = spm_heartbeat_state; pgm_time_t new_heartbeat_spm = next_heartbeat_spm; do { new_heartbeat_spm += sock->spm_heartbeat_interval[new_heartbeat_state++]; if (new_heartbeat_state == sock->spm_heartbeat_len) { new_heartbeat_state = 0; new_heartbeat_spm = now + sock->spm_ambient_interval; break; } } while (pgm_time_after_eq (now, new_heartbeat_spm)); /* check for reset heartbeat */ pgm_mutex_lock (&sock->timer_mutex); if (next_heartbeat_spm == sock->next_heartbeat_spm) { sock->spm_heartbeat_state = new_heartbeat_state; sock->next_heartbeat_spm = new_heartbeat_spm; next_spm = MIN(sock->next_ambient_spm, new_heartbeat_spm); } else next_spm = MIN(sock->next_ambient_spm, sock->next_heartbeat_spm); sock->next_poll = next_expiration > 0 ? MIN(next_expiration, next_spm) : next_spm; pgm_mutex_unlock (&sock->timer_mutex); return TRUE; } next_expiration = next_expiration > 0 ? MIN(next_expiration, next_spm) : next_spm; /* check for reset */ pgm_mutex_lock (&sock->timer_mutex); sock->next_poll = sock->next_poll > now ? MIN(sock->next_poll, next_expiration) : next_expiration; pgm_mutex_unlock (&sock->timer_mutex); } else sock->next_poll = next_expiration; return TRUE; }