From: Eli Zaretskii Date: Sun, 30 Sep 2012 15:49:05 +0000 (+0200) Subject: Support atimers and CPU profiler via profile.c on MS-Windows. X-Git-Tag: emacs-24.2.90~241^2~93 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=c06c382ae494c4129da43f2c1ea0f72e39a45bf1;p=emacs.git Support atimers and CPU profiler via profile.c on MS-Windows. src/w32proc.c (sig_mask, crit_sig): New static variables. (sys_signal): Support SIGALRM and SIGPROF. (sigemptyset, sigaddset, sigfillset, sigprocmask) (pthread_sigmask, setpgrp): Moved here from w32.c. sigaddset, sigfillset, and sigprocmask are no longer no-ops. (sigismember): New function. (struct itimer_data): New definition. (ticks_now, real_itimer, prof_itimer, clocks_min, crit_real) (crit_prof): New static variables. (MAX_SINGLE_SLEEP): New definition. (timer_loop, stop_timer_thread, term_timers, init_timers) (start_timer_thread, getitimer, setitimer): New functions. (alarm): No longer a no-op, calls setitimer. src/w32.c (term_ntproc): Call term_timers. (init_ntproc): Make sure all signals are unblocked at startup, to erase any traces of dumping. Call init_timers. src/w32fns.c (hourglass_timer, HOURGLASS_ID): Remove. Windows-specific code to display the hourglass mouse pointer is no longer used. (w32_wnd_proc): Remove code that handled the WM_TIMER message due to hourglass timer expiration. (start_hourglass, cancel_hourglass, DEFAULT_HOURGLASS_DELAY): Remove, no longer used. (w32_note_current_window, show_hourglass, hide_hourglass): New functions, in support of hourglass cursor display similar to other window systems. (syms_of_w32fns): Don't initialize hourglass_timer. src/xdisp.c (start_hourglass, cancel_hourglass): Now used on WINDOWSNT as well. (start_hourglass) [WINDOWSNT]: Call w32_note_current_window. src/w32.h (init_timers, term_timers): Add prototypes. nt/inc/sys/time.h (ITIMER_REAL, ITIMER_PROF): Define. (struct itimerval): Define. (getitimer, setitimer): Add prototypes. nt/inc/ms-w32.h [_MSVC_VER]: Make the typedef consistent with MinGW. (SA_RESTART, SIGPROF): Define. nt/config.nt (HAVE_SETITIMER): Define to 1. --- diff --git a/nt/ChangeLog b/nt/ChangeLog index 7e064cc3e42..9bc9ee6b10d 100644 --- a/nt/ChangeLog +++ b/nt/ChangeLog @@ -1,3 +1,15 @@ +2012-09-30 Eli Zaretskii + + * inc/sys/time.h (ITIMER_REAL, ITIMER_PROF): Define. + (struct itimerval): Define. + (getitimer, setitimer): Add prototypes. + + * inc/ms-w32.h [_MSVC_VER]: Make the typedef consistent + with MinGW. + (SA_RESTART, SIGPROF): Define. + + * config.nt (HAVE_SETITIMER): Define to 1. + 2012-09-30 Juanma Barranquero * config.nt: Sync with autogen/config.in. diff --git a/nt/config.nt b/nt/config.nt index 3b398eae04c..e342c78e20f 100644 --- a/nt/config.nt +++ b/nt/config.nt @@ -774,7 +774,7 @@ along with GNU Emacs. If not, see . */ #define HAVE_SENDTO 1 /* Define to 1 if you have the `setitimer' function. */ -#undef HAVE_SETITIMER +#define HAVE_SETITIMER 1 /* Define to 1 if you have the `setlocale' function. */ #define HAVE_SETLOCALE 1 diff --git a/nt/inc/ms-w32.h b/nt/inc/ms-w32.h index 107ab6e788b..646ede57c8d 100644 --- a/nt/inc/ms-w32.h +++ b/nt/inc/ms-w32.h @@ -121,7 +121,7 @@ extern char *getenv (); #include #ifdef _MSC_VER -typedef unsigned long sigset_t; +typedef int sigset_t; typedef int ssize_t; #endif @@ -130,6 +130,7 @@ struct sigaction { void (_CALLBACK_ *sa_handler)(int); sigset_t sa_mask; }; +#define SA_RESTART 0 #define SIG_BLOCK 1 #define SIG_SETMASK 2 #define SIG_UNBLOCK 3 @@ -293,6 +294,7 @@ struct timespec #define SIGPIPE 13 /* Write on pipe with no readers */ #define SIGALRM 14 /* Alarm */ #define SIGCHLD 18 /* Death of child */ +#define SIGPROF 19 /* Profiling */ #ifndef NSIG #define NSIG 23 diff --git a/nt/inc/sys/time.h b/nt/inc/sys/time.h index db54c90cf1f..c12c194fd2a 100644 --- a/nt/inc/sys/time.h +++ b/nt/inc/sys/time.h @@ -2,7 +2,8 @@ #define SYS_TIME_H_INCLUDED /* - * sys/time.h doesn't exist on NT + * sys/time.h either doesn't exist on Windows, or doesn't necessarily + * have the below stuff. */ struct timeval @@ -19,6 +20,18 @@ struct timezone void gettimeofday (struct timeval *, struct timezone *); +#define ITIMER_REAL 0 +#define ITIMER_PROF 1 + +struct itimerval +{ + struct timeval it_interval; /* timer interval */ + struct timeval it_value; /* current value */ +}; + +int getitimer (int, struct itimerval *); +int setitimer (int, struct itimerval *, struct itimerval *); + #endif /* SYS_TIME_H_INCLUDED */ /* end of sys/time.h */ diff --git a/src/ChangeLog b/src/ChangeLog index 1f5ad1e6d05..504f8cbaed3 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,42 @@ +2012-09-30 Eli Zaretskii + + Support atimers and CPU profiler via profile.c on MS-Windows. + * w32proc.c (sig_mask, crit_sig): New static variables. + (sys_signal): Support SIGALRM and SIGPROF. + (sigemptyset, sigaddset, sigfillset, sigprocmask) + (pthread_sigmask, setpgrp): Moved here from w32.c. sigaddset, + sigfillset, and sigprocmask are no longer no-ops. + (sigismember): New function. + (struct itimer_data): New definition. + (ticks_now, real_itimer, prof_itimer, clocks_min, crit_real) + (crit_prof): New static variables. + (MAX_SINGLE_SLEEP): New definition. + (timer_loop, stop_timer_thread, term_timers, init_timers) + (start_timer_thread, getitimer, setitimer): New functions. + (alarm): No longer a no-op, calls setitimer. + + * w32.c (term_ntproc): Call term_timers. + (init_ntproc): Make sure all signals are unblocked at startup, to + erase any traces of dumping. Call init_timers. + + * w32fns.c (hourglass_timer, HOURGLASS_ID): Remove. + Windows-specific code to display the hourglass mouse pointer is no + longer used. + (w32_wnd_proc): Remove code that handled the WM_TIMER message due + to hourglass timer expiration. + (start_hourglass, cancel_hourglass, DEFAULT_HOURGLASS_DELAY): + Remove, no longer used. + (w32_note_current_window, show_hourglass, hide_hourglass): New + functions, in support of hourglass cursor display similar to other + window systems. + (syms_of_w32fns): Don't initialize hourglass_timer. + + * xdisp.c (start_hourglass, cancel_hourglass): Now used on + WINDOWSNT as well. + (start_hourglass) [WINDOWSNT]: Call w32_note_current_window. + + * w32.h (init_timers, term_timers): Add prototypes. + 2012-09-30 Kenichi Handa * coding.c (decode_coding_ccl, encode_coding_ccl): Pay attention diff --git a/src/profiler.c b/src/profiler.c index 90a85c5230e..de118d13859 100644 --- a/src/profiler.c +++ b/src/profiler.c @@ -200,8 +200,6 @@ record_backtrace (log_t *log, EMACS_INT count) /* Sample profiler. */ -/* FIXME: Add support for the CPU profiler in W32. */ - #ifdef PROFILER_CPU_SUPPORT /* The profiler timer and whether it was properly initialized, if diff --git a/src/w32.c b/src/w32.c index 3154c725abf..7977e979b13 100644 --- a/src/w32.c +++ b/src/w32.c @@ -1528,52 +1528,6 @@ is_unc_volume (const char *filename) return 1; } -/* Routines that are no-ops on NT but are defined to get Emacs to compile. */ -int -sigemptyset (sigset_t *set) -{ - *set = 0; - return 0; -} - -int -sigaddset (sigset_t *set, int signo) -{ - return 0; -} - -int -sigfillset (sigset_t *set) -{ - return 0; -} - -int -sigprocmask (int how, const sigset_t *set, sigset_t *oset) -{ - return 0; -} - -int -pthread_sigmask (int how, const sigset_t *set, sigset_t *oset) -{ - if (sigprocmask (how, set, oset) == -1) - return EINVAL; - return 0; -} - -int -setpgrp (int pid, int gid) -{ - return 0; -} - -int -alarm (int seconds) -{ - return 0; -} - #define REG_ROOT "SOFTWARE\\GNU\\Emacs" LPBYTE @@ -6623,6 +6577,9 @@ void term_ntproc (int ignored) { (void)ignored; + + term_timers (); + /* shutdown the socket interface if necessary */ term_winsock (); @@ -6632,6 +6589,8 @@ term_ntproc (int ignored) void init_ntproc (int dumping) { + sigset_t initial_mask = 0; + /* Initialize the socket interface now if available and requested by the user by defining PRELOAD_WINSOCK; otherwise loading will be delayed until open-network-stream is called (w32-has-winsock can @@ -6708,7 +6667,12 @@ init_ntproc (int dumping) /* unfortunately, atexit depends on implementation of malloc */ /* atexit (term_ntproc); */ if (!dumping) - signal (SIGABRT, term_ntproc); + { + /* Make sure we start with all signals unblocked. */ + sigprocmask (SIG_SETMASK, &initial_mask, NULL); + signal (SIGABRT, term_ntproc); + } + init_timers (); /* determine which drives are fixed, for GetCachedVolumeInformation */ { diff --git a/src/w32.h b/src/w32.h index a833c8f4315..2e2315e245d 100644 --- a/src/w32.h +++ b/src/w32.h @@ -142,6 +142,9 @@ extern void syms_of_fontset (void); extern void syms_of_w32font (void); extern void check_windows_init_file (void); +extern void term_timers (void); +extern void init_timers (void); + extern int _sys_read_ahead (int fd); extern int _sys_wait_accept (int fd); diff --git a/src/w32fns.c b/src/w32fns.c index d7b45e263b3..b857e450476 100644 --- a/src/w32fns.c +++ b/src/w32fns.c @@ -79,9 +79,7 @@ extern void w32_menu_display_help (HWND, HMENU, UINT, UINT); extern void w32_free_menu_strings (HWND); extern const char *map_w32_filename (const char *, const char **); -/* If non-zero, a w32 timer that, when it expires, displays an - hourglass cursor on all frames. */ -static unsigned hourglass_timer = 0; +/* If non-NULL, a handle to a frame where to display the hourglass cursor. */ static HWND hourglass_hwnd = NULL; #ifndef IDC_HAND @@ -175,7 +173,6 @@ unsigned int msh_mousewheel = 0; #define MOUSE_BUTTON_ID 1 #define MOUSE_MOVE_ID 2 #define MENU_FREE_ID 3 -#define HOURGLASS_ID 4 /* The delay (milliseconds) before a menu is freed after WM_EXITMENULOOP is received. */ #define MENU_FREE_DELAY 1000 @@ -3313,12 +3310,6 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) menubar_in_use = 0; } } - else if (wParam == hourglass_timer) - { - KillTimer (hwnd, hourglass_timer); - hourglass_timer = 0; - w32_show_hourglass (x_window_to_frame (dpyinfo, hwnd)); - } return 0; case WM_NCACTIVATE: @@ -5040,66 +5031,50 @@ no value of TYPE (always string in the MS Windows case). */) Busy cursor ***********************************************************************/ -/* Default number of seconds to wait before displaying an hourglass - cursor. Duplicated from xdisp.c, but cannot use the version there - due to lack of atimers on w32. */ -#define DEFAULT_HOURGLASS_DELAY 1 - -/* Cancel a currently active hourglass timer, and start a new one. */ - void -start_hourglass (void) +w32_note_current_window (void) { - DWORD delay; - int secs, msecs = 0; struct frame * f = SELECTED_FRAME (); - /* No cursors on non GUI frames. */ if (!FRAME_W32_P (f)) return; - cancel_hourglass (); - - if (INTEGERP (Vhourglass_delay) - && XINT (Vhourglass_delay) > 0) - secs = XFASTINT (Vhourglass_delay); - else if (FLOATP (Vhourglass_delay) - && XFLOAT_DATA (Vhourglass_delay) > 0) - { - Lisp_Object tem; - tem = Ftruncate (Vhourglass_delay, Qnil); - secs = XFASTINT (tem); - msecs = (XFLOAT_DATA (Vhourglass_delay) - secs) * 1000; - } - else - secs = DEFAULT_HOURGLASS_DELAY; - - delay = secs * 1000 + msecs; hourglass_hwnd = FRAME_W32_WINDOW (f); - hourglass_timer = SetTimer (hourglass_hwnd, HOURGLASS_ID, delay, NULL); } - -/* Cancel the hourglass cursor timer if active, hide an hourglass - cursor if shown. */ - void -cancel_hourglass (void) +show_hourglass (struct atimer *timer) { - if (hourglass_timer) - { - KillTimer (hourglass_hwnd, hourglass_timer); - hourglass_timer = 0; - } + struct frame *f; - if (hourglass_shown_p) - w32_hide_hourglass (); + hourglass_atimer = NULL; + + block_input (); + f = x_window_to_frame (&one_w32_display_info, + hourglass_hwnd); + + if (f) + f->output_data.w32->hourglass_p = 0; + else + f = SELECTED_FRAME (); + + if (!FRAME_W32_P (f)) + return; + + w32_show_hourglass (f); + unblock_input (); } +void +hide_hourglass (void) +{ + block_input (); + w32_hide_hourglass (); + unblock_input (); +} -/* Timer function of hourglass_timer. - Display an hourglass cursor. Set the hourglass_p flag in display info +/* Display an hourglass cursor. Set the hourglass_p flag in display info to indicate that an hourglass cursor is shown. */ static void @@ -7123,8 +7098,6 @@ only be necessary if the default setting causes problems. */); check_window_system_func = check_w32; - - hourglass_timer = 0; hourglass_hwnd = NULL; defsubr (&Sx_show_tip); diff --git a/src/w32proc.c b/src/w32proc.c index b367b42d8c6..d7c9edac746 100644 --- a/src/w32proc.c +++ b/src/w32proc.c @@ -86,18 +86,23 @@ typedef void (_CALLBACK_ *signal_handler) (int); /* Signal handlers...SIG_DFL == 0 so this is initialized correctly. */ static signal_handler sig_handlers[NSIG]; +static sigset_t sig_mask; + +static CRITICAL_SECTION crit_sig; + /* Improve on the CRT 'signal' implementation so that we could record - the SIGCHLD handler. */ + the SIGCHLD handler and fake interval timers. */ signal_handler sys_signal (int sig, signal_handler handler) { signal_handler old; /* SIGCHLD is needed for supporting subprocesses, see sys_kill - below. All the others are the only ones supported by the MS - runtime. */ + below. SIGALRM and SIGPROF are used by setitimer. All the + others are the only ones supported by the MS runtime. */ if (!(sig == SIGCHLD || sig == SIGSEGV || sig == SIGILL - || sig == SIGFPE || sig == SIGABRT || sig == SIGTERM)) + || sig == SIGFPE || sig == SIGABRT || sig == SIGTERM + || sig == SIGALRM || sig == SIGPROF)) { errno = EINVAL; return SIG_ERR; @@ -111,7 +116,7 @@ sys_signal (int sig, signal_handler handler) if (!(sig == SIGABRT && old == term_ntproc)) { sig_handlers[sig] = handler; - if (sig != SIGCHLD) + if (!(sig == SIGCHLD || sig == SIGALRM || sig == SIGPROF)) signal (sig, handler); } return old; @@ -143,6 +148,523 @@ sigaction (int sig, const struct sigaction *act, struct sigaction *oact) return retval; } +/* Emulate signal sets and blocking of signals used by timers. */ + +int +sigemptyset (sigset_t *set) +{ + *set = 0; + return 0; +} + +int +sigaddset (sigset_t *set, int signo) +{ + if (!set) + { + errno = EINVAL; + return -1; + } + if (signo < 0 || signo >= NSIG) + { + errno = EINVAL; + return -1; + } + + *set |= (1U << signo); + + return 0; +} + +int +sigfillset (sigset_t *set) +{ + if (!set) + { + errno = EINVAL; + return -1; + } + + *set = 0xFFFFFFFF; + return 0; +} + +int +sigprocmask (int how, const sigset_t *set, sigset_t *oset) +{ + if (!(how == SIG_BLOCK || how == SIG_UNBLOCK || how == SIG_SETMASK)) + { + errno = EINVAL; + return -1; + } + + if (oset) + *oset = sig_mask; + + if (!set) + return 0; + + switch (how) + { + case SIG_BLOCK: + sig_mask |= *set; + break; + case SIG_SETMASK: + sig_mask = *set; + break; + case SIG_UNBLOCK: + /* FIXME: Catch signals that are blocked and reissue them when + they are unblocked. Important for SIGALRM and SIGPROF only. */ + sig_mask &= ~(*set); + break; + } + + return 0; +} + +int +pthread_sigmask (int how, const sigset_t *set, sigset_t *oset) +{ + if (sigprocmask (how, set, oset) == -1) + return EINVAL; + return 0; +} + +int +sigismember (const sigset_t *set, int signo) +{ + if (signo < 0 || signo >= NSIG) + { + errno = EINVAL; + return -1; + } + if (signo > sizeof (*set) * BITS_PER_CHAR) + emacs_abort (); + + return (*set & (1U << signo)) != 0; +} + +int +setpgrp (int pid, int gid) +{ + return 0; +} + +/* Emulations of interval timers. + + Limitations: only ITIMER_REAL and ITIMER_PROF are supported. + + Implementation: a separate thread is started for each timer type, + the thread calls the appropriate signal handler when the timer + expires, after stopping the thread which installed the timer. */ + +/* FIXME: clock_t counts overflow after 49 days, need to handle the + wrap-around. */ +struct itimer_data { + clock_t expire; + clock_t reload; + int terminate; + int type; + HANDLE caller_thread; + HANDLE timer_thread; +}; + +static clock_t ticks_now; +static struct itimer_data real_itimer, prof_itimer; +static clock_t clocks_min; + +static CRITICAL_SECTION crit_real, crit_prof; + +#define MAX_SINGLE_SLEEP 30 + +static DWORD WINAPI +timer_loop (LPVOID arg) +{ + struct itimer_data *itimer = (struct itimer_data *)arg; + int which = itimer->type; + int sig = (which == ITIMER_REAL) ? SIGALRM : SIGPROF; + CRITICAL_SECTION *crit = (which == ITIMER_REAL) ? &crit_real : &crit_prof; + const DWORD max_sleep = MAX_SINGLE_SLEEP * 1000 / CLOCKS_PER_SEC; + int new_count = 0; + + while (1) + { + DWORD sleep_time; + signal_handler handler; + clock_t now, expire, reload; + + /* Load new values if requested by setitimer. */ + EnterCriticalSection (crit); + expire = itimer->expire; + reload = itimer->reload; + LeaveCriticalSection (crit); + if (itimer->terminate) + return 0; + + if (itimer->expire == 0) + { + /* We are idle. */ + Sleep (max_sleep); + continue; + } + + expire = itimer->expire; + if (expire > (now = clock ())) + sleep_time = expire - now; + else + sleep_time = 0; + /* Don't sleep too long at a time, to be able to see the + termination flag without too long a delay. */ + while (sleep_time > max_sleep) + { + if (itimer->terminate) + return 0; + Sleep (max_sleep); + expire = itimer->expire; + sleep_time = (expire > (now = clock ())) ? expire - now : 0; + } + if (itimer->terminate) + return 0; + if (sleep_time > 0) + { + Sleep (sleep_time * 1000 / CLOCKS_PER_SEC); + /* Always sleep past the expiration time, to make sure we + never call the handler _before_ the expiration time, + always slightly after it. Sleep(0) relinquishes the rest + of the scheduled slot, so that we let other threads + work. */ + while (clock () < expire) + Sleep (0); + } + + if (itimer->expire == 0) + continue; + + /* Time's up. */ + handler = sig_handlers[sig]; + if (!(handler == SIG_DFL || handler == SIG_IGN || handler == SIG_ERR) + /* FIXME: Don't ignore masked signals. Instead, record that + they happened and reissue them when the signal is + unblocked. */ + && !sigismember (&sig_mask, sig) + /* Simulate masking of SIGALRM and SIGPROF when processing + fatal signals. */ + && !fatal_error_in_progress + && itimer->caller_thread) + { + /* Simulate a signal delivered to the thread which installed + the timer, by suspending that thread while the handler + runs. */ + DWORD result = SuspendThread (itimer->caller_thread); + + if (result == (DWORD)-1) + { + DebPrint (("Thread %d exiting with status 2\n", which)); + return 2; + } + handler (sig); + ResumeThread (itimer->caller_thread); + } + + if (itimer->expire == 0) + continue; + + /* Update expiration time and loop. */ + EnterCriticalSection (crit); + expire = itimer->expire; + reload = itimer->reload; + if (reload > 0) + { + now = clock (); + if (expire <= now) + { + clock_t lag = now - expire; + + /* If we missed some opportunities (presumably while + sleeping or while the signal handler ran), skip + them. */ + if (lag > reload) + expire = now - (lag % reload); + + expire += reload; + } + } + else + expire = 0; /* become idle */ + itimer->expire = expire; + LeaveCriticalSection (crit); + } + return 0; +} + +static void +stop_timer_thread (int which) +{ + struct itimer_data *itimer = + (which == ITIMER_REAL) ? &real_itimer : &prof_itimer; + int i; + DWORD exit_code = 255; + BOOL status, err; + + /* Signal the thread that it should terminate. */ + itimer->terminate = 1; + + if (itimer->timer_thread == NULL) + return; + + /* Wait for the timer thread to terminate voluntarily, then kill it + if it doesn't. This loop waits twice more than the maximum + amount of time a timer thread sleeps, see above. */ + for (i = 0; i < MAX_SINGLE_SLEEP / 5; i++) + { + if (!((status = GetExitCodeThread (itimer->timer_thread, &exit_code)) + && exit_code == STILL_ACTIVE)) + break; + Sleep (10); + } + if ((status == FALSE && (err = GetLastError ()) == ERROR_INVALID_HANDLE) + || exit_code == STILL_ACTIVE) + { + if (!(status == FALSE && err == ERROR_INVALID_HANDLE)) + TerminateThread (itimer->timer_thread, 0); + } + + /* Clean up. */ + CloseHandle (itimer->timer_thread); + itimer->timer_thread = NULL; + if (itimer->caller_thread) + { + CloseHandle (itimer->caller_thread); + itimer->caller_thread = NULL; + } +} + +/* This is called at shutdown time from term_ntproc. */ +void +term_timers (void) +{ + if (real_itimer.timer_thread) + stop_timer_thread (ITIMER_REAL); + if (prof_itimer.timer_thread) + stop_timer_thread (ITIMER_PROF); + + DeleteCriticalSection (&crit_real); + DeleteCriticalSection (&crit_prof); + DeleteCriticalSection (&crit_sig); +} + +/* This is called at initialization time from init_ntproc. */ +void +init_timers (void) +{ + /* Make sure we start with zeroed out itimer structures, since + dumping may have left there traces of threads long dead. */ + memset (&real_itimer, 0, sizeof real_itimer); + memset (&prof_itimer, 0, sizeof prof_itimer); + + InitializeCriticalSection (&crit_real); + InitializeCriticalSection (&crit_prof); + InitializeCriticalSection (&crit_sig); +} + +static int +start_timer_thread (int which) +{ + DWORD exit_code; + struct itimer_data *itimer = + (which == ITIMER_REAL) ? &real_itimer : &prof_itimer; + + if (itimer->timer_thread + && GetExitCodeThread (itimer->timer_thread, &exit_code) + && exit_code == STILL_ACTIVE) + return 0; + + /* Start a new thread. */ + if (!DuplicateHandle (GetCurrentProcess (), GetCurrentThread (), + GetCurrentProcess (), &itimer->caller_thread, 0, + FALSE, DUPLICATE_SAME_ACCESS)) + { + errno = ESRCH; + return -1; + } + + itimer->terminate = 0; + itimer->type = which; + /* Request that no more than 64KB of stack be reserved for this + thread, to avoid reserving too much memory, which would get in + the way of threads we start to wait for subprocesses. See also + new_child below. */ + itimer->timer_thread = CreateThread (NULL, 64 * 1024, timer_loop, + (void *)itimer, 0x00010000, NULL); + + if (!itimer->timer_thread) + { + CloseHandle (itimer->caller_thread); + itimer->caller_thread = NULL; + errno = EAGAIN; + return -1; + } + + /* This is needed to make sure that the timer thread running for + profiling gets CPU as soon as the Sleep call terminates. */ + if (which == ITIMER_PROF) + SetThreadPriority (itimer->caller_thread, THREAD_PRIORITY_TIME_CRITICAL); + + return 0; +} + +/* Most of the code of getitimer and setitimer (but not of their + subroutines) was shamelessly stolen from itimer.c in the DJGPP + library, see www.delorie.com/djgpp. */ +int +getitimer (int which, struct itimerval *value) +{ + volatile clock_t *t_expire; + volatile clock_t *t_reload; + clock_t expire, reload; + __int64 usecs; + CRITICAL_SECTION *crit; + + ticks_now = clock (); + + if (!value) + { + errno = EFAULT; + return -1; + } + + if (which != ITIMER_REAL && which != ITIMER_PROF) + { + errno = EINVAL; + return -1; + } + + t_expire = (which == ITIMER_REAL) ? &real_itimer.expire: &prof_itimer.expire; + t_reload = (which == ITIMER_REAL) ? &real_itimer.reload: &prof_itimer.reload; + crit = (which == ITIMER_REAL) ? &crit_real : &crit_prof; + + EnterCriticalSection (crit); + reload = *t_reload; + expire = *t_expire; + LeaveCriticalSection (crit); + + if (expire) + expire -= ticks_now; + + value->it_value.tv_sec = expire / CLOCKS_PER_SEC; + usecs = (expire % CLOCKS_PER_SEC) * (__int64)1000000 / CLOCKS_PER_SEC; + value->it_value.tv_usec = usecs; + value->it_interval.tv_sec = reload / CLOCKS_PER_SEC; + usecs = (reload % CLOCKS_PER_SEC) * (__int64)1000000 / CLOCKS_PER_SEC; + value->it_interval.tv_usec= usecs; + + return 0; +} + +int +setitimer(int which, struct itimerval *value, struct itimerval *ovalue) +{ + volatile clock_t *t_expire, *t_reload; + clock_t expire, reload, expire_old, reload_old; + __int64 usecs; + CRITICAL_SECTION *crit; + + /* Posix systems expect timer values smaller than the resolution of + the system clock be rounded up to the clock resolution. First + time we are called, measure the clock tick resolution. */ + if (!clocks_min) + { + clock_t t1, t2; + + for (t1 = clock (); (t2 = clock ()) == t1; ) + ; + clocks_min = t2 - t1; + } + + if (ovalue) + { + if (getitimer (which, ovalue)) /* also sets ticks_now */ + return -1; /* errno already set */ + } + else + ticks_now = clock (); + + if (which != ITIMER_REAL && which != ITIMER_PROF) + { + errno = EINVAL; + return -1; + } + + t_expire = + (which == ITIMER_REAL) ? &real_itimer.expire : &prof_itimer.expire; + t_reload = + (which == ITIMER_REAL) ? &real_itimer.reload : &prof_itimer.reload; + + crit = (which == ITIMER_REAL) ? &crit_real : &crit_prof; + + if (!value + || (value->it_value.tv_sec == 0 && value->it_value.tv_usec == 0)) + { + EnterCriticalSection (crit); + /* Disable the timer. */ + *t_expire = 0; + *t_reload = 0; + LeaveCriticalSection (crit); + return 0; + } + + reload = value->it_interval.tv_sec * CLOCKS_PER_SEC; + + usecs = value->it_interval.tv_usec; + if (value->it_interval.tv_sec == 0 + && usecs && usecs * CLOCKS_PER_SEC < clocks_min * 1000000) + reload = clocks_min; + else + { + usecs *= CLOCKS_PER_SEC; + reload += usecs / 1000000; + } + + expire = value->it_value.tv_sec * CLOCKS_PER_SEC; + usecs = value->it_value.tv_usec; + if (value->it_value.tv_sec == 0 + && usecs * CLOCKS_PER_SEC < clocks_min * 1000000) + expire = clocks_min; + else + { + usecs *= CLOCKS_PER_SEC; + expire += usecs / 1000000; + } + + expire += ticks_now; + + EnterCriticalSection (crit); + expire_old = *t_expire; + reload_old = *t_reload; + if (!(expire == expire_old && reload == reload_old)) + { + *t_reload = reload; + *t_expire = expire; + } + LeaveCriticalSection (crit); + + return start_timer_thread (which); +} + +int +alarm (int seconds) +{ + struct itimerval new_values; + + new_values.it_value.tv_sec = seconds; + new_values.it_value.tv_usec = 0; + new_values.it_interval.tv_sec = new_values.it_interval.tv_usec = 0; + + setitimer (ITIMER_REAL, &new_values, NULL); + + return seconds; +} + /* Defined in which conflicts with the local copy */ #define _P_NOWAIT 1 diff --git a/src/xdisp.c b/src/xdisp.c index b23a06ff3d1..1d267bd7ded 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -29357,10 +29357,6 @@ init_xdisp (void) help_echo_showing_p = 0; } -/* Since w32 does not support atimers, it defines its own implementation of - the following three functions in w32fns.c. */ -#ifndef WINDOWSNT - /* Platform-independent portion of hourglass implementation. */ /* Cancel a currently active hourglass timer, and start a new one. */ @@ -29383,6 +29379,10 @@ start_hourglass (void) else delay = make_emacs_time (DEFAULT_HOURGLASS_DELAY, 0); +#ifdef WINDOWSNT + w32_note_current_window (); +#endif + hourglass_atimer = start_atimer (ATIMER_RELATIVE, delay, show_hourglass, NULL); #endif @@ -29405,4 +29405,3 @@ cancel_hourglass (void) hide_hourglass (); #endif } -#endif /* ! WINDOWSNT */