aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorEli Zaretskii2012-10-27 13:21:26 +0200
committerEli Zaretskii2012-10-27 13:21:26 +0200
commit6c16c13ed185acf16dc8f1c6ba458f1e59328e84 (patch)
tree879877acff6356de8e86be0679b04859d4d50451 /src
parentcaa5e5a44dd23b77dfc38f7dd8b63af241149410 (diff)
downloademacs-6c16c13ed185acf16dc8f1c6ba458f1e59328e84.tar.gz
emacs-6c16c13ed185acf16dc8f1c6ba458f1e59328e84.zip
Fix w32 implementation of itimers: overflow and ITIMER_PROF.
Avoid overflow in w32 implementation of interval timers. When possible, for ITIMER_PROF count only times the main thread actually executes. src/w32proc.c <struct itimer_data>: 'expire' and 'reload' are now ULONGLONG types. Likewise for all the other data which was previously clock_t. (GetThreadTimes_Proc): New typedef. (w32_get_timer_time): New function, returns a suitable time value for the timer. (timer_loop): Enter critical section when accessing ULONGLONG values of the itimer_data struct, as these accesses are no longer atomic. Call 'w32_get_timer_time' instead of 'clock'. (init_timers): Initialize s_pfn_Get_Thread_Times. (start_timer_thread): Don't assign itimer->caller_thread here. (getitimer): Assign itimer->caller_thread here. (setitimer): Always call getitimer to get the value of ticks_now.
Diffstat (limited to 'src')
-rw-r--r--src/ChangeLog19
-rw-r--r--src/w32proc.c177
2 files changed, 147 insertions, 49 deletions
diff --git a/src/ChangeLog b/src/ChangeLog
index bf519556334..2efebc6754d 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,22 @@
12012-10-27 Eli Zaretskii <eliz@gnu.org>
2
3 Avoid overflow in w32 implementation of interval timers. When
4 possible, for ITIMER_PROF count only times the main thread
5 actually executes.
6 * w32proc.c <struct itimer_data>: 'expire' and 'reload' are now
7 ULONGLONG types. Likewise for all the other data which was
8 previously clock_t.
9 (GetThreadTimes_Proc): New typedef.
10 (w32_get_timer_time): New function, returns a suitable time value
11 for the timer.
12 (timer_loop): Enter critical section when accessing ULONGLONG
13 values of the itimer_data struct, as these accesses are no longer
14 atomic. Call 'w32_get_timer_time' instead of 'clock'.
15 (init_timers): Initialize s_pfn_Get_Thread_Times.
16 (start_timer_thread): Don't assign itimer->caller_thread here.
17 (getitimer): Assign itimer->caller_thread here.
18 (setitimer): Always call getitimer to get the value of ticks_now.
19
12012-10-26 Eli Zaretskii <eliz@gnu.org> 202012-10-26 Eli Zaretskii <eliz@gnu.org>
2 21
3 * w32fns.c (w32_wnd_proc) <WM_MOUSEMOVE>: Don't enable tracking of 22 * w32fns.c (w32_wnd_proc) <WM_MOUSEMOVE>: Don't enable tracking of
diff --git a/src/w32proc.c b/src/w32proc.c
index 57b3860cb76..fe3bbde167c 100644
--- a/src/w32proc.c
+++ b/src/w32proc.c
@@ -244,28 +244,89 @@ setpgrp (int pid, int gid)
244 the thread calls the appropriate signal handler when the timer 244 the thread calls the appropriate signal handler when the timer
245 expires, after stopping the thread which installed the timer. */ 245 expires, after stopping the thread which installed the timer. */
246 246
247/* FIXME: clock_t counts overflow after 49 days, need to handle the
248 wrap-around. */
249struct itimer_data { 247struct itimer_data {
250 clock_t expire; 248 ULONGLONG expire;
251 clock_t reload; 249 ULONGLONG reload;
252 int terminate; 250 int terminate;
253 int type; 251 int type;
254 HANDLE caller_thread; 252 HANDLE caller_thread;
255 HANDLE timer_thread; 253 HANDLE timer_thread;
256}; 254};
257 255
258static clock_t ticks_now; 256static ULONGLONG ticks_now;
259static struct itimer_data real_itimer, prof_itimer; 257static struct itimer_data real_itimer, prof_itimer;
260static clock_t clocks_min; 258static ULONGLONG clocks_min;
261/* If non-zero, itimers are disabled. Used during shutdown, when we 259/* If non-zero, itimers are disabled. Used during shutdown, when we
262 delete the critical sections used by the timer threads. */ 260 delete the critical sections used by the timer threads. */
263static int disable_itimers; 261static int disable_itimers;
264 262
265static CRITICAL_SECTION crit_real, crit_prof; 263static CRITICAL_SECTION crit_real, crit_prof;
266 264
265/* GetThreadTimes is not available on Windows 9X and posibly also on 2K. */
266typedef BOOL (WINAPI *GetThreadTimes_Proc) (
267 HANDLE hThread,
268 LPFILETIME lpCreationTime,
269 LPFILETIME lpExitTime,
270 LPFILETIME lpKernelTime,
271 LPFILETIME lpUserTime);
272
273static GetThreadTimes_Proc s_pfn_Get_Thread_Times;
274
275/* Return a suitable time value, in 1-ms units, for THREAD, a handle
276 to a thread. If THREAD is NULL or an invalid handle, return the
277 current wall-clock time since January 1, 1601 (UTC). Otherwise,
278 return the sum of kernel and user times used by THREAD since it was
279 created, plus its creation time. */
280static ULONGLONG
281w32_get_timer_time (HANDLE thread)
282{
283 ULONGLONG retval;
284 int use_system_time = 1;
285
286 if (thread && thread != INVALID_HANDLE_VALUE
287 && s_pfn_Get_Thread_Times != NULL)
288 {
289 FILETIME creation_ftime, exit_ftime, kernel_ftime, user_ftime;
290 ULARGE_INTEGER temp_creation, temp_kernel, temp_user;
291
292 if (s_pfn_Get_Thread_Times (thread, &creation_ftime, &exit_ftime,
293 &kernel_ftime, &user_ftime))
294 {
295 use_system_time = 0;
296 temp_creation.LowPart = creation_ftime.dwLowDateTime;
297 temp_creation.HighPart = creation_ftime.dwHighDateTime;
298 temp_kernel.LowPart = kernel_ftime.dwLowDateTime;
299 temp_kernel.HighPart = kernel_ftime.dwHighDateTime;
300 temp_user.LowPart = user_ftime.dwLowDateTime;
301 temp_user.HighPart = user_ftime.dwHighDateTime;
302 retval =
303 temp_creation.QuadPart / 10000 + temp_kernel.QuadPart / 10000
304 + temp_user.QuadPart / 10000;
305 }
306 else
307 DebPrint (("GetThreadTimes failed with error code %lu\n",
308 GetLastError ()));
309 }
310
311 if (use_system_time)
312 {
313 FILETIME current_ftime;
314 ULARGE_INTEGER temp;
315
316 GetSystemTimeAsFileTime (&current_ftime);
317
318 temp.LowPart = current_ftime.dwLowDateTime;
319 temp.HighPart = current_ftime.dwHighDateTime;
320
321 retval = temp.QuadPart / 10000;
322 }
323
324 return retval;
325}
326
267#define MAX_SINGLE_SLEEP 30 327#define MAX_SINGLE_SLEEP 30
268 328
329/* Thread function for a timer thread. */
269static DWORD WINAPI 330static DWORD WINAPI
270timer_loop (LPVOID arg) 331timer_loop (LPVOID arg)
271{ 332{
@@ -275,12 +336,13 @@ timer_loop (LPVOID arg)
275 CRITICAL_SECTION *crit = (which == ITIMER_REAL) ? &crit_real : &crit_prof; 336 CRITICAL_SECTION *crit = (which == ITIMER_REAL) ? &crit_real : &crit_prof;
276 const DWORD max_sleep = MAX_SINGLE_SLEEP * 1000 / CLOCKS_PER_SEC; 337 const DWORD max_sleep = MAX_SINGLE_SLEEP * 1000 / CLOCKS_PER_SEC;
277 int new_count = 0; 338 int new_count = 0;
339 HANDLE hth = (which == ITIMER_REAL) ? NULL : itimer->caller_thread;
278 340
279 while (1) 341 while (1)
280 { 342 {
281 DWORD sleep_time; 343 DWORD sleep_time;
282 signal_handler handler; 344 signal_handler handler;
283 clock_t now, expire, reload; 345 ULONGLONG now, expire, reload;
284 346
285 /* Load new values if requested by setitimer. */ 347 /* Load new values if requested by setitimer. */
286 EnterCriticalSection (crit); 348 EnterCriticalSection (crit);
@@ -290,15 +352,14 @@ timer_loop (LPVOID arg)
290 if (itimer->terminate) 352 if (itimer->terminate)
291 return 0; 353 return 0;
292 354
293 if (itimer->expire == 0) 355 if (expire == 0)
294 { 356 {
295 /* We are idle. */ 357 /* We are idle. */
296 Sleep (max_sleep); 358 Sleep (max_sleep);
297 continue; 359 continue;
298 } 360 }
299 361
300 expire = itimer->expire; 362 if (expire > (now = w32_get_timer_time (hth)))
301 if (expire > (now = clock ()))
302 sleep_time = expire - now; 363 sleep_time = expire - now;
303 else 364 else
304 sleep_time = 0; 365 sleep_time = 0;
@@ -309,8 +370,11 @@ timer_loop (LPVOID arg)
309 if (itimer->terminate) 370 if (itimer->terminate)
310 return 0; 371 return 0;
311 Sleep (max_sleep); 372 Sleep (max_sleep);
373 EnterCriticalSection (crit);
312 expire = itimer->expire; 374 expire = itimer->expire;
313 sleep_time = (expire > (now = clock ())) ? expire - now : 0; 375 LeaveCriticalSection (crit);
376 sleep_time =
377 (expire > (now = w32_get_timer_time (hth))) ? expire - now : 0;
314 } 378 }
315 if (itimer->terminate) 379 if (itimer->terminate)
316 return 0; 380 return 0;
@@ -320,13 +384,16 @@ timer_loop (LPVOID arg)
320 /* Always sleep past the expiration time, to make sure we 384 /* Always sleep past the expiration time, to make sure we
321 never call the handler _before_ the expiration time, 385 never call the handler _before_ the expiration time,
322 always slightly after it. Sleep(5) makes sure we don't 386 always slightly after it. Sleep(5) makes sure we don't
323 hog the CPU by calling 'clock' with high frequency, and 387 hog the CPU by calling 'w32_get_timer_time' with high
324 also let other threads work. */ 388 frequency, and also let other threads work. */
325 while (clock () < expire) 389 while (w32_get_timer_time (hth) < expire)
326 Sleep (5); 390 Sleep (5);
327 } 391 }
328 392
329 if (itimer->expire == 0) 393 EnterCriticalSection (crit);
394 expire = itimer->expire;
395 LeaveCriticalSection (crit);
396 if (expire == 0)
330 continue; 397 continue;
331 398
332 /* Time's up. */ 399 /* Time's up. */
@@ -353,19 +420,21 @@ timer_loop (LPVOID arg)
353 ResumeThread (itimer->caller_thread); 420 ResumeThread (itimer->caller_thread);
354 } 421 }
355 422
356 if (itimer->expire == 0)
357 continue;
358
359 /* Update expiration time and loop. */ 423 /* Update expiration time and loop. */
360 EnterCriticalSection (crit); 424 EnterCriticalSection (crit);
361 expire = itimer->expire; 425 expire = itimer->expire;
426 if (expire == 0)
427 {
428 LeaveCriticalSection (crit);
429 continue;
430 }
362 reload = itimer->reload; 431 reload = itimer->reload;
363 if (reload > 0) 432 if (reload > 0)
364 { 433 {
365 now = clock (); 434 now = w32_get_timer_time (hth);
366 if (expire <= now) 435 if (expire <= now)
367 { 436 {
368 clock_t lag = now - expire; 437 ULONGLONG lag = now - expire;
369 438
370 /* If we missed some opportunities (presumably while 439 /* If we missed some opportunities (presumably while
371 sleeping or while the signal handler ran), skip 440 sleeping or while the signal handler ran), skip
@@ -448,6 +517,15 @@ term_timers (void)
448void 517void
449init_timers (void) 518init_timers (void)
450{ 519{
520 /* GetThreadTimes is not avaiulable on all versions of Windows, so
521 need to probe for its availability dynamically, and call it
522 through a pointer. */
523 s_pfn_Get_Thread_Times = NULL; /* in case dumped Emacs comes with a value */
524 if (os_subtype != OS_9X)
525 s_pfn_Get_Thread_Times =
526 (GetThreadTimes_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"),
527 "GetThreadTimes");
528
451 /* Make sure we start with zeroed out itimer structures, since 529 /* Make sure we start with zeroed out itimer structures, since
452 dumping may have left there traces of threads long dead. */ 530 dumping may have left there traces of threads long dead. */
453 memset (&real_itimer, 0, sizeof real_itimer); 531 memset (&real_itimer, 0, sizeof real_itimer);
@@ -473,14 +551,6 @@ start_timer_thread (int which)
473 return 0; 551 return 0;
474 552
475 /* Start a new thread. */ 553 /* Start a new thread. */
476 if (!DuplicateHandle (GetCurrentProcess (), GetCurrentThread (),
477 GetCurrentProcess (), &itimer->caller_thread, 0,
478 FALSE, DUPLICATE_SAME_ACCESS))
479 {
480 errno = ESRCH;
481 return -1;
482 }
483
484 itimer->terminate = 0; 554 itimer->terminate = 0;
485 itimer->type = which; 555 itimer->type = which;
486 /* Request that no more than 64KB of stack be reserved for this 556 /* Request that no more than 64KB of stack be reserved for this
@@ -512,17 +582,16 @@ start_timer_thread (int which)
512int 582int
513getitimer (int which, struct itimerval *value) 583getitimer (int which, struct itimerval *value)
514{ 584{
515 volatile clock_t *t_expire; 585 volatile ULONGLONG *t_expire;
516 volatile clock_t *t_reload; 586 volatile ULONGLONG *t_reload;
517 clock_t expire, reload; 587 ULONGLONG expire, reload;
518 __int64 usecs; 588 __int64 usecs;
519 CRITICAL_SECTION *crit; 589 CRITICAL_SECTION *crit;
590 struct itimer_data *itimer;
520 591
521 if (disable_itimers) 592 if (disable_itimers)
522 return -1; 593 return -1;
523 594
524 ticks_now = clock ();
525
526 if (!value) 595 if (!value)
527 { 596 {
528 errno = EFAULT; 597 errno = EFAULT;
@@ -535,8 +604,22 @@ getitimer (int which, struct itimerval *value)
535 return -1; 604 return -1;
536 } 605 }
537 606
538 t_expire = (which == ITIMER_REAL) ? &real_itimer.expire: &prof_itimer.expire; 607 itimer = (which == ITIMER_REAL) ? &real_itimer : &prof_itimer;
539 t_reload = (which == ITIMER_REAL) ? &real_itimer.reload: &prof_itimer.reload; 608
609 if (!DuplicateHandle (GetCurrentProcess (), GetCurrentThread (),
610 GetCurrentProcess (), &itimer->caller_thread, 0,
611 FALSE, DUPLICATE_SAME_ACCESS))
612 {
613 errno = ESRCH;
614 return -1;
615 }
616
617 ticks_now = w32_get_timer_time ((which == ITIMER_REAL)
618 ? NULL
619 : itimer->caller_thread);
620
621 t_expire = &itimer->expire;
622 t_reload = &itimer->reload;
540 crit = (which == ITIMER_REAL) ? &crit_real : &crit_prof; 623 crit = (which == ITIMER_REAL) ? &crit_real : &crit_prof;
541 624
542 EnterCriticalSection (crit); 625 EnterCriticalSection (crit);
@@ -560,10 +643,11 @@ getitimer (int which, struct itimerval *value)
560int 643int
561setitimer(int which, struct itimerval *value, struct itimerval *ovalue) 644setitimer(int which, struct itimerval *value, struct itimerval *ovalue)
562{ 645{
563 volatile clock_t *t_expire, *t_reload; 646 volatile ULONGLONG *t_expire, *t_reload;
564 clock_t expire, reload, expire_old, reload_old; 647 ULONGLONG expire, reload, expire_old, reload_old;
565 __int64 usecs; 648 __int64 usecs;
566 CRITICAL_SECTION *crit; 649 CRITICAL_SECTION *crit;
650 struct itimerval tem, *ptem;
567 651
568 if (disable_itimers) 652 if (disable_itimers)
569 return -1; 653 return -1;
@@ -573,26 +657,21 @@ setitimer(int which, struct itimerval *value, struct itimerval *ovalue)
573 time we are called, measure the clock tick resolution. */ 657 time we are called, measure the clock tick resolution. */
574 if (!clocks_min) 658 if (!clocks_min)
575 { 659 {
576 clock_t t1, t2; 660 ULONGLONG t1, t2;
577 661
578 for (t1 = clock (); (t2 = clock ()) == t1; ) 662 for (t1 = w32_get_timer_time (NULL);
663 (t2 = w32_get_timer_time (NULL)) == t1; )
579 ; 664 ;
580 clocks_min = t2 - t1; 665 clocks_min = t2 - t1;
581 } 666 }
582 667
583 if (ovalue) 668 if (ovalue)
584 { 669 ptem = ovalue;
585 if (getitimer (which, ovalue)) /* also sets ticks_now */
586 return -1; /* errno already set */
587 }
588 else 670 else
589 ticks_now = clock (); 671 ptem = &tem;
590 672
591 if (which != ITIMER_REAL && which != ITIMER_PROF) 673 if (getitimer (which, ptem)) /* also sets ticks_now */
592 { 674 return -1; /* errno already set */
593 errno = EINVAL;
594 return -1;
595 }
596 675
597 t_expire = 676 t_expire =
598 (which == ITIMER_REAL) ? &real_itimer.expire : &prof_itimer.expire; 677 (which == ITIMER_REAL) ? &real_itimer.expire : &prof_itimer.expire;