X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/5101529e98c333b07d17dc891ba0a2e1669d82ac..7c2fcf9bad2bed6c0198875384dc2bdb7cbd7e99:/src/w32proc.c diff --git a/src/w32proc.c b/src/w32proc.c index 8dbd063a96..e3c54fe546 100644 --- a/src/w32proc.c +++ b/src/w32proc.c @@ -24,6 +24,7 @@ along with GNU Emacs. If not, see . */ #include #include #include +#include #include #include #include @@ -51,6 +52,7 @@ extern BOOL WINAPI IsValidLocale (LCID, DWORD); #include "lisp.h" #include "w32.h" +#include "w32common.h" #include "w32heap.h" #include "systime.h" #include "syswait.h" @@ -62,43 +64,47 @@ extern BOOL WINAPI IsValidLocale (LCID, DWORD); #define RVA_TO_PTR(var,section,filedata) \ ((void *)((section)->PointerToRawData \ - + ((DWORD)(var) - (section)->VirtualAddress) \ + + ((DWORD_PTR)(var) - (section)->VirtualAddress) \ + (filedata).file_base)) Lisp_Object Qhigh, Qlow; -#ifdef EMACSDEBUG -void -_DebPrint (const char *fmt, ...) -{ - char buf[1024]; - va_list args; - - va_start (args, fmt); - vsprintf (buf, fmt, args); - va_end (args); - OutputDebugString (buf); -} -#endif - -typedef void (_CALLBACK_ *signal_handler) (int); - /* Signal handlers...SIG_DFL == 0 so this is initialized correctly. */ static signal_handler sig_handlers[NSIG]; -/* Fake signal implementation to record the SIGCHLD handler. */ +static sigset_t sig_mask; + +static CRITICAL_SECTION crit_sig; + +/* Improve on the CRT 'signal' implementation so that we could record + the SIGCHLD handler and fake interval timers. */ signal_handler sys_signal (int sig, signal_handler handler) { signal_handler old; - if (sig != SIGCHLD) + /* SIGCHLD is needed for supporting subprocesses, see sys_kill + 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 == SIGALRM || sig == SIGPROF)) { errno = EINVAL; return SIG_ERR; } old = sig_handlers[sig]; - sig_handlers[sig] = handler; + /* SIGABRT is treated specially because w32.c installs term_ntproc + as its handler, so we don't want to override that afterwards. + Aborting Emacs works specially anyway: either by calling + emacs_abort directly or through terminate_due_to_signal, which + calls emacs_abort through emacs_raise. */ + if (!(sig == SIGABRT && old == term_ntproc)) + { + sig_handlers[sig] = handler; + if (!(sig == SIGCHLD || sig == SIGALRM || sig == SIGPROF)) + signal (sig, handler); + } return old; } @@ -106,25 +112,671 @@ sys_signal (int sig, signal_handler handler) int sigaction (int sig, const struct sigaction *act, struct sigaction *oact) { - signal_handler old; + signal_handler old = SIG_DFL; + int retval = 0; - if (sig != SIGCHLD) + if (act) + old = sys_signal (sig, act->sa_handler); + else if (oact) + old = sig_handlers[sig]; + + if (old == SIG_ERR) { errno = EINVAL; - return -1; + retval = -1; } - old = sig_handlers[sig]; - if (act) - sig_handlers[sig] = act->sa_handler; if (oact) { oact->sa_handler = old; oact->sa_flags = 0; oact->sa_mask = empty_mask; } + 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; +} + +pid_t +getpgrp (void) +{ + return getpid (); +} + +int +setpgid (pid_t pid, pid_t pgid) +{ + 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. */ + +struct itimer_data { + volatile ULONGLONG expire; + volatile ULONGLONG reload; + volatile int terminate; + int type; + HANDLE caller_thread; + HANDLE timer_thread; +}; + +static ULONGLONG ticks_now; +static struct itimer_data real_itimer, prof_itimer; +static ULONGLONG clocks_min; +/* If non-zero, itimers are disabled. Used during shutdown, when we + delete the critical sections used by the timer threads. */ +static int disable_itimers; + +static CRITICAL_SECTION crit_real, crit_prof; + +/* GetThreadTimes is not available on Windows 9X and possibly also on 2K. */ +typedef BOOL (WINAPI *GetThreadTimes_Proc) ( + HANDLE hThread, + LPFILETIME lpCreationTime, + LPFILETIME lpExitTime, + LPFILETIME lpKernelTime, + LPFILETIME lpUserTime); + +static GetThreadTimes_Proc s_pfn_Get_Thread_Times; + +#define MAX_SINGLE_SLEEP 30 +#define TIMER_TICKS_PER_SEC 1000 + +/* Return a suitable time value, in 1-ms units, for THREAD, a handle + to a thread. If THREAD is NULL or an invalid handle, return the + current wall-clock time since January 1, 1601 (UTC). Otherwise, + return the sum of kernel and user times used by THREAD since it was + created, plus its creation time. */ +static ULONGLONG +w32_get_timer_time (HANDLE thread) +{ + ULONGLONG retval; + int use_system_time = 1; + /* The functions below return times in 100-ns units. */ + const int tscale = 10 * TIMER_TICKS_PER_SEC; + + if (thread && thread != INVALID_HANDLE_VALUE + && s_pfn_Get_Thread_Times != NULL) + { + FILETIME creation_ftime, exit_ftime, kernel_ftime, user_ftime; + ULARGE_INTEGER temp_creation, temp_kernel, temp_user; + + if (s_pfn_Get_Thread_Times (thread, &creation_ftime, &exit_ftime, + &kernel_ftime, &user_ftime)) + { + use_system_time = 0; + temp_creation.LowPart = creation_ftime.dwLowDateTime; + temp_creation.HighPart = creation_ftime.dwHighDateTime; + temp_kernel.LowPart = kernel_ftime.dwLowDateTime; + temp_kernel.HighPart = kernel_ftime.dwHighDateTime; + temp_user.LowPart = user_ftime.dwLowDateTime; + temp_user.HighPart = user_ftime.dwHighDateTime; + retval = + temp_creation.QuadPart / tscale + temp_kernel.QuadPart / tscale + + temp_user.QuadPart / tscale; + } + else + DebPrint (("GetThreadTimes failed with error code %lu\n", + GetLastError ())); + } + + if (use_system_time) + { + FILETIME current_ftime; + ULARGE_INTEGER temp; + + GetSystemTimeAsFileTime (¤t_ftime); + + temp.LowPart = current_ftime.dwLowDateTime; + temp.HighPart = current_ftime.dwHighDateTime; + + retval = temp.QuadPart / tscale; + } + + return retval; +} + +/* Thread function for a timer thread. */ +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 / TIMER_TICKS_PER_SEC; + HANDLE hth = (which == ITIMER_REAL) ? NULL : itimer->caller_thread; + + while (1) + { + DWORD sleep_time; + signal_handler handler; + ULONGLONG 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 (expire == 0) + { + /* We are idle. */ + Sleep (max_sleep); + continue; + } + + if (expire > (now = w32_get_timer_time (hth))) + 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); + EnterCriticalSection (crit); + expire = itimer->expire; + LeaveCriticalSection (crit); + sleep_time = + (expire > (now = w32_get_timer_time (hth))) ? expire - now : 0; + } + if (itimer->terminate) + return 0; + if (sleep_time > 0) + { + Sleep (sleep_time * 1000 / TIMER_TICKS_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(5) makes sure we don't + hog the CPU by calling 'w32_get_timer_time' with high + frequency, and also let other threads work. */ + while (w32_get_timer_time (hth) < expire) + Sleep (5); + } + + EnterCriticalSection (crit); + expire = itimer->expire; + LeaveCriticalSection (crit); + if (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. */ + HANDLE th = itimer->caller_thread; + DWORD result = SuspendThread (th); + + if (result == (DWORD)-1) + return 2; + + handler (sig); + ResumeThread (th); + } + + /* Update expiration time and loop. */ + EnterCriticalSection (crit); + expire = itimer->expire; + if (expire == 0) + { + LeaveCriticalSection (crit); + continue; + } + reload = itimer->reload; + if (reload > 0) + { + now = w32_get_timer_time (hth); + if (expire <= now) + { + ULONGLONG 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 err, exit_code = 255; + BOOL status; + + /* 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); + + /* We are going to delete the critical sections, so timers cannot + work after this. */ + disable_itimers = 1; + + DeleteCriticalSection (&crit_real); + DeleteCriticalSection (&crit_prof); + DeleteCriticalSection (&crit_sig); +} + +/* This is called at initialization time from init_ntproc. */ +void +init_timers (void) +{ + /* GetThreadTimes is not available on all versions of Windows, so + need to probe for its availability dynamically, and call it + through a pointer. */ + s_pfn_Get_Thread_Times = NULL; /* in case dumped Emacs comes with a value */ + if (os_subtype != OS_9X) + s_pfn_Get_Thread_Times = + (GetThreadTimes_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"), + "GetThreadTimes"); + + /* 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); + + disable_itimers = 0; +} + +static int +start_timer_thread (int which) +{ + DWORD exit_code; + HANDLE th; + 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; + + /* Clean up after possibly exited thread. */ + if (itimer->timer_thread) + { + CloseHandle (itimer->timer_thread); + itimer->timer_thread = NULL; + } + if (itimer->caller_thread) + { + CloseHandle (itimer->caller_thread); + itimer->caller_thread = NULL; + } + + /* Start a new thread. */ + if (!DuplicateHandle (GetCurrentProcess (), GetCurrentThread (), + GetCurrentProcess (), &th, 0, FALSE, + DUPLICATE_SAME_ACCESS)) + { + errno = ESRCH; + return -1; + } + itimer->terminate = 0; + itimer->type = which; + itimer->caller_thread = th; + /* 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->timer_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 ULONGLONG *t_expire; + volatile ULONGLONG *t_reload; + ULONGLONG expire, reload; + __int64 usecs; + CRITICAL_SECTION *crit; + struct itimer_data *itimer; + + if (disable_itimers) + return -1; + + if (!value) + { + errno = EFAULT; + return -1; + } + + if (which != ITIMER_REAL && which != ITIMER_PROF) + { + errno = EINVAL; + return -1; + } + + itimer = (which == ITIMER_REAL) ? &real_itimer : &prof_itimer; + + ticks_now = w32_get_timer_time ((which == ITIMER_REAL) + ? NULL + : GetCurrentThread ()); + + t_expire = &itimer->expire; + t_reload = &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 / TIMER_TICKS_PER_SEC; + usecs = + (expire % TIMER_TICKS_PER_SEC) * (__int64)1000000 / TIMER_TICKS_PER_SEC; + value->it_value.tv_usec = usecs; + value->it_interval.tv_sec = reload / TIMER_TICKS_PER_SEC; + usecs = + (reload % TIMER_TICKS_PER_SEC) * (__int64)1000000 / TIMER_TICKS_PER_SEC; + value->it_interval.tv_usec= usecs; + return 0; } +int +setitimer(int which, struct itimerval *value, struct itimerval *ovalue) +{ + volatile ULONGLONG *t_expire, *t_reload; + ULONGLONG expire, reload, expire_old, reload_old; + __int64 usecs; + CRITICAL_SECTION *crit; + struct itimerval tem, *ptem; + + if (disable_itimers) + return -1; + + /* 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) + { + ULONGLONG t1, t2; + + for (t1 = w32_get_timer_time (NULL); + (t2 = w32_get_timer_time (NULL)) == t1; ) + ; + clocks_min = t2 - t1; + } + + if (ovalue) + ptem = ovalue; + else + ptem = &tem; + + if (getitimer (which, ptem)) /* also sets ticks_now */ + return -1; /* errno already set */ + + 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 * TIMER_TICKS_PER_SEC; + + usecs = value->it_interval.tv_usec; + if (value->it_interval.tv_sec == 0 + && usecs && usecs * TIMER_TICKS_PER_SEC < clocks_min * 1000000) + reload = clocks_min; + else + { + usecs *= TIMER_TICKS_PER_SEC; + reload += usecs / 1000000; + } + + expire = value->it_value.tv_sec * TIMER_TICKS_PER_SEC; + usecs = value->it_value.tv_usec; + if (value->it_value.tv_sec == 0 + && usecs * TIMER_TICKS_PER_SEC < clocks_min * 1000000) + expire = clocks_min; + else + { + usecs *= TIMER_TICKS_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) +{ +#ifdef HAVE_SETITIMER + struct itimerval new_values, old_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; + + if (setitimer (ITIMER_REAL, &new_values, &old_values) < 0) + return 0; + return old_values.it_value.tv_sec; +#else + return seconds; +#endif +} + /* Defined in which conflicts with the local copy */ #define _P_NOWAIT 1 @@ -575,7 +1227,7 @@ get_result: else if (WIFSIGNALED (retval)) { int code = WTERMSIG (retval); - char *signame; + const char *signame; synchronize_system_messages_locale (); signame = strsignal (code); @@ -650,7 +1302,7 @@ w32_executable_type (char * filename, if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) goto unwind; - nt_header = (PIMAGE_NT_HEADERS) ((char *) dos_header + dos_header->e_lfanew); + nt_header = (PIMAGE_NT_HEADERS) ((unsigned char *) dos_header + dos_header->e_lfanew); if ((char *) nt_header > (char *) dos_header + executable.size) { @@ -1034,7 +1686,7 @@ sys_spawnve (int mode, char *cmdname, char **argv, char **envp) numenv++; } /* extra env vars... */ - sprintf (ppid_env_var_buffer, "EM_PARENT_PROCESS_ID=%d", + sprintf (ppid_env_var_buffer, "EM_PARENT_PROCESS_ID=%lu", GetCurrentProcessId ()); arglen += strlen (ppid_env_var_buffer) + 1; numenv++; @@ -1420,6 +2072,7 @@ find_child_console (HWND hwnd, LPARAM arg) return TRUE; } +/* Emulate 'kill', but only for other processes. */ int sys_kill (int pid, int sig) { @@ -1438,6 +2091,11 @@ sys_kill (int pid, int sig) cp = find_child_pid (pid); if (cp == NULL) { + /* We were passed a PID of something other than our subprocess. + If that is our own PID, we will send to ourself a message to + close the selected frame, which does not necessarily + terminates Emacs. But then we are not supposed to call + sys_kill with our own PID. */ proc_hand = OpenProcess (PROCESS_TERMINATE, 0, pid); if (proc_hand == NULL) {