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"
27 WINE_DEFAULT_DEBUG_CHANNEL(quartz
);
33 REFERENCE_TIME rtBaseTime
, rtIntervalTime
;
36 typedef struct SystemClockImpl
{
37 IReferenceClock IReferenceClock_iface
;
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
;
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
);
68 list_add_tail(queue
, &sink
->entry
);
71 static DWORD WINAPI
SystemClockAdviseThread(LPVOID lpParam
) {
72 SystemClockImpl
* This
= lpParam
;
73 struct advise_sink
*sink
, *cursor
;
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
);
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
)
92 SetEvent(sink
->hEvent
);
93 list_remove(&sink
->entry
);
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)
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);
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
;
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
);
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
);
183 static HRESULT WINAPI
SystemClockImpl_GetTime(IReferenceClock
*iface
, REFERENCE_TIME
*time
)
185 SystemClockImpl
*clock
= impl_from_IReferenceClock(iface
);
189 TRACE("clock %p, time %p.\n", clock
, time
);
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
);
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
);
219 if (base
+ offset
<= 0)
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
;
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
);
254 if (start
<= 0 || period
<= 0)
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
;
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
);
292 LeaveCriticalSection(&clock
->safe
);
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
);
303 LeaveCriticalSection(&clock
->safe
);
308 LeaveCriticalSection(&clock
->safe
);
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
))))
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
);