|  | /* | 
|  | * 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 */ | 
|  | for (it = This->pSingleShotAdvise; NULL != it && (it->rtBaseTime + it->rtIntervalTime) <= curTime; it = it->next) { | 
|  | /** send event ... */ | 
|  | SetEvent(it->hEvent); | 
|  | /** ... and Release it */ | 
|  | QUARTZ_RemoveAviseEntryFromQueue(This, it); | 
|  | CoTaskMemFree(it); | 
|  | } | 
|  | if (NULL != it) timeOut = (DWORD) ((it->rtBaseTime + it->rtIntervalTime) - curTime) / (REFERENCE_TIME)10000; | 
|  |  | 
|  | /** 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; | 
|  | 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); | 
|  | } |