PGM_GNUC_INTERNAL bool pgm_time_shutdown (void) { bool retval = TRUE; pgm_return_val_if_fail (pgm_atomic_read32 (&time_ref_count) > 0, FALSE); if (pgm_atomic_exchange_and_add32 (&time_ref_count, (uint32_t)-1) != 1) return retval; #ifdef _WIN32 timeEndPeriod (wTimerRes); #endif #ifdef HAVE_DEV_RTC if (pgm_time_update_now == pgm_rtc_update) retval = pgm_rtc_shutdown (); #endif #ifdef HAVE_DEV_HPET if (pgm_time_update_now == pgm_hpet_update) retval = pgm_hpet_shutdown (); #endif return retval; }
void pgm_messages_init (void) { char *log_mask, *min_log_level; size_t len; errno_t err; if (pgm_atomic_exchange_and_add32 (&messages_ref_count, 1) > 0) return; pgm_mutex_init (&messages_mutex); err = pgm_dupenv_s (&log_mask, &len, "PGM_LOG_MASK"); if (!err && len > 0) { unsigned int value = 0; if (1 == pgm_sscanf_s (log_mask, "0x%4x", &value)) pgm_log_mask = value; pgm_free (log_mask); } err = pgm_dupenv_s (&min_log_level, &len, "PGM_MIN_LOG_LEVEL"); if (!err && len > 0) { switch (min_log_level[0]) { case 'D': pgm_min_log_level = PGM_LOG_LEVEL_DEBUG; break; case 'T': pgm_min_log_level = PGM_LOG_LEVEL_TRACE; break; case 'M': pgm_min_log_level = PGM_LOG_LEVEL_MINOR; break; case 'N': pgm_min_log_level = PGM_LOG_LEVEL_NORMAL; break; case 'W': pgm_min_log_level = PGM_LOG_LEVEL_WARNING; break; case 'E': pgm_min_log_level = PGM_LOG_LEVEL_ERROR; break; case 'F': pgm_min_log_level = PGM_LOG_LEVEL_FATAL; break; default: break; } pgm_free (min_log_level); } }
PGM_GNUC_INTERNAL void pgm_rand_init (void) { if (pgm_atomic_exchange_and_add32 (&rand_ref_count, 1) > 0) return; pgm_mutex_init (&rand_mutex); }
void pgm_messages_shutdown (void) { pgm_return_if_fail (pgm_atomic_read32 (&messages_ref_count) > 0); if (pgm_atomic_exchange_and_add32 (&messages_ref_count, (uint32_t)-1) != 1) return; pgm_mutex_free (&messages_mutex); }
PGM_GNUC_INTERNAL void pgm_rand_shutdown (void) { pgm_return_if_fail (pgm_atomic_read32 (&rand_ref_count) > 0); if (pgm_atomic_exchange_and_add32 (&rand_ref_count, (uint32_t)-1) != 1) return; pgm_mutex_free (&rand_mutex); }
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; }