# HG changeset patch # User Eli Zaretskii # Date 1246704165 0 # Node ID 9a76dfa56921ef6d3dbbeb2b1cee45a7e9e2c4a1 # Parent 7bebd9bb90fccc3ceafd5ac23cf4afe2c62fa7cb Emulation of `getloadavg' on MS-Windows: Include float.h (g_b_init_get_native_system_info, g_b_init_get_system_times) (GetNativeSystemInfo_Proc, GetSystemTimes_Proc): Declare. (get_native_system_info, get_system_times): New functions. (buf_next, buf_prev, sample_system_load, getavg): New subroutines. (getloadavg): Rewrite using GetSystemTimes and GetNativeSystemInfo. (globals_of_w32): Initialize g_b_init_get_native_system_info, g_b_init_get_system_times, and num_of_processors. diff -r 7bebd9bb90fc -r 9a76dfa56921 src/w32.c --- a/src/w32.c Sat Jul 04 08:12:10 2009 +0000 +++ b/src/w32.c Sat Jul 04 10:42:45 2009 +0000 @@ -23,6 +23,7 @@ #include /* for offsetof */ #include #include +#include /* for DBL_EPSILON */ #include #include #include @@ -191,6 +192,8 @@ static BOOL g_b_init_get_length_sid; static BOOL g_b_init_equal_sid; static BOOL g_b_init_copy_sid; +static BOOL g_b_init_get_native_system_info; +static BOOL g_b_init_get_system_times; /* BEGIN: Wrapper functions around OpenProcessToken @@ -293,6 +296,12 @@ PSID pSid2); typedef DWORD (WINAPI * GetLengthSid_Proc) ( PSID pSid); +typedef void (WINAPI * GetNativeSystemInfo_Proc) ( + LPSYSTEM_INFO lpSystemInfo); +typedef BOOL (WINAPI * GetSystemTimes_Proc) ( + LPFILETIME lpIdleTime, + LPFILETIME lpKernelTime, + LPFILETIME lpUserTime); @@ -723,6 +732,47 @@ supported in Windows NT / 2k / XP */ +void WINAPI get_native_system_info ( + LPSYSTEM_INFO lpSystemInfo) +{ + static GetNativeSystemInfo_Proc s_pfn_Get_Native_System_Info = NULL; + if (is_windows_9x () != TRUE) + { + if (g_b_init_get_native_system_info == 0) + { + g_b_init_get_native_system_info = 1; + s_pfn_Get_Native_System_Info = + (GetNativeSystemInfo_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"), + "GetNativeSystemInfo"); + } + if (s_pfn_Get_Native_System_Info != NULL) + s_pfn_Get_Native_System_Info (lpSystemInfo); + } + else + lpSystemInfo->dwNumberOfProcessors = -1; +} + +BOOL WINAPI get_system_times( + LPFILETIME lpIdleTime, + LPFILETIME lpKernelTime, + LPFILETIME lpUserTime) +{ + static GetSystemTimes_Proc s_pfn_Get_System_times = NULL; + if (is_windows_9x () == TRUE) + { + return FALSE; + } + if (g_b_init_get_system_times == 0) + { + g_b_init_get_system_times = 1; + s_pfn_Get_System_times = + (GetSystemTimes_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"), + "GetSystemTimes"); + } + if (s_pfn_Get_System_times == NULL) + return FALSE; + return (s_pfn_Get_System_times (lpIdleTime, lpKernelTime, lpUserTime)); +} /* Equivalent of strerror for W32 error codes. */ char * @@ -795,17 +845,160 @@ #endif /* HAVE_SOCKETS */ /* Emulate getloadavg. */ + +struct load_sample { + time_t sample_time; + ULONGLONG idle; + ULONGLONG kernel; + ULONGLONG user; +}; + +/* Number of processors on this machine. */ +static unsigned num_of_processors; + +/* We maintain 1-sec samples for the last 16 minutes in a circular buffer. */ +static struct load_sample samples[16*60]; +static int first_idx = -1, last_idx = -1; +static int max_idx = sizeof (samples) / sizeof (samples[0]); + +static int +buf_next (int from) +{ + int next_idx = from + 1; + + if (next_idx >= max_idx) + next_idx = 0; + + return next_idx; +} + +static int +buf_prev (int from) +{ + int prev_idx = from - 1; + + if (prev_idx < 0) + prev_idx = max_idx - 1; + + return prev_idx; +} + +static void +sample_system_load (ULONGLONG *idle, ULONGLONG *kernel, ULONGLONG *user) +{ + SYSTEM_INFO sysinfo; + FILETIME ft_idle, ft_user, ft_kernel; + + /* Initialize the number of processors on this machine. */ + if (num_of_processors <= 0) + { + get_native_system_info (&sysinfo); + num_of_processors = sysinfo.dwNumberOfProcessors; + if (num_of_processors <= 0) + { + GetSystemInfo (&sysinfo); + num_of_processors = sysinfo.dwNumberOfProcessors; + } + if (num_of_processors <= 0) + num_of_processors = 1; + } + + /* TODO: Take into account threads that are ready to run, by + sampling the "\System\Processor Queue Length" performance + counter. The code below accounts only for threads that are + actually running. */ + + if (get_system_times (&ft_idle, &ft_kernel, &ft_user)) + { + ULARGE_INTEGER uidle, ukernel, uuser; + + memcpy (&uidle, &ft_idle, sizeof (ft_idle)); + memcpy (&ukernel, &ft_kernel, sizeof (ft_kernel)); + memcpy (&uuser, &ft_user, sizeof (ft_user)); + *idle = uidle.QuadPart; + *kernel = ukernel.QuadPart; + *user = uuser.QuadPart; + } + else + { + *idle = 0; + *kernel = 0; + *user = 0; + } +} + +/* Produce the load average for a given time interval, using the + samples in the samples[] array. WHICH can be 0, 1, or 2, meaning + 1-minute, 5-minute, or 15-minute average, respectively. */ +static double +getavg (int which) +{ + double retval = -1.0; + double tdiff; + int idx; + double span = (which == 0 ? 1.0 : (which == 1 ? 5.0 : 15.0)) * 60; + time_t now = samples[last_idx].sample_time; + + if (first_idx != last_idx) + { + for (idx = buf_prev (last_idx); ; idx = buf_prev (idx)) + { + tdiff = difftime (now, samples[idx].sample_time); + if (tdiff >= span - 2*DBL_EPSILON*now) + { + long double sys = + samples[last_idx].kernel + samples[last_idx].user + - (samples[idx].kernel + samples[idx].user); + long double idl = samples[last_idx].idle - samples[idx].idle; + + retval = (1.0 - idl / sys) * num_of_processors; + break; + } + if (idx == first_idx) + break; + } + } + + return retval; +} + int getloadavg (double loadavg[], int nelem) { - int i; - - /* A faithful emulation is going to have to be saved for a rainy day. */ - for (i = 0; i < nelem; i++) - { - loadavg[i] = 0.0; - } - return i; + int elem; + ULONGLONG idle, kernel, user; + time_t now = time (NULL); + + /* Store another sample. We ignore samples that are less than 1 sec + apart. */ + if (difftime (now, samples[last_idx].sample_time) >= 1.0 - 2*DBL_EPSILON*now) + { + sample_system_load (&idle, &kernel, &user); + last_idx = buf_next (last_idx); + samples[last_idx].sample_time = now; + samples[last_idx].idle = idle; + samples[last_idx].kernel = kernel; + samples[last_idx].user = user; + /* If the buffer has more that 15 min worth of samples, discard + the old ones. */ + if (first_idx == -1) + first_idx = last_idx; + while (first_idx != last_idx + && (difftime (now, samples[first_idx].sample_time) + >= 15.0*60 + 2*DBL_EPSILON*now)) + first_idx = buf_next (first_idx); + } + + for (elem = 0; elem < nelem; elem++) + { + double avg = getavg (elem); + + if (avg < 0) + break; + loadavg[elem] = avg; + } + + return elem; } /* Emulate getpwuid, getpwnam and others. */ @@ -5693,6 +5886,9 @@ g_b_init_equal_sid = 0; g_b_init_copy_sid = 0; g_b_init_get_length_sid = 0; + g_b_init_get_native_system_info = 0; + g_b_init_get_system_times = 0; + num_of_processors = 0; /* The following sets a handler for shutdown notifications for console apps. This actually applies to Emacs in both console and GUI modes, since we had to fool windows into thinking emacs is a