2 * Copyright 2019-2020 Nikolay Sivov for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #define NONAMELESSUNION
26 #include "wine/debug.h"
27 #include "wine/list.h"
29 WINE_DEFAULT_DEBUG_CHANNEL(mfplat
);
31 #define FIRST_USER_QUEUE_HANDLE 5
32 #define MAX_USER_QUEUE_HANDLES 124
34 #define WAIT_ITEM_KEY_MASK (0x82000000)
35 #define SCHEDULED_ITEM_KEY_MASK (0x80000000)
37 static LONG next_item_key
;
39 static RTWQWORKITEM_KEY
get_item_key(DWORD mask
, DWORD key
)
41 return ((RTWQWORKITEM_KEY
)mask
<< 32) | key
;
44 static RTWQWORKITEM_KEY
generate_item_key(DWORD mask
)
46 return get_item_key(mask
, InterlockedIncrement(&next_item_key
));
56 static struct queue_handle user_queues
[MAX_USER_QUEUE_HANDLES
];
57 static struct queue_handle
*next_free_user_queue
;
58 static struct queue_handle
*next_unused_user_queue
= user_queues
;
59 static WORD queue_generation
;
60 static DWORD shared_mt_queue
;
62 static CRITICAL_SECTION queues_section
;
63 static CRITICAL_SECTION_DEBUG queues_critsect_debug
=
65 0, 0, &queues_section
,
66 { &queues_critsect_debug
.ProcessLocksList
, &queues_critsect_debug
.ProcessLocksList
},
67 0, 0, { (DWORD_PTR
)(__FILE__
": queues_section") }
69 static CRITICAL_SECTION queues_section
= { &queues_critsect_debug
, -1, 0, 0, 0, 0 };
71 static LONG platform_lock
;
72 static CO_MTA_USAGE_COOKIE mta_cookie
;
74 static struct queue_handle
*get_queue_obj(DWORD handle
)
76 unsigned int idx
= HIWORD(handle
) - FIRST_USER_QUEUE_HANDLE
;
78 if (idx
< MAX_USER_QUEUE_HANDLES
&& user_queues
[idx
].refcount
)
80 if (LOWORD(handle
) == user_queues
[idx
].generation
)
81 return &user_queues
[idx
];
87 /* Should be kept in sync with corresponding MFASYNC_CALLBACK_ constants. */
88 enum rtwq_callback_queue_id
90 RTWQ_CALLBACK_QUEUE_UNDEFINED
= 0x00000000,
91 RTWQ_CALLBACK_QUEUE_STANDARD
= 0x00000001,
92 RTWQ_CALLBACK_QUEUE_RT
= 0x00000002,
93 RTWQ_CALLBACK_QUEUE_IO
= 0x00000003,
94 RTWQ_CALLBACK_QUEUE_TIMER
= 0x00000004,
95 RTWQ_CALLBACK_QUEUE_MULTITHREADED
= 0x00000005,
96 RTWQ_CALLBACK_QUEUE_LONG_FUNCTION
= 0x00000007,
97 RTWQ_CALLBACK_QUEUE_PRIVATE_MASK
= 0xffff0000,
98 RTWQ_CALLBACK_QUEUE_ALL
= 0xffffffff,
101 /* Should be kept in sync with corresponding MFASYNC_ constants. */
102 enum rtwq_callback_flags
104 RTWQ_FAST_IO_PROCESSING_CALLBACK
= 0x00000001,
105 RTWQ_SIGNAL_CALLBACK
= 0x00000002,
106 RTWQ_BLOCKING_CALLBACK
= 0x00000004,
107 RTWQ_REPLY_CALLBACK
= 0x00000008,
108 RTWQ_LOCALIZE_REMOTE_CALLBACK
= 0x00000010,
111 enum system_queue_index
113 SYS_QUEUE_STANDARD
= 0,
117 SYS_QUEUE_MULTITHREADED
,
118 SYS_QUEUE_DO_NOT_USE
,
119 SYS_QUEUE_LONG_FUNCTION
,
125 IUnknown IUnknown_iface
;
128 IRtwqAsyncResult
*result
;
129 IRtwqAsyncResult
*reply_result
;
131 RTWQWORKITEM_KEY key
;
134 TP_WORK
*work_object
;
135 PTP_SIMPLE_CALLBACK finalization_callback
;
138 TP_WAIT
*wait_object
;
139 TP_TIMER
*timer_object
;
143 static struct work_item
*work_item_impl_from_IUnknown(IUnknown
*iface
)
145 return CONTAINING_RECORD(iface
, struct work_item
, IUnknown_iface
);
148 static const TP_CALLBACK_PRIORITY priorities
[] =
150 TP_CALLBACK_PRIORITY_HIGH
,
151 TP_CALLBACK_PRIORITY_NORMAL
,
152 TP_CALLBACK_PRIORITY_LOW
,
160 HRESULT (*init
)(const struct queue_desc
*desc
, struct queue
*queue
);
161 BOOL (*shutdown
)(struct queue
*queue
);
162 void (*submit
)(struct queue
*queue
, struct work_item
*item
);
167 RTWQ_WORKQUEUE_TYPE queue_type
;
168 const struct queue_ops
*ops
;
174 IRtwqAsyncCallback IRtwqAsyncCallback_iface
;
175 const struct queue_ops
*ops
;
177 TP_CALLBACK_ENVIRON_V3 envs
[ARRAY_SIZE(priorities
)];
179 struct list pending_items
;
181 /* Data used for serial queues only. */
182 PTP_SIMPLE_CALLBACK finalization_callback
;
186 static void shutdown_queue(struct queue
*queue
);
188 static HRESULT
lock_user_queue(DWORD queue
)
190 HRESULT hr
= RTWQ_E_INVALID_WORKQUEUE
;
191 struct queue_handle
*entry
;
193 if (!(queue
& RTWQ_CALLBACK_QUEUE_PRIVATE_MASK
))
196 EnterCriticalSection(&queues_section
);
197 entry
= get_queue_obj(queue
);
198 if (entry
&& entry
->refcount
)
203 LeaveCriticalSection(&queues_section
);
207 static HRESULT
unlock_user_queue(DWORD queue
)
209 HRESULT hr
= RTWQ_E_INVALID_WORKQUEUE
;
210 struct queue_handle
*entry
;
212 if (!(queue
& RTWQ_CALLBACK_QUEUE_PRIVATE_MASK
))
215 EnterCriticalSection(&queues_section
);
216 entry
= get_queue_obj(queue
);
217 if (entry
&& entry
->refcount
)
219 if (--entry
->refcount
== 0)
221 if (shared_mt_queue
== queue
) shared_mt_queue
= 0;
222 shutdown_queue((struct queue
*)entry
->obj
);
224 entry
->obj
= next_free_user_queue
;
225 next_free_user_queue
= entry
;
229 LeaveCriticalSection(&queues_section
);
233 static struct queue
*queue_impl_from_IRtwqAsyncCallback(IRtwqAsyncCallback
*iface
)
235 return CONTAINING_RECORD(iface
, struct queue
, IRtwqAsyncCallback_iface
);
238 static HRESULT WINAPI
queue_serial_callback_QueryInterface(IRtwqAsyncCallback
*iface
, REFIID riid
, void **obj
)
240 if (IsEqualIID(riid
, &IID_IRtwqAsyncCallback
) ||
241 IsEqualIID(riid
, &IID_IUnknown
))
244 IRtwqAsyncCallback_AddRef(iface
);
249 return E_NOINTERFACE
;
252 static ULONG WINAPI
queue_serial_callback_AddRef(IRtwqAsyncCallback
*iface
)
257 static ULONG WINAPI
queue_serial_callback_Release(IRtwqAsyncCallback
*iface
)
262 static HRESULT WINAPI
queue_serial_callback_GetParameters(IRtwqAsyncCallback
*iface
, DWORD
*flags
, DWORD
*queue_id
)
264 struct queue
*queue
= queue_impl_from_IRtwqAsyncCallback(iface
);
267 *queue_id
= queue
->id
;
272 static HRESULT WINAPI
queue_serial_callback_Invoke(IRtwqAsyncCallback
*iface
, IRtwqAsyncResult
*result
)
274 /* Reply callback won't be called in a regular way, pending items and chained queues will make it
275 unnecessary complicated to reach actual work queue that's able to execute this item. Instead
276 serial queues are cleaned up right away on submit(). */
280 static const IRtwqAsyncCallbackVtbl queue_serial_callback_vtbl
=
282 queue_serial_callback_QueryInterface
,
283 queue_serial_callback_AddRef
,
284 queue_serial_callback_Release
,
285 queue_serial_callback_GetParameters
,
286 queue_serial_callback_Invoke
,
289 static struct queue system_queues
[SYS_QUEUE_COUNT
];
291 static struct queue
*get_system_queue(DWORD queue_id
)
295 case RTWQ_CALLBACK_QUEUE_STANDARD
:
296 case RTWQ_CALLBACK_QUEUE_RT
:
297 case RTWQ_CALLBACK_QUEUE_IO
:
298 case RTWQ_CALLBACK_QUEUE_TIMER
:
299 case RTWQ_CALLBACK_QUEUE_MULTITHREADED
:
300 case RTWQ_CALLBACK_QUEUE_LONG_FUNCTION
:
301 return &system_queues
[queue_id
- 1];
307 static HRESULT
grab_queue(DWORD queue_id
, struct queue
**ret
);
309 static void CALLBACK
standard_queue_cleanup_callback(void *object_data
, void *group_data
)
313 static HRESULT
pool_queue_init(const struct queue_desc
*desc
, struct queue
*queue
)
315 TP_CALLBACK_ENVIRON_V3 env
;
316 unsigned int max_thread
, i
;
318 queue
->pool
= CreateThreadpool(NULL
);
320 memset(&env
, 0, sizeof(env
));
322 env
.Size
= sizeof(env
);
323 env
.Pool
= queue
->pool
;
324 env
.CleanupGroup
= CreateThreadpoolCleanupGroup();
325 env
.CleanupGroupCancelCallback
= standard_queue_cleanup_callback
;
326 env
.CallbackPriority
= TP_CALLBACK_PRIORITY_NORMAL
;
327 for (i
= 0; i
< ARRAY_SIZE(queue
->envs
); ++i
)
329 queue
->envs
[i
] = env
;
330 queue
->envs
[i
].CallbackPriority
= priorities
[i
];
332 list_init(&queue
->pending_items
);
333 InitializeCriticalSection(&queue
->cs
);
335 max_thread
= (desc
->queue_type
== RTWQ_STANDARD_WORKQUEUE
|| desc
->queue_type
== RTWQ_WINDOW_WORKQUEUE
) ? 1 : 4;
337 SetThreadpoolThreadMinimum(queue
->pool
, 1);
338 SetThreadpoolThreadMaximum(queue
->pool
, max_thread
);
340 if (desc
->queue_type
== RTWQ_WINDOW_WORKQUEUE
)
341 FIXME("RTWQ_WINDOW_WORKQUEUE is not supported.\n");
346 static BOOL
pool_queue_shutdown(struct queue
*queue
)
351 CloseThreadpoolCleanupGroupMembers(queue
->envs
[0].CleanupGroup
, TRUE
, NULL
);
352 CloseThreadpool(queue
->pool
);
358 static void CALLBACK
standard_queue_worker(TP_CALLBACK_INSTANCE
*instance
, void *context
, TP_WORK
*work
)
360 struct work_item
*item
= context
;
361 RTWQASYNCRESULT
*result
= (RTWQASYNCRESULT
*)item
->result
;
363 TRACE("result object %p.\n", result
);
365 /* Submitting from serial queue in reply mode, use different result object acting as receipt token.
366 It's submitted to user callback still, but when invoked, special serial queue callback will be used
367 to ensure correct destination queue. */
369 IRtwqAsyncCallback_Invoke(result
->pCallback
, item
->reply_result
? item
->reply_result
: item
->result
);
371 IUnknown_Release(&item
->IUnknown_iface
);
374 static void pool_queue_submit(struct queue
*queue
, struct work_item
*item
)
376 TP_CALLBACK_PRIORITY callback_priority
;
377 TP_CALLBACK_ENVIRON_V3 env
;
379 if (item
->priority
== 0)
380 callback_priority
= TP_CALLBACK_PRIORITY_NORMAL
;
381 else if (item
->priority
< 0)
382 callback_priority
= TP_CALLBACK_PRIORITY_LOW
;
384 callback_priority
= TP_CALLBACK_PRIORITY_HIGH
;
386 env
= queue
->envs
[callback_priority
];
387 env
.FinalizationCallback
= item
->finalization_callback
;
388 /* Worker pool callback will release one reference. Grab one more to keep object alive when
389 we need finalization callback. */
390 if (item
->finalization_callback
)
391 IUnknown_AddRef(&item
->IUnknown_iface
);
392 item
->work_object
= CreateThreadpoolWork(standard_queue_worker
, item
, (TP_CALLBACK_ENVIRON
*)&env
);
393 SubmitThreadpoolWork(item
->work_object
);
395 TRACE("dispatched %p.\n", item
->result
);
398 static const struct queue_ops pool_queue_ops
=
405 static struct work_item
* serial_queue_get_next(struct queue
*queue
, struct work_item
*item
)
407 struct work_item
*next_item
= NULL
;
409 list_remove(&item
->entry
);
410 if (!list_empty(&item
->queue
->pending_items
))
411 next_item
= LIST_ENTRY(list_head(&item
->queue
->pending_items
), struct work_item
, entry
);
416 static void CALLBACK
serial_queue_finalization_callback(PTP_CALLBACK_INSTANCE instance
, void *user_data
)
418 struct work_item
*item
= (struct work_item
*)user_data
, *next_item
;
419 struct queue
*target_queue
, *queue
= item
->queue
;
422 EnterCriticalSection(&queue
->cs
);
424 if ((next_item
= serial_queue_get_next(queue
, item
)))
426 if (SUCCEEDED(hr
= grab_queue(queue
->target_queue
, &target_queue
)))
427 target_queue
->ops
->submit(target_queue
, next_item
);
429 WARN("Failed to grab queue for id %#lx, hr %#lx.\n", queue
->target_queue
, hr
);
432 LeaveCriticalSection(&queue
->cs
);
434 IUnknown_Release(&item
->IUnknown_iface
);
437 static HRESULT
serial_queue_init(const struct queue_desc
*desc
, struct queue
*queue
)
439 queue
->IRtwqAsyncCallback_iface
.lpVtbl
= &queue_serial_callback_vtbl
;
440 queue
->target_queue
= desc
->target_queue
;
441 lock_user_queue(queue
->target_queue
);
442 queue
->finalization_callback
= serial_queue_finalization_callback
;
447 static BOOL
serial_queue_shutdown(struct queue
*queue
)
449 unlock_user_queue(queue
->target_queue
);
454 static struct work_item
* serial_queue_is_ack_token(struct queue
*queue
, struct work_item
*item
)
456 RTWQASYNCRESULT
*async_result
= (RTWQASYNCRESULT
*)item
->result
;
457 struct work_item
*head
;
459 if (list_empty(&queue
->pending_items
))
462 head
= LIST_ENTRY(list_head(&queue
->pending_items
), struct work_item
, entry
);
463 if (head
->reply_result
== item
->result
&& async_result
->pCallback
== &queue
->IRtwqAsyncCallback_iface
)
469 static void serial_queue_submit(struct queue
*queue
, struct work_item
*item
)
471 struct work_item
*head
, *next_item
= NULL
;
472 struct queue
*target_queue
;
475 /* In reply mode queue will advance when 'reply_result' is invoked, in regular mode it will advance automatically,
476 via finalization callback. */
478 if (item
->flags
& RTWQ_REPLY_CALLBACK
)
480 if (FAILED(hr
= RtwqCreateAsyncResult(NULL
, &queue
->IRtwqAsyncCallback_iface
, NULL
, &item
->reply_result
)))
481 WARN("Failed to create reply object, hr %#lx.\n", hr
);
484 item
->finalization_callback
= queue
->finalization_callback
;
486 /* Serial queues could be chained together, detach from current queue before transitioning item to this one.
487 Items are not detached when submitted to pool queues, because pool queues won't forward them further. */
488 EnterCriticalSection(&item
->queue
->cs
);
489 list_remove(&item
->entry
);
490 LeaveCriticalSection(&item
->queue
->cs
);
492 EnterCriticalSection(&queue
->cs
);
496 if ((head
= serial_queue_is_ack_token(queue
, item
)))
498 /* Ack receipt token - pop waiting item, advance. */
499 next_item
= serial_queue_get_next(queue
, head
);
500 IUnknown_Release(&head
->IUnknown_iface
);
504 if (list_empty(&queue
->pending_items
))
506 list_add_tail(&queue
->pending_items
, &item
->entry
);
507 IUnknown_AddRef(&item
->IUnknown_iface
);
512 if (SUCCEEDED(hr
= grab_queue(queue
->target_queue
, &target_queue
)))
513 target_queue
->ops
->submit(target_queue
, next_item
);
515 WARN("Failed to grab queue for id %#lx, hr %#lx.\n", queue
->target_queue
, hr
);
518 LeaveCriticalSection(&queue
->cs
);
521 static const struct queue_ops serial_queue_ops
=
524 serial_queue_shutdown
,
528 static HRESULT WINAPI
work_item_QueryInterface(IUnknown
*iface
, REFIID riid
, void **obj
)
530 if (IsEqualIID(riid
, &IID_IUnknown
))
533 IUnknown_AddRef(iface
);
538 return E_NOINTERFACE
;
541 static ULONG WINAPI
work_item_AddRef(IUnknown
*iface
)
543 struct work_item
*item
= work_item_impl_from_IUnknown(iface
);
544 return InterlockedIncrement(&item
->refcount
);
547 static ULONG WINAPI
work_item_Release(IUnknown
*iface
)
549 struct work_item
*item
= work_item_impl_from_IUnknown(iface
);
550 ULONG refcount
= InterlockedDecrement(&item
->refcount
);
554 if (item
->work_object
)
555 CloseThreadpoolWork(item
->work_object
);
556 if (item
->reply_result
)
557 IRtwqAsyncResult_Release(item
->reply_result
);
558 IRtwqAsyncResult_Release(item
->result
);
565 static const IUnknownVtbl work_item_vtbl
=
567 work_item_QueryInterface
,
572 static struct work_item
* alloc_work_item(struct queue
*queue
, LONG priority
, IRtwqAsyncResult
*result
)
574 RTWQASYNCRESULT
*async_result
= (RTWQASYNCRESULT
*)result
;
575 DWORD flags
= 0, queue_id
= 0;
576 struct work_item
*item
;
578 item
= calloc(1, sizeof(*item
));
580 item
->IUnknown_iface
.lpVtbl
= &work_item_vtbl
;
581 item
->result
= result
;
582 IRtwqAsyncResult_AddRef(item
->result
);
585 list_init(&item
->entry
);
586 item
->priority
= priority
;
588 if (SUCCEEDED(IRtwqAsyncCallback_GetParameters(async_result
->pCallback
, &flags
, &queue_id
)))
594 static void init_work_queue(const struct queue_desc
*desc
, struct queue
*queue
)
596 assert(desc
->ops
!= NULL
);
598 queue
->ops
= desc
->ops
;
599 if (SUCCEEDED(queue
->ops
->init(desc
, queue
)))
601 list_init(&queue
->pending_items
);
602 InitializeCriticalSection(&queue
->cs
);
606 static HRESULT
grab_queue(DWORD queue_id
, struct queue
**ret
)
608 struct queue
*queue
= get_system_queue(queue_id
);
609 RTWQ_WORKQUEUE_TYPE queue_type
;
610 struct queue_handle
*entry
;
614 if (!system_queues
[SYS_QUEUE_STANDARD
].pool
)
615 return RTWQ_E_SHUTDOWN
;
617 if (queue
&& queue
->pool
)
624 struct queue_desc desc
;
626 EnterCriticalSection(&queues_section
);
629 case RTWQ_CALLBACK_QUEUE_IO
:
630 case RTWQ_CALLBACK_QUEUE_MULTITHREADED
:
631 case RTWQ_CALLBACK_QUEUE_LONG_FUNCTION
:
632 queue_type
= RTWQ_MULTITHREADED_WORKQUEUE
;
635 queue_type
= RTWQ_STANDARD_WORKQUEUE
;
638 desc
.queue_type
= queue_type
;
639 desc
.ops
= &pool_queue_ops
;
640 desc
.target_queue
= 0;
641 init_work_queue(&desc
, queue
);
642 LeaveCriticalSection(&queues_section
);
647 /* Handles user queues. */
648 if ((entry
= get_queue_obj(queue_id
)))
651 return *ret
? S_OK
: RTWQ_E_INVALID_WORKQUEUE
;
654 static void shutdown_queue(struct queue
*queue
)
656 struct work_item
*item
, *item2
;
658 if (!queue
->ops
|| !queue
->ops
->shutdown(queue
))
661 EnterCriticalSection(&queue
->cs
);
662 LIST_FOR_EACH_ENTRY_SAFE(item
, item2
, &queue
->pending_items
, struct work_item
, entry
)
664 list_remove(&item
->entry
);
665 IUnknown_Release(&item
->IUnknown_iface
);
667 LeaveCriticalSection(&queue
->cs
);
669 DeleteCriticalSection(&queue
->cs
);
671 memset(queue
, 0, sizeof(*queue
));
674 static HRESULT
queue_submit_item(struct queue
*queue
, LONG priority
, IRtwqAsyncResult
*result
)
676 struct work_item
*item
;
678 if (!(item
= alloc_work_item(queue
, priority
, result
)))
679 return E_OUTOFMEMORY
;
681 queue
->ops
->submit(queue
, item
);
686 static HRESULT
queue_put_work_item(DWORD queue_id
, LONG priority
, IRtwqAsyncResult
*result
)
691 if (FAILED(hr
= grab_queue(queue_id
, &queue
)))
694 return queue_submit_item(queue
, priority
, result
);
697 static HRESULT
invoke_async_callback(IRtwqAsyncResult
*result
)
699 RTWQASYNCRESULT
*result_data
= (RTWQASYNCRESULT
*)result
;
700 DWORD queue
= RTWQ_CALLBACK_QUEUE_STANDARD
, flags
;
703 if (FAILED(IRtwqAsyncCallback_GetParameters(result_data
->pCallback
, &flags
, &queue
)))
704 queue
= RTWQ_CALLBACK_QUEUE_STANDARD
;
706 if (FAILED(lock_user_queue(queue
)))
707 queue
= RTWQ_CALLBACK_QUEUE_STANDARD
;
709 hr
= queue_put_work_item(queue
, 0, result
);
711 unlock_user_queue(queue
);
716 static void queue_release_pending_item(struct work_item
*item
)
718 EnterCriticalSection(&item
->queue
->cs
);
721 list_remove(&item
->entry
);
723 IUnknown_Release(&item
->IUnknown_iface
);
725 LeaveCriticalSection(&item
->queue
->cs
);
728 static void CALLBACK
waiting_item_callback(TP_CALLBACK_INSTANCE
*instance
, void *context
, TP_WAIT
*wait
,
729 TP_WAIT_RESULT wait_result
)
731 struct work_item
*item
= context
;
733 TRACE("result object %p.\n", item
->result
);
735 invoke_async_callback(item
->result
);
737 IUnknown_Release(&item
->IUnknown_iface
);
740 static void CALLBACK
waiting_item_cancelable_callback(TP_CALLBACK_INSTANCE
*instance
, void *context
, TP_WAIT
*wait
,
741 TP_WAIT_RESULT wait_result
)
743 struct work_item
*item
= context
;
745 TRACE("result object %p.\n", item
->result
);
747 queue_release_pending_item(item
);
749 invoke_async_callback(item
->result
);
751 IUnknown_Release(&item
->IUnknown_iface
);
754 static void CALLBACK
scheduled_item_callback(TP_CALLBACK_INSTANCE
*instance
, void *context
, TP_TIMER
*timer
)
756 struct work_item
*item
= context
;
758 TRACE("result object %p.\n", item
->result
);
760 invoke_async_callback(item
->result
);
762 IUnknown_Release(&item
->IUnknown_iface
);
765 static void CALLBACK
scheduled_item_cancelable_callback(TP_CALLBACK_INSTANCE
*instance
, void *context
, TP_TIMER
*timer
)
767 struct work_item
*item
= context
;
769 TRACE("result object %p.\n", item
->result
);
771 queue_release_pending_item(item
);
773 invoke_async_callback(item
->result
);
775 IUnknown_Release(&item
->IUnknown_iface
);
778 static void CALLBACK
periodic_item_callback(TP_CALLBACK_INSTANCE
*instance
, void *context
, TP_TIMER
*timer
)
780 struct work_item
*item
= context
;
782 IUnknown_AddRef(&item
->IUnknown_iface
);
784 invoke_async_callback(item
->result
);
786 IUnknown_Release(&item
->IUnknown_iface
);
789 static void queue_mark_item_pending(DWORD mask
, struct work_item
*item
, RTWQWORKITEM_KEY
*key
)
791 *key
= generate_item_key(mask
);
794 EnterCriticalSection(&item
->queue
->cs
);
795 list_add_tail(&item
->queue
->pending_items
, &item
->entry
);
796 IUnknown_AddRef(&item
->IUnknown_iface
);
797 LeaveCriticalSection(&item
->queue
->cs
);
800 static HRESULT
queue_submit_wait(struct queue
*queue
, HANDLE event
, LONG priority
, IRtwqAsyncResult
*result
,
801 RTWQWORKITEM_KEY
*key
)
803 PTP_WAIT_CALLBACK callback
;
804 struct work_item
*item
;
806 if (!(item
= alloc_work_item(queue
, priority
, result
)))
807 return E_OUTOFMEMORY
;
811 queue_mark_item_pending(WAIT_ITEM_KEY_MASK
, item
, key
);
812 callback
= waiting_item_cancelable_callback
;
815 callback
= waiting_item_callback
;
817 item
->u
.wait_object
= CreateThreadpoolWait(callback
, item
,
818 (TP_CALLBACK_ENVIRON
*)&queue
->envs
[TP_CALLBACK_PRIORITY_NORMAL
]);
819 SetThreadpoolWait(item
->u
.wait_object
, event
, NULL
);
821 TRACE("dispatched %p.\n", result
);
826 static HRESULT
queue_submit_timer(struct queue
*queue
, IRtwqAsyncResult
*result
, INT64 timeout
, DWORD period
,
827 RTWQWORKITEM_KEY
*key
)
829 PTP_TIMER_CALLBACK callback
;
830 struct work_item
*item
;
834 if (!(item
= alloc_work_item(queue
, 0, result
)))
835 return E_OUTOFMEMORY
;
839 queue_mark_item_pending(SCHEDULED_ITEM_KEY_MASK
, item
, key
);
843 callback
= periodic_item_callback
;
845 callback
= key
? scheduled_item_cancelable_callback
: scheduled_item_callback
;
847 t
.QuadPart
= timeout
* 1000 * 10;
848 filetime
.dwLowDateTime
= t
.u
.LowPart
;
849 filetime
.dwHighDateTime
= t
.u
.HighPart
;
851 item
->u
.timer_object
= CreateThreadpoolTimer(callback
, item
,
852 (TP_CALLBACK_ENVIRON
*)&queue
->envs
[TP_CALLBACK_PRIORITY_NORMAL
]);
853 SetThreadpoolTimer(item
->u
.timer_object
, &filetime
, period
, 0);
855 TRACE("dispatched %p.\n", result
);
860 static HRESULT
queue_cancel_item(struct queue
*queue
, RTWQWORKITEM_KEY key
)
862 HRESULT hr
= RTWQ_E_NOT_FOUND
;
863 struct work_item
*item
;
865 EnterCriticalSection(&queue
->cs
);
866 LIST_FOR_EACH_ENTRY(item
, &queue
->pending_items
, struct work_item
, entry
)
868 if (item
->key
== key
)
871 if ((key
& WAIT_ITEM_KEY_MASK
) == WAIT_ITEM_KEY_MASK
)
873 IRtwqAsyncResult_SetStatus(item
->result
, RTWQ_E_OPERATION_CANCELLED
);
874 invoke_async_callback(item
->result
);
875 CloseThreadpoolWait(item
->u
.wait_object
);
877 else if ((key
& SCHEDULED_ITEM_KEY_MASK
) == SCHEDULED_ITEM_KEY_MASK
)
878 CloseThreadpoolTimer(item
->u
.timer_object
);
880 WARN("Unknown item key mask %#I64x.\n", key
);
881 queue_release_pending_item(item
);
886 LeaveCriticalSection(&queue
->cs
);
891 static HRESULT
alloc_user_queue(const struct queue_desc
*desc
, DWORD
*queue_id
)
893 struct queue_handle
*entry
;
897 *queue_id
= RTWQ_CALLBACK_QUEUE_UNDEFINED
;
899 if (platform_lock
<= 0)
900 return RTWQ_E_SHUTDOWN
;
902 if (!(queue
= calloc(1, sizeof(*queue
))))
903 return E_OUTOFMEMORY
;
905 init_work_queue(desc
, queue
);
907 EnterCriticalSection(&queues_section
);
909 entry
= next_free_user_queue
;
911 next_free_user_queue
= entry
->obj
;
912 else if (next_unused_user_queue
< user_queues
+ MAX_USER_QUEUE_HANDLES
)
913 entry
= next_unused_user_queue
++;
916 LeaveCriticalSection(&queues_section
);
918 WARN("Out of user queue handles.\n");
919 return E_OUTOFMEMORY
;
924 if (++queue_generation
== 0xffff) queue_generation
= 1;
925 entry
->generation
= queue_generation
;
926 idx
= entry
- user_queues
+ FIRST_USER_QUEUE_HANDLE
;
927 *queue_id
= (idx
<< 16) | entry
->generation
;
929 LeaveCriticalSection(&queues_section
);
936 RTWQASYNCRESULT result
;
942 static struct async_result
*impl_from_IRtwqAsyncResult(IRtwqAsyncResult
*iface
)
944 return CONTAINING_RECORD(iface
, struct async_result
, result
.AsyncResult
);
947 static HRESULT WINAPI
async_result_QueryInterface(IRtwqAsyncResult
*iface
, REFIID riid
, void **obj
)
949 TRACE("%p, %s, %p.\n", iface
, debugstr_guid(riid
), obj
);
951 if (IsEqualIID(riid
, &IID_IRtwqAsyncResult
) ||
952 IsEqualIID(riid
, &IID_IUnknown
))
955 IRtwqAsyncResult_AddRef(iface
);
960 WARN("Unsupported interface %s.\n", debugstr_guid(riid
));
961 return E_NOINTERFACE
;
964 static ULONG WINAPI
async_result_AddRef(IRtwqAsyncResult
*iface
)
966 struct async_result
*result
= impl_from_IRtwqAsyncResult(iface
);
967 ULONG refcount
= InterlockedIncrement(&result
->refcount
);
969 TRACE("%p, %lu.\n", iface
, refcount
);
974 static ULONG WINAPI
async_result_Release(IRtwqAsyncResult
*iface
)
976 struct async_result
*result
= impl_from_IRtwqAsyncResult(iface
);
977 ULONG refcount
= InterlockedDecrement(&result
->refcount
);
979 TRACE("%p, %lu.\n", iface
, refcount
);
983 if (result
->result
.pCallback
)
984 IRtwqAsyncCallback_Release(result
->result
.pCallback
);
986 IUnknown_Release(result
->object
);
988 IUnknown_Release(result
->state
);
989 if (result
->result
.hEvent
)
990 CloseHandle(result
->result
.hEvent
);
993 RtwqUnlockPlatform();
999 static HRESULT WINAPI
async_result_GetState(IRtwqAsyncResult
*iface
, IUnknown
**state
)
1001 struct async_result
*result
= impl_from_IRtwqAsyncResult(iface
);
1003 TRACE("%p, %p.\n", iface
, state
);
1008 *state
= result
->state
;
1009 IUnknown_AddRef(*state
);
1014 static HRESULT WINAPI
async_result_GetStatus(IRtwqAsyncResult
*iface
)
1016 struct async_result
*result
= impl_from_IRtwqAsyncResult(iface
);
1018 TRACE("%p.\n", iface
);
1020 return result
->result
.hrStatusResult
;
1023 static HRESULT WINAPI
async_result_SetStatus(IRtwqAsyncResult
*iface
, HRESULT status
)
1025 struct async_result
*result
= impl_from_IRtwqAsyncResult(iface
);
1027 TRACE("%p, %#lx.\n", iface
, status
);
1029 result
->result
.hrStatusResult
= status
;
1034 static HRESULT WINAPI
async_result_GetObject(IRtwqAsyncResult
*iface
, IUnknown
**object
)
1036 struct async_result
*result
= impl_from_IRtwqAsyncResult(iface
);
1038 TRACE("%p, %p.\n", iface
, object
);
1040 if (!result
->object
)
1043 *object
= result
->object
;
1044 IUnknown_AddRef(*object
);
1049 static IUnknown
* WINAPI
async_result_GetStateNoAddRef(IRtwqAsyncResult
*iface
)
1051 struct async_result
*result
= impl_from_IRtwqAsyncResult(iface
);
1053 TRACE("%p.\n", iface
);
1055 return result
->state
;
1058 static const IRtwqAsyncResultVtbl async_result_vtbl
=
1060 async_result_QueryInterface
,
1061 async_result_AddRef
,
1062 async_result_Release
,
1063 async_result_GetState
,
1064 async_result_GetStatus
,
1065 async_result_SetStatus
,
1066 async_result_GetObject
,
1067 async_result_GetStateNoAddRef
,
1070 static HRESULT
create_async_result(IUnknown
*object
, IRtwqAsyncCallback
*callback
, IUnknown
*state
, IRtwqAsyncResult
**out
)
1072 struct async_result
*result
;
1075 return E_INVALIDARG
;
1077 if (!(result
= calloc(1, sizeof(*result
))))
1078 return E_OUTOFMEMORY
;
1082 result
->result
.AsyncResult
.lpVtbl
= &async_result_vtbl
;
1083 result
->refcount
= 1;
1084 result
->object
= object
;
1086 IUnknown_AddRef(result
->object
);
1087 result
->result
.pCallback
= callback
;
1088 if (result
->result
.pCallback
)
1089 IRtwqAsyncCallback_AddRef(result
->result
.pCallback
);
1090 result
->state
= state
;
1092 IUnknown_AddRef(result
->state
);
1094 *out
= &result
->result
.AsyncResult
;
1096 TRACE("Created async result object %p.\n", *out
);
1101 HRESULT WINAPI
RtwqCreateAsyncResult(IUnknown
*object
, IRtwqAsyncCallback
*callback
, IUnknown
*state
,
1102 IRtwqAsyncResult
**out
)
1104 TRACE("%p, %p, %p, %p.\n", object
, callback
, state
, out
);
1106 return create_async_result(object
, callback
, state
, out
);
1109 HRESULT WINAPI
RtwqLockPlatform(void)
1111 InterlockedIncrement(&platform_lock
);
1116 HRESULT WINAPI
RtwqUnlockPlatform(void)
1118 InterlockedDecrement(&platform_lock
);
1123 static void init_system_queues(void)
1125 struct queue_desc desc
;
1128 /* Always initialize standard queue, keep the rest lazy. */
1130 EnterCriticalSection(&queues_section
);
1132 if (system_queues
[SYS_QUEUE_STANDARD
].pool
)
1134 LeaveCriticalSection(&queues_section
);
1138 if (FAILED(hr
= CoIncrementMTAUsage(&mta_cookie
)))
1139 WARN("Failed to initialize MTA, hr %#lx.\n", hr
);
1141 desc
.queue_type
= RTWQ_STANDARD_WORKQUEUE
;
1142 desc
.ops
= &pool_queue_ops
;
1143 desc
.target_queue
= 0;
1144 init_work_queue(&desc
, &system_queues
[SYS_QUEUE_STANDARD
]);
1146 LeaveCriticalSection(&queues_section
);
1149 HRESULT WINAPI
RtwqStartup(void)
1151 if (InterlockedIncrement(&platform_lock
) == 1)
1153 init_system_queues();
1159 static void shutdown_system_queues(void)
1164 EnterCriticalSection(&queues_section
);
1166 for (i
= 0; i
< ARRAY_SIZE(system_queues
); ++i
)
1168 shutdown_queue(&system_queues
[i
]);
1171 if (FAILED(hr
= CoDecrementMTAUsage(mta_cookie
)))
1172 WARN("Failed to uninitialize MTA, hr %#lx.\n", hr
);
1174 LeaveCriticalSection(&queues_section
);
1177 HRESULT WINAPI
RtwqShutdown(void)
1179 if (platform_lock
<= 0)
1182 if (InterlockedExchangeAdd(&platform_lock
, -1) == 1)
1184 shutdown_system_queues();
1190 HRESULT WINAPI
RtwqPutWaitingWorkItem(HANDLE event
, LONG priority
, IRtwqAsyncResult
*result
, RTWQWORKITEM_KEY
*key
)
1192 struct queue
*queue
;
1195 TRACE("%p, %ld, %p, %p.\n", event
, priority
, result
, key
);
1197 if (FAILED(hr
= grab_queue(RTWQ_CALLBACK_QUEUE_TIMER
, &queue
)))
1200 hr
= queue_submit_wait(queue
, event
, priority
, result
, key
);
1205 static HRESULT
schedule_work_item(IRtwqAsyncResult
*result
, INT64 timeout
, RTWQWORKITEM_KEY
*key
)
1207 struct queue
*queue
;
1210 if (FAILED(hr
= grab_queue(RTWQ_CALLBACK_QUEUE_TIMER
, &queue
)))
1213 TRACE("%p, %s, %p.\n", result
, wine_dbgstr_longlong(timeout
), key
);
1215 return queue_submit_timer(queue
, result
, timeout
, 0, key
);
1218 HRESULT WINAPI
RtwqScheduleWorkItem(IRtwqAsyncResult
*result
, INT64 timeout
, RTWQWORKITEM_KEY
*key
)
1220 TRACE("%p, %s, %p.\n", result
, wine_dbgstr_longlong(timeout
), key
);
1222 return schedule_work_item(result
, timeout
, key
);
1225 struct periodic_callback
1227 IRtwqAsyncCallback IRtwqAsyncCallback_iface
;
1229 RTWQPERIODICCALLBACK callback
;
1232 static struct periodic_callback
*impl_from_IRtwqAsyncCallback(IRtwqAsyncCallback
*iface
)
1234 return CONTAINING_RECORD(iface
, struct periodic_callback
, IRtwqAsyncCallback_iface
);
1237 static HRESULT WINAPI
periodic_callback_QueryInterface(IRtwqAsyncCallback
*iface
, REFIID riid
, void **obj
)
1239 if (IsEqualIID(riid
, &IID_IRtwqAsyncCallback
) ||
1240 IsEqualIID(riid
, &IID_IUnknown
))
1243 IRtwqAsyncCallback_AddRef(iface
);
1248 return E_NOINTERFACE
;
1251 static ULONG WINAPI
periodic_callback_AddRef(IRtwqAsyncCallback
*iface
)
1253 struct periodic_callback
*callback
= impl_from_IRtwqAsyncCallback(iface
);
1254 ULONG refcount
= InterlockedIncrement(&callback
->refcount
);
1256 TRACE("%p, %lu.\n", iface
, refcount
);
1261 static ULONG WINAPI
periodic_callback_Release(IRtwqAsyncCallback
*iface
)
1263 struct periodic_callback
*callback
= impl_from_IRtwqAsyncCallback(iface
);
1264 ULONG refcount
= InterlockedDecrement(&callback
->refcount
);
1266 TRACE("%p, %lu.\n", iface
, refcount
);
1274 static HRESULT WINAPI
periodic_callback_GetParameters(IRtwqAsyncCallback
*iface
, DWORD
*flags
, DWORD
*queue
)
1279 static HRESULT WINAPI
periodic_callback_Invoke(IRtwqAsyncCallback
*iface
, IRtwqAsyncResult
*result
)
1281 struct periodic_callback
*callback
= impl_from_IRtwqAsyncCallback(iface
);
1282 IUnknown
*context
= NULL
;
1284 if (FAILED(IRtwqAsyncResult_GetObject(result
, &context
)))
1285 WARN("Expected object to be set for result object.\n");
1287 callback
->callback(context
);
1290 IUnknown_Release(context
);
1295 static const IRtwqAsyncCallbackVtbl periodic_callback_vtbl
=
1297 periodic_callback_QueryInterface
,
1298 periodic_callback_AddRef
,
1299 periodic_callback_Release
,
1300 periodic_callback_GetParameters
,
1301 periodic_callback_Invoke
,
1304 static HRESULT
create_periodic_callback_obj(RTWQPERIODICCALLBACK callback
, IRtwqAsyncCallback
**out
)
1306 struct periodic_callback
*object
;
1308 if (!(object
= calloc(1, sizeof(*object
))))
1309 return E_OUTOFMEMORY
;
1311 object
->IRtwqAsyncCallback_iface
.lpVtbl
= &periodic_callback_vtbl
;
1312 object
->refcount
= 1;
1313 object
->callback
= callback
;
1315 *out
= &object
->IRtwqAsyncCallback_iface
;
1320 HRESULT WINAPI
RtwqAddPeriodicCallback(RTWQPERIODICCALLBACK callback
, IUnknown
*context
, DWORD
*key
)
1322 IRtwqAsyncCallback
*periodic_callback
;
1323 RTWQWORKITEM_KEY workitem_key
;
1324 IRtwqAsyncResult
*result
;
1325 struct queue
*queue
;
1328 TRACE("%p, %p, %p.\n", callback
, context
, key
);
1330 if (FAILED(hr
= grab_queue(RTWQ_CALLBACK_QUEUE_TIMER
, &queue
)))
1333 if (FAILED(hr
= create_periodic_callback_obj(callback
, &periodic_callback
)))
1336 hr
= create_async_result(context
, periodic_callback
, NULL
, &result
);
1337 IRtwqAsyncCallback_Release(periodic_callback
);
1341 /* Same period MFGetTimerPeriodicity() returns. */
1342 hr
= queue_submit_timer(queue
, result
, 0, 10, key
? &workitem_key
: NULL
);
1344 IRtwqAsyncResult_Release(result
);
1347 *key
= workitem_key
;
1352 HRESULT WINAPI
RtwqRemovePeriodicCallback(DWORD key
)
1354 struct queue
*queue
;
1357 TRACE("%#lx.\n", key
);
1359 if (FAILED(hr
= grab_queue(RTWQ_CALLBACK_QUEUE_TIMER
, &queue
)))
1362 return queue_cancel_item(queue
, get_item_key(SCHEDULED_ITEM_KEY_MASK
, key
));
1365 HRESULT WINAPI
RtwqCancelWorkItem(RTWQWORKITEM_KEY key
)
1367 struct queue
*queue
;
1370 TRACE("%s.\n", wine_dbgstr_longlong(key
));
1372 if (FAILED(hr
= grab_queue(RTWQ_CALLBACK_QUEUE_TIMER
, &queue
)))
1375 return queue_cancel_item(queue
, key
);
1378 HRESULT WINAPI
RtwqInvokeCallback(IRtwqAsyncResult
*result
)
1380 TRACE("%p.\n", result
);
1382 return invoke_async_callback(result
);
1385 HRESULT WINAPI
RtwqPutWorkItem(DWORD queue
, LONG priority
, IRtwqAsyncResult
*result
)
1387 TRACE("%#lx, %ld, %p.\n", queue
, priority
, result
);
1389 return queue_put_work_item(queue
, priority
, result
);
1392 HRESULT WINAPI
RtwqAllocateWorkQueue(RTWQ_WORKQUEUE_TYPE queue_type
, DWORD
*queue
)
1394 struct queue_desc desc
;
1396 TRACE("%d, %p.\n", queue_type
, queue
);
1398 desc
.queue_type
= queue_type
;
1399 desc
.ops
= &pool_queue_ops
;
1400 desc
.target_queue
= 0;
1401 return alloc_user_queue(&desc
, queue
);
1404 HRESULT WINAPI
RtwqLockWorkQueue(DWORD queue
)
1406 TRACE("%#lx.\n", queue
);
1408 return lock_user_queue(queue
);
1411 HRESULT WINAPI
RtwqUnlockWorkQueue(DWORD queue
)
1413 TRACE("%#lx.\n", queue
);
1415 return unlock_user_queue(queue
);
1418 HRESULT WINAPI
RtwqSetLongRunning(DWORD queue_id
, BOOL enable
)
1420 struct queue
*queue
;
1424 TRACE("%#lx, %d.\n", queue_id
, enable
);
1426 lock_user_queue(queue_id
);
1428 if (SUCCEEDED(hr
= grab_queue(queue_id
, &queue
)))
1430 for (i
= 0; i
< ARRAY_SIZE(queue
->envs
); ++i
)
1431 queue
->envs
[i
].u
.s
.LongFunction
= !!enable
;
1434 unlock_user_queue(queue_id
);
1439 HRESULT WINAPI
RtwqLockSharedWorkQueue(const WCHAR
*usageclass
, LONG priority
, DWORD
*taskid
, DWORD
*queue
)
1441 struct queue_desc desc
;
1444 TRACE("%s, %ld, %p, %p.\n", debugstr_w(usageclass
), priority
, taskid
, queue
);
1449 if (!*usageclass
&& taskid
)
1450 return E_INVALIDARG
;
1453 FIXME("Class name is ignored.\n");
1455 EnterCriticalSection(&queues_section
);
1457 if (shared_mt_queue
)
1458 hr
= lock_user_queue(shared_mt_queue
);
1461 desc
.queue_type
= RTWQ_MULTITHREADED_WORKQUEUE
;
1462 desc
.ops
= &pool_queue_ops
;
1463 desc
.target_queue
= 0;
1464 hr
= alloc_user_queue(&desc
, &shared_mt_queue
);
1467 *queue
= shared_mt_queue
;
1469 LeaveCriticalSection(&queues_section
);
1474 HRESULT WINAPI
RtwqSetDeadline(DWORD queue_id
, LONGLONG deadline
, HANDLE
*request
)
1476 FIXME("%#lx, %s, %p.\n", queue_id
, wine_dbgstr_longlong(deadline
), request
);
1481 HRESULT WINAPI
RtwqSetDeadline2(DWORD queue_id
, LONGLONG deadline
, LONGLONG predeadline
, HANDLE
*request
)
1483 FIXME("%#lx, %s, %s, %p.\n", queue_id
, wine_dbgstr_longlong(deadline
), wine_dbgstr_longlong(predeadline
), request
);
1488 HRESULT WINAPI
RtwqCancelDeadline(HANDLE request
)
1490 FIXME("%p.\n", request
);
1495 HRESULT WINAPI
RtwqAllocateSerialWorkQueue(DWORD target_queue
, DWORD
*queue
)
1497 struct queue_desc desc
;
1499 TRACE("%#lx, %p.\n", target_queue
, queue
);
1501 desc
.queue_type
= RTWQ_STANDARD_WORKQUEUE
;
1502 desc
.ops
= &serial_queue_ops
;
1503 desc
.target_queue
= target_queue
;
1504 return alloc_user_queue(&desc
, queue
);
1507 HRESULT WINAPI
RtwqJoinWorkQueue(DWORD queue
, HANDLE hFile
, HANDLE
*cookie
)
1509 FIXME("%#lx, %p, %p.\n", queue
, hFile
, cookie
);
1514 HRESULT WINAPI
RtwqUnjoinWorkQueue(DWORD queue
, HANDLE cookie
)
1516 FIXME("%#lx, %p.\n", queue
, cookie
);
1521 HRESULT WINAPI
RtwqGetWorkQueueMMCSSClass(DWORD queue
, WCHAR
*class, DWORD
*length
)
1523 FIXME("%#lx, %p, %p.\n", queue
, class, length
);
1528 HRESULT WINAPI
RtwqGetWorkQueueMMCSSTaskId(DWORD queue
, DWORD
*taskid
)
1530 FIXME("%#lx, %p.\n", queue
, taskid
);
1535 HRESULT WINAPI
RtwqGetWorkQueueMMCSSPriority(DWORD queue
, LONG
*priority
)
1537 FIXME("%#lx, %p.\n", queue
, priority
);
1542 HRESULT WINAPI
RtwqRegisterPlatformWithMMCSS(const WCHAR
*class, DWORD
*taskid
, LONG priority
)
1544 FIXME("%s, %p, %ld.\n", debugstr_w(class), taskid
, priority
);
1549 HRESULT WINAPI
RtwqUnregisterPlatformFromMMCSS(void)
1556 HRESULT WINAPI
RtwqBeginRegisterWorkQueueWithMMCSS(DWORD queue
, const WCHAR
*class, DWORD taskid
, LONG priority
,
1557 IRtwqAsyncCallback
*callback
, IUnknown
*state
)
1559 FIXME("%#lx, %s, %lu, %ld, %p, %p.\n", queue
, debugstr_w(class), taskid
, priority
, callback
, state
);
1564 HRESULT WINAPI
RtwqEndRegisterWorkQueueWithMMCSS(IRtwqAsyncResult
*result
, DWORD
*taskid
)
1566 FIXME("%p, %p.\n", result
, taskid
);
1571 HRESULT WINAPI
RtwqBeginUnregisterWorkQueueWithMMCSS(DWORD queue
, IRtwqAsyncCallback
*callback
, IUnknown
*state
)
1573 FIXME("%#lx, %p, %p.\n", queue
, callback
, state
);
1578 HRESULT WINAPI
RtwqEndUnregisterWorkQueueWithMMCSS(IRtwqAsyncResult
*result
)
1580 FIXME("%p.\n", result
);
1585 HRESULT WINAPI
RtwqRegisterPlatformEvents(IRtwqPlatformEvents
*events
)
1587 FIXME("%p.\n", events
);
1592 HRESULT WINAPI
RtwqUnregisterPlatformEvents(IRtwqPlatformEvents
*events
)
1594 FIXME("%p.\n", events
);