diff options
| author | Eli Zaretskii | 2012-10-27 13:21:26 +0200 |
|---|---|---|
| committer | Eli Zaretskii | 2012-10-27 13:21:26 +0200 |
| commit | 6c16c13ed185acf16dc8f1c6ba458f1e59328e84 (patch) | |
| tree | 879877acff6356de8e86be0679b04859d4d50451 /src | |
| parent | caa5e5a44dd23b77dfc38f7dd8b63af241149410 (diff) | |
| download | emacs-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/ChangeLog | 19 | ||||
| -rw-r--r-- | src/w32proc.c | 177 |
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 @@ | |||
| 1 | 2012-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 | |||
| 1 | 2012-10-26 Eli Zaretskii <eliz@gnu.org> | 20 | 2012-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. */ | ||
| 249 | struct itimer_data { | 247 | struct 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 | ||
| 258 | static clock_t ticks_now; | 256 | static ULONGLONG ticks_now; |
| 259 | static struct itimer_data real_itimer, prof_itimer; | 257 | static struct itimer_data real_itimer, prof_itimer; |
| 260 | static clock_t clocks_min; | 258 | static 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. */ |
| 263 | static int disable_itimers; | 261 | static int disable_itimers; |
| 264 | 262 | ||
| 265 | static CRITICAL_SECTION crit_real, crit_prof; | 263 | static CRITICAL_SECTION crit_real, crit_prof; |
| 266 | 264 | ||
| 265 | /* GetThreadTimes is not available on Windows 9X and posibly also on 2K. */ | ||
| 266 | typedef BOOL (WINAPI *GetThreadTimes_Proc) ( | ||
| 267 | HANDLE hThread, | ||
| 268 | LPFILETIME lpCreationTime, | ||
| 269 | LPFILETIME lpExitTime, | ||
| 270 | LPFILETIME lpKernelTime, | ||
| 271 | LPFILETIME lpUserTime); | ||
| 272 | |||
| 273 | static 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. */ | ||
| 280 | static ULONGLONG | ||
| 281 | w32_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 (¤t_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. */ | ||
| 269 | static DWORD WINAPI | 330 | static DWORD WINAPI |
| 270 | timer_loop (LPVOID arg) | 331 | timer_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) | |||
| 448 | void | 517 | void |
| 449 | init_timers (void) | 518 | init_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) | |||
| 512 | int | 582 | int |
| 513 | getitimer (int which, struct itimerval *value) | 583 | getitimer (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) | |||
| 560 | int | 643 | int |
| 561 | setitimer(int which, struct itimerval *value, struct itimerval *ovalue) | 644 | setitimer(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; |