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 <sigset_t> [_MSVC_VER]: Make the typedef consistent with MinGW. (SA_RESTART, SIGPROF): Define. nt/config.nt (HAVE_SETITIMER): Define to 1.
This commit is contained in:
parent
8223b1d233
commit
c06c382ae4
11 changed files with 643 additions and 118 deletions
12
nt/ChangeLog
12
nt/ChangeLog
|
@ -1,3 +1,15 @@
|
|||
2012-09-30 Eli Zaretskii <eliz@gnu.org>
|
||||
|
||||
* inc/sys/time.h (ITIMER_REAL, ITIMER_PROF): Define.
|
||||
(struct itimerval): Define.
|
||||
(getitimer, setitimer): Add prototypes.
|
||||
|
||||
* inc/ms-w32.h <sigset_t> [_MSVC_VER]: Make the typedef consistent
|
||||
with MinGW.
|
||||
(SA_RESTART, SIGPROF): Define.
|
||||
|
||||
* config.nt (HAVE_SETITIMER): Define to 1.
|
||||
|
||||
2012-09-30 Juanma Barranquero <lekktu@gmail.com>
|
||||
|
||||
* config.nt: Sync with autogen/config.in.
|
||||
|
|
|
@ -774,7 +774,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
|
|||
#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
|
||||
|
|
|
@ -121,7 +121,7 @@ extern char *getenv ();
|
|||
#include <sys/types.h>
|
||||
|
||||
#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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -1,3 +1,42 @@
|
|||
2012-09-30 Eli Zaretskii <eliz@gnu.org>
|
||||
|
||||
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 <handa@gnu.org>
|
||||
|
||||
* coding.c (decode_coding_ccl, encode_coding_ccl): Pay attention
|
||||
|
|
|
@ -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
|
||||
|
|
58
src/w32.c
58
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 */
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
85
src/w32fns.c
85
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);
|
||||
|
|
532
src/w32proc.c
532
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 <process.h> which conflicts with the local copy */
|
||||
#define _P_NOWAIT 1
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Add table
Reference in a new issue