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
;
45 BOOL thread_created
, thread_stopped
;
47 LARGE_INTEGER frequency
;
48 REFERENCE_TIME last_time
;
50 CONDITION_VARIABLE cv
;
55 static REFERENCE_TIME
get_current_time(const struct system_clock
*clock
)
59 QueryPerformanceCounter(&time
);
60 return (time
.QuadPart
* 1000) / clock
->frequency
.QuadPart
* 10000;
63 static inline struct system_clock
*impl_from_IUnknown(IUnknown
*iface
)
65 return CONTAINING_RECORD(iface
, struct system_clock
, IUnknown_inner
);
68 static HRESULT WINAPI
system_clock_inner_QueryInterface(IUnknown
*iface
, REFIID iid
, void **out
)
70 struct system_clock
*clock
= impl_from_IUnknown(iface
);
71 TRACE("clock %p, iid %s, out %p.\n", clock
, debugstr_guid(iid
), out
);
73 if (IsEqualGUID(iid
, &IID_IUnknown
))
75 else if (IsEqualGUID(iid
, &IID_IReferenceClock
))
76 *out
= &clock
->IReferenceClock_iface
;
79 WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid
));
84 IUnknown_AddRef((IUnknown
*)*out
);
88 static ULONG WINAPI
system_clock_inner_AddRef(IUnknown
*iface
)
90 struct system_clock
*clock
= impl_from_IUnknown(iface
);
91 ULONG refcount
= InterlockedIncrement(&clock
->refcount
);
93 TRACE("%p increasing refcount to %u.\n", clock
, refcount
);
98 static ULONG WINAPI
system_clock_inner_Release(IUnknown
*iface
)
100 struct system_clock
*clock
= impl_from_IUnknown(iface
);
101 ULONG refcount
= InterlockedDecrement(&clock
->refcount
);
102 struct advise_sink
*sink
, *cursor
;
104 TRACE("%p decreasing refcount to %u.\n", clock
, refcount
);
110 EnterCriticalSection(&clock
->cs
);
111 clock
->thread_stopped
= TRUE
;
112 LeaveCriticalSection(&clock
->cs
);
113 WakeConditionVariable(&clock
->cv
);
114 WaitForSingleObject(clock
->thread
, INFINITE
);
115 CloseHandle(clock
->thread
);
118 LIST_FOR_EACH_ENTRY_SAFE(sink
, cursor
, &clock
->sinks
, struct advise_sink
, entry
)
120 list_remove(&sink
->entry
);
124 clock
->cs
.DebugInfo
->Spare
[0] = 0;
125 DeleteCriticalSection(&clock
->cs
);
131 static const IUnknownVtbl system_clock_inner_vtbl
=
133 system_clock_inner_QueryInterface
,
134 system_clock_inner_AddRef
,
135 system_clock_inner_Release
,
138 static inline struct system_clock
*impl_from_IReferenceClock(IReferenceClock
*iface
)
140 return CONTAINING_RECORD(iface
, struct system_clock
, IReferenceClock_iface
);
143 static DWORD WINAPI
SystemClockAdviseThread(void *param
)
145 struct system_clock
*clock
= param
;
146 struct advise_sink
*sink
, *cursor
;
147 REFERENCE_TIME current_time
;
149 TRACE("Starting advise thread for clock %p.\n", clock
);
153 DWORD timeout
= INFINITE
;
155 EnterCriticalSection(&clock
->cs
);
157 current_time
= get_current_time(clock
);
159 LIST_FOR_EACH_ENTRY_SAFE(sink
, cursor
, &clock
->sinks
, struct advise_sink
, entry
)
161 if (sink
->due_time
<= current_time
)
165 DWORD periods
= ((current_time
- sink
->due_time
) / sink
->period
) + 1;
166 ReleaseSemaphore(sink
->handle
, periods
, NULL
);
167 sink
->due_time
+= periods
* sink
->period
;
171 SetEvent(sink
->handle
);
172 list_remove(&sink
->entry
);
178 timeout
= min(timeout
, (sink
->due_time
- current_time
) / 10000);
181 SleepConditionVariableCS(&clock
->cv
, &clock
->cs
, timeout
);
182 if (clock
->thread_stopped
)
184 LeaveCriticalSection(&clock
->cs
);
187 LeaveCriticalSection(&clock
->cs
);
191 static HRESULT
add_sink(struct system_clock
*clock
, DWORD_PTR handle
,
192 REFERENCE_TIME due_time
, REFERENCE_TIME period
, DWORD_PTR
*cookie
)
194 struct advise_sink
*sink
;
202 if (!(sink
= heap_alloc_zero(sizeof(*sink
))))
203 return E_OUTOFMEMORY
;
205 sink
->handle
= (HANDLE
)handle
;
206 sink
->due_time
= due_time
;
207 sink
->period
= period
;
208 sink
->cookie
= InterlockedIncrement(&cookie_counter
);
209 *cookie
= sink
->cookie
;
211 EnterCriticalSection(&clock
->cs
);
212 list_add_tail(&clock
->sinks
, &sink
->entry
);
213 LeaveCriticalSection(&clock
->cs
);
215 if (!InterlockedCompareExchange(&clock
->thread_created
, TRUE
, FALSE
))
217 clock
->thread
= CreateThread(NULL
, 0, SystemClockAdviseThread
, clock
, 0, NULL
);
219 WakeConditionVariable(&clock
->cv
);
224 static HRESULT WINAPI
SystemClockImpl_QueryInterface(IReferenceClock
*iface
, REFIID iid
, void **out
)
226 struct system_clock
*clock
= impl_from_IReferenceClock(iface
);
227 return IUnknown_QueryInterface(clock
->outer_unk
, iid
, out
);
230 static ULONG WINAPI
SystemClockImpl_AddRef(IReferenceClock
*iface
)
232 struct system_clock
*clock
= impl_from_IReferenceClock(iface
);
233 return IUnknown_AddRef(clock
->outer_unk
);
236 static ULONG WINAPI
SystemClockImpl_Release(IReferenceClock
*iface
)
238 struct system_clock
*clock
= impl_from_IReferenceClock(iface
);
239 return IUnknown_Release(clock
->outer_unk
);
242 static HRESULT WINAPI
SystemClockImpl_GetTime(IReferenceClock
*iface
, REFERENCE_TIME
*time
)
244 struct system_clock
*clock
= impl_from_IReferenceClock(iface
);
252 ret
= get_current_time(clock
);
254 EnterCriticalSection(&clock
->cs
);
256 hr
= (ret
== clock
->last_time
) ? S_FALSE
: S_OK
;
257 *time
= clock
->last_time
= ret
;
259 LeaveCriticalSection(&clock
->cs
);
261 TRACE("clock %p, time %p, returning %s.\n", clock
, time
, debugstr_time(ret
));
265 static HRESULT WINAPI
SystemClockImpl_AdviseTime(IReferenceClock
*iface
,
266 REFERENCE_TIME base
, REFERENCE_TIME offset
, HEVENT event
, DWORD_PTR
*cookie
)
268 struct system_clock
*clock
= impl_from_IReferenceClock(iface
);
270 TRACE("clock %p, base %s, offset %s, event %#lx, cookie %p.\n",
271 clock
, debugstr_time(base
), debugstr_time(offset
), event
, cookie
);
273 if (base
+ offset
<= 0)
276 return add_sink(clock
, event
, base
+ offset
, 0, cookie
);
279 static HRESULT WINAPI
SystemClockImpl_AdvisePeriodic(IReferenceClock
* iface
,
280 REFERENCE_TIME start
, REFERENCE_TIME period
, HSEMAPHORE semaphore
, DWORD_PTR
*cookie
)
282 struct system_clock
*clock
= impl_from_IReferenceClock(iface
);
284 TRACE("clock %p, start %s, period %s, semaphore %#lx, cookie %p.\n",
285 clock
, debugstr_time(start
), debugstr_time(period
), semaphore
, cookie
);
287 if (start
<= 0 || period
<= 0)
290 return add_sink(clock
, semaphore
, start
, period
, cookie
);
293 static HRESULT WINAPI
SystemClockImpl_Unadvise(IReferenceClock
*iface
, DWORD_PTR cookie
)
295 struct system_clock
*clock
= impl_from_IReferenceClock(iface
);
296 struct advise_sink
*sink
;
298 TRACE("clock %p, cookie %#lx.\n", clock
, cookie
);
300 EnterCriticalSection(&clock
->cs
);
302 LIST_FOR_EACH_ENTRY(sink
, &clock
->sinks
, struct advise_sink
, entry
)
304 if (sink
->cookie
== cookie
)
306 list_remove(&sink
->entry
);
308 LeaveCriticalSection(&clock
->cs
);
313 LeaveCriticalSection(&clock
->cs
);
318 static const IReferenceClockVtbl SystemClock_vtbl
=
320 SystemClockImpl_QueryInterface
,
321 SystemClockImpl_AddRef
,
322 SystemClockImpl_Release
,
323 SystemClockImpl_GetTime
,
324 SystemClockImpl_AdviseTime
,
325 SystemClockImpl_AdvisePeriodic
,
326 SystemClockImpl_Unadvise
329 HRESULT
system_clock_create(IUnknown
*outer
, IUnknown
**out
)
331 struct system_clock
*object
;
333 TRACE("outer %p, out %p.\n", outer
, out
);
335 if (!(object
= heap_alloc_zero(sizeof(*object
))))
338 return E_OUTOFMEMORY
;
341 object
->IReferenceClock_iface
.lpVtbl
= &SystemClock_vtbl
;
342 object
->IUnknown_inner
.lpVtbl
= &system_clock_inner_vtbl
;
343 object
->outer_unk
= outer
? outer
: &object
->IUnknown_inner
;
344 object
->refcount
= 1;
345 list_init(&object
->sinks
);
346 InitializeCriticalSection(&object
->cs
);
347 object
->cs
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": SystemClockImpl.cs");
348 QueryPerformanceFrequency(&object
->frequency
);
350 TRACE("Created system clock %p.\n", object
);
351 *out
= &object
->IUnknown_inner
;