/* * 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 <assert.h> WINE_DEFAULT_DEBUG_CHANNEL(quartz); static int cookie_counter; struct advise_sink { struct list entry; HANDLE handle; REFERENCE_TIME due_time, period; int cookie; }; struct system_clock { IReferenceClock IReferenceClock_iface; IUnknown IUnknown_inner; IUnknown *outer_unk; LONG refcount; BOOL thread_created; HANDLE thread, notify_event, stop_event; REFERENCE_TIME last_time; CRITICAL_SECTION cs; struct list sinks; }; static inline struct system_clock *impl_from_IUnknown(IUnknown *iface) { return CONTAINING_RECORD(iface, struct system_clock, IUnknown_inner); } static HRESULT WINAPI system_clock_inner_QueryInterface(IUnknown *iface, REFIID iid, void **out) { struct system_clock *clock = impl_from_IUnknown(iface); TRACE("clock %p, iid %s, out %p.\n", clock, debugstr_guid(iid), out); if (IsEqualGUID(iid, &IID_IUnknown)) *out = iface; else if (IsEqualGUID(iid, &IID_IReferenceClock)) *out = &clock->IReferenceClock_iface; else { WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); *out = NULL; return E_NOINTERFACE; } IUnknown_AddRef((IUnknown *)*out); return S_OK; } static ULONG WINAPI system_clock_inner_AddRef(IUnknown *iface) { struct system_clock *clock = impl_from_IUnknown(iface); ULONG refcount = InterlockedIncrement(&clock->refcount); TRACE("%p increasing refcount to %u.\n", clock, refcount); return refcount; } static ULONG WINAPI system_clock_inner_Release(IUnknown *iface) { struct system_clock *clock = impl_from_IUnknown(iface); ULONG refcount = InterlockedDecrement(&clock->refcount); TRACE("%p decreasing refcount to %u.\n", clock, refcount); if (!refcount) { if (clock->thread) { SetEvent(clock->stop_event); WaitForSingleObject(clock->thread, INFINITE); CloseHandle(clock->thread); CloseHandle(clock->notify_event); CloseHandle(clock->stop_event); } clock->cs.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&clock->cs); heap_free(clock); InterlockedDecrement(&object_locks); } return refcount; } static const IUnknownVtbl system_clock_inner_vtbl = { system_clock_inner_QueryInterface, system_clock_inner_AddRef, system_clock_inner_Release, }; static inline struct system_clock *impl_from_IReferenceClock(IReferenceClock *iface) { return CONTAINING_RECORD(iface, struct system_clock, IReferenceClock_iface); } static DWORD WINAPI SystemClockAdviseThread(void *param) { struct system_clock *clock = param; struct advise_sink *sink, *cursor; REFERENCE_TIME current_time; HANDLE handles[2] = {clock->stop_event, clock->notify_event}; TRACE("Starting advise thread for clock %p.\n", clock); for (;;) { DWORD timeout = INFINITE; EnterCriticalSection(&clock->cs); current_time = GetTickCount64() * 10000; LIST_FOR_EACH_ENTRY_SAFE(sink, cursor, &clock->sinks, struct advise_sink, entry) { if (sink->due_time <= current_time) { if (sink->period) { DWORD periods = ((current_time - sink->due_time) / sink->period) + 1; ReleaseSemaphore(sink->handle, periods, NULL); sink->due_time += periods * sink->period; } else { SetEvent(sink->handle); list_remove(&sink->entry); heap_free(sink); continue; } } timeout = min(timeout, (sink->due_time - current_time) / 10000); } LeaveCriticalSection(&clock->cs); if (WaitForMultipleObjects(2, handles, FALSE, timeout) == 0) return 0; } } static void notify_thread(struct system_clock *clock) { if (!InterlockedCompareExchange(&clock->thread_created, TRUE, FALSE)) { clock->notify_event = CreateEventW(NULL, FALSE, FALSE, NULL); clock->stop_event = CreateEventW(NULL, TRUE, FALSE, NULL); clock->thread = CreateThread(NULL, 0, SystemClockAdviseThread, clock, 0, NULL); } SetEvent(clock->notify_event); } static HRESULT WINAPI SystemClockImpl_QueryInterface(IReferenceClock *iface, REFIID iid, void **out) { struct system_clock *clock = impl_from_IReferenceClock(iface); return IUnknown_QueryInterface(clock->outer_unk, iid, out); } static ULONG WINAPI SystemClockImpl_AddRef(IReferenceClock *iface) { struct system_clock *clock = impl_from_IReferenceClock(iface); return IUnknown_AddRef(clock->outer_unk); } static ULONG WINAPI SystemClockImpl_Release(IReferenceClock *iface) { struct system_clock *clock = impl_from_IReferenceClock(iface); return IUnknown_Release(clock->outer_unk); } static HRESULT WINAPI SystemClockImpl_GetTime(IReferenceClock *iface, REFERENCE_TIME *time) { struct system_clock *clock = impl_from_IReferenceClock(iface); REFERENCE_TIME ret; HRESULT hr; if (!time) { return E_POINTER; } ret = GetTickCount64() * 10000; EnterCriticalSection(&clock->cs); hr = (ret == clock->last_time) ? S_FALSE : S_OK; *time = clock->last_time = ret; LeaveCriticalSection(&clock->cs); TRACE("clock %p, time %p, returning %s.\n", clock, time, debugstr_time(ret)); return hr; } static HRESULT WINAPI SystemClockImpl_AdviseTime(IReferenceClock *iface, REFERENCE_TIME base, REFERENCE_TIME offset, HEVENT event, DWORD_PTR *cookie) { struct system_clock *clock = impl_from_IReferenceClock(iface); struct advise_sink *sink; TRACE("clock %p, base %s, offset %s, event %#lx, cookie %p.\n", clock, debugstr_time(base), debugstr_time(offset), event, cookie); if (!event) return E_INVALIDARG; if (base + offset <= 0) return E_INVALIDARG; if (!cookie) return E_POINTER; if (!(sink = heap_alloc_zero(sizeof(*sink)))) return E_OUTOFMEMORY; sink->handle = (HANDLE)event; sink->due_time = base + offset; sink->period = 0; sink->cookie = InterlockedIncrement(&cookie_counter); EnterCriticalSection(&clock->cs); list_add_tail(&clock->sinks, &sink->entry); LeaveCriticalSection(&clock->cs); notify_thread(clock); *cookie = sink->cookie; return S_OK; } static HRESULT WINAPI SystemClockImpl_AdvisePeriodic(IReferenceClock* iface, REFERENCE_TIME start, REFERENCE_TIME period, HSEMAPHORE semaphore, DWORD_PTR *cookie) { struct system_clock *clock = impl_from_IReferenceClock(iface); struct advise_sink *sink; TRACE("clock %p, start %s, period %s, semaphore %#lx, cookie %p.\n", clock, debugstr_time(start), debugstr_time(period), semaphore, cookie); if (!semaphore) return E_INVALIDARG; if (start <= 0 || period <= 0) return E_INVALIDARG; if (!cookie) return E_POINTER; if (!(sink = heap_alloc_zero(sizeof(*sink)))) return E_OUTOFMEMORY; sink->handle = (HANDLE)semaphore; sink->due_time = start; sink->period = period; sink->cookie = InterlockedIncrement(&cookie_counter); EnterCriticalSection(&clock->cs); list_add_tail(&clock->sinks, &sink->entry); LeaveCriticalSection(&clock->cs); notify_thread(clock); *cookie = sink->cookie; return S_OK; } static HRESULT WINAPI SystemClockImpl_Unadvise(IReferenceClock *iface, DWORD_PTR cookie) { struct system_clock *clock = impl_from_IReferenceClock(iface); struct advise_sink *sink; TRACE("clock %p, cookie %#lx.\n", clock, cookie); EnterCriticalSection(&clock->cs); LIST_FOR_EACH_ENTRY(sink, &clock->sinks, struct advise_sink, entry) { if (sink->cookie == cookie) { list_remove(&sink->entry); heap_free(sink); LeaveCriticalSection(&clock->cs); return S_OK; } } LeaveCriticalSection(&clock->cs); return S_FALSE; } static const IReferenceClockVtbl SystemClock_vtbl = { SystemClockImpl_QueryInterface, SystemClockImpl_AddRef, SystemClockImpl_Release, SystemClockImpl_GetTime, SystemClockImpl_AdviseTime, SystemClockImpl_AdvisePeriodic, SystemClockImpl_Unadvise }; HRESULT system_clock_create(IUnknown *outer, IUnknown **out) { struct system_clock *object; TRACE("outer %p, out %p.\n", outer, out); if (!(object = heap_alloc_zero(sizeof(*object)))) { *out = NULL; return E_OUTOFMEMORY; } object->IReferenceClock_iface.lpVtbl = &SystemClock_vtbl; object->IUnknown_inner.lpVtbl = &system_clock_inner_vtbl; object->outer_unk = outer ? outer : &object->IUnknown_inner; object->refcount = 1; list_init(&object->sinks); InitializeCriticalSection(&object->cs); object->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": SystemClockImpl.cs"); TRACE("Created system clock %p.\n", object); *out = &object->IUnknown_inner; return S_OK; }