355 lines
9.8 KiB
C
355 lines
9.8 KiB
C
/*
|
|
* 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);
|
|
}
|
|
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;
|
|
}
|