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