Commit c06c382a authored by Eli Zaretskii's avatar Eli Zaretskii
Browse files

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.
parent 8223b1d2
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 */
......
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
......
......@@ -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);
......
......@@ -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);
......
......@@ -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;