Bug #77580 [Opn->Fbk]: DeleteTimerQueueTimer() return code
| From: | ab@php.net | Date: | Fri, 08 Feb 2019 19:07:05 +0000 |
| Subject: | Bug #77580 [Opn->Fbk]: DeleteTimerQueueTimer() return code | ||
| References: | 1 | Groups: | php.bugs |
| Request: | Send a blank email to php-bugs+get-219448@lists.php.net to get a copy of this message | ||
Edit report at https://bugs.php.net/bug.php?id=77580&edit=1
ID: 77580
Updated by: ab@php.net
Reported by: oliver dot pfister at contaware dot com
Summary: DeleteTimerQueueTimer() return code
-Status: Open
+Status: Feedback
Type: Bug
Package: Scripting Engine problem
Operating System: Windows
PHP Version: Irrelevant
Block user comment: N
Private report: N
New Comment:
Thanks for the report and detailed analysis. Out of curiosity - I can certainly test Wine, but how
do you run ReactOS? Would it work under with Hyper-V or VMware?
Disregarding of that, we should not pass when ERROR_IO_PENDING is returned. The timer callback sets
flags for Zend VM to recognize the timeout and to properly shutdown. If the timer callback was too
slow and didn't set the flags, chances are that ZVM will continue executing some instructions
where timeout should happen.
The handling could be possibly improved by two things
- use INVALID_HANDLE_VALUE for the completion event + care about thread safety
- repeat the call once when
GetLastError() != ERROR_IO_PENDING
If the timer deletion still fails after that, there's no choice other than dying hard, because
in that case we can't ensure the timeout is handled properly. Would it workout on ReactOS?
Thanks.
Previous Comments:
------------------------------------------------------------------------
[2019-02-07 14:21:52] oliver dot pfister at contaware dot com
Description:
------------
DeleteTimerQueueTimer(HANDLE TimerQueue, HANDLE Timer, HANDLE CompletionEvent) return code problem
in file zend_execute_API.c
Because of vague Microsoft API documentation (see point 3. below) there is an important
implementation difference between Windows and Wine/ReactOS in case of CompletionEvent set to NULL
(PHP uses DeleteTimerQueueTimer with CompletionEvent set to NULL):
Windows (tested on all versions of Windows from XP to Win10):
calling DeleteTimerQueueTimer returns TRUE if the callback code is not yet executing, then when the
thread (of the pool) enters the callback code, calling DeleteTimerQueueTimer returns FALSE with
ERROR_IO_PENDING. When the thread (of the pool) has terminated execution of the callback code,
calling DeleteTimerQueueTimer returns again TRUE.
ReactOS/Wine (tested on latest versions of Wine and ReactOS):
calling DeleteTimerQueueTimer always returns FALSE with ERROR_IO_PENDING.
Important points of the DeleteTimerQueueTimer Microsoft Windows documentation
(https://msdn.microsoft.com/en-us/library/windows/desktop/ms682569(v=vs.85).aspx):
1. If CompletionEvent is NULL, the function marks the timer for deletion and returns immediately. If
the timer has already expired, the timer callback function will run to completion.
2. If the error code is ERROR_IO_PENDING, it is not necessary to call this function again. For any
other error, you should retry the call.
3. If there are outstanding callback functions and CompletionEvent is NULL, the function will fail
and set the error code to ERROR_IO_PENDING. This indicates that there are outstanding callback
functions. Those callbacks either will execute or are in the middle of executing. The timer is
cleaned up when the callback function is finished executing.
As a result in Wine/ReactOS it's not possible to use any PHP, it fails with a fatal error. In
Windows there is the possibility that the timer is deleted exactly when a thread (of the pool) is
executing the callback, in that case a PHP fatal error would be risen for no good reason. So I
propose to correct the PHP code and always check GetLastError() making sure it is not returning
ERROR_IO_PENDING before failing.
Test script:
---------------
In zend_set_timeout_ex() and zend_unset_timeout() please correct the return code check from:
if (!DeleteTimerQueueTimer(NULL, tq_timer, NULL)) {
to:
if (!DeleteTimerQueueTimer(NULL, tq_timer, NULL) && GetLastError() != ERROR_IO_PENDING) {
------------------------------------------------------------------------
--
Edit this bug report at https://bugs.php.net/bug.php?id=77580&edit=1