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