dmloader: complete rewrite and full implementation.
[wine] / dlls / quartz / systemclock.c
1 /*
2  * Implementation of IReferenceClock
3  *
4  * Copyright 2004 Raphael Junqueira
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #define COM_NO_WINDOWS_H
22 #include "quartz_private.h"
23
24 #include "wine/debug.h"
25 #include "wine/unicode.h"
26 #include "uuids.h"
27 #include <assert.h>
28
29 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
30
31 typedef struct SystemClockAdviseEntry SystemClockAdviseEntry;
32 struct SystemClockAdviseEntry {
33   SystemClockAdviseEntry* next;
34   SystemClockAdviseEntry* prev;
35
36   HANDLE           hEvent;
37   REFERENCE_TIME   rtBaseTime;
38   REFERENCE_TIME   rtIntervalTime;
39 };
40
41 typedef struct SystemClockImpl {
42   ICOM_VFIELD(IReferenceClock);
43   ULONG ref;
44
45   /** IReferenceClock */
46   HANDLE         adviseThread;
47   DWORD          adviseThreadId;
48   BOOL           adviseThreadActive;
49   REFERENCE_TIME lastRefTime;
50   DWORD          lastTimeTickCount;
51   CRITICAL_SECTION safe;
52
53   SystemClockAdviseEntry* pSingleShotAdvise;
54   SystemClockAdviseEntry* pPeriodicAdvise;
55 } SystemClockImpl;
56
57
58 static void QUARTZ_RemoveAviseEntryFromQueue(SystemClockImpl* This, SystemClockAdviseEntry* pEntry) {
59   if (pEntry->prev) pEntry->prev->next = pEntry->next;
60   if (pEntry->next) pEntry->next->prev = pEntry->prev;
61   if (This->pSingleShotAdvise == pEntry) This->pSingleShotAdvise = pEntry->next;
62   if (This->pPeriodicAdvise == pEntry)    This->pPeriodicAdvise = pEntry->next;
63 }
64
65 static void QUARTZ_InsertAviseEntryFromQueue(SystemClockImpl* This, SystemClockAdviseEntry* pEntry, SystemClockAdviseEntry** pQueue) {
66   SystemClockAdviseEntry* prev_it = NULL;
67   SystemClockAdviseEntry* it = NULL;
68   REFERENCE_TIME bornTime =  pEntry->rtBaseTime + pEntry->rtIntervalTime;
69
70   for (it = *pQueue; NULL != it && (it->rtBaseTime + it->rtIntervalTime) < bornTime; it = it->next) {
71     prev_it = it;
72   }
73   if (NULL == prev_it) {
74     pEntry->prev = NULL;
75     if (NULL != (*pQueue)) pEntry->next = (*pQueue)->next;
76     /*assert( NULL == pEntry->next->prev );*/
77     if (NULL != pEntry->next) pEntry->next->prev = pEntry;
78     (*pQueue) = pEntry;
79   } else {
80     pEntry->prev = prev_it;
81     pEntry->next = prev_it->next;
82     prev_it->next = pEntry;
83     if (NULL != pEntry->next) pEntry->next->prev = pEntry;
84   }
85 }
86
87 #define MAX_REFTIME            (REFERENCE_TIME)(0x7FFFFFFFFFFFFFFF)
88 #define ADVISE_EXIT            (WM_APP + 0)
89 #define ADVISE_REMOVE          (WM_APP + 2)
90 #define ADVISE_ADD_SINGLESHOT  (WM_APP + 4)
91 #define ADVISE_ADD_PERIODIC    (WM_APP + 8)
92
93 static DWORD WINAPI SystemClockAdviseThread(LPVOID lpParam) {
94   SystemClockImpl* This = (SystemClockImpl*) lpParam;
95   DWORD timeOut = INFINITE;
96   DWORD tmpTimeOut;
97   MSG msg;
98   HRESULT hr;
99   REFERENCE_TIME curTime;
100   SystemClockAdviseEntry* it = NULL;
101
102   TRACE("(%p): Main Loop\n", This);
103
104   while (TRUE) {
105     if (timeOut > 0) MsgWaitForMultipleObjects(0, NULL, FALSE, timeOut, QS_POSTMESSAGE|QS_SENDMESSAGE|QS_TIMER);
106     
107     EnterCriticalSection(&This->safe);
108     /*timeOut = IReferenceClock_OnTimerUpdated(This); */
109     hr = IReferenceClock_GetTime((IReferenceClock*) This, &curTime);
110     if (FAILED(hr)) {
111       timeOut = INFINITE;
112       goto outrefresh;
113     }
114     
115     /** First SingleShots Advice: sorted list */
116     for (it = This->pSingleShotAdvise; NULL != it && it->rtBaseTime <= curTime; it = it->next) {
117       /** send event ... */
118       SetEvent((HANDLE) it->hEvent);
119       /** ... and Release it */
120       QUARTZ_RemoveAviseEntryFromQueue(This, it);
121       HeapFree(GetProcessHeap(), 0, it);
122     }
123     if (NULL != it) timeOut = (DWORD) (curTime - (it->rtBaseTime + it->rtIntervalTime));
124
125     /** Now Periodics Advice: semi sorted list (sort cannot be used) */
126     for (it = This->pPeriodicAdvise; NULL != it; it = it->next) {
127       if (it->rtBaseTime <= curTime) {
128         DWORD nPeriods = (DWORD) ((curTime - it->rtBaseTime) / it->rtIntervalTime);
129         /** Release the semaphore ... */
130         ReleaseSemaphore((HANDLE) it->hEvent, nPeriods, NULL);
131         /** ... and refresh time */
132         it->rtBaseTime += it->rtIntervalTime;
133         /*assert( it->rtBaseTime + it->rtIntervalTime < curTime );*/
134       }
135       tmpTimeOut = (DWORD) (curTime - (it->rtBaseTime + it->rtIntervalTime));
136       if (timeOut > tmpTimeOut) timeOut = tmpTimeOut; 
137     }
138
139 outrefresh:
140     LeaveCriticalSection(&This->safe);
141     
142     while (TRUE == PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) {
143       /** if hwnd we suppose that is a windows event ... */
144       if  (NULL != msg.hwnd) {
145         TranslateMessage(&msg);
146         DispatchMessageA(&msg);
147       } else {
148         switch (msg.message) {      
149         case WM_QUIT:
150         case ADVISE_EXIT:
151           goto outofthread;
152           break;
153         case ADVISE_ADD_SINGLESHOT:
154         case ADVISE_ADD_PERIODIC:
155           /** set timeout to 0 to do a rescan now */
156           timeOut = 0;
157           break;
158         case ADVISE_REMOVE:
159           /** hmmmm what we can do here ... */
160           timeOut = INFINITE;
161           break;
162         default:
163           ERR("Unhandled message %u. Critical Path\n", msg.message);
164           break;
165         }
166       }
167     }
168   }
169
170 outofthread:
171   TRACE("(%p): Exiting\n", This);
172   return 0;
173 }
174 /*static DWORD WINAPI SystemClockAdviseThread(LPVOID lpParam) { */
175
176 static BOOL SystemClockPostMessageToAdviseThread(SystemClockImpl* This, UINT iMsg) {
177   if (FALSE == This->adviseThreadActive) {
178     This->adviseThread = CreateThread(NULL, 0, SystemClockAdviseThread, This, 0, &This->adviseThreadId);
179     if (NULL == This->adviseThread) return FALSE;
180     SetThreadPriority(This->adviseThread, THREAD_PRIORITY_TIME_CRITICAL);
181     This->adviseThreadActive = TRUE;
182   }
183   return PostThreadMessageA(This->adviseThreadId, iMsg, 0, 0);
184 }
185
186 ICOM_VTABLE(IReferenceClock) SystemClock_Vtbl;
187
188 static ULONG WINAPI SystemClockImpl_AddRef(IReferenceClock* iface) {
189   ICOM_THIS(SystemClockImpl,iface);
190   TRACE("(%p): AddRef from %ld\n", This, This->ref);
191   return ++(This->ref);
192 }
193
194 static HRESULT WINAPI SystemClockImpl_QueryInterface(IReferenceClock* iface, REFIID riid, void** ppobj) {
195   ICOM_THIS(SystemClockImpl,iface);
196   TRACE("(%p, %s,%p)\n", This, debugstr_guid(riid), ppobj);
197   
198   if (IsEqualIID (riid, &IID_IUnknown) || 
199       IsEqualIID (riid, &IID_IReferenceClock)) {
200     SystemClockImpl_AddRef(iface);
201     *ppobj = This;
202     return S_OK;
203   }
204   
205   WARN("(%p, %s,%p): not found\n", This, debugstr_guid(riid), ppobj);
206   return E_NOINTERFACE;
207 }
208
209 static ULONG WINAPI SystemClockImpl_Release(IReferenceClock* iface) {
210   ICOM_THIS(SystemClockImpl,iface);
211   ULONG ref = --This->ref;
212   TRACE("(%p): ReleaseRef to %ld\n", This, This->ref);
213   if (ref == 0) {
214     if (SystemClockPostMessageToAdviseThread(This, ADVISE_EXIT)) {
215       WaitForSingleObject(This->adviseThread, INFINITE);
216       CloseHandle(This->adviseThread);
217     }
218     DeleteCriticalSection(&This->safe);
219     HeapFree(GetProcessHeap(), 0, This);
220   }
221   return ref;
222 }
223
224 static HRESULT WINAPI SystemClockImpl_GetTime(IReferenceClock* iface, REFERENCE_TIME* pTime) {
225   ICOM_THIS(SystemClockImpl,iface);
226   DWORD curTimeTickCount;
227   HRESULT hr = S_OK;
228
229   TRACE("(%p, %p)\n", This, pTime);
230
231   if (NULL == pTime) {
232     return E_POINTER;
233   }
234
235   curTimeTickCount = GetTickCount();
236
237   EnterCriticalSection(&This->safe);
238   /** TODO: safe this not using * 10000 */
239   This->lastRefTime += (REFERENCE_TIME) (DWORD) (curTimeTickCount - This->lastTimeTickCount) * (REFERENCE_TIME) 10000;
240   This->lastTimeTickCount = curTimeTickCount;
241   LeaveCriticalSection(&This->safe);
242
243   *pTime = This->lastRefTime;
244   if (This->lastTimeTickCount == curTimeTickCount) hr = S_FALSE;
245   This->lastTimeTickCount = curTimeTickCount;
246   return hr;
247 }
248
249 static HRESULT WINAPI SystemClockImpl_AdviseTime(IReferenceClock* iface, REFERENCE_TIME rtBaseTime, REFERENCE_TIME rtStreamTime, HEVENT hEvent, DWORD_PTR* pdwAdviseCookie) {
250   ICOM_THIS(SystemClockImpl,iface);
251   SystemClockAdviseEntry* pEntry = NULL;
252
253   TRACE("(%p, %lld, %lld, %ld, %p)\n", This, rtBaseTime, rtStreamTime, hEvent, pdwAdviseCookie);
254
255   if ((HEVENT) 0 == hEvent) {
256     return E_INVALIDARG;
257   }
258   if (0 >= rtBaseTime + rtStreamTime) {
259     return E_INVALIDARG;
260   }
261   if (NULL == pdwAdviseCookie) {
262     return E_POINTER;
263   }
264   pEntry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SystemClockAdviseEntry));
265   if (NULL == pEntry) {
266     return E_OUTOFMEMORY;
267   }
268
269   pEntry->hEvent = (HANDLE) hEvent;
270   pEntry->rtBaseTime = rtBaseTime + rtStreamTime;
271   pEntry->rtIntervalTime = 0;
272
273   EnterCriticalSection(&This->safe);
274   QUARTZ_InsertAviseEntryFromQueue(This, pEntry, &This->pSingleShotAdvise);
275   LeaveCriticalSection(&This->safe);
276
277   SystemClockPostMessageToAdviseThread(This, ADVISE_ADD_SINGLESHOT);
278
279   *pdwAdviseCookie = (DWORD_PTR) (pEntry);
280   return S_OK;
281 }
282
283 static HRESULT WINAPI SystemClockImpl_AdvisePeriodic(IReferenceClock* iface, REFERENCE_TIME rtStartTime, REFERENCE_TIME rtPeriodTime, HSEMAPHORE hSemaphore, DWORD_PTR* pdwAdviseCookie) {
284   ICOM_THIS(SystemClockImpl,iface);
285   SystemClockAdviseEntry* pEntry = NULL;
286
287   TRACE("(%p, %lld, %lld, %ld, %p)\n", This, rtStartTime, rtPeriodTime, hSemaphore, pdwAdviseCookie);
288
289   if ((HSEMAPHORE) 0 == hSemaphore) {
290     return E_INVALIDARG;
291   }
292   if (0 >= rtStartTime || 0 >= rtPeriodTime) {
293     return E_INVALIDARG;
294   }
295   if (NULL == pdwAdviseCookie) {
296     return E_POINTER;
297   }
298   pEntry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SystemClockAdviseEntry));
299   if (NULL == pEntry) {
300     return E_OUTOFMEMORY;
301   }
302
303   pEntry->hEvent = (HANDLE) hSemaphore;
304   pEntry->rtBaseTime = rtStartTime;
305   pEntry->rtIntervalTime = rtPeriodTime;
306
307   EnterCriticalSection(&This->safe);
308   QUARTZ_InsertAviseEntryFromQueue(This, pEntry, &This->pPeriodicAdvise);
309   LeaveCriticalSection(&This->safe);
310
311   SystemClockPostMessageToAdviseThread(This, ADVISE_ADD_PERIODIC);
312
313   *pdwAdviseCookie = (DWORD_PTR) (pEntry);
314   return S_OK;
315 }
316
317 static HRESULT WINAPI SystemClockImpl_Unadvise(IReferenceClock* iface, DWORD_PTR dwAdviseCookie) {
318   ICOM_THIS(SystemClockImpl,iface);
319   SystemClockAdviseEntry* pEntry = NULL;
320   SystemClockAdviseEntry* it = NULL;
321   HRESULT ret = S_OK;
322   TRACE("(%p, %lu)\n", This, dwAdviseCookie);
323
324   pEntry = (SystemClockAdviseEntry*) dwAdviseCookie;
325
326   EnterCriticalSection(&This->safe);
327   for (it = This->pPeriodicAdvise; NULL != it && it != pEntry; it = it->next) ;
328   if (it != pEntry) {
329     for (it = This->pSingleShotAdvise; NULL != it && it != pEntry; it = it->next) ;
330     if (it != pEntry) {
331       ret = S_FALSE;
332       goto out;
333     }
334   }
335
336   QUARTZ_RemoveAviseEntryFromQueue(This, pEntry);
337   HeapFree(GetProcessHeap(), 0, pEntry);
338
339   SystemClockPostMessageToAdviseThread(This, ADVISE_REMOVE);
340
341 out:
342   LeaveCriticalSection(&This->safe);
343   return ret;
344 }
345
346 ICOM_VTABLE(IReferenceClock) SystemClock_Vtbl = 
347 {
348     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
349     SystemClockImpl_QueryInterface,
350     SystemClockImpl_AddRef,
351     SystemClockImpl_Release,
352     SystemClockImpl_GetTime,
353     SystemClockImpl_AdviseTime,
354     SystemClockImpl_AdvisePeriodic,
355     SystemClockImpl_Unadvise
356 };
357
358 HRESULT QUARTZ_CreateSystemClock(IUnknown * pUnkOuter, LPVOID * ppv) {
359   SystemClockImpl* obj = NULL;
360   
361   TRACE("(%p,%p)\n", ppv, pUnkOuter);
362   
363   obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IReferenceClock));
364   if (NULL == obj)      {
365     *ppv = NULL;
366     return E_OUTOFMEMORY;
367   }
368   obj->lpVtbl = &SystemClock_Vtbl;
369   obj->ref = 0;  /* will be inited by QueryInterface */
370
371   obj->lastTimeTickCount = GetTickCount();
372   InitializeCriticalSection(&obj->safe);
373
374   return SystemClockImpl_QueryInterface((IReferenceClock*) obj, &IID_IReferenceClock, ppv);
375 }