This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
fix over/underflow issues in win32_msgwait
authorDaniel Dragan <bulk88@hotmail.com>
Tue, 18 Sep 2012 08:17:21 +0000 (04:17 -0400)
committerSteve Hay <steve.m.hay@googlemail.com>
Tue, 18 Sep 2012 16:34:55 +0000 (17:34 +0100)
This commit does not completely fix 33096, since the real problem is that
VS IDE's debugger causes a Windows message to be sent to perl on each
breakpoint. Depending on where the breakpoint was set. The BP may make it
impossible to exit the loop because of the Visual Studio IDE Debugger's
design. Various overflow and underflow issues were fixed in win32_msgwait.
Specifically, the time count rolling forwards through zero
(GetSystemTimeAsFileTime), and the time count running ahead of the end
time (rolling backwards through zero) were fixed ("<=" check).

pod/perldelta.pod
pod/perlport.pod
win32/win32.c

index 7d3ef03..a06ea12 100644 (file)
@@ -643,6 +643,9 @@ Fixed a problem where perl could crash while cleaning up threads (including the
 main thread) in threaded debugging builds on Win32 and possibly other platforms
 [perl #114496].
 
+A rare race condition that would lead to L<sleep|perlfunc/sleep>
+taking more time than requested, and upto a hang has been fixed [perl #33096].
+
 =item Solaris
 
 In Configure, avoid running sed commands with flags not supported on Solaris.
index a37bc7c..839468b 100644 (file)
@@ -1921,6 +1921,12 @@ Not implemented. (S<Plan 9>)
 
 Not implemented. (Win32, VMS, S<RISC OS>, VOS)
 
+=item sleep
+
+On Win32 C<sleep> is limited to a maximum of 4294967 seconds, approximately 49
+days, and also signals do not cause C<sleep> to return early once a signal
+fires. C<sleep> will always wait the full time period before returning.
+
 =item sockatmark
 
 A relatively recent addition to socket functions, may not
index c2ad58f..47127d9 100644 (file)
@@ -2217,13 +2217,32 @@ DllExport DWORD
 win32_msgwait(pTHX_ DWORD count, LPHANDLE handles, DWORD timeout, LPDWORD resultp)
 {
     /* We may need several goes at this - so compute when we stop */
-    DWORD ticks = 0;
+    FT_t ticks = {0};
+    unsigned __int64 endtime = timeout;
     if (timeout != INFINITE) {
-       ticks = GetTickCount();
-       timeout += ticks;
-    }
-    while (1) {
-       DWORD result = MsgWaitForMultipleObjects(count,handles,FALSE,timeout-ticks, QS_POSTMESSAGE|QS_TIMER|QS_SENDMESSAGE);
+       GetSystemTimeAsFileTime(&ticks.ft_val);
+       ticks.ft_i64 /= 10000;
+       endtime += ticks.ft_i64;
+    }
+    /* This was a race condition. Do not let a non INFINITE timeout to
+     * MsgWaitForMultipleObjects roll under 0 creating a near
+     * infinity/~(UINT32)0 timeout which will appear as a deadlock to the
+     * user who did a CORE perl function with a non infinity timeout,
+     * sleep for example.  This is 64 to 32 truncation minefield.
+     *
+     * This scenario can only be created if the timespan from the return of
+     * MsgWaitForMultipleObjects to GetSystemTimeAsFileTime exceeds 1 ms. To
+     * generate the scenario, manual breakpoints in a C debugger are required,
+     * or a context switch occured in win32_async_check in PeekMessage, or random
+     * messages are delivered to the *thread* message queue of the Perl thread
+     * from another process (msctf.dll doing IPC among its instances, VS debugger
+     * causes msctf.dll to be loaded into Perl by kernel), see [perl #33096].
+     */
+    while (ticks.ft_i64 <= endtime) {
+    /* if timeout's type is lengthened, remember to split 64b timeout
+     * into multiple non-infinity runs of MWFMO */
+       DWORD result = MsgWaitForMultipleObjects(count,handles,FALSE,(DWORD)(endtime-ticks.ft_i64)
+                                            , QS_POSTMESSAGE|QS_TIMER|QS_SENDMESSAGE);
        if (resultp)
           *resultp = result;
        if (result == WAIT_TIMEOUT) {
@@ -2233,8 +2252,9 @@ win32_msgwait(pTHX_ DWORD count, LPHANDLE handles, DWORD timeout, LPDWORD result
            return 0;
        }
        if (timeout != INFINITE) {
-           ticks = GetTickCount();
-        }
+           GetSystemTimeAsFileTime(&ticks.ft_val);
+           ticks.ft_i64 /= 10000;
+       }
        if (result == WAIT_OBJECT_0 + count) {
            /* Message has arrived - check it */
            (void)win32_async_check(aTHX);
@@ -2244,10 +2264,13 @@ win32_msgwait(pTHX_ DWORD count, LPHANDLE handles, DWORD timeout, LPDWORD result
           break;
        }
     }
-    /* compute time left to wait */
-    ticks = timeout - ticks;
     /* If we are past the end say zero */
-    return (ticks > 0) ? ticks : 0;
+    if(!ticks.ft_i64 || ticks.ft_i64 > endtime)
+       return 0;
+    /* compute time left to wait */
+    ticks.ft_i64 = endtime - ticks.ft_i64;
+    /*if more ms than DWORD, then return max DWORD*/
+    return ticks.ft_i64 <= UINT_MAX ? (DWORD)ticks.ft_i64:UINT_MAX;
 }
 
 int