コード例 #1
0
ファイル: rate_control.c プロジェクト: banburybill/openpgm
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;
}
コード例 #2
0
ファイル: rand.c プロジェクト: banburybill/openpgm
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);
}
コード例 #3
0
ファイル: rate_control.c プロジェクト: banburybill/openpgm
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);
}
コード例 #4
0
ファイル: rate_control.c プロジェクト: banburybill/openpgm
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;
}
コード例 #5
0
ファイル: timer.c プロジェクト: banburybill/openpgm
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);
}
コード例 #6
0
ファイル: timer.c プロジェクト: banburybill/openpgm
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;
}
コード例 #7
0
ファイル: timer.c プロジェクト: banburybill/openpgm
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;
}
コード例 #8
0
ファイル: time.c プロジェクト: banburybill/openpgm
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;
}
コード例 #9
0
ファイル: FEC_ReedSolomon.c プロジェクト: phrasz/CookBook
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;
コード例 #10
0
ファイル: rate_control.c プロジェクト: banburybill/openpgm
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;
}
コード例 #11
0
ファイル: rate_control.c プロジェクト: banburybill/openpgm
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;
}
コード例 #12
0
ファイル: timer.c プロジェクト: banburybill/openpgm
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;
}