From: Jan Dubois Date: Fri, 16 Dec 2005 07:12:00 +0000 (+0000) Subject: RE: PeekMessage() call in win32\win32.c win32_async_check X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=aeecf691f59fe1423b7011655dd5de7d5fbd2192;p=p5sagit%2Fp5-mst-13.2.git RE: PeekMessage() call in win32\win32.c win32_async_check Message-ID: <015901c60207$abd64210$d563a8c0@candy> (Without the proposed alarm.t test) p4raw-id: //depot/perl@26379 --- diff --git a/t/op/fork.t b/t/op/fork.t index 4300894..588d88b 100755 --- a/t/op/fork.t +++ b/t/op/fork.t @@ -97,6 +97,24 @@ ok 1 ok 2 ######## $| = 1; +if ($cid = fork) { + sleep 1; + print "not " unless kill 'INT', $cid; + print "ok 2\n"; +} +else { + # XXX On Windows the default signal handler kills the + # XXX whole process, not just the thread (pseudo-process) + $SIG{INT} = sub { exit }; + print "ok 1\n"; + sleep 5; + die; +} +EXPECT +ok 1 +ok 2 +######## +$| = 1; sub forkit { print "iteration $i start\n"; my $x = fork; diff --git a/win32/perlhost.h b/win32/perlhost.h index 92d5757..e2227e3 100644 --- a/win32/perlhost.h +++ b/win32/perlhost.h @@ -1693,6 +1693,7 @@ win32_start_child(LPVOID arg) PerlInterpreter *my_perl = (PerlInterpreter*)arg; GV *tmpgv; int status; + HWND parent_message_hwnd; #ifdef PERL_SYNC_FORK static long sync_fork_id = 0; long id = ++sync_fork_id; @@ -1723,6 +1724,12 @@ win32_start_child(LPVOID arg) hv_clear(PL_pidstatus); #endif + /* create message window and tell parent about it */ + parent_message_hwnd = w32_message_hwnd; + w32_message_hwnd = win32_create_message_window(); + if (parent_message_hwnd != NULL) + PostMessage(parent_message_hwnd, WM_USER_MESSAGE, w32_pseudo_id, (LONG)w32_message_hwnd); + /* push a zero on the stack (we are the child) */ { dSP; @@ -1826,6 +1833,11 @@ PerlProcFork(struct IPerlProc* piPerl) id = win32_start_child((LPVOID)new_perl); PERL_SET_THX(aTHX); # else + if (w32_message_hwnd == INVALID_HANDLE_VALUE) + w32_message_hwnd = win32_create_message_window(); + new_perl->Isys_intern.message_hwnd = w32_message_hwnd; + w32_pseudo_child_message_hwnds[w32_num_pseudo_children] = + (w32_message_hwnd == NULL) ? NULL : INVALID_HANDLE_VALUE; # ifdef USE_RTL_THREAD_API handle = (HANDLE)_beginthreadex((void*)NULL, 0, win32_start_child, (void*)new_perl, 0, (unsigned*)&id); diff --git a/win32/win32.c b/win32/win32.c index 56b5da1..b09ae94 100644 --- a/win32/win32.c +++ b/win32/win32.c @@ -15,6 +15,9 @@ #define Win32_Winsock #endif #include +#ifndef HWND_MESSAGE +# define HWND_MESSAGE ((HWND)-3) +#endif /* GCC-2.95.2/Mingw32-1.1 forgot the WINAPI on CommandLineToArgvW() */ #if defined(__MINGW32__) && (__MINGW32_MAJOR_VERSION==1) # include @@ -113,7 +116,7 @@ HANDLE w32_perldll_handle = INVALID_HANDLE_VALUE; char w32_module_name[MAX_PATH+1]; END_EXTERN_C -static DWORD w32_platform = (DWORD)-1; +static OSVERSIONINFO g_osver = {0, 0, 0, 0, 0, ""}; #define ONE_K_BUFSIZE 1024 @@ -130,13 +133,13 @@ _matherr(struct _exception *a) int IsWin95(void) { - return (win32_os_id() == VER_PLATFORM_WIN32_WINDOWS); + return (g_osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS); } int IsWinNT(void) { - return (win32_os_id() == VER_PLATFORM_WIN32_NT); + return (g_osver.dwPlatformId == VER_PLATFORM_WIN32_NT); } EXTERN_C void @@ -426,15 +429,7 @@ Perl_my_pclose(pTHX_ PerlIO *fp) DllExport unsigned long win32_os_id(void) { - static OSVERSIONINFO osver; - - if (osver.dwPlatformId != w32_platform) { - memset(&osver, 0, sizeof(OSVERSIONINFO)); - osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - GetVersionEx(&osver); - w32_platform = osver.dwPlatformId; - } - return (unsigned long)w32_platform; + return (unsigned long)g_osver.dwPlatformId; } DllExport int @@ -1038,6 +1033,8 @@ remove_dead_pseudo_process(long child) (w32_num_pseudo_children-child-1), HANDLE); Move(&w32_pseudo_child_pids[child+1], &w32_pseudo_child_pids[child], (w32_num_pseudo_children-child-1), DWORD); + Move(&w32_pseudo_child_message_hwnds[child+1], &w32_pseudo_child_message_hwnds[child], + (w32_num_pseudo_children-child-1), HWND); w32_num_pseudo_children--; } } @@ -1055,11 +1052,13 @@ win32_kill(int pid, int sig) /* it is a pseudo-forked child */ child = find_pseudo_pid(-pid); if (child >= 0) { + HWND hwnd = w32_pseudo_child_message_hwnds[child]; hProcess = w32_pseudo_child_handles[child]; switch (sig) { case 0: /* "Does process exist?" use of kill */ return 0; + case 9: /* kill -9 style un-graceful exit */ if (TerminateThread(hProcess, sig)) { @@ -1067,16 +1066,30 @@ win32_kill(int pid, int sig) return 0; } break; - default: - /* We fake signals to pseudo-processes using Win32 - * message queue. In Win9X the pids are negative already. */ - if (PostThreadMessage(IsWin95() ? pid : -pid,WM_USER,sig,0)) { - /* It might be us ... */ - PERL_ASYNC_CHECK(); - return 0; - } + + default: { + int count = 0; + /* pseudo-process has not yet properly initialized if hwnd isn't set */ + while (hwnd == INVALID_HANDLE_VALUE && count < 5) { + /* Yield and wait for the other thread to send us its message_hwnd */ + Sleep(0); + win32_async_check(aTHX); + ++count; + } + if (hwnd != INVALID_HANDLE_VALUE) { + /* We fake signals to pseudo-processes using Win32 + * message queue. In Win9X the pids are negative already. */ + if ((hwnd != NULL && PostMessage(hwnd, WM_USER_KILL, sig, 0)) || + PostThreadMessage(IsWin95() ? pid : -pid, WM_USER_KILL, sig, 0)) + { + /* It might be us ... */ + PERL_ASYNC_CHECK(); + return 0; + } + } break; } + } /* switch */ } else if (IsWin95()) { pid = -pid; @@ -1585,44 +1598,34 @@ win32_uname(struct utsname *name) { struct hostent *hep; STRLEN nodemax = sizeof(name->nodename)-1; - OSVERSIONINFO osver; - - memset(&osver, 0, sizeof(OSVERSIONINFO)); - osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - if (GetVersionEx(&osver)) { - /* sysname */ - switch (osver.dwPlatformId) { - case VER_PLATFORM_WIN32_WINDOWS: - strcpy(name->sysname, "Windows"); - break; - case VER_PLATFORM_WIN32_NT: - strcpy(name->sysname, "Windows NT"); - break; - case VER_PLATFORM_WIN32s: - strcpy(name->sysname, "Win32s"); - break; - default: - strcpy(name->sysname, "Win32 Unknown"); - break; - } - /* release */ - sprintf(name->release, "%d.%d", - osver.dwMajorVersion, osver.dwMinorVersion); - - /* version */ - sprintf(name->version, "Build %d", - osver.dwPlatformId == VER_PLATFORM_WIN32_NT - ? osver.dwBuildNumber : (osver.dwBuildNumber & 0xffff)); - if (osver.szCSDVersion[0]) { - char *buf = name->version + strlen(name->version); - sprintf(buf, " (%s)", osver.szCSDVersion); - } + /* sysname */ + switch (g_osver.dwPlatformId) { + case VER_PLATFORM_WIN32_WINDOWS: + strcpy(name->sysname, "Windows"); + break; + case VER_PLATFORM_WIN32_NT: + strcpy(name->sysname, "Windows NT"); + break; + case VER_PLATFORM_WIN32s: + strcpy(name->sysname, "Win32s"); + break; + default: + strcpy(name->sysname, "Win32 Unknown"); + break; } - else { - *name->sysname = '\0'; - *name->version = '\0'; - *name->release = '\0'; + + /* release */ + sprintf(name->release, "%d.%d", + g_osver.dwMajorVersion, g_osver.dwMinorVersion); + + /* version */ + sprintf(name->version, "Build %d", + g_osver.dwPlatformId == VER_PLATFORM_WIN32_NT + ? g_osver.dwBuildNumber : (g_osver.dwBuildNumber & 0xffff)); + if (g_osver.szCSDVersion[0]) { + char *buf = name->version + strlen(name->version); + sprintf(buf, " (%s)", g_osver.szCSDVersion); } /* nodename */ @@ -1757,65 +1760,60 @@ DllExport int win32_async_check(pTHX) { MSG msg; - int ours = 1; + HWND hwnd = w32_message_hwnd; + + w32_poll_count = 0; + + if (hwnd == INVALID_HANDLE_VALUE) + return 1; + /* Passing PeekMessage -1 as HWND (2nd arg) only get PostThreadMessage() messages * and ignores window messages - should co-exist better with windows apps e.g. Tk */ - while (PeekMessage(&msg, (HWND)-1, 0, 0, PM_REMOVE|PM_NOYIELD)) { - int sig; - switch(msg.message) { + if (hwnd == NULL) + hwnd = (HWND)-1; -#if 0 - /* Perhaps some other messages could map to signals ? ... */ - case WM_CLOSE: - case WM_QUIT: - /* Treat WM_QUIT like SIGHUP? */ - sig = SIGHUP; - goto Raise; - break; + while (PeekMessage(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE|PM_NOYIELD) || + PeekMessage(&msg, hwnd, WM_USER_MIN, WM_USER_MAX, PM_REMOVE|PM_NOYIELD)) + { + switch (msg.message) { +#ifdef USE_ITHREADS + case WM_USER_MESSAGE: { + int child = find_pseudo_pid(msg.wParam); + if (child >= 0) + w32_pseudo_child_message_hwnds[child] = (HWND)msg.lParam; + break; + } #endif - /* We use WM_USER to fake kill() with other signals */ - case WM_USER: { - sig = msg.wParam; - Raise: - if (do_raise(aTHX_ sig)) { - sig_terminate(aTHX_ sig); - } + case WM_USER_KILL: { + /* We use WM_USER to fake kill() with other signals */ + int sig = msg.wParam; + if (do_raise(aTHX_ sig)) + sig_terminate(aTHX_ sig); break; } case WM_TIMER: { /* alarm() is a one-shot but SetTimer() repeats so kill it */ if (w32_timerid && w32_timerid==msg.wParam) { - KillTimer(NULL,w32_timerid); + KillTimer(w32_message_hwnd, w32_timerid); w32_timerid=0; - } - else - goto FallThrough; - /* Now fake a call to signal handler */ - if (do_raise(aTHX_ 14)) { - sig_terminate(aTHX_ 14); - } - break; - } - /* Otherwise do normal Win32 thing - in case it is useful */ - default: - FallThrough: - TranslateMessage(&msg); - DispatchMessage(&msg); - ours = 0; + /* Now fake a call to signal handler */ + if (do_raise(aTHX_ 14)) + sig_terminate(aTHX_ 14); + } break; } + } /* switch */ } - w32_poll_count = 0; /* Above or other stuff may have set a signal flag */ if (PL_sig_pending) { despatch_signals(); } - return ours; + return 1; } /* This function will not return until the timeout has elapsed, or until @@ -2028,13 +2026,22 @@ win32_alarm(unsigned int sec) * one of the supported codes in */ dTHX; + + if (w32_message_hwnd == INVALID_HANDLE_VALUE) + w32_message_hwnd = win32_create_message_window(); + if (sec) { - w32_timerid = SetTimer(NULL,w32_timerid,sec*1000,NULL); + if (w32_message_hwnd == NULL) + w32_timerid = SetTimer(NULL, w32_timerid, sec*1000, NULL); + else { + w32_timerid = 1; + SetTimer(w32_message_hwnd, w32_timerid, sec*1000, NULL); + } } else { if (w32_timerid) { - KillTimer(NULL,w32_timerid); - w32_timerid=0; + KillTimer(w32_message_hwnd, w32_timerid); + w32_timerid = 0; } } return 0; @@ -4881,10 +4888,33 @@ win32_csighandler(int sig) /* Does nothing */ } +HWND +win32_create_message_window() +{ + /* "message-only" windows have been implemented in Windows 2000 and later. + * On earlier versions we'll continue to post messages to a specific + * thread and use hwnd==NULL. This is brittle when either an embedding + * application or an XS module is also posting messages to hwnd=NULL + * because once removed from the queue they cannot be delivered to the + * "right" place with DispatchMessage() anymore, as there is no WindowProc + * if there is no window handle. + */ + if (g_osver.dwMajorVersion < 5) + return NULL; + + return CreateWindow("Static", "", 0, 0, 0, 0, 0, HWND_MESSAGE, 0, 0, NULL); +} + void Perl_sys_intern_init(pTHX) { int i; + + if (g_osver.dwOSVersionInfoSize == 0) { + g_osver.dwOSVersionInfoSize = sizeof(g_osver); + GetVersionEx(&g_osver); + } + w32_perlshell_tokens = Nullch; w32_perlshell_vec = (char**)NULL; w32_perlshell_items = 0; @@ -4893,10 +4923,11 @@ Perl_sys_intern_init(pTHX) w32_num_children = 0; # ifdef USE_ITHREADS w32_pseudo_id = 0; - Newx(w32_pseudo_children, 1, child_tab); + Newx(w32_pseudo_children, 1, pseudo_child_tab); w32_num_pseudo_children = 0; # endif w32_timerid = 0; + w32_message_hwnd = INVALID_HANDLE_VALUE; w32_poll_count = 0; for (i=0; i < SIG_SIZE; i++) { w32_sighandler[i] = SIG_DFL; @@ -4922,9 +4953,11 @@ Perl_sys_intern_clear(pTHX) /* NOTE: w32_fdpid is freed by sv_clean_all() */ Safefree(w32_children); if (w32_timerid) { - KillTimer(NULL,w32_timerid); - w32_timerid=0; + KillTimer(w32_message_hwnd, w32_timerid); + w32_timerid = 0; } + if (w32_message_hwnd != NULL && w32_message_hwnd != INVALID_HANDLE_VALUE) + DestroyWindow(w32_message_hwnd); # ifdef MULTIPLICITY if (my_perl == PL_curinterp) { # else @@ -4948,9 +4981,10 @@ Perl_sys_intern_dup(pTHX_ struct interp_intern *src, struct interp_intern *dst) dst->fdpid = newAV(); Newxz(dst->children, 1, child_tab); dst->pseudo_id = 0; - Newxz(dst->pseudo_children, 1, child_tab); - dst->timerid = 0; - dst->poll_count = 0; + Newxz(dst->pseudo_children, 1, pseudo_child_tab); + dst->timerid = 0; + w32_message_hwnd = INVALID_HANDLE_VALUE; + dst->poll_count = 0; Copy(src->sigtable,dst->sigtable,SIG_SIZE,Sighandler_t); } # endif /* USE_ITHREADS */ diff --git a/win32/win32.h b/win32/win32.h index 8f5b27d..f318890 100644 --- a/win32/win32.h +++ b/win32/win32.h @@ -304,6 +304,7 @@ typedef struct { } child_IO_table; DllExport void win32_get_child_IO(child_IO_table* ptr); +DllExport HWND win32_create_message_window(); #ifndef USE_SOCKETS_AS_HANDLES extern FILE * my_fdopen(int, char *); @@ -353,6 +354,16 @@ typedef char * caddr_t; /* In malloc.c (core address). */ * Now Win32 specific per-thread data stuff */ +/* Leave the first couple ids after WM_USER unused because they + * might be used by an embedding application, and on Windows + * version before 2000 we might end up eating those messages + * if they were not meant for us. + */ +#define WM_USER_MIN (WM_USER+30) +#define WM_USER_MESSAGE (WM_USER_MIN) +#define WM_USER_KILL (WM_USER_MIN+1) +#define WM_USER_MAX (WM_USER_MIN+1) + struct thread_intern { /* XXX can probably use one buffer instead of several */ char Wstrerror_buffer[512]; @@ -375,6 +386,15 @@ typedef struct { HANDLE handles[MAXIMUM_WAIT_OBJECTS]; } child_tab; +#ifdef USE_ITHREADS +typedef struct { + long num; + DWORD pids[MAXIMUM_WAIT_OBJECTS]; + HANDLE handles[MAXIMUM_WAIT_OBJECTS]; + HWND message_hwnds[MAXIMUM_WAIT_OBJECTS]; +} pseudo_child_tab; +#endif + #ifndef Sighandler_t typedef Signal_t (*Sighandler_t) (int); #define Sighandler_t Sighandler_t @@ -388,10 +408,11 @@ struct interp_intern { child_tab * children; #ifdef USE_ITHREADS DWORD pseudo_id; - child_tab * pseudo_children; + pseudo_child_tab * pseudo_children; #endif void * internal_host; struct thread_intern thr_intern; + HWND message_hwnd; UINT timerid; unsigned poll_count; Sighandler_t sigtable[SIG_SIZE]; @@ -415,8 +436,10 @@ DllExport int win32_async_check(pTHX); #define w32_num_pseudo_children (w32_pseudo_children->num) #define w32_pseudo_child_pids (w32_pseudo_children->pids) #define w32_pseudo_child_handles (w32_pseudo_children->handles) +#define w32_pseudo_child_message_hwnds (w32_pseudo_children->message_hwnds) #define w32_internal_host (PL_sys_intern.internal_host) #define w32_timerid (PL_sys_intern.timerid) +#define w32_message_hwnd (PL_sys_intern.message_hwnd) #define w32_sighandler (PL_sys_intern.sigtable) #define w32_poll_count (PL_sys_intern.poll_count) #define w32_do_async (w32_poll_count++ > WIN32_POLL_INTERVAL)