quartz/systemclock: Simplify notifying the advise thread.
[wine.git] / dlls / quartz / systemclock.c
blob8291fe5fb283ff31f0474db453de7109512eaf28
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 "wine/unicode.h"
25 #include <assert.h>
27 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
29 struct advise_sink
31 struct list entry;
32 HANDLE hEvent;
33 REFERENCE_TIME rtBaseTime, rtIntervalTime;
36 typedef struct SystemClockImpl {
37 IReferenceClock IReferenceClock_iface;
38 LONG ref;
40 BOOL thread_created;
41 HANDLE thread, notify_event, stop_event;
42 REFERENCE_TIME last_time;
43 CRITICAL_SECTION safe;
45 /* These lists are ordered by expiration time (soonest first). */
46 struct list single_sinks, periodic_sinks;
47 } SystemClockImpl;
49 static inline SystemClockImpl *impl_from_IReferenceClock(IReferenceClock *iface)
51 return CONTAINING_RECORD(iface, SystemClockImpl, IReferenceClock_iface);
54 static void insert_advise_sink(struct advise_sink *sink, struct list *queue)
56 REFERENCE_TIME due_time = sink->rtBaseTime + sink->rtIntervalTime;
57 struct advise_sink *cursor;
59 LIST_FOR_EACH_ENTRY(cursor, queue, struct advise_sink, entry)
61 if (cursor->rtBaseTime + cursor->rtIntervalTime > due_time)
63 list_add_before(&cursor->entry, &sink->entry);
64 return;
68 list_add_tail(queue, &sink->entry);
71 static DWORD WINAPI SystemClockAdviseThread(LPVOID lpParam) {
72 SystemClockImpl* This = lpParam;
73 struct advise_sink *sink, *cursor;
74 struct list *entry;
75 DWORD timeOut = INFINITE;
76 REFERENCE_TIME curTime;
77 HANDLE handles[2] = {This->stop_event, This->notify_event};
79 TRACE("(%p): Main Loop\n", This);
81 while (TRUE) {
82 EnterCriticalSection(&This->safe);
84 curTime = GetTickCount64() * 10000;
86 /** First SingleShots Advice: sorted list */
87 LIST_FOR_EACH_ENTRY_SAFE(sink, cursor, &This->single_sinks, struct advise_sink, entry)
89 if (sink->rtBaseTime + sink->rtIntervalTime > curTime)
90 break;
92 SetEvent(sink->hEvent);
93 list_remove(&sink->entry);
94 heap_free(sink);
97 if ((entry = list_head(&This->single_sinks)))
99 sink = LIST_ENTRY(entry, struct advise_sink, entry);
100 timeOut = (sink->rtBaseTime + sink->rtIntervalTime - curTime) / 10000;
102 else timeOut = INFINITE;
104 /** Now Periodics Advice: semi sorted list (sort cannot be used) */
105 LIST_FOR_EACH_ENTRY(sink, &This->periodic_sinks, struct advise_sink, entry)
107 if (sink->rtBaseTime <= curTime)
109 DWORD periods = (curTime - sink->rtBaseTime) / sink->rtIntervalTime;
110 ReleaseSemaphore(sink->hEvent, periods, NULL);
111 sink->rtBaseTime += periods * sink->rtIntervalTime;
113 timeOut = min(timeOut, ((sink->rtBaseTime + sink->rtIntervalTime) - curTime) / 10000);
116 LeaveCriticalSection(&This->safe);
118 if (WaitForMultipleObjects(2, handles, FALSE, timeOut) == 0)
119 return 0;
123 static void notify_thread(SystemClockImpl *clock)
125 if (!InterlockedCompareExchange(&clock->thread_created, TRUE, FALSE))
127 clock->thread = CreateThread(NULL, 0, SystemClockAdviseThread, clock, 0, NULL);
128 clock->notify_event = CreateEventW(NULL, FALSE, FALSE, NULL);
129 clock->stop_event = CreateEventW(NULL, TRUE, FALSE, NULL);
131 SetEvent(clock->notify_event);
134 static ULONG WINAPI SystemClockImpl_AddRef(IReferenceClock* iface) {
135 SystemClockImpl *This = impl_from_IReferenceClock(iface);
136 ULONG ref = InterlockedIncrement(&This->ref);
138 TRACE("(%p): AddRef from %d\n", This, ref - 1);
140 return ref;
143 static HRESULT WINAPI SystemClockImpl_QueryInterface(IReferenceClock* iface, REFIID riid, void** ppobj) {
144 SystemClockImpl *This = impl_from_IReferenceClock(iface);
145 TRACE("(%p, %s,%p)\n", This, debugstr_guid(riid), ppobj);
147 if (IsEqualIID (riid, &IID_IUnknown) ||
148 IsEqualIID (riid, &IID_IReferenceClock)) {
149 SystemClockImpl_AddRef(iface);
150 *ppobj = &This->IReferenceClock_iface;
151 return S_OK;
154 *ppobj = NULL;
155 WARN("(%p, %s,%p): not found\n", This, debugstr_guid(riid), ppobj);
156 return E_NOINTERFACE;
159 static ULONG WINAPI SystemClockImpl_Release(IReferenceClock *iface)
161 SystemClockImpl *clock = impl_from_IReferenceClock(iface);
162 ULONG refcount = InterlockedDecrement(&clock->ref);
164 TRACE("%p decreasing refcount to %u.\n", clock, refcount);
166 if (!refcount)
168 if (clock->thread)
170 SetEvent(clock->stop_event);
171 WaitForSingleObject(clock->thread, INFINITE);
172 CloseHandle(clock->thread);
173 CloseHandle(clock->notify_event);
174 CloseHandle(clock->stop_event);
176 clock->safe.DebugInfo->Spare[0] = 0;
177 DeleteCriticalSection(&clock->safe);
178 heap_free(clock);
180 return refcount;
183 static HRESULT WINAPI SystemClockImpl_GetTime(IReferenceClock *iface, REFERENCE_TIME *time)
185 SystemClockImpl *clock = impl_from_IReferenceClock(iface);
186 REFERENCE_TIME ret;
187 HRESULT hr;
189 TRACE("clock %p, time %p.\n", clock, time);
191 if (!time) {
192 return E_POINTER;
195 ret = GetTickCount64() * 10000;
197 EnterCriticalSection(&clock->safe);
199 hr = (ret == clock->last_time) ? S_FALSE : S_OK;
200 *time = clock->last_time = ret;
202 LeaveCriticalSection(&clock->safe);
204 return hr;
207 static HRESULT WINAPI SystemClockImpl_AdviseTime(IReferenceClock *iface,
208 REFERENCE_TIME base, REFERENCE_TIME offset, HEVENT event, DWORD_PTR *cookie)
210 SystemClockImpl *clock = impl_from_IReferenceClock(iface);
211 struct advise_sink *sink;
213 TRACE("clock %p, base %s, offset %s, event %#lx, cookie %p.\n",
214 clock, wine_dbgstr_longlong(base), wine_dbgstr_longlong(offset), event, cookie);
216 if (!event)
217 return E_INVALIDARG;
219 if (base + offset <= 0)
220 return E_INVALIDARG;
222 if (!cookie)
223 return E_POINTER;
225 if (!(sink = heap_alloc_zero(sizeof(*sink))))
226 return E_OUTOFMEMORY;
228 sink->hEvent = (HANDLE)event;
229 sink->rtBaseTime = base + offset;
230 sink->rtIntervalTime = 0;
232 EnterCriticalSection(&clock->safe);
233 insert_advise_sink(sink, &clock->single_sinks);
234 LeaveCriticalSection(&clock->safe);
236 notify_thread(clock);
238 *cookie = (DWORD_PTR)sink;
239 return S_OK;
242 static HRESULT WINAPI SystemClockImpl_AdvisePeriodic(IReferenceClock* iface,
243 REFERENCE_TIME start, REFERENCE_TIME period, HSEMAPHORE semaphore, DWORD_PTR *cookie)
245 SystemClockImpl *clock = impl_from_IReferenceClock(iface);
246 struct advise_sink *sink;
248 TRACE("clock %p, start %s, period %s, semaphore %#lx, cookie %p.\n",
249 clock, wine_dbgstr_longlong(start), wine_dbgstr_longlong(period), semaphore, cookie);
251 if (!semaphore)
252 return E_INVALIDARG;
254 if (start <= 0 || period <= 0)
255 return E_INVALIDARG;
257 if (!cookie)
258 return E_POINTER;
260 if (!(sink = heap_alloc_zero(sizeof(*sink))))
261 return E_OUTOFMEMORY;
263 sink->hEvent = (HANDLE)semaphore;
264 sink->rtBaseTime = start;
265 sink->rtIntervalTime = period;
267 EnterCriticalSection(&clock->safe);
268 insert_advise_sink(sink, &clock->periodic_sinks);
269 LeaveCriticalSection(&clock->safe);
271 notify_thread(clock);
273 *cookie = (DWORD_PTR)sink;
274 return S_OK;
277 static HRESULT WINAPI SystemClockImpl_Unadvise(IReferenceClock *iface, DWORD_PTR cookie)
279 SystemClockImpl *clock = impl_from_IReferenceClock(iface);
280 struct advise_sink *sink;
282 TRACE("clock %p, cookie %#lx.\n", clock, cookie);
284 EnterCriticalSection(&clock->safe);
286 LIST_FOR_EACH_ENTRY(sink, &clock->single_sinks, struct advise_sink, entry)
288 if (sink == (struct advise_sink *)cookie)
290 list_remove(&sink->entry);
291 heap_free(sink);
292 LeaveCriticalSection(&clock->safe);
293 return S_OK;
297 LIST_FOR_EACH_ENTRY(sink, &clock->periodic_sinks, struct advise_sink, entry)
299 if (sink == (struct advise_sink *)cookie)
301 list_remove(&sink->entry);
302 heap_free(sink);
303 LeaveCriticalSection(&clock->safe);
304 return S_OK;
308 LeaveCriticalSection(&clock->safe);
310 return S_FALSE;
313 static const IReferenceClockVtbl SystemClock_Vtbl =
315 SystemClockImpl_QueryInterface,
316 SystemClockImpl_AddRef,
317 SystemClockImpl_Release,
318 SystemClockImpl_GetTime,
319 SystemClockImpl_AdviseTime,
320 SystemClockImpl_AdvisePeriodic,
321 SystemClockImpl_Unadvise
324 HRESULT QUARTZ_CreateSystemClock(IUnknown *outer, void **out)
326 SystemClockImpl *object;
328 TRACE("outer %p, out %p.\n", outer, out);
330 if (!(object = heap_alloc_zero(sizeof(*object))))
332 *out = NULL;
333 return E_OUTOFMEMORY;
336 object->IReferenceClock_iface.lpVtbl = &SystemClock_Vtbl;
337 list_init(&object->single_sinks);
338 list_init(&object->periodic_sinks);
339 InitializeCriticalSection(&object->safe);
340 object->safe.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": SystemClockImpl.safe");
342 return SystemClockImpl_QueryInterface(&object->IReferenceClock_iface, &IID_IReferenceClock, out);