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
);
147 SetThreadDescription(GetCurrentThread(), L
"wine_qz_clock_advise");
151 DWORD timeout
= INFINITE
;
153 EnterCriticalSection(&clock
->cs
);
155 current_time
= get_current_time();
157 LIST_FOR_EACH_ENTRY_SAFE(sink
, cursor
, &clock
->sinks
, struct advise_sink
, entry
)
159 if (sink
->due_time
<= current_time
)
163 DWORD periods
= ((current_time
- sink
->due_time
) / sink
->period
) + 1;
164 ReleaseSemaphore(sink
->handle
, periods
, NULL
);
165 sink
->due_time
+= periods
* sink
->period
;
169 SetEvent(sink
->handle
);
170 list_remove(&sink
->entry
);
176 timeout
= min(timeout
, (sink
->due_time
- current_time
) / 10000);
179 SleepConditionVariableCS(&clock
->cv
, &clock
->cs
, timeout
);
180 if (clock
->thread_stopped
)
182 LeaveCriticalSection(&clock
->cs
);
185 LeaveCriticalSection(&clock
->cs
);
189 static HRESULT
add_sink(struct system_clock
*clock
, DWORD_PTR handle
,
190 REFERENCE_TIME due_time
, REFERENCE_TIME period
, DWORD_PTR
*cookie
)
192 struct advise_sink
*sink
;
200 if (!(sink
= heap_alloc_zero(sizeof(*sink
))))
201 return E_OUTOFMEMORY
;
203 sink
->handle
= (HANDLE
)handle
;
204 sink
->due_time
= due_time
;
205 sink
->period
= period
;
206 sink
->cookie
= InterlockedIncrement(&cookie_counter
);
207 *cookie
= sink
->cookie
;
209 EnterCriticalSection(&clock
->cs
);
210 list_add_tail(&clock
->sinks
, &sink
->entry
);
211 LeaveCriticalSection(&clock
->cs
);
213 if (!InterlockedCompareExchange(&clock
->thread_created
, TRUE
, FALSE
))
215 clock
->thread
= CreateThread(NULL
, 0, SystemClockAdviseThread
, clock
, 0, NULL
);
217 WakeConditionVariable(&clock
->cv
);
222 static HRESULT WINAPI
SystemClockImpl_QueryInterface(IReferenceClock
*iface
, REFIID iid
, void **out
)
224 struct system_clock
*clock
= impl_from_IReferenceClock(iface
);
225 return IUnknown_QueryInterface(clock
->outer_unk
, iid
, out
);
228 static ULONG WINAPI
SystemClockImpl_AddRef(IReferenceClock
*iface
)
230 struct system_clock
*clock
= impl_from_IReferenceClock(iface
);
231 return IUnknown_AddRef(clock
->outer_unk
);
234 static ULONG WINAPI
SystemClockImpl_Release(IReferenceClock
*iface
)
236 struct system_clock
*clock
= impl_from_IReferenceClock(iface
);
237 return IUnknown_Release(clock
->outer_unk
);
240 static HRESULT WINAPI
SystemClockImpl_GetTime(IReferenceClock
*iface
, REFERENCE_TIME
*time
)
242 struct system_clock
*clock
= impl_from_IReferenceClock(iface
);
250 ret
= get_current_time();
252 EnterCriticalSection(&clock
->cs
);
254 hr
= (ret
== clock
->last_time
) ? S_FALSE
: S_OK
;
255 *time
= clock
->last_time
= ret
;
257 LeaveCriticalSection(&clock
->cs
);
259 TRACE("clock %p, time %p, returning %s.\n", clock
, time
, debugstr_time(ret
));
263 static HRESULT WINAPI
SystemClockImpl_AdviseTime(IReferenceClock
*iface
,
264 REFERENCE_TIME base
, REFERENCE_TIME offset
, HEVENT event
, DWORD_PTR
*cookie
)
266 struct system_clock
*clock
= impl_from_IReferenceClock(iface
);
268 TRACE("clock %p, base %s, offset %s, event %#Ix, cookie %p.\n",
269 clock
, debugstr_time(base
), debugstr_time(offset
), event
, cookie
);
271 if (base
+ offset
<= 0)
274 return add_sink(clock
, event
, base
+ offset
, 0, cookie
);
277 static HRESULT WINAPI
SystemClockImpl_AdvisePeriodic(IReferenceClock
* iface
,
278 REFERENCE_TIME start
, REFERENCE_TIME period
, HSEMAPHORE semaphore
, DWORD_PTR
*cookie
)
280 struct system_clock
*clock
= impl_from_IReferenceClock(iface
);
282 TRACE("clock %p, start %s, period %s, semaphore %#Ix, cookie %p.\n",
283 clock
, debugstr_time(start
), debugstr_time(period
), semaphore
, cookie
);
285 if (start
<= 0 || period
<= 0)
288 return add_sink(clock
, semaphore
, start
, period
, cookie
);
291 static HRESULT WINAPI
SystemClockImpl_Unadvise(IReferenceClock
*iface
, DWORD_PTR cookie
)
293 struct system_clock
*clock
= impl_from_IReferenceClock(iface
);
294 struct advise_sink
*sink
;
296 TRACE("clock %p, cookie %#Ix.\n", clock
, cookie
);
298 EnterCriticalSection(&clock
->cs
);
300 LIST_FOR_EACH_ENTRY(sink
, &clock
->sinks
, struct advise_sink
, entry
)
302 if (sink
->cookie
== cookie
)
304 list_remove(&sink
->entry
);
306 LeaveCriticalSection(&clock
->cs
);
311 LeaveCriticalSection(&clock
->cs
);
316 static const IReferenceClockVtbl SystemClock_vtbl
=
318 SystemClockImpl_QueryInterface
,
319 SystemClockImpl_AddRef
,
320 SystemClockImpl_Release
,
321 SystemClockImpl_GetTime
,
322 SystemClockImpl_AdviseTime
,
323 SystemClockImpl_AdvisePeriodic
,
324 SystemClockImpl_Unadvise
327 HRESULT
system_clock_create(IUnknown
*outer
, IUnknown
**out
)
329 struct system_clock
*object
;
331 TRACE("outer %p, out %p.\n", outer
, out
);
333 if (!(object
= heap_alloc_zero(sizeof(*object
))))
336 return E_OUTOFMEMORY
;
339 object
->IReferenceClock_iface
.lpVtbl
= &SystemClock_vtbl
;
340 object
->IUnknown_inner
.lpVtbl
= &system_clock_inner_vtbl
;
341 object
->outer_unk
= outer
? outer
: &object
->IUnknown_inner
;
342 object
->refcount
= 1;
343 list_init(&object
->sinks
);
344 InitializeCriticalSectionEx(&object
->cs
, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO
);
345 object
->cs
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": SystemClockImpl.cs");
347 TRACE("Created system clock %p.\n", object
);
348 *out
= &object
->IUnknown_inner
;