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 LONG cookie_counter
;
34 REFERENCE_TIME due_time
, period
;
40 IReferenceClock IReferenceClock_iface
;
41 IUnknown IUnknown_inner
;
48 REFERENCE_TIME last_time
;
50 CONDITION_VARIABLE cv
;
55 static REFERENCE_TIME
get_current_time(void)
57 return (REFERENCE_TIME
)timeGetTime() * 10000;
60 static inline struct system_clock
*impl_from_IUnknown(IUnknown
*iface
)
62 return CONTAINING_RECORD(iface
, struct system_clock
, IUnknown_inner
);
65 static HRESULT WINAPI
system_clock_inner_QueryInterface(IUnknown
*iface
, REFIID iid
, void **out
)
67 struct system_clock
*clock
= impl_from_IUnknown(iface
);
68 TRACE("clock %p, iid %s, out %p.\n", clock
, debugstr_guid(iid
), out
);
70 if (IsEqualGUID(iid
, &IID_IUnknown
))
72 else if (IsEqualGUID(iid
, &IID_IReferenceClock
))
73 *out
= &clock
->IReferenceClock_iface
;
76 WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid
));
81 IUnknown_AddRef((IUnknown
*)*out
);
85 static ULONG WINAPI
system_clock_inner_AddRef(IUnknown
*iface
)
87 struct system_clock
*clock
= impl_from_IUnknown(iface
);
88 ULONG refcount
= InterlockedIncrement(&clock
->refcount
);
90 TRACE("%p increasing refcount to %lu.\n", clock
, refcount
);
95 static ULONG WINAPI
system_clock_inner_Release(IUnknown
*iface
)
97 struct system_clock
*clock
= impl_from_IUnknown(iface
);
98 ULONG refcount
= InterlockedDecrement(&clock
->refcount
);
99 struct advise_sink
*sink
, *cursor
;
101 TRACE("%p decreasing refcount to %lu.\n", clock
, refcount
);
107 EnterCriticalSection(&clock
->cs
);
108 clock
->thread_stopped
= TRUE
;
109 LeaveCriticalSection(&clock
->cs
);
110 WakeConditionVariable(&clock
->cv
);
111 WaitForSingleObject(clock
->thread
, INFINITE
);
112 CloseHandle(clock
->thread
);
115 LIST_FOR_EACH_ENTRY_SAFE(sink
, cursor
, &clock
->sinks
, struct advise_sink
, entry
)
117 list_remove(&sink
->entry
);
121 clock
->cs
.DebugInfo
->Spare
[0] = 0;
122 DeleteCriticalSection(&clock
->cs
);
128 static const IUnknownVtbl system_clock_inner_vtbl
=
130 system_clock_inner_QueryInterface
,
131 system_clock_inner_AddRef
,
132 system_clock_inner_Release
,
135 static inline struct system_clock
*impl_from_IReferenceClock(IReferenceClock
*iface
)
137 return CONTAINING_RECORD(iface
, struct system_clock
, IReferenceClock_iface
);
140 static DWORD WINAPI
SystemClockAdviseThread(void *param
)
142 struct system_clock
*clock
= param
;
143 struct advise_sink
*sink
, *cursor
;
144 REFERENCE_TIME current_time
;
146 TRACE("Starting advise thread for clock %p.\n", clock
);
150 DWORD timeout
= INFINITE
;
152 EnterCriticalSection(&clock
->cs
);
154 current_time
= get_current_time();
156 LIST_FOR_EACH_ENTRY_SAFE(sink
, cursor
, &clock
->sinks
, struct advise_sink
, entry
)
158 if (sink
->due_time
<= current_time
)
162 DWORD periods
= ((current_time
- sink
->due_time
) / sink
->period
) + 1;
163 ReleaseSemaphore(sink
->handle
, periods
, NULL
);
164 sink
->due_time
+= periods
* sink
->period
;
168 SetEvent(sink
->handle
);
169 list_remove(&sink
->entry
);
175 timeout
= min(timeout
, (sink
->due_time
- current_time
) / 10000);
178 SleepConditionVariableCS(&clock
->cv
, &clock
->cs
, timeout
);
179 if (clock
->thread_stopped
)
181 LeaveCriticalSection(&clock
->cs
);
184 LeaveCriticalSection(&clock
->cs
);
188 static HRESULT
add_sink(struct system_clock
*clock
, DWORD_PTR handle
,
189 REFERENCE_TIME due_time
, REFERENCE_TIME period
, DWORD_PTR
*cookie
)
191 struct advise_sink
*sink
;
199 if (!(sink
= heap_alloc_zero(sizeof(*sink
))))
200 return E_OUTOFMEMORY
;
202 sink
->handle
= (HANDLE
)handle
;
203 sink
->due_time
= due_time
;
204 sink
->period
= period
;
205 sink
->cookie
= InterlockedIncrement(&cookie_counter
);
206 *cookie
= sink
->cookie
;
208 EnterCriticalSection(&clock
->cs
);
209 list_add_tail(&clock
->sinks
, &sink
->entry
);
210 LeaveCriticalSection(&clock
->cs
);
212 if (!InterlockedCompareExchange(&clock
->thread_created
, TRUE
, FALSE
))
214 clock
->thread
= CreateThread(NULL
, 0, SystemClockAdviseThread
, clock
, 0, NULL
);
216 WakeConditionVariable(&clock
->cv
);
221 static HRESULT WINAPI
SystemClockImpl_QueryInterface(IReferenceClock
*iface
, REFIID iid
, void **out
)
223 struct system_clock
*clock
= impl_from_IReferenceClock(iface
);
224 return IUnknown_QueryInterface(clock
->outer_unk
, iid
, out
);
227 static ULONG WINAPI
SystemClockImpl_AddRef(IReferenceClock
*iface
)
229 struct system_clock
*clock
= impl_from_IReferenceClock(iface
);
230 return IUnknown_AddRef(clock
->outer_unk
);
233 static ULONG WINAPI
SystemClockImpl_Release(IReferenceClock
*iface
)
235 struct system_clock
*clock
= impl_from_IReferenceClock(iface
);
236 return IUnknown_Release(clock
->outer_unk
);
239 static HRESULT WINAPI
SystemClockImpl_GetTime(IReferenceClock
*iface
, REFERENCE_TIME
*time
)
241 struct system_clock
*clock
= impl_from_IReferenceClock(iface
);
249 ret
= get_current_time();
251 EnterCriticalSection(&clock
->cs
);
253 hr
= (ret
== clock
->last_time
) ? S_FALSE
: S_OK
;
254 *time
= clock
->last_time
= ret
;
256 LeaveCriticalSection(&clock
->cs
);
258 TRACE("clock %p, time %p, returning %s.\n", clock
, time
, debugstr_time(ret
));
262 static HRESULT WINAPI
SystemClockImpl_AdviseTime(IReferenceClock
*iface
,
263 REFERENCE_TIME base
, REFERENCE_TIME offset
, HEVENT event
, DWORD_PTR
*cookie
)
265 struct system_clock
*clock
= impl_from_IReferenceClock(iface
);
267 TRACE("clock %p, base %s, offset %s, event %#Ix, cookie %p.\n",
268 clock
, debugstr_time(base
), debugstr_time(offset
), event
, cookie
);
270 if (base
+ offset
<= 0)
273 return add_sink(clock
, event
, base
+ offset
, 0, cookie
);
276 static HRESULT WINAPI
SystemClockImpl_AdvisePeriodic(IReferenceClock
* iface
,
277 REFERENCE_TIME start
, REFERENCE_TIME period
, HSEMAPHORE semaphore
, DWORD_PTR
*cookie
)
279 struct system_clock
*clock
= impl_from_IReferenceClock(iface
);
281 TRACE("clock %p, start %s, period %s, semaphore %#Ix, cookie %p.\n",
282 clock
, debugstr_time(start
), debugstr_time(period
), semaphore
, cookie
);
284 if (start
<= 0 || period
<= 0)
287 return add_sink(clock
, semaphore
, start
, period
, cookie
);
290 static HRESULT WINAPI
SystemClockImpl_Unadvise(IReferenceClock
*iface
, DWORD_PTR cookie
)
292 struct system_clock
*clock
= impl_from_IReferenceClock(iface
);
293 struct advise_sink
*sink
;
295 TRACE("clock %p, cookie %#Ix.\n", clock
, cookie
);
297 EnterCriticalSection(&clock
->cs
);
299 LIST_FOR_EACH_ENTRY(sink
, &clock
->sinks
, struct advise_sink
, entry
)
301 if (sink
->cookie
== cookie
)
303 list_remove(&sink
->entry
);
305 LeaveCriticalSection(&clock
->cs
);
310 LeaveCriticalSection(&clock
->cs
);
315 static const IReferenceClockVtbl SystemClock_vtbl
=
317 SystemClockImpl_QueryInterface
,
318 SystemClockImpl_AddRef
,
319 SystemClockImpl_Release
,
320 SystemClockImpl_GetTime
,
321 SystemClockImpl_AdviseTime
,
322 SystemClockImpl_AdvisePeriodic
,
323 SystemClockImpl_Unadvise
326 HRESULT
system_clock_create(IUnknown
*outer
, IUnknown
**out
)
328 struct system_clock
*object
;
330 TRACE("outer %p, out %p.\n", outer
, out
);
332 if (!(object
= heap_alloc_zero(sizeof(*object
))))
335 return E_OUTOFMEMORY
;
338 object
->IReferenceClock_iface
.lpVtbl
= &SystemClock_vtbl
;
339 object
->IUnknown_inner
.lpVtbl
= &system_clock_inner_vtbl
;
340 object
->outer_unk
= outer
? outer
: &object
->IUnknown_inner
;
341 object
->refcount
= 1;
342 list_init(&object
->sinks
);
343 InitializeCriticalSection(&object
->cs
);
344 object
->cs
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": SystemClockImpl.cs");
346 TRACE("Created system clock %p.\n", object
);
347 *out
= &object
->IUnknown_inner
;