From 0e35ebada0324d103857a60d5c39d74cc1d94a54 Mon Sep 17 00:00:00 2001
From: Eric Pouech <Eric.Pouech@wanadoo.fr>
Date: Sat, 12 Jun 1999 08:25:36 +0000
Subject: [PATCH] - simplified internal data - thread safe access to internal
 structures - fixed bug on ONE_SHOT timers - deferred service thread call
 until really needed (no longer on WINMM   or MMSYSTEM attachement)

---
 multimedia/time.c | 201 ++++++++++++++++++++++++++--------------------
 1 file changed, 114 insertions(+), 87 deletions(-)

diff --git a/multimedia/time.c b/multimedia/time.c
index 3d85130395d..4f3646d58a9 100644
--- a/multimedia/time.c
+++ b/multimedia/time.c
@@ -7,23 +7,17 @@
  */
 
 #include <stdlib.h>
-#include <string.h>
 #include <time.h>
 #include <sys/time.h>
 #include "winbase.h"
 #include "wine/winbase16.h" /* GetTaskDS */
 #include "callback.h"
 #include "mmsystem.h"
-#include "xmalloc.h"
 #include "services.h"
-#include "options.h"
 #include "debugtools.h"
 
 DEFAULT_DEBUG_CHANNEL(mmtime)
 
-static MMTIME16 mmSysTimeMS;
-static MMTIME16 mmSysTimeSMPTE;
-
 typedef struct tagTIMERENTRY {
     UINT			wDelay;
     UINT			wResol;
@@ -34,11 +28,13 @@ typedef struct tagTIMERENTRY {
     UINT			wTimerID;
     UINT			wCurTime;
     UINT			isWin32;
-    struct tagTIMERENTRY*	Next;
+    struct tagTIMERENTRY*	lpNext;
 } TIMERENTRY, *LPTIMERENTRY;
 
-static LPTIMERENTRY lpTimerList = NULL;
-static HANDLE hMMTimer;
+static DWORD		mmSysTimeMS;
+static LPTIMERENTRY 	lpTimerList = NULL;
+static HANDLE 		hMMTimer = 0;
+CRITICAL_SECTION	mmTimeCritSect;
 
 /*
  * FIXME
@@ -51,14 +47,11 @@ static HANDLE hMMTimer;
 
 static	void	TIME_TriggerCallBack(LPTIMERENTRY lpTimer, DWORD dwCurrent)
 {
-    lpTimer->wCurTime = lpTimer->wDelay;
-    
     if (lpTimer->lpFunc != (FARPROC16) NULL) {
-	TRACE("before CallBack16 (%lu)!\n", dwCurrent);
+	TRACE("before CallBack (%lu)!\n", dwCurrent);
 	TRACE("lpFunc=%p wTimerID=%04X dwUser=%08lX !\n",
 	      lpTimer->lpFunc, lpTimer->wTimerID, lpTimer->dwUser);
-	TRACE("hInstance=%04X !\n", lpTimer->hInstance);
-	
+	TRACE("hInstance=%04X !\n", lpTimer->hInstance);	
 	
 	/* - TimeProc callback that is called here is something strange, under Windows 3.1x it is called 
 	 * 		during interrupt time,  is allowed to execute very limited number of API calls (like
@@ -67,46 +60,52 @@ static	void	TIME_TriggerCallBack(LPTIMERENTRY lpTimer, DWORD dwCurrent)
 	 */
 	switch (lpTimer->wFlags & 0x30) {
 	case TIME_CALLBACK_FUNCTION:
-		if (lpTimer->isWin32)
-		    lpTimer->lpFunc(lpTimer->wTimerID, 0, lpTimer->dwUser, 0, 0);
-		else
-		    Callbacks->CallTimeFuncProc(lpTimer->lpFunc,
-						lpTimer->wTimerID, 0,
-						lpTimer->dwUser, 0, 0);
-		break;
+	    if (lpTimer->isWin32)
+		((LPTIMECALLBACK)lpTimer->lpFunc)(lpTimer->wTimerID, 0, lpTimer->dwUser, 0, 0);
+	    else
+		Callbacks->CallTimeFuncProc(lpTimer->lpFunc,
+					    lpTimer->wTimerID, 0,
+					    lpTimer->dwUser, 0, 0);
+	    break;
 	case TIME_CALLBACK_EVENT_SET:
-		SetEvent((HANDLE)lpTimer->lpFunc);
-		break;
+	    SetEvent((HANDLE)lpTimer->lpFunc);
+	    break;
 	case TIME_CALLBACK_EVENT_PULSE:
-		PulseEvent((HANDLE)lpTimer->lpFunc);
-		break;
+	    PulseEvent((HANDLE)lpTimer->lpFunc);
+	    break;
 	default:
-		FIXME("Unknown callback type 0x%04x for mmtime callback (%p), ignored.\n", lpTimer->wFlags, lpTimer->lpFunc);
-		break;
+	    FIXME("Unknown callback type 0x%04x for mmtime callback (%p), ignored.\n", lpTimer->wFlags, lpTimer->lpFunc);
+	    break;
 	}
-	TRACE("after CallBack16 !\n");
+	TRACE("after CallBack !\n");
     }
-    if (lpTimer->wFlags & TIME_ONESHOT)
-	timeKillEvent(lpTimer->wTimerID);
 }
 
 /**************************************************************************
  *           TIME_MMSysTimeCallback
  */
-static VOID CALLBACK TIME_MMSysTimeCallback( ULONG_PTR dummy )
+static void CALLBACK TIME_MMSysTimeCallback( ULONG_PTR dummy )
 {
-    LPTIMERENTRY lpTimer;
-    
-    mmSysTimeMS.u.ms += MMSYSTIME_MININTERVAL;
-    mmSysTimeSMPTE.u.smpte.frame++;
-    
-    for (lpTimer = lpTimerList; lpTimer != NULL; lpTimer = lpTimer->Next) {
+    LPTIMERENTRY lpTimer, lpNextTimer;
+
+    mmSysTimeMS += MMSYSTIME_MININTERVAL;
+
+    EnterCriticalSection(&mmTimeCritSect);
+
+    for (lpTimer = lpTimerList; lpTimer != NULL; ) {
+	lpNextTimer = lpTimer->lpNext;
 	if (lpTimer->wCurTime < MMSYSTIME_MININTERVAL) {
-	    TIME_TriggerCallBack(lpTimer, mmSysTimeMS.u.ms);
+	    lpTimer->wCurTime = lpTimer->wDelay;    
+	    TIME_TriggerCallBack(lpTimer, mmSysTimeMS);
+	    if (lpTimer->wFlags & TIME_ONESHOT)
+		timeKillEvent(lpTimer->wTimerID);
 	} else {
 	    lpTimer->wCurTime -= MMSYSTIME_MININTERVAL;
 	}
+	lpTimer = lpNextTimer;
     }
+
+    LeaveCriticalSection(&mmTimeCritSect);
 }
 
 /**************************************************************************
@@ -114,20 +113,29 @@ static VOID CALLBACK TIME_MMSysTimeCallback( ULONG_PTR dummy )
  */
 BOOL	MULTIMEDIA_MMTimeInit(void)
 {
-    mmSysTimeMS.wType = TIME_MS;
-    mmSysTimeMS.u.ms = GetTickCount();
-    mmSysTimeSMPTE.wType = TIME_SMPTE;
-    mmSysTimeSMPTE.u.smpte.hour = 0;
-    mmSysTimeSMPTE.u.smpte.min = 0;
-    mmSysTimeSMPTE.u.smpte.sec = 0;
-    mmSysTimeSMPTE.u.smpte.frame = 0;
-    mmSysTimeSMPTE.u.smpte.fps = 0;
-    mmSysTimeSMPTE.u.smpte.dummy = 0;
-    hMMTimer = SERVICE_AddTimer( MMSYSTIME_MININTERVAL*1000L, TIME_MMSysTimeCallback, 0 );
+    InitializeCriticalSection(&mmTimeCritSect);
+    MakeCriticalSectionGlobal(&mmTimeCritSect);
     
     return TRUE;
 }
 
+/**************************************************************************
+ * 				MULTIMEDIA_MMTimeStart		[internal]
+ */
+static	BOOL	MULTIMEDIA_MMTimeStart(void)
+{
+    /* one could think it's possible to stop the service thread activity when no more
+     * mm timers are active, but this would require to handle delta (in time) between
+     * GetTickCount() and timeGetTime() which are not -currently- synchronized.
+     */
+    if (!hMMTimer) {
+	mmSysTimeMS = GetTickCount();
+	hMMTimer = SERVICE_AddTimer( MMSYSTIME_MININTERVAL*1000L, TIME_MMSysTimeCallback, 0 );
+    }
+
+    return TRUE;
+}
+
 /**************************************************************************
  * 				timeGetSystemTime	[WINMM.140]
  */
@@ -135,10 +143,13 @@ MMRESULT WINAPI timeGetSystemTime(LPMMTIME lpTime, UINT wSize)
 {
     TRACE("(%p, %u);\n", lpTime, wSize);
 
-    lpTime->wType = TIME_MS;
-    lpTime->u.ms = mmSysTimeMS.u.ms;
+    if (wSize >= sizeof(*lpTime)) {
+	MULTIMEDIA_MMTimeStart();
+	lpTime->wType = TIME_MS;
+	lpTime->u.ms = mmSysTimeMS;
 
-    TRACE("=> %lu\n", mmSysTimeMS.u.ms);
+	TRACE("=> %lu\n", mmSysTimeMS);
+    }
 
     return 0;
 }
@@ -150,10 +161,13 @@ MMRESULT16 WINAPI timeGetSystemTime16(LPMMTIME16 lpTime, UINT16 wSize)
 {
     TRACE("(%p, %u);\n", lpTime, wSize);
 
-    lpTime->wType = TIME_MS;
-    lpTime->u.ms = mmSysTimeMS.u.ms;
+    if (wSize >= sizeof(*lpTime)) {
+	MULTIMEDIA_MMTimeStart();
+	lpTime->wType = TIME_MS;
+	lpTime->u.ms = mmSysTimeMS;
 
-    TRACE("=> %lu\n", mmSysTimeMS.u.ms);
+	TRACE("=> %lu\n", mmSysTimeMS);
+    }
 
     return 0;
 }
@@ -167,55 +181,58 @@ static	WORD	timeSetEventInternal(UINT wDelay, UINT wResol,
 {
     WORD 		wNewID = 0;
     LPTIMERENTRY	lpNewTimer;
-    LPTIMERENTRY	lpTimer = lpTimerList;
+    LPTIMERENTRY	lpTimer;
     
-    TRACE("(%u, %u, %p, %08lX, %04X);\n",
-	  wDelay, wResol, lpFunc, dwUser, wFlags);
+    TRACE("(%u, %u, %p, %08lX, %04X);\n", wDelay, wResol, lpFunc, dwUser, wFlags);
 
-    lpNewTimer = (LPTIMERENTRY)xmalloc(sizeof(TIMERENTRY));
+    lpNewTimer = (LPTIMERENTRY)malloc(sizeof(TIMERENTRY));
     if (lpNewTimer == NULL)
 	return 0;
-    while (lpTimer != NULL) {
-	wNewID = MAX(wNewID, lpTimer->wTimerID);
-	lpTimer = lpTimer->Next;
-    }
-    
-    lpNewTimer->Next = lpTimerList;
-    lpTimerList = lpNewTimer;
-    lpNewTimer->wTimerID = wNewID + 1;
+
+    MULTIMEDIA_MMTimeStart();
+
     lpNewTimer->wCurTime = wDelay;
     lpNewTimer->wDelay = wDelay;
     lpNewTimer->wResol = wResol;
     lpNewTimer->lpFunc = lpFunc;
     lpNewTimer->isWin32 = isWin32;
     lpNewTimer->hInstance = GetTaskDS16();
-    TRACE("hInstance=%04X !\n", lpNewTimer->hInstance);
-    TRACE("lpFunc=0x%08lx !\n", (DWORD)lpFunc );
     lpNewTimer->dwUser = dwUser;
     lpNewTimer->wFlags = wFlags;
-    return lpNewTimer->wTimerID;
+
+    TRACE("hInstance=%04X,lpFunc=0x%08lx !\n", lpNewTimer->hInstance, (DWORD)lpFunc);
+
+    EnterCriticalSection(&mmTimeCritSect);
+
+    for (lpTimer = lpTimerList; lpTimer != NULL; lpTimer = lpTimer->lpNext) {
+	wNewID = MAX(wNewID, lpTimer->wTimerID);
+    }
+    
+    lpNewTimer->lpNext = lpTimerList;
+    lpTimerList = lpNewTimer;
+    lpNewTimer->wTimerID = wNewID + 1;
+
+    LeaveCriticalSection(&mmTimeCritSect);
+
+    return wNewID + 1;
 }
 
 /**************************************************************************
  * 				timeSetEvent		[MMSYSTEM.602]
  */
-MMRESULT WINAPI timeSetEvent(UINT wDelay, UINT wResol,
-			     LPTIMECALLBACK lpFunc, DWORD dwUser,
-			     UINT wFlags)
+MMRESULT WINAPI timeSetEvent(UINT wDelay, UINT wResol, LPTIMECALLBACK lpFunc,
+			     DWORD dwUser, UINT wFlags)
 {
-    return timeSetEventInternal(wDelay, wResol, (FARPROC16)lpFunc, 
-				dwUser, wFlags, 1);
+    return timeSetEventInternal(wDelay, wResol, (FARPROC16)lpFunc, dwUser, wFlags, 1);
 }
 
 /**************************************************************************
  * 				timeSetEvent		[MMSYSTEM.602]
  */
-MMRESULT16 WINAPI timeSetEvent16(UINT16 wDelay, UINT16 wResol,
-				 LPTIMECALLBACK16 lpFunc, DWORD dwUser,
-				 UINT16 wFlags)
+MMRESULT16 WINAPI timeSetEvent16(UINT16 wDelay, UINT16 wResol, LPTIMECALLBACK16 lpFunc, 
+				 DWORD dwUser, UINT16 wFlags)
 {
-    return timeSetEventInternal(wDelay, wResol, (FARPROC16)lpFunc, 
-				dwUser, wFlags, 0);
+    return timeSetEventInternal(wDelay, wResol, (FARPROC16)lpFunc, dwUser, wFlags, 0);
 }
 
 /**************************************************************************
@@ -224,17 +241,24 @@ MMRESULT16 WINAPI timeSetEvent16(UINT16 wDelay, UINT16 wResol,
 MMRESULT WINAPI timeKillEvent(UINT wID)
 {
     LPTIMERENTRY*	lpTimer;
-    
-    for (lpTimer = &lpTimerList; *lpTimer; lpTimer = &((*lpTimer)->Next)) {
+    MMRESULT		ret = MMSYSERR_INVALPARAM;
+
+    EnterCriticalSection(&mmTimeCritSect);
+
+    for (lpTimer = &lpTimerList; *lpTimer; lpTimer = &((*lpTimer)->lpNext)) {
 	if (wID == (*lpTimer)->wTimerID) {
-	    LPTIMERENTRY xlptimer = (*lpTimer)->Next;
+	    LPTIMERENTRY xlptimer = (*lpTimer)->lpNext;
 	    
 	    free(*lpTimer);
 	    *lpTimer = xlptimer;
-	    return TRUE;
+	    ret = TIMERR_NOERROR;
+	    break;
 	}
     }
-    return 0;
+
+    LeaveCriticalSection(&mmTimeCritSect);
+
+    return ret;
 }
 
 /**************************************************************************
@@ -280,8 +304,9 @@ MMRESULT WINAPI timeBeginPeriod(UINT wPeriod)
 	return TIMERR_NOCANDO;
     return 0;
 }
+
 /**************************************************************************
- * 				timeBeginPeriod		[MMSYSTEM.605]
+ * 				timeBeginPeriod16	[MMSYSTEM.605]
  */
 MMRESULT16 WINAPI timeBeginPeriod16(UINT16 wPeriod)
 {
@@ -305,11 +330,12 @@ MMRESULT WINAPI timeEndPeriod(UINT wPeriod)
 }
 
 /**************************************************************************
- * 				timeEndPeriod		[MMSYSTEM.606]
+ * 				timeEndPeriod16		[MMSYSTEM.606]
  */
 MMRESULT16 WINAPI timeEndPeriod16(UINT16 wPeriod)
 {
     TRACE("(%u) !\n", wPeriod);
+
     if (wPeriod < MMSYSTIME_MININTERVAL || wPeriod > MMSYSTIME_MAXINTERVAL) 
 	return TIMERR_NOCANDO;
     return 0;
@@ -320,5 +346,6 @@ MMRESULT16 WINAPI timeEndPeriod16(UINT16 wPeriod)
  */
 DWORD WINAPI timeGetTime(void)
 {
-    return mmSysTimeMS.u.ms;
+    MULTIMEDIA_MMTimeStart();
+    return mmSysTimeMS;
 }
-- 
GitLab