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"
26 WINE_DEFAULT_DEBUG_CHANNEL(quartz
);
28 static int cookie_counter
;
34 REFERENCE_TIME due_time
, period
;
40 IReferenceClock IReferenceClock_iface
;
41 IUnknown IUnknown_inner
;
46 HANDLE thread
, notify_event
, stop_event
;
47 REFERENCE_TIME last_time
;
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
))
65 else if (IsEqualGUID(iid
, &IID_IReferenceClock
))
66 *out
= &clock
->IReferenceClock_iface
;
69 WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid
));
74 IUnknown_AddRef((IUnknown
*)*out
);
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
);
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
);
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
);
109 InterlockedDecrement(&object_locks
);
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
);
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
)
149 DWORD periods
= ((current_time
- sink
->due_time
) / sink
->period
) + 1;
150 ReleaseSemaphore(sink
->handle
, periods
, NULL
);
151 sink
->due_time
+= periods
* sink
->period
;
155 SetEvent(sink
->handle
);
156 list_remove(&sink
->entry
);
162 timeout
= min(timeout
, (sink
->due_time
- current_time
) / 10000);
165 LeaveCriticalSection(&clock
->cs
);
167 if (WaitForMultipleObjects(2, handles
, FALSE
, timeout
) == 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
);
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
));
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
);
236 if (base
+ offset
<= 0)
242 if (!(sink
= heap_alloc_zero(sizeof(*sink
))))
243 return E_OUTOFMEMORY
;
245 sink
->handle
= (HANDLE
)event
;
246 sink
->due_time
= base
+ offset
;
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
;
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
);
272 if (start
<= 0 || period
<= 0)
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
;
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
);
311 LeaveCriticalSection(&clock
->cs
);
316 LeaveCriticalSection(&clock
->cs
);
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
))))
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
;