quartz: Handle case where ReceiveConnection is called on an already connected pin.
[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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "quartz_private.h"
22
23 #include "wine/debug.h"
24 #include "wine/unicode.h"
25 #include <assert.h>
26
27 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
28
29 typedef struct SystemClockAdviseEntry SystemClockAdviseEntry;
30 struct SystemClockAdviseEntry {
31   SystemClockAdviseEntry* next;
32   SystemClockAdviseEntry* prev;
33
34   HANDLE           hEvent;
35   REFERENCE_TIME   rtBaseTime;
36   REFERENCE_TIME   rtIntervalTime;
37 };
38
39 typedef struct SystemClockImpl {
40   const IReferenceClockVtbl *lpVtbl;
41   LONG ref;
42
43   /** IReferenceClock */
44   HANDLE         adviseThread;
45   DWORD          adviseThreadId;
46   BOOL           adviseThreadActive;
47   REFERENCE_TIME lastRefTime;
48   DWORD          lastTimeTickCount;
49   CRITICAL_SECTION safe;
50
51   SystemClockAdviseEntry* pSingleShotAdvise;
52   SystemClockAdviseEntry* pPeriodicAdvise;
53 } SystemClockImpl;
54
55
56 static void QUARTZ_RemoveAviseEntryFromQueue(SystemClockImpl* This, SystemClockAdviseEntry* pEntry) {
57   if (pEntry->prev) pEntry->prev->next = pEntry->next;
58   if (pEntry->next) pEntry->next->prev = pEntry->prev;
59   if (This->pSingleShotAdvise == pEntry) This->pSingleShotAdvise = pEntry->next;
60   if (This->pPeriodicAdvise == pEntry)    This->pPeriodicAdvise = pEntry->next;
61 }
62
63 static void QUARTZ_InsertAviseEntryFromQueue(SystemClockImpl* This, SystemClockAdviseEntry* pEntry, SystemClockAdviseEntry** pQueue) {
64   SystemClockAdviseEntry* prev_it = NULL;
65   SystemClockAdviseEntry* it = NULL;
66   REFERENCE_TIME bornTime =  pEntry->rtBaseTime + pEntry->rtIntervalTime;
67
68   for (it = *pQueue; NULL != it && (it->rtBaseTime + it->rtIntervalTime) < bornTime; it = it->next) {
69     prev_it = it;
70   }
71   if (NULL == prev_it) {
72     pEntry->prev = NULL;
73     if (NULL != (*pQueue)) pEntry->next = (*pQueue)->next;
74     /*assert( NULL == pEntry->next->prev );*/
75     if (NULL != pEntry->next) pEntry->next->prev = pEntry;
76     (*pQueue) = pEntry;
77   } else {
78     pEntry->prev = prev_it;
79     pEntry->next = prev_it->next;
80     prev_it->next = pEntry;
81     if (NULL != pEntry->next) pEntry->next->prev = pEntry;
82   }
83 }
84
85 #define MAX_REFTIME            (REFERENCE_TIME)(0x7FFFFFFFFFFFFFFF)
86 #define ADVISE_EXIT            (WM_APP + 0)
87 #define ADVISE_REMOVE          (WM_APP + 2)
88 #define ADVISE_ADD_SINGLESHOT  (WM_APP + 4)
89 #define ADVISE_ADD_PERIODIC    (WM_APP + 8)
90
91 static DWORD WINAPI SystemClockAdviseThread(LPVOID lpParam) {
92   SystemClockImpl* This = (SystemClockImpl*) lpParam;
93   DWORD timeOut = INFINITE;
94   DWORD tmpTimeOut;
95   MSG msg;
96   HRESULT hr;
97   REFERENCE_TIME curTime;
98   SystemClockAdviseEntry* it = NULL;
99
100   TRACE("(%p): Main Loop\n", This);
101
102   while (TRUE) {
103     if (timeOut > 0) MsgWaitForMultipleObjects(0, NULL, FALSE, timeOut, QS_POSTMESSAGE|QS_SENDMESSAGE|QS_TIMER);
104     
105     EnterCriticalSection(&This->safe);
106     /*timeOut = IReferenceClock_OnTimerUpdated(This); */
107     hr = IReferenceClock_GetTime((IReferenceClock*) This, &curTime);
108     if (FAILED(hr)) {
109       timeOut = INFINITE;
110       goto outrefresh;
111     }
112
113     /** First SingleShots Advice: sorted list */
114     for (it = This->pSingleShotAdvise; NULL != it && (it->rtBaseTime + it->rtIntervalTime) <= curTime; it = it->next) {
115       /** send event ... */
116       SetEvent(it->hEvent);
117       /** ... and Release it */
118       QUARTZ_RemoveAviseEntryFromQueue(This, it);
119       CoTaskMemFree(it);
120     }
121     if (NULL != it) timeOut = (DWORD) ((it->rtBaseTime + it->rtIntervalTime) - curTime) / (REFERENCE_TIME)10000;
122
123     /** Now Periodics Advice: semi sorted list (sort cannot be used) */
124     for (it = This->pPeriodicAdvise; NULL != it; it = it->next) {
125       if (it->rtBaseTime <= curTime) {
126         DWORD nPeriods = (DWORD) ((curTime - it->rtBaseTime) / it->rtIntervalTime);
127         /** Release the semaphore ... */
128         ReleaseSemaphore(it->hEvent, nPeriods, NULL);
129         /** ... and refresh time */
130         it->rtBaseTime += nPeriods * it->rtIntervalTime;
131         /*assert( it->rtBaseTime + it->rtIntervalTime < curTime );*/
132       }
133       tmpTimeOut = (DWORD) ((it->rtBaseTime + it->rtIntervalTime) - curTime) / (REFERENCE_TIME)10000;
134       if (timeOut > tmpTimeOut) timeOut = tmpTimeOut; 
135     }
136
137 outrefresh:
138     LeaveCriticalSection(&This->safe);
139     
140     while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) {
141       /** if hwnd we suppose that is a windows event ... */
142       if  (NULL != msg.hwnd) {
143         TranslateMessage(&msg);
144         DispatchMessageA(&msg);
145       } else {
146         switch (msg.message) {      
147         case WM_QUIT:
148         case ADVISE_EXIT:
149           goto outofthread;
150         case ADVISE_ADD_SINGLESHOT:
151         case ADVISE_ADD_PERIODIC:
152           /** set timeout to 0 to do a rescan now */
153           timeOut = 0;
154           break;
155         case ADVISE_REMOVE:
156           /** hmmmm what we can do here ... */
157           timeOut = INFINITE;
158           break;
159         default:
160           ERR("Unhandled message %u. Critical Path\n", msg.message);
161           break;
162         }
163       }
164     }
165   }
166
167 outofthread:
168   TRACE("(%p): Exiting\n", This);
169   return 0;
170 }
171 /*static DWORD WINAPI SystemClockAdviseThread(LPVOID lpParam) { */
172
173 static BOOL SystemClockPostMessageToAdviseThread(SystemClockImpl* This, UINT iMsg) {
174   if (FALSE == This->adviseThreadActive) {
175     BOOL res;
176     This->adviseThread = CreateThread(NULL, 0, SystemClockAdviseThread, This, 0, &This->adviseThreadId);
177     if (NULL == This->adviseThread) return FALSE;
178     SetThreadPriority(This->adviseThread, THREAD_PRIORITY_TIME_CRITICAL);
179     This->adviseThreadActive = TRUE;
180     while(1) {
181       res = PostThreadMessageA(This->adviseThreadId, iMsg, 0, 0);
182       /* Let the thread creates its message queue (with MsgWaitForMultipleObjects call) by yielding and retrying */
183       if (!res && (GetLastError() == ERROR_INVALID_THREAD_ID))
184         Sleep(0);
185       else
186         break;
187     }
188     return res;
189   }
190   return PostThreadMessageA(This->adviseThreadId, iMsg, 0, 0);
191 }
192
193 static ULONG WINAPI SystemClockImpl_AddRef(IReferenceClock* iface) {
194   SystemClockImpl *This = (SystemClockImpl *)iface;
195   ULONG ref = InterlockedIncrement(&This->ref);
196
197   TRACE("(%p): AddRef from %d\n", This, ref - 1);
198
199   return ref;
200 }
201
202 static HRESULT WINAPI SystemClockImpl_QueryInterface(IReferenceClock* iface, REFIID riid, void** ppobj) {
203   SystemClockImpl *This = (SystemClockImpl *)iface;
204   TRACE("(%p, %s,%p)\n", This, debugstr_guid(riid), ppobj);
205   
206   if (IsEqualIID (riid, &IID_IUnknown) || 
207       IsEqualIID (riid, &IID_IReferenceClock)) {
208     SystemClockImpl_AddRef(iface);
209     *ppobj = This;
210     return S_OK;
211   }
212   
213   *ppobj = NULL;
214   WARN("(%p, %s,%p): not found\n", This, debugstr_guid(riid), ppobj);
215   return E_NOINTERFACE;
216 }
217
218 static ULONG WINAPI SystemClockImpl_Release(IReferenceClock* iface) {
219   SystemClockImpl *This = (SystemClockImpl *)iface;
220   ULONG ref = InterlockedDecrement(&This->ref);
221   TRACE("(%p): ReleaseRef to %d\n", This, ref);
222   if (ref == 0) {
223     if (SystemClockPostMessageToAdviseThread(This, ADVISE_EXIT)) {
224       WaitForSingleObject(This->adviseThread, INFINITE);
225       CloseHandle(This->adviseThread);
226     }
227     This->safe.DebugInfo->Spare[0] = 0;
228     DeleteCriticalSection(&This->safe);
229     CoTaskMemFree(This);
230   }
231   return ref;
232 }
233
234 static HRESULT WINAPI SystemClockImpl_GetTime(IReferenceClock* iface, REFERENCE_TIME* pTime) {
235   SystemClockImpl *This = (SystemClockImpl *)iface;
236   DWORD curTimeTickCount;
237   HRESULT hr = S_OK;
238
239   TRACE("(%p, %p)\n", This, pTime);
240
241   if (NULL == pTime) {
242     return E_POINTER;
243   }
244
245   curTimeTickCount = GetTickCount();
246
247   EnterCriticalSection(&This->safe);
248   if (This->lastTimeTickCount == curTimeTickCount) hr = S_FALSE;
249   This->lastRefTime += (REFERENCE_TIME) (DWORD) (curTimeTickCount - This->lastTimeTickCount) * (REFERENCE_TIME) 10000;
250   This->lastTimeTickCount = curTimeTickCount;
251   *pTime = This->lastRefTime;
252   LeaveCriticalSection(&This->safe);
253   return hr;
254 }
255
256 static HRESULT WINAPI SystemClockImpl_AdviseTime(IReferenceClock* iface, REFERENCE_TIME rtBaseTime, REFERENCE_TIME rtStreamTime, HEVENT hEvent, DWORD_PTR* pdwAdviseCookie) {
257   SystemClockImpl *This = (SystemClockImpl *)iface;
258   SystemClockAdviseEntry* pEntry = NULL;
259
260   TRACE("(%p, 0x%s, 0x%s, %ld, %p)\n", This, wine_dbgstr_longlong(rtBaseTime),
261       wine_dbgstr_longlong(rtStreamTime), hEvent, pdwAdviseCookie);
262
263   if ((HEVENT) 0 == hEvent) {
264     return E_INVALIDARG;
265   }
266   if (0 >= rtBaseTime + rtStreamTime) {
267     return E_INVALIDARG;
268   }
269   if (NULL == pdwAdviseCookie) {
270     return E_POINTER;
271   }
272   pEntry = CoTaskMemAlloc(sizeof(SystemClockAdviseEntry));
273   if (NULL == pEntry) {
274     return E_OUTOFMEMORY;
275   }
276   ZeroMemory(pEntry, sizeof(SystemClockAdviseEntry));
277
278   pEntry->hEvent = (HANDLE) hEvent;
279   pEntry->rtBaseTime = rtBaseTime + rtStreamTime;
280   pEntry->rtIntervalTime = 0;
281
282   EnterCriticalSection(&This->safe);
283   QUARTZ_InsertAviseEntryFromQueue(This, pEntry, &This->pSingleShotAdvise);
284   LeaveCriticalSection(&This->safe);
285
286   SystemClockPostMessageToAdviseThread(This, ADVISE_ADD_SINGLESHOT);
287
288   *pdwAdviseCookie = (DWORD_PTR) (pEntry);
289   return S_OK;
290 }
291
292 static HRESULT WINAPI SystemClockImpl_AdvisePeriodic(IReferenceClock* iface, REFERENCE_TIME rtStartTime, REFERENCE_TIME rtPeriodTime, HSEMAPHORE hSemaphore, DWORD_PTR* pdwAdviseCookie) {
293   SystemClockImpl *This = (SystemClockImpl *)iface;
294   SystemClockAdviseEntry* pEntry = NULL;
295
296   TRACE("(%p, 0x%s, 0x%s, %ld, %p)\n", This, wine_dbgstr_longlong(rtStartTime),
297       wine_dbgstr_longlong(rtPeriodTime), hSemaphore, pdwAdviseCookie);
298
299   if ((HSEMAPHORE) 0 == hSemaphore) {
300     return E_INVALIDARG;
301   }
302   if (0 >= rtStartTime || 0 >= rtPeriodTime) {
303     return E_INVALIDARG;
304   }
305   if (NULL == pdwAdviseCookie) {
306     return E_POINTER;
307   }
308   pEntry = CoTaskMemAlloc(sizeof(SystemClockAdviseEntry));
309   if (NULL == pEntry) {
310     return E_OUTOFMEMORY;
311   }
312   ZeroMemory(pEntry, sizeof(SystemClockAdviseEntry));
313
314   pEntry->hEvent = (HANDLE) hSemaphore;
315   pEntry->rtBaseTime = rtStartTime;
316   pEntry->rtIntervalTime = rtPeriodTime;
317
318   EnterCriticalSection(&This->safe);
319   QUARTZ_InsertAviseEntryFromQueue(This, pEntry, &This->pPeriodicAdvise);
320   LeaveCriticalSection(&This->safe);
321
322   SystemClockPostMessageToAdviseThread(This, ADVISE_ADD_PERIODIC);
323
324   *pdwAdviseCookie = (DWORD_PTR) (pEntry);
325   return S_OK;
326 }
327
328 static HRESULT WINAPI SystemClockImpl_Unadvise(IReferenceClock* iface, DWORD_PTR dwAdviseCookie) {
329   SystemClockImpl *This = (SystemClockImpl *)iface;
330   SystemClockAdviseEntry* pEntry = NULL;
331   SystemClockAdviseEntry* it = NULL;
332   HRESULT ret = S_OK;
333   TRACE("(%p, %lu)\n", This, dwAdviseCookie);
334
335   pEntry = (SystemClockAdviseEntry*) dwAdviseCookie;
336
337   EnterCriticalSection(&This->safe);
338   for (it = This->pPeriodicAdvise; NULL != it && it != pEntry; it = it->next) ;
339   if (it != pEntry) {
340     for (it = This->pSingleShotAdvise; NULL != it && it != pEntry; it = it->next) ;
341     if (it != pEntry) {
342       ret = S_FALSE;
343       goto out;
344     }
345   }
346
347   QUARTZ_RemoveAviseEntryFromQueue(This, pEntry);
348   CoTaskMemFree(pEntry);
349
350   SystemClockPostMessageToAdviseThread(This, ADVISE_REMOVE);
351
352 out:
353   LeaveCriticalSection(&This->safe);
354   return ret;
355 }
356
357 static const IReferenceClockVtbl SystemClock_Vtbl = 
358 {
359     SystemClockImpl_QueryInterface,
360     SystemClockImpl_AddRef,
361     SystemClockImpl_Release,
362     SystemClockImpl_GetTime,
363     SystemClockImpl_AdviseTime,
364     SystemClockImpl_AdvisePeriodic,
365     SystemClockImpl_Unadvise
366 };
367
368 HRESULT QUARTZ_CreateSystemClock(IUnknown * pUnkOuter, LPVOID * ppv) {
369   SystemClockImpl* obj = NULL;
370   
371   TRACE("(%p,%p)\n", ppv, pUnkOuter);
372   
373   obj = CoTaskMemAlloc(sizeof(SystemClockImpl));
374   if (NULL == obj)      {
375     *ppv = NULL;
376     return E_OUTOFMEMORY;
377   }
378   ZeroMemory(obj, sizeof(SystemClockImpl));
379
380   obj->lpVtbl = &SystemClock_Vtbl;
381   obj->ref = 0;  /* will be inited by QueryInterface */
382
383   obj->lastTimeTickCount = GetTickCount();
384   InitializeCriticalSection(&obj->safe);
385   obj->safe.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": SystemClockImpl.safe");
386
387   return SystemClockImpl_QueryInterface((IReferenceClock*) obj, &IID_IReferenceClock, ppv);
388 }