msvcrt: Import fmod implementation from musl.
[wine.git] / dlls / quartz / systemclock.c
blob24d8651aabaeaec762475b9a31f8e598aeeb88c6
1 /*
2 * Implementation of IReferenceClock
4 * Copyright 2004 Raphael Junqueira
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.
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.
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
21 #include "quartz_private.h"
23 #include "wine/debug.h"
24 #include <assert.h>
26 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
28 static int cookie_counter;
30 struct advise_sink
32 struct list entry;
33 HANDLE handle;
34 REFERENCE_TIME due_time, period;
35 int cookie;
38 struct system_clock
40 IReferenceClock IReferenceClock_iface;
41 IUnknown IUnknown_inner;
42 IUnknown *outer_unk;
43 LONG refcount;
45 BOOL thread_created;
46 HANDLE thread, notify_event, stop_event;
47 REFERENCE_TIME last_time;
48 CRITICAL_SECTION cs;
50 struct list sinks;
53 static inline struct system_clock *impl_from_IUnknown(IUnknown *iface)
55 return CONTAINING_RECORD(iface, struct system_clock, IUnknown_inner);
58 static HRESULT WINAPI system_clock_inner_QueryInterface(IUnknown *iface, REFIID iid, void **out)
60 struct system_clock *clock = impl_from_IUnknown(iface);
61 TRACE("clock %p, iid %s, out %p.\n", clock, debugstr_guid(iid), out);
63 if (IsEqualGUID(iid, &IID_IUnknown))
64 *out = iface;
65 else if (IsEqualGUID(iid, &IID_IReferenceClock))
66 *out = &clock->IReferenceClock_iface;
67 else
69 WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid));
70 *out = NULL;
71 return E_NOINTERFACE;
74 IUnknown_AddRef((IUnknown *)*out);
75 return S_OK;
78 static ULONG WINAPI system_clock_inner_AddRef(IUnknown *iface)
80 struct system_clock *clock = impl_from_IUnknown(iface);
81 ULONG refcount = InterlockedIncrement(&clock->refcount);
83 TRACE("%p increasing refcount to %u.\n", clock, refcount);
85 return refcount;
88 static ULONG WINAPI system_clock_inner_Release(IUnknown *iface)
90 struct system_clock *clock = impl_from_IUnknown(iface);
91 ULONG refcount = InterlockedDecrement(&clock->refcount);
93 TRACE("%p decreasing refcount to %u.\n", clock, refcount);
95 if (!refcount)
97 if (clock->thread)
99 SetEvent(clock->stop_event);
100 WaitForSingleObject(clock->thread, INFINITE);
101 CloseHandle(clock->thread);
102 CloseHandle(clock->notify_event);
103 CloseHandle(clock->stop_event);
105 clock->cs.DebugInfo->Spare[0] = 0;
106 DeleteCriticalSection(&clock->cs);
107 heap_free(clock);
109 InterlockedDecrement(&object_locks);
111 return refcount;
114 static const IUnknownVtbl system_clock_inner_vtbl =
116 system_clock_inner_QueryInterface,
117 system_clock_inner_AddRef,
118 system_clock_inner_Release,
121 static inline struct system_clock *impl_from_IReferenceClock(IReferenceClock *iface)
123 return CONTAINING_RECORD(iface, struct system_clock, IReferenceClock_iface);
126 static DWORD WINAPI SystemClockAdviseThread(void *param)
128 struct system_clock *clock = param;
129 struct advise_sink *sink, *cursor;
130 REFERENCE_TIME current_time;
131 HANDLE handles[2] = {clock->stop_event, clock->notify_event};
133 TRACE("Starting advise thread for clock %p.\n", clock);
135 for (;;)
137 DWORD timeout = INFINITE;
139 EnterCriticalSection(&clock->cs);
141 current_time = GetTickCount64() * 10000;
143 LIST_FOR_EACH_ENTRY_SAFE(sink, cursor, &clock->sinks, struct advise_sink, entry)
145 if (sink->due_time <= current_time)
147 if (sink->period)
149 DWORD periods = ((current_time - sink->due_time) / sink->period) + 1;
150 ReleaseSemaphore(sink->handle, periods, NULL);
151 sink->due_time += periods * sink->period;
153 else
155 SetEvent(sink->handle);
156 list_remove(&sink->entry);
157 heap_free(sink);
158 continue;
162 timeout = min(timeout, (sink->due_time - current_time) / 10000);
165 LeaveCriticalSection(&clock->cs);
167 if (WaitForMultipleObjects(2, handles, FALSE, timeout) == 0)
168 return 0;
172 static void notify_thread(struct system_clock *clock)
174 if (!InterlockedCompareExchange(&clock->thread_created, TRUE, FALSE))
176 clock->notify_event = CreateEventW(NULL, FALSE, FALSE, NULL);
177 clock->stop_event = CreateEventW(NULL, TRUE, FALSE, NULL);
178 clock->thread = CreateThread(NULL, 0, SystemClockAdviseThread, clock, 0, NULL);
180 SetEvent(clock->notify_event);
183 static HRESULT WINAPI SystemClockImpl_QueryInterface(IReferenceClock *iface, REFIID iid, void **out)
185 struct system_clock *clock = impl_from_IReferenceClock(iface);
186 return IUnknown_QueryInterface(clock->outer_unk, iid, out);
189 static ULONG WINAPI SystemClockImpl_AddRef(IReferenceClock *iface)
191 struct system_clock *clock = impl_from_IReferenceClock(iface);
192 return IUnknown_AddRef(clock->outer_unk);
195 static ULONG WINAPI SystemClockImpl_Release(IReferenceClock *iface)
197 struct system_clock *clock = impl_from_IReferenceClock(iface);
198 return IUnknown_Release(clock->outer_unk);
201 static HRESULT WINAPI SystemClockImpl_GetTime(IReferenceClock *iface, REFERENCE_TIME *time)
203 struct system_clock *clock = impl_from_IReferenceClock(iface);
204 REFERENCE_TIME ret;
205 HRESULT hr;
207 if (!time) {
208 return E_POINTER;
211 ret = GetTickCount64() * 10000;
213 EnterCriticalSection(&clock->cs);
215 hr = (ret == clock->last_time) ? S_FALSE : S_OK;
216 *time = clock->last_time = ret;
218 LeaveCriticalSection(&clock->cs);
220 TRACE("clock %p, time %p, returning %s.\n", clock, time, debugstr_time(ret));
221 return hr;
224 static HRESULT WINAPI SystemClockImpl_AdviseTime(IReferenceClock *iface,
225 REFERENCE_TIME base, REFERENCE_TIME offset, HEVENT event, DWORD_PTR *cookie)
227 struct system_clock *clock = impl_from_IReferenceClock(iface);
228 struct advise_sink *sink;
230 TRACE("clock %p, base %s, offset %s, event %#lx, cookie %p.\n",
231 clock, debugstr_time(base), debugstr_time(offset), event, cookie);
233 if (!event)
234 return E_INVALIDARG;
236 if (base + offset <= 0)
237 return E_INVALIDARG;
239 if (!cookie)
240 return E_POINTER;
242 if (!(sink = heap_alloc_zero(sizeof(*sink))))
243 return E_OUTOFMEMORY;
245 sink->handle = (HANDLE)event;
246 sink->due_time = base + offset;
247 sink->period = 0;
248 sink->cookie = InterlockedIncrement(&cookie_counter);
250 EnterCriticalSection(&clock->cs);
251 list_add_tail(&clock->sinks, &sink->entry);
252 LeaveCriticalSection(&clock->cs);
254 notify_thread(clock);
256 *cookie = sink->cookie;
257 return S_OK;
260 static HRESULT WINAPI SystemClockImpl_AdvisePeriodic(IReferenceClock* iface,
261 REFERENCE_TIME start, REFERENCE_TIME period, HSEMAPHORE semaphore, DWORD_PTR *cookie)
263 struct system_clock *clock = impl_from_IReferenceClock(iface);
264 struct advise_sink *sink;
266 TRACE("clock %p, start %s, period %s, semaphore %#lx, cookie %p.\n",
267 clock, debugstr_time(start), debugstr_time(period), semaphore, cookie);
269 if (!semaphore)
270 return E_INVALIDARG;
272 if (start <= 0 || period <= 0)
273 return E_INVALIDARG;
275 if (!cookie)
276 return E_POINTER;
278 if (!(sink = heap_alloc_zero(sizeof(*sink))))
279 return E_OUTOFMEMORY;
281 sink->handle = (HANDLE)semaphore;
282 sink->due_time = start;
283 sink->period = period;
284 sink->cookie = InterlockedIncrement(&cookie_counter);
286 EnterCriticalSection(&clock->cs);
287 list_add_tail(&clock->sinks, &sink->entry);
288 LeaveCriticalSection(&clock->cs);
290 notify_thread(clock);
292 *cookie = sink->cookie;
293 return S_OK;
296 static HRESULT WINAPI SystemClockImpl_Unadvise(IReferenceClock *iface, DWORD_PTR cookie)
298 struct system_clock *clock = impl_from_IReferenceClock(iface);
299 struct advise_sink *sink;
301 TRACE("clock %p, cookie %#lx.\n", clock, cookie);
303 EnterCriticalSection(&clock->cs);
305 LIST_FOR_EACH_ENTRY(sink, &clock->sinks, struct advise_sink, entry)
307 if (sink->cookie == cookie)
309 list_remove(&sink->entry);
310 heap_free(sink);
311 LeaveCriticalSection(&clock->cs);
312 return S_OK;
316 LeaveCriticalSection(&clock->cs);
318 return S_FALSE;
321 static const IReferenceClockVtbl SystemClock_vtbl =
323 SystemClockImpl_QueryInterface,
324 SystemClockImpl_AddRef,
325 SystemClockImpl_Release,
326 SystemClockImpl_GetTime,
327 SystemClockImpl_AdviseTime,
328 SystemClockImpl_AdvisePeriodic,
329 SystemClockImpl_Unadvise
332 HRESULT system_clock_create(IUnknown *outer, IUnknown **out)
334 struct system_clock *object;
336 TRACE("outer %p, out %p.\n", outer, out);
338 if (!(object = heap_alloc_zero(sizeof(*object))))
340 *out = NULL;
341 return E_OUTOFMEMORY;
344 object->IReferenceClock_iface.lpVtbl = &SystemClock_vtbl;
345 object->IUnknown_inner.lpVtbl = &system_clock_inner_vtbl;
346 object->outer_unk = outer ? outer : &object->IUnknown_inner;
347 object->refcount = 1;
348 list_init(&object->sinks);
349 InitializeCriticalSection(&object->cs);
350 object->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": SystemClockImpl.cs");
352 TRACE("Created system clock %p.\n", object);
353 *out = &object->IUnknown_inner;
355 return S_OK;