| /* | |
| * Implementation of IReferenceClock | |
| * | |
| * Copyright 2004 Raphael Junqueira | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
| */ | |
| #include "quartz_private.h" | |
| #include "wine/debug.h" | |
| #include "wine/unicode.h" | |
| #include <assert.h> | |
| WINE_DEFAULT_DEBUG_CHANNEL(quartz); | |
| typedef struct SystemClockAdviseEntry SystemClockAdviseEntry; | |
| struct SystemClockAdviseEntry { | |
| SystemClockAdviseEntry* next; | |
| SystemClockAdviseEntry* prev; | |
| HANDLE hEvent; | |
| REFERENCE_TIME rtBaseTime; | |
| REFERENCE_TIME rtIntervalTime; | |
| }; | |
| typedef struct SystemClockImpl { | |
| IReferenceClock IReferenceClock_iface; | |
| LONG ref; | |
| /** IReferenceClock */ | |
| HANDLE adviseThread; | |
| DWORD adviseThreadId; | |
| BOOL adviseThreadActive; | |
| REFERENCE_TIME lastRefTime; | |
| DWORD lastTimeTickCount; | |
| CRITICAL_SECTION safe; | |
| SystemClockAdviseEntry* pSingleShotAdvise; | |
| SystemClockAdviseEntry* pPeriodicAdvise; | |
| } SystemClockImpl; | |
| static inline SystemClockImpl *impl_from_IReferenceClock(IReferenceClock *iface) | |
| { | |
| return CONTAINING_RECORD(iface, SystemClockImpl, IReferenceClock_iface); | |
| } | |
| static void QUARTZ_RemoveAviseEntryFromQueue(SystemClockImpl* This, SystemClockAdviseEntry* pEntry) { | |
| if (pEntry->prev) pEntry->prev->next = pEntry->next; | |
| if (pEntry->next) pEntry->next->prev = pEntry->prev; | |
| if (This->pSingleShotAdvise == pEntry) This->pSingleShotAdvise = pEntry->next; | |
| if (This->pPeriodicAdvise == pEntry) This->pPeriodicAdvise = pEntry->next; | |
| } | |
| static void QUARTZ_InsertAviseEntryFromQueue(SystemClockImpl* This, SystemClockAdviseEntry* pEntry, SystemClockAdviseEntry** pQueue) { | |
| SystemClockAdviseEntry* prev_it = NULL; | |
| SystemClockAdviseEntry* it = NULL; | |
| REFERENCE_TIME bornTime = pEntry->rtBaseTime + pEntry->rtIntervalTime; | |
| for (it = *pQueue; NULL != it && (it->rtBaseTime + it->rtIntervalTime) < bornTime; it = it->next) { | |
| prev_it = it; | |
| } | |
| if (NULL == prev_it) { | |
| pEntry->prev = NULL; | |
| if (NULL != (*pQueue)) pEntry->next = (*pQueue)->next; | |
| /*assert( NULL == pEntry->next->prev );*/ | |
| if (NULL != pEntry->next) pEntry->next->prev = pEntry; | |
| (*pQueue) = pEntry; | |
| } else { | |
| pEntry->prev = prev_it; | |
| pEntry->next = prev_it->next; | |
| prev_it->next = pEntry; | |
| if (NULL != pEntry->next) pEntry->next->prev = pEntry; | |
| } | |
| } | |
| #define MAX_REFTIME (REFERENCE_TIME)(0x7FFFFFFFFFFFFFFF) | |
| #define ADVISE_EXIT (WM_APP + 0) | |
| #define ADVISE_REMOVE (WM_APP + 2) | |
| #define ADVISE_ADD_SINGLESHOT (WM_APP + 4) | |
| #define ADVISE_ADD_PERIODIC (WM_APP + 8) | |
| static DWORD WINAPI SystemClockAdviseThread(LPVOID lpParam) { | |
| SystemClockImpl* This = lpParam; | |
| DWORD timeOut = INFINITE; | |
| DWORD tmpTimeOut; | |
| MSG msg; | |
| HRESULT hr; | |
| REFERENCE_TIME curTime; | |
| SystemClockAdviseEntry* it = NULL; | |
| TRACE("(%p): Main Loop\n", This); | |
| while (TRUE) { | |
| if (timeOut > 0) MsgWaitForMultipleObjects(0, NULL, FALSE, timeOut, QS_POSTMESSAGE|QS_SENDMESSAGE|QS_TIMER); | |
| EnterCriticalSection(&This->safe); | |
| /*timeOut = IReferenceClock_OnTimerUpdated(This); */ | |
| hr = IReferenceClock_GetTime(&This->IReferenceClock_iface, &curTime); | |
| if (FAILED(hr)) { | |
| timeOut = INFINITE; | |
| goto outrefresh; | |
| } | |
| /** First SingleShots Advice: sorted list */ | |
| it = This->pSingleShotAdvise; | |
| while ((NULL != it) && (it->rtBaseTime + it->rtIntervalTime) <= curTime) { | |
| SystemClockAdviseEntry* nextit = it->next; | |
| /** send event ... */ | |
| SetEvent(it->hEvent); | |
| /** ... and Release it */ | |
| QUARTZ_RemoveAviseEntryFromQueue(This, it); | |
| CoTaskMemFree(it); | |
| it = nextit; | |
| } | |
| if (NULL != it) timeOut = (DWORD) ((it->rtBaseTime + it->rtIntervalTime) - curTime) / (REFERENCE_TIME)10000; | |
| else timeOut = INFINITE; | |
| /** Now Periodics Advice: semi sorted list (sort cannot be used) */ | |
| for (it = This->pPeriodicAdvise; NULL != it; it = it->next) { | |
| if (it->rtBaseTime <= curTime) { | |
| DWORD nPeriods = (DWORD) ((curTime - it->rtBaseTime) / it->rtIntervalTime); | |
| /** Release the semaphore ... */ | |
| ReleaseSemaphore(it->hEvent, nPeriods, NULL); | |
| /** ... and refresh time */ | |
| it->rtBaseTime += nPeriods * it->rtIntervalTime; | |
| /*assert( it->rtBaseTime + it->rtIntervalTime < curTime );*/ | |
| } | |
| tmpTimeOut = (DWORD) ((it->rtBaseTime + it->rtIntervalTime) - curTime) / (REFERENCE_TIME)10000; | |
| if (timeOut > tmpTimeOut) timeOut = tmpTimeOut; | |
| } | |
| outrefresh: | |
| LeaveCriticalSection(&This->safe); | |
| while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { | |
| /** if hwnd we suppose that is a windows event ... */ | |
| if (NULL != msg.hwnd) { | |
| TranslateMessage(&msg); | |
| DispatchMessageW(&msg); | |
| } else { | |
| switch (msg.message) { | |
| case WM_QUIT: | |
| case ADVISE_EXIT: | |
| goto outofthread; | |
| case ADVISE_ADD_SINGLESHOT: | |
| case ADVISE_ADD_PERIODIC: | |
| /** set timeout to 0 to do a rescan now */ | |
| timeOut = 0; | |
| break; | |
| case ADVISE_REMOVE: | |
| /** hmmmm what we can do here ... */ | |
| timeOut = INFINITE; | |
| break; | |
| default: | |
| ERR("Unhandled message %u. Critical Path\n", msg.message); | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| outofthread: | |
| TRACE("(%p): Exiting\n", This); | |
| return 0; | |
| } | |
| /*static DWORD WINAPI SystemClockAdviseThread(LPVOID lpParam) { */ | |
| static BOOL SystemClockPostMessageToAdviseThread(SystemClockImpl* This, UINT iMsg) { | |
| if (FALSE == This->adviseThreadActive) { | |
| BOOL res; | |
| This->adviseThread = CreateThread(NULL, 0, SystemClockAdviseThread, This, 0, &This->adviseThreadId); | |
| if (NULL == This->adviseThread) return FALSE; | |
| SetThreadPriority(This->adviseThread, THREAD_PRIORITY_TIME_CRITICAL); | |
| This->adviseThreadActive = TRUE; | |
| while(1) { | |
| res = PostThreadMessageW(This->adviseThreadId, iMsg, 0, 0); | |
| /* Let the thread creates its message queue (with MsgWaitForMultipleObjects call) by yielding and retrying */ | |
| if (!res && (GetLastError() == ERROR_INVALID_THREAD_ID)) | |
| Sleep(0); | |
| else | |
| break; | |
| } | |
| return res; | |
| } | |
| return PostThreadMessageW(This->adviseThreadId, iMsg, 0, 0); | |
| } | |
| static ULONG WINAPI SystemClockImpl_AddRef(IReferenceClock* iface) { | |
| SystemClockImpl *This = impl_from_IReferenceClock(iface); | |
| ULONG ref = InterlockedIncrement(&This->ref); | |
| TRACE("(%p): AddRef from %d\n", This, ref - 1); | |
| return ref; | |
| } | |
| static HRESULT WINAPI SystemClockImpl_QueryInterface(IReferenceClock* iface, REFIID riid, void** ppobj) { | |
| SystemClockImpl *This = impl_from_IReferenceClock(iface); | |
| TRACE("(%p, %s,%p)\n", This, debugstr_guid(riid), ppobj); | |
| if (IsEqualIID (riid, &IID_IUnknown) || | |
| IsEqualIID (riid, &IID_IReferenceClock)) { | |
| SystemClockImpl_AddRef(iface); | |
| *ppobj = &This->IReferenceClock_iface; | |
| return S_OK; | |
| } | |
| *ppobj = NULL; | |
| WARN("(%p, %s,%p): not found\n", This, debugstr_guid(riid), ppobj); | |
| return E_NOINTERFACE; | |
| } | |
| static ULONG WINAPI SystemClockImpl_Release(IReferenceClock* iface) { | |
| SystemClockImpl *This = impl_from_IReferenceClock(iface); | |
| ULONG ref = InterlockedDecrement(&This->ref); | |
| TRACE("(%p): ReleaseRef to %d\n", This, ref); | |
| if (ref == 0) { | |
| if (SystemClockPostMessageToAdviseThread(This, ADVISE_EXIT)) { | |
| WaitForSingleObject(This->adviseThread, INFINITE); | |
| CloseHandle(This->adviseThread); | |
| } | |
| This->safe.DebugInfo->Spare[0] = 0; | |
| DeleteCriticalSection(&This->safe); | |
| CoTaskMemFree(This); | |
| } | |
| return ref; | |
| } | |
| static HRESULT WINAPI SystemClockImpl_GetTime(IReferenceClock* iface, REFERENCE_TIME* pTime) { | |
| SystemClockImpl *This = impl_from_IReferenceClock(iface); | |
| DWORD curTimeTickCount; | |
| HRESULT hr = S_OK; | |
| TRACE("(%p, %p)\n", This, pTime); | |
| if (NULL == pTime) { | |
| return E_POINTER; | |
| } | |
| curTimeTickCount = GetTickCount(); | |
| EnterCriticalSection(&This->safe); | |
| if (This->lastTimeTickCount == curTimeTickCount) hr = S_FALSE; | |
| This->lastRefTime += (REFERENCE_TIME) (DWORD) (curTimeTickCount - This->lastTimeTickCount) * (REFERENCE_TIME) 10000; | |
| This->lastTimeTickCount = curTimeTickCount; | |
| *pTime = This->lastRefTime; | |
| LeaveCriticalSection(&This->safe); | |
| return hr; | |
| } | |
| static HRESULT WINAPI SystemClockImpl_AdviseTime(IReferenceClock* iface, REFERENCE_TIME rtBaseTime, REFERENCE_TIME rtStreamTime, HEVENT hEvent, DWORD_PTR* pdwAdviseCookie) { | |
| SystemClockImpl *This = impl_from_IReferenceClock(iface); | |
| SystemClockAdviseEntry* pEntry = NULL; | |
| TRACE("(%p, 0x%s, 0x%s, %ld, %p)\n", This, wine_dbgstr_longlong(rtBaseTime), | |
| wine_dbgstr_longlong(rtStreamTime), hEvent, pdwAdviseCookie); | |
| if (!hEvent) { | |
| return E_INVALIDARG; | |
| } | |
| if (0 >= rtBaseTime + rtStreamTime) { | |
| return E_INVALIDARG; | |
| } | |
| if (NULL == pdwAdviseCookie) { | |
| return E_POINTER; | |
| } | |
| pEntry = CoTaskMemAlloc(sizeof(SystemClockAdviseEntry)); | |
| if (NULL == pEntry) { | |
| return E_OUTOFMEMORY; | |
| } | |
| ZeroMemory(pEntry, sizeof(SystemClockAdviseEntry)); | |
| pEntry->hEvent = (HANDLE) hEvent; | |
| pEntry->rtBaseTime = rtBaseTime + rtStreamTime; | |
| pEntry->rtIntervalTime = 0; | |
| EnterCriticalSection(&This->safe); | |
| QUARTZ_InsertAviseEntryFromQueue(This, pEntry, &This->pSingleShotAdvise); | |
| LeaveCriticalSection(&This->safe); | |
| SystemClockPostMessageToAdviseThread(This, ADVISE_ADD_SINGLESHOT); | |
| *pdwAdviseCookie = (DWORD_PTR) (pEntry); | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI SystemClockImpl_AdvisePeriodic(IReferenceClock* iface, REFERENCE_TIME rtStartTime, REFERENCE_TIME rtPeriodTime, HSEMAPHORE hSemaphore, DWORD_PTR* pdwAdviseCookie) { | |
| SystemClockImpl *This = impl_from_IReferenceClock(iface); | |
| SystemClockAdviseEntry* pEntry = NULL; | |
| TRACE("(%p, 0x%s, 0x%s, %ld, %p)\n", This, wine_dbgstr_longlong(rtStartTime), | |
| wine_dbgstr_longlong(rtPeriodTime), hSemaphore, pdwAdviseCookie); | |
| if (!hSemaphore) { | |
| return E_INVALIDARG; | |
| } | |
| if (0 >= rtStartTime || 0 >= rtPeriodTime) { | |
| return E_INVALIDARG; | |
| } | |
| if (NULL == pdwAdviseCookie) { | |
| return E_POINTER; | |
| } | |
| pEntry = CoTaskMemAlloc(sizeof(SystemClockAdviseEntry)); | |
| if (NULL == pEntry) { | |
| return E_OUTOFMEMORY; | |
| } | |
| ZeroMemory(pEntry, sizeof(SystemClockAdviseEntry)); | |
| pEntry->hEvent = (HANDLE) hSemaphore; | |
| pEntry->rtBaseTime = rtStartTime; | |
| pEntry->rtIntervalTime = rtPeriodTime; | |
| EnterCriticalSection(&This->safe); | |
| QUARTZ_InsertAviseEntryFromQueue(This, pEntry, &This->pPeriodicAdvise); | |
| LeaveCriticalSection(&This->safe); | |
| SystemClockPostMessageToAdviseThread(This, ADVISE_ADD_PERIODIC); | |
| *pdwAdviseCookie = (DWORD_PTR) (pEntry); | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI SystemClockImpl_Unadvise(IReferenceClock* iface, DWORD_PTR dwAdviseCookie) { | |
| SystemClockImpl *This = impl_from_IReferenceClock(iface); | |
| SystemClockAdviseEntry* pEntry = NULL; | |
| SystemClockAdviseEntry* it = NULL; | |
| HRESULT ret = S_OK; | |
| TRACE("(%p, %lu)\n", This, dwAdviseCookie); | |
| pEntry = (SystemClockAdviseEntry*) dwAdviseCookie; | |
| EnterCriticalSection(&This->safe); | |
| for (it = This->pPeriodicAdvise; NULL != it && it != pEntry; it = it->next) ; | |
| if (it != pEntry) { | |
| for (it = This->pSingleShotAdvise; NULL != it && it != pEntry; it = it->next) ; | |
| if (it != pEntry) { | |
| ret = S_FALSE; | |
| goto out; | |
| } | |
| } | |
| QUARTZ_RemoveAviseEntryFromQueue(This, pEntry); | |
| CoTaskMemFree(pEntry); | |
| SystemClockPostMessageToAdviseThread(This, ADVISE_REMOVE); | |
| out: | |
| LeaveCriticalSection(&This->safe); | |
| return ret; | |
| } | |
| static const IReferenceClockVtbl SystemClock_Vtbl = | |
| { | |
| SystemClockImpl_QueryInterface, | |
| SystemClockImpl_AddRef, | |
| SystemClockImpl_Release, | |
| SystemClockImpl_GetTime, | |
| SystemClockImpl_AdviseTime, | |
| SystemClockImpl_AdvisePeriodic, | |
| SystemClockImpl_Unadvise | |
| }; | |
| HRESULT QUARTZ_CreateSystemClock(IUnknown * pUnkOuter, LPVOID * ppv) { | |
| SystemClockImpl* obj = NULL; | |
| TRACE("(%p,%p)\n", ppv, pUnkOuter); | |
| obj = CoTaskMemAlloc(sizeof(SystemClockImpl)); | |
| if (NULL == obj) { | |
| *ppv = NULL; | |
| return E_OUTOFMEMORY; | |
| } | |
| ZeroMemory(obj, sizeof(SystemClockImpl)); | |
| obj->IReferenceClock_iface.lpVtbl = &SystemClock_Vtbl; | |
| obj->ref = 0; /* will be inited by QueryInterface */ | |
| obj->lastTimeTickCount = GetTickCount(); | |
| InitializeCriticalSection(&obj->safe); | |
| obj->safe.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": SystemClockImpl.safe"); | |
| return SystemClockImpl_QueryInterface(&obj->IReferenceClock_iface, &IID_IReferenceClock, ppv); | |
| } |