diff --git a/dlls/rtworkq/queue.c b/dlls/rtworkq/queue.c index dc6f6efeb15..16411bfc491 100644 --- a/dlls/rtworkq/queue.c +++ b/dlls/rtworkq/queue.c @@ -838,6 +838,147 @@ HRESULT WINAPI RtwqScheduleWorkItem(IRtwqAsyncResult *result, INT64 timeout, RTW return schedule_work_item(result, timeout, key); } +struct periodic_callback +{ + IRtwqAsyncCallback IRtwqAsyncCallback_iface; + LONG refcount; + RTWQPERIODICCALLBACK callback; +}; + +static struct periodic_callback *impl_from_IRtwqAsyncCallback(IRtwqAsyncCallback *iface) +{ + return CONTAINING_RECORD(iface, struct periodic_callback, IRtwqAsyncCallback_iface); +} + +static HRESULT WINAPI periodic_callback_QueryInterface(IRtwqAsyncCallback *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IRtwqAsyncCallback) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IRtwqAsyncCallback_AddRef(iface); + return S_OK; + } + + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI periodic_callback_AddRef(IRtwqAsyncCallback *iface) +{ + struct periodic_callback *callback = impl_from_IRtwqAsyncCallback(iface); + ULONG refcount = InterlockedIncrement(&callback->refcount); + + TRACE("%p, %u.\n", iface, refcount); + + return refcount; +} + +static ULONG WINAPI periodic_callback_Release(IRtwqAsyncCallback *iface) +{ + struct periodic_callback *callback = impl_from_IRtwqAsyncCallback(iface); + ULONG refcount = InterlockedDecrement(&callback->refcount); + + TRACE("%p, %u.\n", iface, refcount); + + if (!refcount) + heap_free(callback); + + return refcount; +} + +static HRESULT WINAPI periodic_callback_GetParameters(IRtwqAsyncCallback *iface, DWORD *flags, DWORD *queue) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI periodic_callback_Invoke(IRtwqAsyncCallback *iface, IRtwqAsyncResult *result) +{ + struct periodic_callback *callback = impl_from_IRtwqAsyncCallback(iface); + IUnknown *context = NULL; + + if (FAILED(IRtwqAsyncResult_GetObject(result, &context))) + WARN("Expected object to be set for result object.\n"); + + callback->callback(context); + + if (context) + IUnknown_Release(context); + + return S_OK; +} + +static const IRtwqAsyncCallbackVtbl periodic_callback_vtbl = +{ + periodic_callback_QueryInterface, + periodic_callback_AddRef, + periodic_callback_Release, + periodic_callback_GetParameters, + periodic_callback_Invoke, +}; + +static HRESULT create_periodic_callback_obj(RTWQPERIODICCALLBACK callback, IRtwqAsyncCallback **out) +{ + struct periodic_callback *object; + + object = heap_alloc(sizeof(*object)); + if (!object) + return E_OUTOFMEMORY; + + object->IRtwqAsyncCallback_iface.lpVtbl = &periodic_callback_vtbl; + object->refcount = 1; + object->callback = callback; + + *out = &object->IRtwqAsyncCallback_iface; + + return S_OK; +} + +HRESULT WINAPI RtwqAddPeriodicCallback(RTWQPERIODICCALLBACK callback, IUnknown *context, DWORD *key) +{ + IRtwqAsyncCallback *periodic_callback; + RTWQWORKITEM_KEY workitem_key; + IRtwqAsyncResult *result; + struct queue *queue; + HRESULT hr; + + TRACE("%p, %p, %p.\n", callback, context, key); + + if (FAILED(hr = grab_queue(RTWQ_CALLBACK_QUEUE_TIMER, &queue))) + return hr; + + if (FAILED(hr = create_periodic_callback_obj(callback, &periodic_callback))) + return hr; + + hr = create_async_result(context, periodic_callback, NULL, &result); + IRtwqAsyncCallback_Release(periodic_callback); + if (FAILED(hr)) + return hr; + + /* Same period MFGetTimerPeriodicity() returns. */ + hr = queue_submit_timer(queue, result, 0, 10, key ? &workitem_key : NULL); + + IRtwqAsyncResult_Release(result); + + if (key) + *key = workitem_key; + + return S_OK; +} + +HRESULT WINAPI RtwqRemovePeriodicCallback(DWORD key) +{ + struct queue *queue; + HRESULT hr; + + TRACE("%#x.\n", key); + + if (FAILED(hr = grab_queue(RTWQ_CALLBACK_QUEUE_TIMER, &queue))) + return hr; + + return queue_cancel_item(queue, get_item_key(SCHEDULED_ITEM_KEY_MASK, key)); +} + HRESULT WINAPI RtwqCancelWorkItem(RTWQWORKITEM_KEY key) { struct queue *queue; diff --git a/dlls/rtworkq/rtworkq.spec b/dlls/rtworkq/rtworkq.spec index eb3d73d8e72..8846047d46d 100644 --- a/dlls/rtworkq/rtworkq.spec +++ b/dlls/rtworkq/rtworkq.spec @@ -1,4 +1,4 @@ -@ stub RtwqAddPeriodicCallback +@ stdcall RtwqAddPeriodicCallback(ptr ptr ptr) @ stub RtwqAllocateSerialWorkQueue @ stub RtwqAllocateWorkQueue @ stub RtwqBeginRegisterWorkQueueWithMMCSS @@ -23,7 +23,7 @@ @ stub RtwqPutWorkItem @ stub RtwqRegisterPlatformEvents @ stub RtwqRegisterPlatformWithMMCSS -@ stub RtwqRemovePeriodicCallback +@ stdcall RtwqRemovePeriodicCallback(long) @ stdcall RtwqScheduleWorkItem(ptr int64 ptr) @ stub RtwqSetDeadline @ stub RtwqSetDeadline2 diff --git a/include/rtworkq.idl b/include/rtworkq.idl index 36468304b4e..3c167460c8a 100644 --- a/include/rtworkq.idl +++ b/include/rtworkq.idl @@ -75,12 +75,16 @@ cpp_quote(" DWORD dwBytesTransferred;") cpp_quote(" HANDLE hEvent;") cpp_quote("} RTWQASYNCRESULT;") +cpp_quote("typedef void (WINAPI *RTWQPERIODICCALLBACK)(IUnknown *context);") + +cpp_quote("HRESULT WINAPI RtwqAddPeriodicCallback(RTWQPERIODICCALLBACK callback, IUnknown *context, DWORD *key);") cpp_quote("HRESULT WINAPI RtwqCancelWorkItem(RTWQWORKITEM_KEY key);") cpp_quote("HRESULT WINAPI RtwqCreateAsyncResult(IUnknown *object, IRtwqAsyncCallback *callback, IUnknown *state, IRtwqAsyncResult **result);") cpp_quote("HRESULT WINAPI RtwqInvokeCallback(IRtwqAsyncResult *result);") cpp_quote("HRESULT WINAPI RtwqLockPlatform(void);") cpp_quote("HRESULT WINAPI RtwqLockWorkQueue(DWORD queue);") cpp_quote("HRESULT WINAPI RtwqPutWaitingWorkItem(HANDLE event, LONG priority, IRtwqAsyncResult *result, RTWQWORKITEM_KEY *key);") +cpp_quote("HRESULT WINAPI RtwqRemovePeriodicCallback(DWORD key);") cpp_quote("HRESULT WINAPI RtwqScheduleWorkItem(IRtwqAsyncResult *result, INT64 timeout, RTWQWORKITEM_KEY *key);") cpp_quote("HRESULT WINAPI RtwqShutdown(void);") cpp_quote("HRESULT WINAPI RtwqStartup(void);")