winmm: Use fd's to synchronize timers
Maarten Lankhorst
m.b.lankhorst at gmail.com
Sat Jun 2 09:07:23 CDT 2007
Use fd's for timers, for better kernel scheduling.
-------------- next part --------------
>From b00faabf1c7d2f36855892c63ecc573e313a9320 Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <m.b.lankhorst at gmail.com>
Date: Sat, 2 Jun 2007 15:49:21 +0200
Subject: [PATCH] winmm: Use fd's to synchronize timers
---
dlls/winmm/time.c | 233 +++++++++++++++++++++++++++++++---------------------
1 files changed, 139 insertions(+), 94 deletions(-)
diff --git a/dlls/winmm/time.c b/dlls/winmm/time.c
index 27a4e46..ce89bce 100644
--- a/dlls/winmm/time.c
+++ b/dlls/winmm/time.c
@@ -4,6 +4,7 @@
* MMSYSTEM time functions
*
* Copyright 1993 Martin Ayotte
+ * Copyright 2007 Maarten Lankhorst
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -20,6 +21,8 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
+/* Wine <= 0.9.38 uses win32 handles instead of unix pipes to sync, for those who are interested */
+
#include "config.h"
#include "wine/port.h"
@@ -32,6 +35,14 @@
# include <unistd.h>
#endif
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#endif
+#ifdef HAVE_SYS_POLL_H
+#include <sys/poll.h>
+#endif
+#include <errno.h>
+
#include "windef.h"
#include "winbase.h"
#include "mmsystem.h"
@@ -40,13 +51,15 @@
#include "wine/debug.h"
+#define TIME_DESTROY 0x0200
+
WINE_DEFAULT_DEBUG_CHANNEL(mmtime);
static HANDLE TIME_hMMTimer;
static LPWINE_TIMERENTRY TIME_TimersList;
-static HANDLE TIME_hWakeEvent;
static CRITICAL_SECTION TIME_cbcrst;
static BOOL TIME_TimeToDie = TRUE;
+static int TIME_fdWake[2] = { -1, -1 };
/*
* Some observations on the behavior of winmm on Windows.
@@ -77,8 +90,7 @@ static BOOL TIME_TimeToDie = TRUE;
#define MMSYSTIME_MININTERVAL (1)
#define MMSYSTIME_MAXINTERVAL (65535)
-
-static void TIME_TriggerCallBack(LPWINE_TIMERENTRY lpTimer)
+static void TIME_TriggerCallBack(LPWINE_MM_IDATA iData, LPWINE_TIMERENTRY lpTimer)
{
TRACE("%04x:CallBack => lpFunc=%p wTimerID=%04X dwUser=%08X dwTriggerTime %d(delta %d)\n",
GetCurrentThreadId(), lpTimer->lpFunc, lpTimer->wTimerID, lpTimer->dwUser,
@@ -89,13 +101,17 @@ static void TIME_TriggerCallBack(LPWINE_TIMERENTRY lpTimer)
* PostMessage), and must reside in DLL (therefore uses stack of active application). So I
* guess current implementation via SetTimer has to be improved upon.
*/
+ EnterCriticalSection(&TIME_cbcrst);
switch (lpTimer->wFlags & 0x30) {
case TIME_CALLBACK_FUNCTION:
if (lpTimer->wFlags & WINE_TIMER_IS32)
(lpTimer->lpFunc)(lpTimer->wTimerID, 0, lpTimer->dwUser, 0, 0);
else if (pFnCallMMDrvFunc16)
- pFnCallMMDrvFunc16((DWORD)lpTimer->lpFunc, lpTimer->wTimerID, 0,
- lpTimer->dwUser, 0, 0);
+ {
+ LeaveCriticalSection(&iData->cs);
+ pFnCallMMDrvFunc16((DWORD)lpTimer->lpFunc, lpTimer->wTimerID, 0, lpTimer->dwUser, 0, 0);
+ EnterCriticalSection(&iData->cs);
+ }
break;
case TIME_CALLBACK_EVENT_SET:
SetEvent((HANDLE)lpTimer->lpFunc);
@@ -108,73 +124,52 @@ static void TIME_TriggerCallBack(LPWINE_TIMERENTRY lpTimer)
lpTimer->wFlags, lpTimer->lpFunc);
break;
}
+ LeaveCriticalSection(&TIME_cbcrst);
}
/**************************************************************************
* TIME_MMSysTimeCallback
*/
-static DWORD CALLBACK TIME_MMSysTimeCallback(LPWINE_MM_IDATA iData)
+static int CALLBACK TIME_MMSysTimeCallback(LPWINE_MM_IDATA iData)
{
-static int nSizeLpTimers;
-static LPWINE_TIMERENTRY lpTimers;
-
- LPWINE_TIMERENTRY timer, *ptimer, *next_ptimer;
- int idx;
- DWORD cur_time;
- DWORD delta_time;
- DWORD ret_time = INFINITE;
- DWORD adjust_time;
-
+ LPWINE_TIMERENTRY timer, *ptimer, *next_ptimer;
+ DWORD cur_time, delta_time, ret_time = INFINITE, adjust_time;
/* optimize for the most frequent case - no events */
if (! TIME_TimersList)
- return(ret_time);
-
- /* since timeSetEvent() and timeKillEvent() can be called
- * from 16 bit code, there are cases where win16 lock is
- * locked upon entering timeSetEvent(), and then the mm timer
- * critical section is locked. This function cannot call the
- * timer callback with the crit sect locked (because callback
- * may need to acquire Win16 lock, thus providing a deadlock
- * situation).
- * To cope with that, we just copy the WINE_TIMERENTRY struct
- * that need to trigger the callback, and call it without the
- * mm timer crit sect locked.
- * the hKillTimeEvent is used to mark the section where we
- * handle the callbacks so we can do synchronous kills.
- * EPP 99/07/13, updated 04/01/10
- */
- idx = 0;
+ return -1;
+
cur_time = GetTickCount();
EnterCriticalSection(&iData->cs);
for (ptimer = &TIME_TimersList; *ptimer != NULL; ) {
timer = *ptimer;
next_ptimer = &timer->lpNext;
- if (cur_time >= timer->dwTriggerTime)
+ if (timer->wFlags & TIME_DESTROY)
+ { /* Do nothing here, more down here.. */ }
+ else if (cur_time >= timer->dwTriggerTime)
{
- if (timer->lpFunc) {
- if (idx == nSizeLpTimers) {
- if (lpTimers)
- lpTimers = (LPWINE_TIMERENTRY)
- HeapReAlloc(GetProcessHeap(), 0, lpTimers,
- ++nSizeLpTimers * sizeof(WINE_TIMERENTRY));
- else
- lpTimers = (LPWINE_TIMERENTRY)
- HeapAlloc(GetProcessHeap(), 0,
- ++nSizeLpTimers * sizeof(WINE_TIMERENTRY));
- }
- lpTimers[idx++] = *timer;
-
- }
+ /* I'm assuming that 32 bits code isn't allowed to call timeSetEvent or timeKillEvent.
+ * Basically the we hold winmm idata critsect, except when calling 16 bits code, then
+ * it holds the kill critical section.
+ *
+ * If someone calls TimeKillEvent, it will try to obtain the winmm idata lock and then
+ * possibly the kill lock. If it can obtain kill lock it just destroys the timer. If
+ * it fails to obtain the kill lock it will either spin if it is meant to kill
+ * synchronously, or set a flag that the thread should destroy it.
+ */
+ if (timer->lpFunc)
+ TIME_TriggerCallBack(iData, timer);
/* Update the time after we make the copy to preserve
the original trigger time */
timer->dwTriggerTime += timer->wDelay;
- /* TIME_ONESHOT is defined as 0 */
- if (!(timer->wFlags & TIME_PERIODIC))
+ if (timer->wFlags & TIME_DESTROY)
+ { /* Do nothing here, more down here.. */ }
+ else if (!(timer->wFlags & TIME_PERIODIC))
{
+ /* TIME_ONESHOT is defined as 0 */
/* unlink timer from timers list */
*ptimer = *next_ptimer;
HeapFree(GetProcessHeap(), 0, timer);
@@ -195,29 +190,32 @@ static LPWINE_TIMERENTRY lpTimers;
else
delta_time = timer->dwTriggerTime - cur_time;
+ if (timer->wFlags & TIME_DESTROY)
+ {
+ *ptimer = *next_ptimer;
+ HeapFree(GetProcessHeap(), 0, timer);
+ }
+ else
/* Determine when we need to return to this function */
- ret_time = min(ret_time, delta_time);
+ ret_time = min(ret_time, delta_time);
ptimer = next_ptimer;
}
LeaveCriticalSection(&iData->cs);
- EnterCriticalSection(&TIME_cbcrst);
- while (idx > 0) TIME_TriggerCallBack(&lpTimers[--idx]);
- LeaveCriticalSection(&TIME_cbcrst);
+ if (ret_time == INFINITE)
+ return -1;
/* Finally, adjust the recommended wait time downward
by the amount of time the processing routines
actually took */
adjust_time = GetTickCount() - cur_time;
if (adjust_time > ret_time)
- ret_time = 0;
- else
- ret_time -= adjust_time;
+ return 0;
/* We return the amount of time our caller should sleep
before needing to check in on us again */
- return(ret_time);
+ return ret_time - adjust_time;
}
/**************************************************************************
@@ -226,8 +224,13 @@ static LPWINE_TIMERENTRY lpTimers;
static DWORD CALLBACK TIME_MMSysTimeThread(LPVOID arg)
{
LPWINE_MM_IDATA iData = (LPWINE_MM_IDATA)arg;
- DWORD sleep_time;
- DWORD rc;
+ int sleep_time;
+ char readme[16];
+ int ret;
+
+ struct pollfd pfd;
+ pfd.fd = TIME_fdWake[0];
+ pfd.events = POLLIN;
TRACE("Starting main winmm thread\n");
@@ -239,18 +242,28 @@ static DWORD CALLBACK TIME_MMSysTimeThread(LPVOID arg)
while (! TIME_TimeToDie)
{
- sleep_time = TIME_MMSysTimeCallback(iData);
+ /* Sleep time should always be < 65536 anyway, safe to convert */
+ sleep_time = TIME_MMSysTimeCallback(iData);
if (sleep_time == 0)
continue;
- rc = WaitForSingleObject(TIME_hWakeEvent, sleep_time);
- if (rc != WAIT_TIMEOUT && rc != WAIT_OBJECT_0)
- {
- FIXME("Unexpected error %d(%d) in timer thread\n", rc, GetLastError());
- break;
+ while ((ret = poll(&pfd, 1, sleep_time)) < 0)
+ {
+ if (errno != EINTR && errno != EAGAIN)
+ {
+ ERR("Unexpected error in poll: %s(%d)\n", strerror(errno), errno);
+ goto end;
+ }
}
+
+ if (ret > 0)
+ do {
+ ret = read(TIME_fdWake[0], readme, sizeof(*readme));
+ } while (ret > 0);
}
+
+ end:
TRACE("Exiting main winmm thread\n");
return 0;
}
@@ -261,10 +274,21 @@ static DWORD CALLBACK TIME_MMSysTimeThread(LPVOID arg)
void TIME_MMTimeStart(void)
{
if (!TIME_hMMTimer) {
- TIME_TimersList = NULL;
- TIME_hWakeEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
+ TIME_TimersList = NULL;
+
+ if (pipe(TIME_fdWake) < 0)
+ {
+ TIME_fdWake[0] = TIME_fdWake[1] = -1;
+ ERR("Cannot create pipe: %s\n", strerror(errno));
+ }
+ else
+ {
+ fcntl(TIME_fdWake[0], F_SETFL, O_NONBLOCK);
+ fcntl(TIME_fdWake[1], F_SETFL, O_NONBLOCK);
+ }
+
TIME_TimeToDie = FALSE;
- TIME_hMMTimer = CreateThread(NULL, 0, TIME_MMSysTimeThread, &WINMM_IData, 0, NULL);
+ TIME_hMMTimer = CreateThread(NULL, 0, TIME_MMSysTimeThread, &WINMM_IData, 0, NULL);
SetThreadPriority(TIME_hMMTimer, THREAD_PRIORITY_TIME_CRITICAL);
InitializeCriticalSection(&TIME_cbcrst);
TIME_cbcrst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": WINMM.TIME_cbcrst");
@@ -277,16 +301,18 @@ void TIME_MMTimeStart(void)
void TIME_MMTimeStop(void)
{
if (TIME_hMMTimer) {
-
+ const char a='a';
TIME_TimeToDie = TRUE;
- SetEvent(TIME_hWakeEvent);
+ write(TIME_fdWake[1], &a, sizeof(a));
/* FIXME: in the worst case, we're going to wait 65 seconds here :-( */
- WaitForSingleObject(TIME_hMMTimer, INFINITE);
+ WaitForSingleObject(TIME_hMMTimer, INFINITE);
- CloseHandle(TIME_hMMTimer);
- CloseHandle(TIME_hWakeEvent);
- TIME_hMMTimer = 0;
+ CloseHandle(TIME_hMMTimer);
+ close(TIME_fdWake[0]);
+ close(TIME_fdWake[1]);
+ TIME_fdWake[0] = TIME_fdWake[1] = -1;
+ TIME_hMMTimer = 0;
TIME_cbcrst.DebugInfo->Spare[0] = 0;
DeleteCriticalSection(&TIME_cbcrst);
TIME_TimersList = NULL;
@@ -317,6 +343,7 @@ WORD TIME_SetEventInternal(UINT wDelay, UINT wResol,
WORD wNewID = 0;
LPWINE_TIMERENTRY lpNewTimer;
LPWINE_TIMERENTRY lpTimer;
+ const char c = 'c';
TRACE("(%u, %u, %p, %08X, %04X);\n", wDelay, wResol, lpFunc, dwUser, wFlags);
@@ -352,7 +379,7 @@ WORD TIME_SetEventInternal(UINT wDelay, UINT wResol,
LeaveCriticalSection(&WINMM_IData.cs);
/* Wake the service thread in case there is work to be done */
- SetEvent(TIME_hWakeEvent);
+ write(TIME_fdWake[1], &c, sizeof(c));
TRACE("=> %u\n", wNewID + 1);
@@ -377,19 +404,33 @@ MMRESULT WINAPI timeSetEvent(UINT wDelay, UINT wResol, LPTIMECALLBACK lpFunc,
*/
MMRESULT WINAPI timeKillEvent(UINT wID)
{
- LPWINE_TIMERENTRY lpSelf = NULL, *lpTimer;
+ LPWINE_TIMERENTRY lpSelf = NULL, *lpTimer;
+ BOOL cb;
TRACE("(%u)\n", wID);
+
+ /* 16 bit code can call this, but it then we can't obtain the TIME_cbcrst
+ * cb = processing thread in callback ?
+ */
EnterCriticalSection(&WINMM_IData.cs);
+ cb = !TryEnterCriticalSection(&TIME_cbcrst);
+
/* remove WINE_TIMERENTRY from list */
for (lpTimer = &TIME_TimersList; *lpTimer; lpTimer = &(*lpTimer)->lpNext) {
- if (wID == (*lpTimer)->wTimerID) {
+ if (wID == (*lpTimer)->wTimerID) {
lpSelf = *lpTimer;
/* unlink timer of id 'wID' */
- *lpTimer = (*lpTimer)->lpNext;
- break;
- }
+ break;
+ }
}
+
+ if (!cb && lpSelf) /* Not in callback */
+ goto destroy_noerr;
+ if (lpSelf && !(lpSelf->wFlags & TIME_KILL_SYNCHRONOUS))
+ lpSelf->wFlags |= TIME_DESTROY;
+ if (!cb)
+ LeaveCriticalSection(&TIME_cbcrst);
+
LeaveCriticalSection(&WINMM_IData.cs);
if (!lpSelf)
@@ -397,11 +438,24 @@ MMRESULT WINAPI timeKillEvent(UINT wID)
WARN("wID=%u is not a valid timer ID\n", wID);
return MMSYSERR_INVALPARAM;
}
+
if (lpSelf->wFlags & TIME_KILL_SYNCHRONOUS)
- EnterCriticalSection(&TIME_cbcrst);
+ {
+ /* If we obtain both locks, thread isn't running since it always holds at least one */
+ EnterCriticalSection(&WINMM_IData.cs);
+ if (TryEnterCriticalSection(&TIME_cbcrst))
+ goto destroy_noerr;
+ Sleep(1);
+ LeaveCriticalSection(&WINMM_IData.cs);
+ }
+
+ return TIMERR_NOERROR;
+
+ destroy_noerr:
+ *lpTimer = (*lpTimer)->lpNext;
HeapFree(GetProcessHeap(), 0, lpSelf);
- if (lpSelf->wFlags & TIME_KILL_SYNCHRONOUS)
- LeaveCriticalSection(&TIME_cbcrst);
+ LeaveCriticalSection(&TIME_cbcrst);
+ LeaveCriticalSection(&WINMM_IData.cs);
return TIMERR_NOERROR;
}
@@ -464,15 +518,6 @@ MMRESULT WINAPI timeEndPeriod(UINT wPeriod)
*/
DWORD WINAPI timeGetTime(void)
{
-#if defined(COMMENTOUTPRIORTODELETING)
- DWORD count;
-
- /* FIXME: releasing the win16 lock here is a temporary hack (I hope)
- * that lets mciavi32.dll run correctly
- */
- if (pFnReleaseThunkLock) pFnReleaseThunkLock(&count);
- if (pFnRestoreThunkLock) pFnRestoreThunkLock(count);
-#endif
-
+ TRACE("\n");
return GetTickCount();
}
--
1.4.4.2
More information about the wine-patches
mailing list