mirror of
https://gitlab.winehq.org/wine/wine-staging.git
synced 2024-09-13 09:17:20 -07:00
Added patchset to implement Vista+ threadpool functions for work / timers.
This commit is contained in:
parent
e51f6d2439
commit
af245c20be
@ -38,12 +38,15 @@ Wine. All those differences are also documented on the
|
||||
Included bug fixes and improvements
|
||||
===================================
|
||||
|
||||
**Bugfixes and features included in the next upcoming release [4]:**
|
||||
**Bugfixes and features included in the next upcoming release [7]:**
|
||||
|
||||
* Add implementation for CreateThreadpool ([Wine Bug #35192](https://bugs.winehq.org/show_bug.cgi?id=35192))
|
||||
* Call DriverUnload function when unloading a device driver.
|
||||
* Fix check for end_frame in RtlUnwindEx on x86_64. ([Wine Bug #34254](https://bugs.winehq.org/show_bug.cgi?id=34254))
|
||||
* Fix mouse jittering in Planetside 2 ([Wine Bug #32913](https://bugs.winehq.org/show_bug.cgi?id=32913))
|
||||
* Implement additional stubs for vcomp dlls ([Wine Bug #31640](https://bugs.winehq.org/show_bug.cgi?id=31640))
|
||||
* Implement threadpool timers ([Wine Bug #37306](https://bugs.winehq.org/show_bug.cgi?id=37306))
|
||||
* Implement threadpool work items ([Wine Bug #32531](https://bugs.winehq.org/show_bug.cgi?id=32531))
|
||||
|
||||
|
||||
**Bugs fixed in Wine Staging 1.7.35 [146]:**
|
||||
|
1
debian/changelog
vendored
1
debian/changelog
vendored
@ -3,6 +3,7 @@ wine-staging (1.7.36) UNRELEASED; urgency=low
|
||||
* Added patch to fix check for end_frame in RtlUnwindEx on x86_64.
|
||||
* Added patch to fix mouse jittering in Planetside 2.
|
||||
* Added patch to implement additional stubs for vcomp dlls.
|
||||
* Added patchset to implement Vista+ threadpool functions for work / timers.
|
||||
-- Sebastian Lackner <sebastian@fds-team.de> Sun, 25 Jan 2015 05:58:36 +0100
|
||||
|
||||
wine-staging (1.7.35) unstable; urgency=low
|
||||
|
@ -0,0 +1,81 @@
|
||||
From 31df48db7d885ca5679ccc010d954d5df7fba288 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Lackner <sebastian@fds-team.de>
|
||||
Date: Sat, 31 Jan 2015 02:26:17 +0100
|
||||
Subject: ntdll: Add threadpool stub functions to specfile.
|
||||
|
||||
---
|
||||
dlls/ntdll/ntdll.spec | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
1 file changed, 62 insertions(+)
|
||||
|
||||
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec
|
||||
index 51de6e7..771f669 100644
|
||||
--- a/dlls/ntdll/ntdll.spec
|
||||
+++ b/dlls/ntdll/ntdll.spec
|
||||
@@ -969,2 +969,64 @@
|
||||
@ stdcall RtlxUnicodeStringToOemSize(ptr) RtlUnicodeStringToOemSize
|
||||
+# @ stub TpAllocAlpcCompletion
|
||||
+# @ stub TpAllocAlpcCompletionEx
|
||||
+# @ stub TpAllocCleanupGroup
|
||||
+# @ stub TpAllocIoCompletion
|
||||
+# @ stub TpAllocJobNotification
|
||||
+# @ stub TpAllocPool
|
||||
+# @ stub TpAllocTimer
|
||||
+# @ stub TpAllocWait
|
||||
+# @ stub TpAllocWork
|
||||
+# @ stub TpAlpcRegisterCompletionList
|
||||
+# @ stub TpAlpcUnregisterCompletionList
|
||||
+# @ stub TpCallbackDetectedUnrecoverableError
|
||||
+# @ stub TpCallbackIndependent
|
||||
+# @ stub TpCallbackLeaveCriticalSectionOnCompletion
|
||||
+# @ stub TpCallbackMayRunLong
|
||||
+# @ stub TpCallbackReleaseMutexOnCompletion
|
||||
+# @ stub TpCallbackReleaseSemaphoreOnCompletion
|
||||
+# @ stub TpCallbackSendAlpcMessageOnCompletion
|
||||
+# @ stub TpCallbackSendPendingAlpcMessage
|
||||
+# @ stub TpCallbackSetEventOnCompletion
|
||||
+# @ stub TpCallbackUnloadDllOnCompletion
|
||||
+# @ stub TpCancelAsyncIoOperation
|
||||
+# @ stub TpCaptureCaller
|
||||
+# @ stub TpCheckTerminateWorker
|
||||
+# @ stub TpDbgDumpHeapUsage
|
||||
+# @ stub TpDbgSetLogRoutine
|
||||
+# @ stub TpDisablePoolCallbackChecks
|
||||
+# @ stub TpDisassociateCallback
|
||||
+# @ stub TpIsTimerSet
|
||||
+# @ stub TpPostWork
|
||||
+# @ stub TpQueryPoolStackInformation
|
||||
+# @ stub TpReleaseAlpcCompletion
|
||||
+# @ stub TpReleaseCleanupGroup
|
||||
+# @ stub TpReleaseCleanupGroupMembers
|
||||
+# @ stub TpReleaseIoCompletion
|
||||
+# @ stub TpReleaseJobNotification
|
||||
+# @ stub TpReleasePool
|
||||
+# @ stub TpReleaseTimer
|
||||
+# @ stub TpReleaseWait
|
||||
+# @ stub TpReleaseWork
|
||||
+# @ stub TpSetDefaultPoolMaxThreads
|
||||
+# @ stub TpSetDefaultPoolStackInformation
|
||||
+# @ stub TpSetPoolMaxThreads
|
||||
+# @ stub TpSetPoolMaxThreadsSoftLimit
|
||||
+# @ stub TpSetPoolMinThreads
|
||||
+# @ stub TpSetPoolStackInformation
|
||||
+# @ stub TpSetPoolThreadBasePriority
|
||||
+# @ stub TpSetPoolWorkerThreadIdleTimeout
|
||||
+# @ stub TpSetTimer
|
||||
+# @ stub TpSetTimerEx
|
||||
+# @ stub TpSetWait
|
||||
+# @ stub TpSetWaitEx
|
||||
+# @ stub TpSimpleTryPost
|
||||
+# @ stub TpStartAsyncIoOperation
|
||||
+# @ stub TpTimerOutstandingCallbackCount
|
||||
+# @ stub TpTrimPools
|
||||
+# @ stub TpWaitForAlpcCompletion
|
||||
+# @ stub TpWaitForIoCompletion
|
||||
+# @ stub TpWaitForJobNotification
|
||||
+# @ stub TpWaitForTimer
|
||||
+# @ stub TpWaitForWait
|
||||
+# @ stub TpWaitForWork
|
||||
@ stdcall -ret64 VerSetConditionMask(int64 long long)
|
||||
--
|
||||
2.2.2
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,212 @@
|
||||
From 1dc1074ad196b6d869028d3dea283dbab8a8a707 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Lackner <sebastian@fds-team.de>
|
||||
Date: Sun, 1 Feb 2015 18:06:08 +0100
|
||||
Subject: ntdll: Implement additional threadpool work item functions.
|
||||
|
||||
---
|
||||
dlls/ntdll/ntdll.spec | 8 ++--
|
||||
dlls/ntdll/threadpool2.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++-
|
||||
2 files changed, 102 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec
|
||||
index 256ec6d..bf9e795 100644
|
||||
--- a/dlls/ntdll/ntdll.spec
|
||||
+++ b/dlls/ntdll/ntdll.spec
|
||||
@@ -975,7 +975,7 @@
|
||||
@ stdcall TpAllocPool(ptr ptr)
|
||||
# @ stub TpAllocTimer
|
||||
# @ stub TpAllocWait
|
||||
-# @ stub TpAllocWork
|
||||
+@ stdcall TpAllocWork(ptr ptr ptr ptr)
|
||||
# @ stub TpAlpcRegisterCompletionList
|
||||
# @ stub TpAlpcUnregisterCompletionList
|
||||
# @ stub TpCallbackDetectedUnrecoverableError
|
||||
@@ -996,7 +996,7 @@
|
||||
# @ stub TpDisablePoolCallbackChecks
|
||||
@ stdcall TpDisassociateCallback(ptr)
|
||||
# @ stub TpIsTimerSet
|
||||
-# @ stub TpPostWork
|
||||
+@ stdcall TpPostWork(ptr)
|
||||
# @ stub TpQueryPoolStackInformation
|
||||
# @ stub TpReleaseAlpcCompletion
|
||||
@ stdcall TpReleaseCleanupGroup(ptr)
|
||||
@@ -1006,7 +1006,7 @@
|
||||
@ stdcall TpReleasePool(ptr)
|
||||
# @ stub TpReleaseTimer
|
||||
# @ stub TpReleaseWait
|
||||
-# @ stub TpReleaseWork
|
||||
+@ stdcall TpReleaseWork(ptr)
|
||||
# @ stub TpSetDefaultPoolMaxThreads
|
||||
# @ stub TpSetDefaultPoolStackInformation
|
||||
@ stdcall TpSetPoolMaxThreads(ptr long)
|
||||
@@ -1030,3 +1030,3 @@
|
||||
# @ stub TpWaitForWait
|
||||
-# @ stub TpWaitForWork
|
||||
+@ stdcall TpWaitForWork(ptr long)
|
||||
@ stdcall -ret64 VerSetConditionMask(int64 long long)
|
||||
diff --git a/dlls/ntdll/threadpool2.c b/dlls/ntdll/threadpool2.c
|
||||
index c4f54af..acc477f 100644
|
||||
--- a/dlls/ntdll/threadpool2.c
|
||||
+++ b/dlls/ntdll/threadpool2.c
|
||||
@@ -123,7 +123,8 @@ struct threadpool_object
|
||||
enum
|
||||
{
|
||||
TP_OBJECT_TYPE_UNDEFINED,
|
||||
- TP_OBJECT_TYPE_SIMPLE
|
||||
+ TP_OBJECT_TYPE_SIMPLE,
|
||||
+ TP_OBJECT_TYPE_WORK
|
||||
} type;
|
||||
|
||||
/* arguments for callback */
|
||||
@@ -134,6 +135,11 @@ struct threadpool_object
|
||||
{
|
||||
PTP_SIMPLE_CALLBACK callback;
|
||||
} simple;
|
||||
+ /* work callback */
|
||||
+ struct
|
||||
+ {
|
||||
+ PTP_WORK_CALLBACK callback;
|
||||
+ } work;
|
||||
} u;
|
||||
};
|
||||
|
||||
@@ -153,6 +159,13 @@ static inline struct threadpool *impl_from_TP_POOL( TP_POOL *pool )
|
||||
return (struct threadpool *)pool;
|
||||
}
|
||||
|
||||
+static inline struct threadpool_object *impl_from_TP_WORK( TP_WORK *work )
|
||||
+{
|
||||
+ struct threadpool_object *object = (struct threadpool_object *)work;
|
||||
+ assert( !object || object->type == TP_OBJECT_TYPE_WORK );
|
||||
+ return object;
|
||||
+}
|
||||
+
|
||||
static inline struct threadpool_group *impl_from_TP_CLEANUP_GROUP( TP_CLEANUP_GROUP *group )
|
||||
{
|
||||
return (struct threadpool_group *)group;
|
||||
@@ -410,6 +423,16 @@ static void CALLBACK threadpool_worker_proc( void *param )
|
||||
break;
|
||||
}
|
||||
|
||||
+ case TP_OBJECT_TYPE_WORK:
|
||||
+ {
|
||||
+ TP_CALLBACK_INSTANCE *cb_instance = (TP_CALLBACK_INSTANCE *)&instance;
|
||||
+ TRACE( "executing callback %p(%p, %p)\n",
|
||||
+ object->u.work.callback, cb_instance, object->userdata );
|
||||
+ object->u.work.callback( cb_instance, object->userdata, (TP_WORK *)object );
|
||||
+ TRACE( "callback %p returned\n", object->u.work.callback );
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
default:
|
||||
FIXME( "callback type %u not implemented\n", object->type );
|
||||
break;
|
||||
@@ -554,6 +577,31 @@ static NTSTATUS tp_object_alloc_simple( struct threadpool_object **out, PTP_SIMP
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
+static NTSTATUS tp_object_alloc_work( struct threadpool_object **out, PTP_WORK_CALLBACK callback,
|
||||
+ PVOID userdata, TP_CALLBACK_ENVIRON *environment )
|
||||
+{
|
||||
+ struct threadpool_object *object;
|
||||
+ struct threadpool *pool;
|
||||
+
|
||||
+ /* determine threadpool */
|
||||
+ pool = environment ? (struct threadpool *)environment->Pool : NULL;
|
||||
+ if (!pool) pool = get_default_threadpool();
|
||||
+ if (!pool) return STATUS_NO_MEMORY;
|
||||
+
|
||||
+ object = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*object) );
|
||||
+ if (!object)
|
||||
+ return STATUS_NO_MEMORY;
|
||||
+
|
||||
+ object->type = TP_OBJECT_TYPE_WORK;
|
||||
+ object->u.work.callback = callback;
|
||||
+ tp_object_initialize( object, pool, userdata, environment );
|
||||
+
|
||||
+ TRACE("allocated object %p of type %u\n", object, object->type);
|
||||
+
|
||||
+ *out = object;
|
||||
+ return STATUS_SUCCESS;
|
||||
+}
|
||||
+
|
||||
static BOOL tp_object_release( struct threadpool_object *object )
|
||||
{
|
||||
struct threadpool_group *group;
|
||||
@@ -795,6 +843,16 @@ NTSTATUS WINAPI TpAllocPool( TP_POOL **out, PVOID reserved )
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
+ * TpAllocWork (NTDLL.@)
|
||||
+ */
|
||||
+NTSTATUS WINAPI TpAllocWork( TP_WORK **out, PTP_WORK_CALLBACK callback, PVOID userdata,
|
||||
+ TP_CALLBACK_ENVIRON *environment )
|
||||
+{
|
||||
+ TRACE("%p %p %p %p\n", out, callback, userdata, environment);
|
||||
+ return tp_object_alloc_work( (struct threadpool_object **)out, callback, userdata, environment );
|
||||
+}
|
||||
+
|
||||
+/***********************************************************************
|
||||
* TpCallbackLeaveCriticalSectionOnCompletion (NTDLL.@)
|
||||
*/
|
||||
VOID WINAPI TpCallbackLeaveCriticalSectionOnCompletion( TP_CALLBACK_INSTANCE *instance, CRITICAL_SECTION *crit )
|
||||
@@ -889,6 +947,16 @@ VOID WINAPI TpDisassociateCallback( TP_CALLBACK_INSTANCE *instance )
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
+ * TpPostWork (NTDLL.@)
|
||||
+ */
|
||||
+VOID WINAPI TpPostWork( TP_WORK *work )
|
||||
+{
|
||||
+ struct threadpool_object *this = impl_from_TP_WORK( work );
|
||||
+ TRACE("%p\n", work);
|
||||
+ if (this) tp_object_submit( this );
|
||||
+}
|
||||
+
|
||||
+/***********************************************************************
|
||||
* TpReleaseCleanupGroup (NTDLL.@)
|
||||
*/
|
||||
VOID WINAPI TpReleaseCleanupGroup( TP_CLEANUP_GROUP *group )
|
||||
@@ -927,6 +995,20 @@ VOID WINAPI TpReleasePool( TP_POOL *pool )
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
+ * TpReleaseWork (NTDLL.@)
|
||||
+ */
|
||||
+VOID WINAPI TpReleaseWork( TP_WORK *work )
|
||||
+{
|
||||
+ struct threadpool_object *this = impl_from_TP_WORK( work );
|
||||
+ TRACE("%p\n", work);
|
||||
+ if (this)
|
||||
+ {
|
||||
+ tp_object_shutdown( this );
|
||||
+ tp_object_release( this );
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/***********************************************************************
|
||||
* TpSetPoolMaxThreads (NTDLL.@)
|
||||
*/
|
||||
VOID WINAPI TpSetPoolMaxThreads( TP_POOL *pool, DWORD maximum )
|
||||
@@ -964,3 +1046,18 @@ NTSTATUS WINAPI TpSimpleTryPost( PTP_SIMPLE_CALLBACK callback, PVOID userdata, T
|
||||
}
|
||||
return status;
|
||||
}
|
||||
+
|
||||
+/***********************************************************************
|
||||
+ * TpWaitForWork (NTDLL.@)
|
||||
+ */
|
||||
+VOID WINAPI TpWaitForWork( TP_WORK *work, BOOL cancel_pending )
|
||||
+{
|
||||
+ struct threadpool_object *this = impl_from_TP_WORK( work );
|
||||
+ TRACE("%p %d\n", work, cancel_pending);
|
||||
+ if (this)
|
||||
+ {
|
||||
+ if (cancel_pending)
|
||||
+ tp_object_cancel( this, FALSE, NULL );
|
||||
+ tp_object_wait( this );
|
||||
+ }
|
||||
+}
|
||||
--
|
||||
2.2.2
|
||||
|
@ -0,0 +1,551 @@
|
||||
From 248c2929e4cef9d57b6ae641cbf428cfb73efe0f Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Lackner <sebastian@fds-team.de>
|
||||
Date: Sun, 1 Feb 2015 18:21:28 +0100
|
||||
Subject: ntdll: Implement threadpool timer functions.
|
||||
|
||||
---
|
||||
dlls/ntdll/ntdll.spec | 10 +-
|
||||
dlls/ntdll/threadpool2.c | 387 ++++++++++++++++++++++++++++++++++++++++++++++-
|
||||
2 files changed, 391 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec
|
||||
index bf9e795..f4328a9 100644
|
||||
--- a/dlls/ntdll/ntdll.spec
|
||||
+++ b/dlls/ntdll/ntdll.spec
|
||||
@@ -973,7 +973,7 @@
|
||||
# @ stub TpAllocIoCompletion
|
||||
# @ stub TpAllocJobNotification
|
||||
@ stdcall TpAllocPool(ptr ptr)
|
||||
-# @ stub TpAllocTimer
|
||||
+@ stdcall TpAllocTimer(ptr ptr ptr)
|
||||
# @ stub TpAllocWait
|
||||
@ stdcall TpAllocWork(ptr ptr ptr ptr)
|
||||
# @ stub TpAlpcRegisterCompletionList
|
||||
@@ -995,7 +995,7 @@
|
||||
# @ stub TpDbgSetLogRoutine
|
||||
# @ stub TpDisablePoolCallbackChecks
|
||||
@ stdcall TpDisassociateCallback(ptr)
|
||||
-# @ stub TpIsTimerSet
|
||||
+@ stdcall TpIsTimerSet(ptr)
|
||||
@ stdcall TpPostWork(ptr)
|
||||
# @ stub TpQueryPoolStackInformation
|
||||
# @ stub TpReleaseAlpcCompletion
|
||||
@@ -1004,7 +1004,7 @@
|
||||
# @ stub TpReleaseIoCompletion
|
||||
# @ stub TpReleaseJobNotification
|
||||
@ stdcall TpReleasePool(ptr)
|
||||
-# @ stub TpReleaseTimer
|
||||
+@ stdcall TpReleaseTimer(ptr)
|
||||
# @ stub TpReleaseWait
|
||||
@ stdcall TpReleaseWork(ptr)
|
||||
# @ stub TpSetDefaultPoolMaxThreads
|
||||
@@ -1015,7 +1015,7 @@
|
||||
# @ stub TpSetPoolStackInformation
|
||||
# @ stub TpSetPoolThreadBasePriority
|
||||
# @ stub TpSetPoolWorkerThreadIdleTimeout
|
||||
-# @ stub TpSetTimer
|
||||
+@ stdcall TpSetTimer(ptr ptr long long)
|
||||
# @ stub TpSetTimerEx
|
||||
# @ stub TpSetWait
|
||||
# @ stub TpSetWaitEx
|
||||
@@ -1026,7 +1026,7 @@
|
||||
# @ stub TpWaitForAlpcCompletion
|
||||
# @ stub TpWaitForIoCompletion
|
||||
# @ stub TpWaitForJobNotification
|
||||
-# @ stub TpWaitForTimer
|
||||
+@ stdcall TpWaitForTimer(ptr long)
|
||||
# @ stub TpWaitForWait
|
||||
@ stdcall TpWaitForWork(ptr long)
|
||||
@ stdcall -ret64 VerSetConditionMask(int64 long long)
|
||||
diff --git a/dlls/ntdll/threadpool2.c b/dlls/ntdll/threadpool2.c
|
||||
index acc477f..4fa22bb 100644
|
||||
--- a/dlls/ntdll/threadpool2.c
|
||||
+++ b/dlls/ntdll/threadpool2.c
|
||||
@@ -124,7 +124,8 @@ struct threadpool_object
|
||||
{
|
||||
TP_OBJECT_TYPE_UNDEFINED,
|
||||
TP_OBJECT_TYPE_SIMPLE,
|
||||
- TP_OBJECT_TYPE_WORK
|
||||
+ TP_OBJECT_TYPE_WORK,
|
||||
+ TP_OBJECT_TYPE_TIMER
|
||||
} type;
|
||||
|
||||
/* arguments for callback */
|
||||
@@ -140,6 +141,19 @@ struct threadpool_object
|
||||
{
|
||||
PTP_WORK_CALLBACK callback;
|
||||
} work;
|
||||
+ /* timer callback */
|
||||
+ struct
|
||||
+ {
|
||||
+ PTP_TIMER_CALLBACK callback;
|
||||
+
|
||||
+ /* information about the timer, locked via timerqueue.cs */
|
||||
+ BOOL is_timer_set;
|
||||
+ BOOL timer_pending;
|
||||
+ struct list timer_entry;
|
||||
+ ULONGLONG timeout;
|
||||
+ LONG period;
|
||||
+ LONG window_length;
|
||||
+ } timer;
|
||||
} u;
|
||||
};
|
||||
|
||||
@@ -154,6 +168,35 @@ struct threadpool_group
|
||||
struct list members;
|
||||
};
|
||||
|
||||
+/* global timerqueue object */
|
||||
+static RTL_CRITICAL_SECTION_DEBUG timerqueue_debug;
|
||||
+static struct
|
||||
+{
|
||||
+ CRITICAL_SECTION cs;
|
||||
+ BOOL thread_running;
|
||||
+
|
||||
+ /* number of timer objects total */
|
||||
+ LONG num_timers;
|
||||
+
|
||||
+ /* list of pending timers */
|
||||
+ struct list pending_timers;
|
||||
+ RTL_CONDITION_VARIABLE update_event;
|
||||
+}
|
||||
+timerqueue =
|
||||
+{
|
||||
+ { &timerqueue_debug, -1, 0, 0, 0, 0 },
|
||||
+ FALSE,
|
||||
+ 0,
|
||||
+ LIST_INIT( timerqueue.pending_timers ),
|
||||
+ RTL_CONDITION_VARIABLE_INIT
|
||||
+};
|
||||
+static RTL_CRITICAL_SECTION_DEBUG timerqueue_debug =
|
||||
+{
|
||||
+ 0, 0, &timerqueue.cs,
|
||||
+ { &timerqueue_debug.ProcessLocksList, &timerqueue_debug.ProcessLocksList },
|
||||
+ 0, 0, { (DWORD_PTR)(__FILE__ ": timerqueue.cs") }
|
||||
+};
|
||||
+
|
||||
static inline struct threadpool *impl_from_TP_POOL( TP_POOL *pool )
|
||||
{
|
||||
return (struct threadpool *)pool;
|
||||
@@ -166,6 +209,13 @@ static inline struct threadpool_object *impl_from_TP_WORK( TP_WORK *work )
|
||||
return object;
|
||||
}
|
||||
|
||||
+static inline struct threadpool_object *impl_from_TP_TIMER( TP_TIMER *timer )
|
||||
+{
|
||||
+ struct threadpool_object *object = (struct threadpool_object *)timer;
|
||||
+ assert( !object || object->type == TP_OBJECT_TYPE_TIMER );
|
||||
+ return object;
|
||||
+}
|
||||
+
|
||||
static inline struct threadpool_group *impl_from_TP_CLEANUP_GROUP( TP_CLEANUP_GROUP *group )
|
||||
{
|
||||
return (struct threadpool_group *)group;
|
||||
@@ -177,6 +227,7 @@ static inline struct threadpool_instance *impl_from_TP_CALLBACK_INSTANCE( TP_CAL
|
||||
}
|
||||
|
||||
static void CALLBACK threadpool_worker_proc( void *param );
|
||||
+static void CALLBACK timerqueue_thread_proc( void *param );
|
||||
|
||||
static NTSTATUS tp_threadpool_alloc( struct threadpool **out );
|
||||
static BOOL tp_threadpool_release( struct threadpool *pool );
|
||||
@@ -189,6 +240,222 @@ static void tp_object_shutdown( struct threadpool_object *object );
|
||||
static BOOL tp_group_release( struct threadpool_group *group );
|
||||
|
||||
/***********************************************************************
|
||||
+ * TIMERQUEUE IMPLEMENTATION
|
||||
+ ***********************************************************************
|
||||
+ *
|
||||
+ * Based on [1] there is only one (persistent) thread which handles
|
||||
+ * timer events. There is a similar implementation in ntdll/
|
||||
+ * threadpool.c, but its not directly possible to merge them because of
|
||||
+ * specific implementation differences, like handling several events at
|
||||
+ * once using a windowlength parameter. */
|
||||
+
|
||||
+static NTSTATUS tp_timerqueue_acquire( void )
|
||||
+{
|
||||
+ NTSTATUS status = STATUS_SUCCESS;
|
||||
+ RtlEnterCriticalSection( &timerqueue.cs );
|
||||
+
|
||||
+ if (!timerqueue.thread_running)
|
||||
+ {
|
||||
+ HANDLE thread;
|
||||
+ status = RtlCreateUserThread( GetCurrentProcess(), NULL, FALSE, NULL, 0, 0,
|
||||
+ timerqueue_thread_proc, NULL, &thread, NULL );
|
||||
+ if (status == STATUS_SUCCESS)
|
||||
+ {
|
||||
+ NtClose( thread );
|
||||
+ timerqueue.thread_running = TRUE;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (!status) timerqueue.num_timers++;
|
||||
+ RtlLeaveCriticalSection( &timerqueue.cs );
|
||||
+ return status;
|
||||
+}
|
||||
+
|
||||
+static void tp_timerqueue_release( void )
|
||||
+{
|
||||
+ RtlEnterCriticalSection( &timerqueue.cs );
|
||||
+ if (!--timerqueue.num_timers)
|
||||
+ {
|
||||
+ assert( list_empty( &timerqueue.pending_timers ) );
|
||||
+ RtlWakeAllConditionVariable( &timerqueue.update_event );
|
||||
+ }
|
||||
+ RtlLeaveCriticalSection( &timerqueue.cs );
|
||||
+}
|
||||
+
|
||||
+static void tp_timerqueue_update_timer( struct threadpool_object *new_timer, LARGE_INTEGER *timeout,
|
||||
+ LONG period, LONG window_length )
|
||||
+{
|
||||
+ BOOL queue_timer = FALSE, delete_timer = FALSE;
|
||||
+ struct threadpool_object *timer;
|
||||
+ ULONGLONG when;
|
||||
+
|
||||
+ assert( new_timer->type == TP_OBJECT_TYPE_TIMER );
|
||||
+ RtlEnterCriticalSection( &timerqueue.cs );
|
||||
+
|
||||
+ /* Remember if the timer is set or unset */
|
||||
+ new_timer->u.timer.is_timer_set = timeout != NULL;
|
||||
+
|
||||
+ if (!timeout)
|
||||
+ goto update_timer;
|
||||
+
|
||||
+ /* A timeout of zero is a special case, it means that the callback is queued immediately */
|
||||
+ if ((when = timeout->QuadPart) == 0)
|
||||
+ {
|
||||
+ queue_timer = TRUE;
|
||||
+
|
||||
+ if (!period)
|
||||
+ {
|
||||
+ timeout = NULL;
|
||||
+ goto update_timer;
|
||||
+ }
|
||||
+
|
||||
+ when = (ULONGLONG)period * -10000;
|
||||
+ }
|
||||
+
|
||||
+ /* Convert relative timeouts into absolute timeouts */
|
||||
+ if ((LONGLONG)when < 0)
|
||||
+ {
|
||||
+ LARGE_INTEGER now;
|
||||
+ NtQuerySystemTime( &now );
|
||||
+ when = now.QuadPart - when;
|
||||
+ }
|
||||
+
|
||||
+update_timer:
|
||||
+
|
||||
+ /* If timer is still pending, then remove the old one */
|
||||
+ if (new_timer->u.timer.timer_pending)
|
||||
+ {
|
||||
+ list_remove( &new_timer->u.timer.timer_entry );
|
||||
+ new_timer->u.timer.timer_pending = FALSE;
|
||||
+
|
||||
+ /* defer calls to tp_object_release until we are done */
|
||||
+ delete_timer = TRUE;
|
||||
+ }
|
||||
+
|
||||
+ /* Timer should be enabled again, add it to the queue */
|
||||
+ if (timeout)
|
||||
+ {
|
||||
+ interlocked_inc( &new_timer->refcount );
|
||||
+ new_timer->u.timer.timeout = when;
|
||||
+ new_timer->u.timer.period = period;
|
||||
+ new_timer->u.timer.window_length = window_length;
|
||||
+
|
||||
+ /* insert new_timer into the timer queue */
|
||||
+ LIST_FOR_EACH_ENTRY( timer, &timerqueue.pending_timers, struct threadpool_object, u.timer.timer_entry )
|
||||
+ {
|
||||
+ assert( timer->type == TP_OBJECT_TYPE_TIMER );
|
||||
+ if (new_timer->u.timer.timeout < timer->u.timer.timeout)
|
||||
+ break;
|
||||
+ }
|
||||
+ list_add_before( &timer->u.timer.timer_entry, &new_timer->u.timer.timer_entry );
|
||||
+
|
||||
+ /* wake up thread if it should expire earlier than before */
|
||||
+ if (list_head( &timerqueue.pending_timers ) == &new_timer->u.timer.timer_entry )
|
||||
+ RtlWakeAllConditionVariable( &timerqueue.update_event );
|
||||
+
|
||||
+ new_timer->u.timer.timer_pending = TRUE;
|
||||
+ }
|
||||
+
|
||||
+ RtlLeaveCriticalSection( &timerqueue.cs );
|
||||
+ if (queue_timer)
|
||||
+ tp_object_submit( new_timer );
|
||||
+ if (delete_timer)
|
||||
+ tp_object_release( new_timer );
|
||||
+}
|
||||
+
|
||||
+static void CALLBACK timerqueue_thread_proc( void *param )
|
||||
+{
|
||||
+ LARGE_INTEGER now, timeout;
|
||||
+ ULONGLONG timeout_lower, timeout_upper;
|
||||
+ struct threadpool_object *other_timer;
|
||||
+ struct list *ptr;
|
||||
+
|
||||
+ RtlEnterCriticalSection( &timerqueue.cs );
|
||||
+
|
||||
+ for (;;)
|
||||
+ {
|
||||
+ NtQuerySystemTime( &now );
|
||||
+
|
||||
+ while ((ptr = list_head( &timerqueue.pending_timers )))
|
||||
+ {
|
||||
+ struct threadpool_object *timer = LIST_ENTRY( ptr, struct threadpool_object, u.timer.timer_entry );
|
||||
+ assert( timer->type == TP_OBJECT_TYPE_TIMER );
|
||||
+
|
||||
+ /* Timeout didn't expire yet, nothing to do */
|
||||
+ if (timer->u.timer.timeout > now.QuadPart)
|
||||
+ break;
|
||||
+
|
||||
+ /* Queue a new callback in one of the worker threads */
|
||||
+ list_remove( &timer->u.timer.timer_entry );
|
||||
+ tp_object_submit( timer );
|
||||
+
|
||||
+ /* Requeue the timer, except its marked for shutdown */
|
||||
+ if (!timer->shutdown && timer->u.timer.period)
|
||||
+ {
|
||||
+ /* Update the timeout, make sure its at least the current time (to avoid too many work items) */
|
||||
+ timer->u.timer.timeout += (ULONGLONG)timer->u.timer.period * 10000;
|
||||
+ if (timer->u.timer.timeout <= now.QuadPart)
|
||||
+ timer->u.timer.timeout = now.QuadPart + 1;
|
||||
+
|
||||
+ /* Insert timer back into the timer queue */
|
||||
+ LIST_FOR_EACH_ENTRY( other_timer, &timerqueue.pending_timers, struct threadpool_object, u.timer.timer_entry )
|
||||
+ {
|
||||
+ assert( other_timer->type == TP_OBJECT_TYPE_TIMER );
|
||||
+ if (timer->u.timer.timeout < other_timer->u.timer.timeout)
|
||||
+ break;
|
||||
+ }
|
||||
+ list_add_before( &other_timer->u.timer.timer_entry, &timer->u.timer.timer_entry );
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ /* We no longer need the reference to this timer object */
|
||||
+ timer->u.timer.timer_pending = FALSE;
|
||||
+ tp_object_release( timer );
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* Determine next timeout - we use the window_length arguments to optimize wakeup times */
|
||||
+ timeout_lower = timeout_upper = TIMEOUT_INFINITE;
|
||||
+ LIST_FOR_EACH_ENTRY( other_timer, &timerqueue.pending_timers, struct threadpool_object, u.timer.timer_entry )
|
||||
+ {
|
||||
+ ULONGLONG new_timeout_upper;
|
||||
+ assert( other_timer->type == TP_OBJECT_TYPE_TIMER );
|
||||
+ if (other_timer->u.timer.timeout >= timeout_upper)
|
||||
+ break;
|
||||
+
|
||||
+ timeout_lower = other_timer->u.timer.timeout;
|
||||
+ new_timeout_upper = timeout_lower + (ULONGLONG)other_timer->u.timer.window_length * 10000;
|
||||
+
|
||||
+ if (timeout_upper > new_timeout_upper)
|
||||
+ timeout_upper = new_timeout_upper;
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ if (!timerqueue.num_timers)
|
||||
+ {
|
||||
+ /* All timers have been destroyed, if no new timers are created within some amount of
|
||||
+ * time, then we can shutdown this thread. */
|
||||
+ timeout.QuadPart = (ULONGLONG)THREADPOOL_WORKER_TIMEOUT * -10000;
|
||||
+ if (RtlSleepConditionVariableCS( &timerqueue.update_event,
|
||||
+ &timerqueue.cs, &timeout ) == STATUS_TIMEOUT && !timerqueue.num_timers)
|
||||
+ {
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ /* Wait for timer update events or until the next timer expires. */
|
||||
+ timeout.QuadPart = timeout_lower;
|
||||
+ RtlSleepConditionVariableCS( &timerqueue.update_event, &timerqueue.cs, &timeout );
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ timerqueue.thread_running = FALSE;
|
||||
+ RtlLeaveCriticalSection( &timerqueue.cs );
|
||||
+}
|
||||
+
|
||||
+
|
||||
+/***********************************************************************
|
||||
* THREADPOOL INSTANCE IMPLEMENTATION
|
||||
***********************************************************************/
|
||||
|
||||
@@ -433,6 +700,16 @@ static void CALLBACK threadpool_worker_proc( void *param )
|
||||
break;
|
||||
}
|
||||
|
||||
+ case TP_OBJECT_TYPE_TIMER:
|
||||
+ {
|
||||
+ TP_CALLBACK_INSTANCE *cb_instance = (TP_CALLBACK_INSTANCE *)&instance;
|
||||
+ TRACE( "executing callback %p(%p, %p)\n",
|
||||
+ object->u.timer.callback, cb_instance, object->userdata );
|
||||
+ object->u.timer.callback( cb_instance, object->userdata, (TP_TIMER *)object );
|
||||
+ TRACE( "callback %p returned\n", object->u.timer.callback );
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
default:
|
||||
FIXME( "callback type %u not implemented\n", object->type );
|
||||
break;
|
||||
@@ -602,6 +879,48 @@ static NTSTATUS tp_object_alloc_work( struct threadpool_object **out, PTP_WORK_C
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
+static NTSTATUS tp_object_alloc_timer( struct threadpool_object **out, PTP_TIMER_CALLBACK callback,
|
||||
+ PVOID userdata, TP_CALLBACK_ENVIRON *environment )
|
||||
+{
|
||||
+ struct threadpool_object *object;
|
||||
+ struct threadpool *pool;
|
||||
+ NTSTATUS status;
|
||||
+
|
||||
+ /* determine threadpool */
|
||||
+ pool = environment ? (struct threadpool *)environment->Pool : NULL;
|
||||
+ if (!pool) pool = get_default_threadpool();
|
||||
+ if (!pool) return STATUS_NO_MEMORY;
|
||||
+
|
||||
+ object = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*object) );
|
||||
+ if (!object)
|
||||
+ return STATUS_NO_MEMORY;
|
||||
+
|
||||
+ status = tp_timerqueue_acquire();
|
||||
+ if (status)
|
||||
+ {
|
||||
+ RtlFreeHeap( GetProcessHeap(), 0, object );
|
||||
+ return status;
|
||||
+ }
|
||||
+
|
||||
+ object->type = TP_OBJECT_TYPE_TIMER;
|
||||
+ object->u.timer.callback = callback;
|
||||
+
|
||||
+ object->u.timer.timer_pending = FALSE;
|
||||
+ memset( &object->u.timer.timer_entry, 0, sizeof(object->u.timer.timer_entry));
|
||||
+
|
||||
+ object->u.timer.is_timer_set = FALSE;
|
||||
+ object->u.timer.timeout = 0;
|
||||
+ object->u.timer.period = 0;
|
||||
+ object->u.timer.window_length = 0;
|
||||
+
|
||||
+ tp_object_initialize( object, pool, userdata, environment );
|
||||
+
|
||||
+ TRACE("allocated object %p of type %u\n", object, object->type);
|
||||
+
|
||||
+ *out = object;
|
||||
+ return STATUS_SUCCESS;
|
||||
+}
|
||||
+
|
||||
static BOOL tp_object_release( struct threadpool_object *object )
|
||||
{
|
||||
struct threadpool_group *group;
|
||||
@@ -624,6 +943,13 @@ static BOOL tp_object_release( struct threadpool_object *object )
|
||||
tp_group_release( group );
|
||||
}
|
||||
|
||||
+ /* release reference on the timerqueue */
|
||||
+ if (object->type == TP_OBJECT_TYPE_TIMER)
|
||||
+ {
|
||||
+ assert( !object->u.timer.timer_pending );
|
||||
+ tp_timerqueue_release();
|
||||
+ }
|
||||
+
|
||||
/* release reference to library */
|
||||
if (object->race_dll)
|
||||
LdrUnloadDll( object->race_dll );
|
||||
@@ -843,6 +1169,16 @@ NTSTATUS WINAPI TpAllocPool( TP_POOL **out, PVOID reserved )
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
+ * TpAllocTimer (NTDLL.@)
|
||||
+ */
|
||||
+NTSTATUS WINAPI TpAllocTimer( TP_TIMER **out, PTP_TIMER_CALLBACK callback, PVOID userdata,
|
||||
+ TP_CALLBACK_ENVIRON *environment )
|
||||
+{
|
||||
+ TRACE("%p %p %p %p\n", out, callback, userdata, environment);
|
||||
+ return tp_object_alloc_timer( (struct threadpool_object **)out, callback, userdata, environment );
|
||||
+}
|
||||
+
|
||||
+/***********************************************************************
|
||||
* TpAllocWork (NTDLL.@)
|
||||
*/
|
||||
NTSTATUS WINAPI TpAllocWork( TP_WORK **out, PTP_WORK_CALLBACK callback, PVOID userdata,
|
||||
@@ -947,6 +1283,16 @@ VOID WINAPI TpDisassociateCallback( TP_CALLBACK_INSTANCE *instance )
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
+ * TpIsTimerSet (NTDLL.@)
|
||||
+ */
|
||||
+BOOL WINAPI TpIsTimerSet( TP_TIMER *timer )
|
||||
+{
|
||||
+ struct threadpool_object *this = impl_from_TP_TIMER( timer );
|
||||
+ TRACE("%p\n", timer);
|
||||
+ return this ? this->u.timer.is_timer_set : FALSE;
|
||||
+}
|
||||
+
|
||||
+/***********************************************************************
|
||||
* TpPostWork (NTDLL.@)
|
||||
*/
|
||||
VOID WINAPI TpPostWork( TP_WORK *work )
|
||||
@@ -995,6 +1341,20 @@ VOID WINAPI TpReleasePool( TP_POOL *pool )
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
+ * TpReleaseTimer (NTDLL.@)
|
||||
+ */
|
||||
+VOID WINAPI TpReleaseTimer( TP_TIMER *timer )
|
||||
+{
|
||||
+ struct threadpool_object *this = impl_from_TP_TIMER( timer );
|
||||
+ TRACE("%p\n", timer);
|
||||
+ if (this)
|
||||
+ {
|
||||
+ tp_object_shutdown( this );
|
||||
+ tp_object_release( this );
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/***********************************************************************
|
||||
* TpReleaseWork (NTDLL.@)
|
||||
*/
|
||||
VOID WINAPI TpReleaseWork( TP_WORK *work )
|
||||
@@ -1030,6 +1390,16 @@ BOOL WINAPI TpSetPoolMinThreads( TP_POOL *pool, DWORD minimum )
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
+ * TpSetTimer (NTDLL.@)
|
||||
+ */
|
||||
+VOID WINAPI TpSetTimer( TP_TIMER *timer, LARGE_INTEGER *timeout, LONG period, LONG window_length )
|
||||
+{
|
||||
+ struct threadpool_object *this = impl_from_TP_TIMER( timer );
|
||||
+ TRACE("%p %p %u %u\n", timer, timeout, period, window_length);
|
||||
+ if (this) tp_timerqueue_update_timer( this, timeout, period, window_length );
|
||||
+}
|
||||
+
|
||||
+/***********************************************************************
|
||||
* TpSimpleTryPost (NTDLL.@)
|
||||
*/
|
||||
NTSTATUS WINAPI TpSimpleTryPost( PTP_SIMPLE_CALLBACK callback, PVOID userdata, TP_CALLBACK_ENVIRON *environment )
|
||||
@@ -1048,6 +1418,21 @@ NTSTATUS WINAPI TpSimpleTryPost( PTP_SIMPLE_CALLBACK callback, PVOID userdata, T
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
+ * TpWaitForTimer (NTDLL.@)
|
||||
+ */
|
||||
+VOID WINAPI TpWaitForTimer( TP_TIMER *timer, BOOL cancel_pending )
|
||||
+{
|
||||
+ struct threadpool_object *this = impl_from_TP_TIMER( timer );
|
||||
+ TRACE("%p %d\n", timer, cancel_pending);
|
||||
+ if (this)
|
||||
+ {
|
||||
+ if (cancel_pending)
|
||||
+ tp_object_cancel( this, FALSE, NULL );
|
||||
+ tp_object_wait( this );
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/***********************************************************************
|
||||
* TpWaitForWork (NTDLL.@)
|
||||
*/
|
||||
VOID WINAPI TpWaitForWork( TP_WORK *work, BOOL cancel_pending )
|
||||
--
|
||||
2.2.2
|
||||
|
@ -0,0 +1,809 @@
|
||||
From 19b57d882236efe0708f7d495ca7301a6c4955e4 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Lackner <sebastian@fds-team.de>
|
||||
Date: Sun, 1 Feb 2015 18:14:09 +0100
|
||||
Subject: ntdll/tests: Add tests for Tp* threadpool functions.
|
||||
|
||||
---
|
||||
dlls/ntdll/tests/Makefile.in | 1 +
|
||||
dlls/ntdll/tests/threadpool.c | 779 ++++++++++++++++++++++++++++++++++++++++++
|
||||
2 files changed, 780 insertions(+)
|
||||
create mode 100644 dlls/ntdll/tests/threadpool.c
|
||||
|
||||
diff --git a/dlls/ntdll/tests/Makefile.in b/dlls/ntdll/tests/Makefile.in
|
||||
index 81b4466..fc352dd 100644
|
||||
--- a/dlls/ntdll/tests/Makefile.in
|
||||
+++ b/dlls/ntdll/tests/Makefile.in
|
||||
@@ -21,4 +21,5 @@ C_SRCS = \
|
||||
rtlbitmap.c \
|
||||
rtlstr.c \
|
||||
string.c \
|
||||
+ threadpool.c \
|
||||
time.c
|
||||
diff --git a/dlls/ntdll/tests/threadpool.c b/dlls/ntdll/tests/threadpool.c
|
||||
new file mode 100644
|
||||
index 0000000..434bd9a
|
||||
--- /dev/null
|
||||
+++ b/dlls/ntdll/tests/threadpool.c
|
||||
@@ -0,0 +1,779 @@
|
||||
+/* Unit test suite for Threadpool functions
|
||||
+ *
|
||||
+ * Copyright 2015 Sebastian Lackner
|
||||
+ *
|
||||
+ * This library is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU Lesser General Public
|
||||
+ * License as published by the Free Software Foundation; either
|
||||
+ * version 2.1 of the License, or (at your option) any later version.
|
||||
+ *
|
||||
+ * This library is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ * Lesser General Public License for more details.
|
||||
+ *
|
||||
+ * You should have received a copy of the GNU Lesser General Public
|
||||
+ * License along with this library; if not, write to the Free Software
|
||||
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
+ */
|
||||
+
|
||||
+#include "ntdll_test.h"
|
||||
+
|
||||
+static HMODULE hntdll = 0;
|
||||
+static NTSTATUS (WINAPI *pTpAllocCleanupGroup)(TP_CLEANUP_GROUP **);
|
||||
+static NTSTATUS (WINAPI *pTpAllocIoCompletion)(TP_IO **,HANDLE,PTP_WIN32_IO_CALLBACK,PVOID,TP_CALLBACK_ENVIRON *);
|
||||
+static NTSTATUS (WINAPI *pTpAllocPool)(TP_POOL **,PVOID);
|
||||
+static NTSTATUS (WINAPI *pTpAllocTimer)(TP_TIMER **,PTP_TIMER_CALLBACK,PVOID,TP_CALLBACK_ENVIRON *);
|
||||
+static NTSTATUS (WINAPI *pTpAllocWait)(TP_WAIT **,PTP_WAIT_CALLBACK,PVOID,TP_CALLBACK_ENVIRON *);
|
||||
+static NTSTATUS (WINAPI *pTpAllocWork)(TP_WORK **,PTP_WORK_CALLBACK,PVOID,TP_CALLBACK_ENVIRON *);
|
||||
+static VOID (WINAPI *pTpCallbackLeaveCriticalSectionOnCompletion)(TP_CALLBACK_INSTANCE *,CRITICAL_SECTION *);
|
||||
+static NTSTATUS (WINAPI *pTpCallbackMayRunLong)(TP_CALLBACK_INSTANCE *);
|
||||
+static VOID (WINAPI *pTpCallbackReleaseMutexOnCompletion)(TP_CALLBACK_INSTANCE *,HANDLE);
|
||||
+static VOID (WINAPI *pTpCallbackReleaseSemaphoreOnCompletion)(TP_CALLBACK_INSTANCE *,HANDLE,DWORD);
|
||||
+static VOID (WINAPI *pTpCallbackSetEventOnCompletion)(TP_CALLBACK_INSTANCE *,HANDLE);
|
||||
+static VOID (WINAPI *pTpCallbackUnloadDllOnCompletion)(TP_CALLBACK_INSTANCE *,HMODULE);
|
||||
+static VOID (WINAPI *pTpCancelAsyncIoOperation)(TP_IO *);
|
||||
+static VOID (WINAPI *pTpDisassociateCallback)(TP_CALLBACK_INSTANCE *);
|
||||
+static BOOL (WINAPI *pTpIsTimerSet)(TP_TIMER *);
|
||||
+static VOID (WINAPI *pTpPostWork)(TP_WORK *);
|
||||
+static VOID (WINAPI *pTpReleaseCleanupGroup)(TP_CLEANUP_GROUP *);
|
||||
+static VOID (WINAPI *pTpReleaseCleanupGroupMembers)(TP_CLEANUP_GROUP *,BOOL,PVOID);
|
||||
+static VOID (WINAPI *pTpReleaseIoCompletion)(TP_IO *);
|
||||
+static VOID (WINAPI *pTpReleasePool)(TP_POOL *);
|
||||
+static VOID (WINAPI *pTpReleaseTimer)(TP_TIMER *);
|
||||
+static VOID (WINAPI *pTpReleaseWait)(TP_WAIT *);
|
||||
+static VOID (WINAPI *pTpReleaseWork)(TP_WORK *);
|
||||
+static VOID (WINAPI *pTpSetPoolMaxThreads)(TP_POOL *,DWORD);
|
||||
+static BOOL (WINAPI *pTpSetPoolMinThreads)(TP_POOL *,DWORD);
|
||||
+static VOID (WINAPI *pTpSetTimer)(TP_TIMER *,LARGE_INTEGER *,LONG,LONG);
|
||||
+static VOID (WINAPI *pTpSetWait)(TP_WAIT *,HANDLE,LARGE_INTEGER *);
|
||||
+static NTSTATUS (WINAPI *pTpSimpleTryPost)(PTP_SIMPLE_CALLBACK,PVOID,TP_CALLBACK_ENVIRON *);
|
||||
+static VOID (WINAPI *pTpStartAsyncIoOperation)(TP_IO *);
|
||||
+static VOID (WINAPI *pTpWaitForIoCompletion)(TP_IO *,BOOL);
|
||||
+static VOID (WINAPI *pTpWaitForTimer)(TP_TIMER *,BOOL);
|
||||
+static VOID (WINAPI *pTpWaitForWait)(TP_WAIT *,BOOL);
|
||||
+static VOID (WINAPI *pTpWaitForWork)(TP_WORK *,BOOL);
|
||||
+
|
||||
+#define NTDLL_GET_PROC(func) \
|
||||
+ do \
|
||||
+ { \
|
||||
+ p ## func = (void *)GetProcAddress(hntdll, #func); \
|
||||
+ if (!p ## func) trace("Failed to get address for %s\n", #func); \
|
||||
+ } \
|
||||
+ while (0)
|
||||
+
|
||||
+static BOOL init_threadpool(void)
|
||||
+{
|
||||
+ hntdll = GetModuleHandleA("ntdll");
|
||||
+ if (!hntdll)
|
||||
+ {
|
||||
+ win_skip("Could not load ntdll\n");
|
||||
+ return FALSE;
|
||||
+ }
|
||||
+
|
||||
+ NTDLL_GET_PROC(TpAllocCleanupGroup);
|
||||
+ NTDLL_GET_PROC(TpAllocIoCompletion);
|
||||
+ NTDLL_GET_PROC(TpAllocPool);
|
||||
+ NTDLL_GET_PROC(TpAllocTimer);
|
||||
+ NTDLL_GET_PROC(TpAllocWait);
|
||||
+ NTDLL_GET_PROC(TpAllocWork);
|
||||
+ NTDLL_GET_PROC(TpCallbackLeaveCriticalSectionOnCompletion);
|
||||
+ NTDLL_GET_PROC(TpCallbackMayRunLong);
|
||||
+ NTDLL_GET_PROC(TpCallbackReleaseMutexOnCompletion);
|
||||
+ NTDLL_GET_PROC(TpCallbackReleaseSemaphoreOnCompletion);
|
||||
+ NTDLL_GET_PROC(TpCallbackSetEventOnCompletion);
|
||||
+ NTDLL_GET_PROC(TpCallbackUnloadDllOnCompletion);
|
||||
+ NTDLL_GET_PROC(TpCancelAsyncIoOperation);
|
||||
+ NTDLL_GET_PROC(TpDisassociateCallback);
|
||||
+ NTDLL_GET_PROC(TpIsTimerSet);
|
||||
+ NTDLL_GET_PROC(TpPostWork);
|
||||
+ NTDLL_GET_PROC(TpReleaseCleanupGroup);
|
||||
+ NTDLL_GET_PROC(TpReleaseCleanupGroupMembers);
|
||||
+ NTDLL_GET_PROC(TpReleaseIoCompletion);
|
||||
+ NTDLL_GET_PROC(TpReleasePool);
|
||||
+ NTDLL_GET_PROC(TpReleaseTimer);
|
||||
+ NTDLL_GET_PROC(TpReleaseWait);
|
||||
+ NTDLL_GET_PROC(TpReleaseWork);
|
||||
+ NTDLL_GET_PROC(TpSetPoolMaxThreads);
|
||||
+ NTDLL_GET_PROC(TpSetPoolMinThreads);
|
||||
+ NTDLL_GET_PROC(TpSetTimer);
|
||||
+ NTDLL_GET_PROC(TpSetWait);
|
||||
+ NTDLL_GET_PROC(TpSimpleTryPost);
|
||||
+ NTDLL_GET_PROC(TpStartAsyncIoOperation);
|
||||
+ NTDLL_GET_PROC(TpWaitForIoCompletion);
|
||||
+ NTDLL_GET_PROC(TpWaitForTimer);
|
||||
+ NTDLL_GET_PROC(TpWaitForWait);
|
||||
+ NTDLL_GET_PROC(TpWaitForWork);
|
||||
+
|
||||
+ if (!pTpAllocPool)
|
||||
+ {
|
||||
+ win_skip("Threadpool functions not supported, skipping tests\n");
|
||||
+ return FALSE;
|
||||
+ }
|
||||
+
|
||||
+ return TRUE;
|
||||
+}
|
||||
+
|
||||
+#undef NTDLL_GET_PROC
|
||||
+
|
||||
+
|
||||
+static void CALLBACK simple_cb(TP_CALLBACK_INSTANCE *instance, void *userdata)
|
||||
+{
|
||||
+ trace("Running simple callback\n");
|
||||
+ Sleep(100);
|
||||
+ InterlockedIncrement( (LONG *)userdata );
|
||||
+}
|
||||
+
|
||||
+static void test_default_threadpool(void)
|
||||
+{
|
||||
+ TP_CALLBACK_ENVIRON environment;
|
||||
+ NTSTATUS status;
|
||||
+ LONG userdata;
|
||||
+
|
||||
+ /* Post the callback and manually wait until it has finished */
|
||||
+ userdata = 0;
|
||||
+ memset(&environment, 0, sizeof(environment));
|
||||
+ environment.Version = 1;
|
||||
+ status = pTpSimpleTryPost(simple_cb, &userdata, &environment);
|
||||
+ ok(!status, "TpSimpleTryPost failed with status %x\n", status);
|
||||
+ while (userdata == 0) Sleep(10);
|
||||
+ ok(userdata == 1, "expected userdata = 1, got %u\n", userdata);
|
||||
+}
|
||||
+
|
||||
+static void test_tp_simple(void)
|
||||
+{
|
||||
+ TP_CALLBACK_ENVIRON environment;
|
||||
+ TP_CLEANUP_GROUP *group;
|
||||
+ TP_POOL *pool;
|
||||
+ NTSTATUS status;
|
||||
+ LONG userdata;
|
||||
+ int i;
|
||||
+
|
||||
+ /* Allocate new threadpool */
|
||||
+ pool = NULL;
|
||||
+ status = pTpAllocPool(&pool, NULL);
|
||||
+ ok(!status, "TpAllocPool failed with status %x\n", status);
|
||||
+ ok(pool != NULL, "expected pool != NULL\n");
|
||||
+
|
||||
+ /* Post the callback and manually wait until it has finished */
|
||||
+ userdata = 0;
|
||||
+ memset(&environment, 0, sizeof(environment));
|
||||
+ environment.Version = 1;
|
||||
+ environment.Pool = pool;
|
||||
+ status = pTpSimpleTryPost(simple_cb, &userdata, &environment);
|
||||
+ ok(!status, "TpSimpleTryPost failed with status %x\n", status);
|
||||
+ while (userdata == 0) Sleep(10);
|
||||
+ ok(userdata == 1, "expected userdata = 1, got %u\n", userdata);
|
||||
+
|
||||
+ /* Test with invalid version number */
|
||||
+ userdata = 0;
|
||||
+ memset(&environment, 0, sizeof(environment));
|
||||
+ environment.Version = 9999;
|
||||
+ environment.Pool = pool;
|
||||
+ status = pTpSimpleTryPost(simple_cb, &userdata, &environment);
|
||||
+ ok(!status, "TpSimpleTryPost failed with status %x\n", status);
|
||||
+ while (userdata == 0) Sleep(10);
|
||||
+ ok(userdata == 1, "expected userdata = 1, got %u\n", userdata);
|
||||
+
|
||||
+ /* Allocate a cleanup group for synchronization */
|
||||
+ group = NULL;
|
||||
+ status = pTpAllocCleanupGroup(&group);
|
||||
+ ok(!status, "TpAllocCleanupGroup failed with status %x\n", status);
|
||||
+ ok(group != NULL, "expected pool != NULL\n");
|
||||
+
|
||||
+ userdata = 0;
|
||||
+ memset(&environment, 0, sizeof(environment));
|
||||
+ environment.Version = 1;
|
||||
+ environment.Pool = pool;
|
||||
+ environment.CleanupGroup = group;
|
||||
+ status = pTpSimpleTryPost(simple_cb, &userdata, &environment);
|
||||
+ ok(!status, "TpSimpleTryPost failed with status %x\n", status);
|
||||
+
|
||||
+ pTpReleaseCleanupGroupMembers(group, FALSE, NULL);
|
||||
+ ok(userdata == 1, "expected userdata = 1, got %u\n", userdata);
|
||||
+
|
||||
+ /* Test cancellation of pending simple callbacks. */
|
||||
+ userdata = 0;
|
||||
+ memset(&environment, 0, sizeof(environment));
|
||||
+ environment.Version = 1;
|
||||
+ environment.Pool = pool;
|
||||
+ environment.CleanupGroup = group;
|
||||
+ for (i = 0; i < 100; i++)
|
||||
+ {
|
||||
+ status = pTpSimpleTryPost(simple_cb, &userdata, &environment);
|
||||
+ ok(!status, "TpSimpleTryPost failed with status %x\n", status);
|
||||
+ }
|
||||
+
|
||||
+ pTpReleaseCleanupGroupMembers(group, TRUE, NULL);
|
||||
+ ok(userdata < 100, "expected userdata < 100, got %u\n", userdata);
|
||||
+
|
||||
+ /* Cleanup */
|
||||
+ pTpReleaseCleanupGroup(group);
|
||||
+ pTpReleasePool(pool);
|
||||
+}
|
||||
+
|
||||
+static void CALLBACK work_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, TP_WORK *work)
|
||||
+{
|
||||
+ trace("Running work callback\n");
|
||||
+ Sleep(10);
|
||||
+ InterlockedIncrement( (LONG *)userdata );
|
||||
+}
|
||||
+
|
||||
+static void CALLBACK work2_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, TP_WORK *work)
|
||||
+{
|
||||
+ trace("Running work2 callback\n");
|
||||
+ Sleep(10);
|
||||
+ InterlockedExchangeAdd( (LONG *)userdata, 0x10000 );
|
||||
+}
|
||||
+
|
||||
+static void test_tp_work(void)
|
||||
+{
|
||||
+ TP_CALLBACK_ENVIRON environment;
|
||||
+ TP_WORK *work;
|
||||
+ TP_POOL *pool;
|
||||
+ NTSTATUS status;
|
||||
+ LONG userdata;
|
||||
+ int i;
|
||||
+
|
||||
+ /* Allocate new threadpool */
|
||||
+ pool = NULL;
|
||||
+ status = pTpAllocPool(&pool, NULL);
|
||||
+ ok(!status, "TpAllocPool failed with status %x\n", status);
|
||||
+ ok(pool != NULL, "expected pool != NULL\n");
|
||||
+
|
||||
+ /* Allocate new work item */
|
||||
+ work = NULL;
|
||||
+ memset(&environment, 0, sizeof(environment));
|
||||
+ environment.Version = 1;
|
||||
+ environment.Pool = pool;
|
||||
+ status = pTpAllocWork(&work, work_cb, &userdata, &environment);
|
||||
+ ok(!status, "TpAllocWork failed with status %x\n", status);
|
||||
+ ok(work != NULL, "expected work != NULL\n");
|
||||
+
|
||||
+ /* Post 10 identical work items at once */
|
||||
+ userdata = 0;
|
||||
+ for (i = 0; i < 10; i++)
|
||||
+ pTpPostWork(work);
|
||||
+ pTpWaitForWork(work, FALSE);
|
||||
+ ok(userdata == 10, "expected userdata = 10, got %u\n", userdata);
|
||||
+
|
||||
+ /* Add more tasks and cancel them immediately */
|
||||
+ userdata = 0;
|
||||
+ for (i = 0; i < 10; i++)
|
||||
+ pTpPostWork(work);
|
||||
+ pTpWaitForWork(work, TRUE);
|
||||
+ ok(userdata < 10, "expected userdata < 10, got %u\n", userdata);
|
||||
+
|
||||
+ /* Cleanup */
|
||||
+ pTpReleaseWork(work);
|
||||
+ pTpReleasePool(pool);
|
||||
+}
|
||||
+
|
||||
+static void test_tp_work_scheduler(void)
|
||||
+{
|
||||
+ TP_CALLBACK_ENVIRON environment;
|
||||
+ TP_CLEANUP_GROUP *group;
|
||||
+ TP_WORK *work, *work2;
|
||||
+ TP_POOL *pool;
|
||||
+ NTSTATUS status;
|
||||
+ LONG userdata;
|
||||
+ int i;
|
||||
+
|
||||
+ /* Allocate new threadpool */
|
||||
+ pool = NULL;
|
||||
+ status = pTpAllocPool(&pool, NULL);
|
||||
+ ok(!status, "TpAllocPool failed with status %x\n", status);
|
||||
+ ok(pool != NULL, "expected pool != NULL\n");
|
||||
+
|
||||
+ /* We limit the pool to a single thread */
|
||||
+ pTpSetPoolMaxThreads(pool, 1);
|
||||
+
|
||||
+ /* Create a cleanup group */
|
||||
+ group = NULL;
|
||||
+ status = pTpAllocCleanupGroup(&group);
|
||||
+ ok(!status, "TpAllocCleanupGroup failed with status %x\n", status);
|
||||
+ ok(group != NULL, "expected pool != NULL\n");
|
||||
+
|
||||
+ /* The first work item has no cleanup group associated */
|
||||
+ work = NULL;
|
||||
+ memset(&environment, 0, sizeof(environment));
|
||||
+ environment.Version = 1;
|
||||
+ environment.Pool = pool;
|
||||
+ status = pTpAllocWork(&work, work_cb, &userdata, &environment);
|
||||
+ ok(!status, "TpAllocWork failed with status %x\n", status);
|
||||
+ ok(work != NULL, "expected work != NULL\n");
|
||||
+
|
||||
+ /* Allocate a second work item with a cleanup group */
|
||||
+ work2 = NULL;
|
||||
+ memset(&environment, 0, sizeof(environment));
|
||||
+ environment.Version = 1;
|
||||
+ environment.Pool = pool;
|
||||
+ environment.CleanupGroup = group;
|
||||
+ status = pTpAllocWork(&work2, work2_cb, &userdata, &environment);
|
||||
+ ok(!status, "TpAllocWork failed with status %x\n", status);
|
||||
+ ok(work2 != NULL, "expected work2 != NULL\n");
|
||||
+
|
||||
+ /* The 'work' callbacks are not blocking execution of work2 callbacks */
|
||||
+ userdata = 0;
|
||||
+ for (i = 0; i < 10; i++)
|
||||
+ pTpPostWork(work);
|
||||
+ for (i = 0; i < 10; i++)
|
||||
+ pTpPostWork(work2);
|
||||
+ Sleep(30);
|
||||
+ pTpWaitForWork(work, TRUE);
|
||||
+ pTpWaitForWork(work2, TRUE);
|
||||
+ ok(userdata & 0xffff, "expected userdata & 0xffff != 0, got %u\n", userdata & 0xffff);
|
||||
+ ok(userdata >> 16, "expected userdata >> 16 != 0, got %u\n", userdata >> 16);
|
||||
+
|
||||
+ /* Test ReleaseCleanupGroupMembers on a work item */
|
||||
+ userdata = 0;
|
||||
+ for (i = 0; i < 100; i++)
|
||||
+ pTpPostWork(work);
|
||||
+ for (i = 0; i < 10; i++)
|
||||
+ pTpPostWork(work2);
|
||||
+ pTpReleaseCleanupGroupMembers(group, FALSE, NULL);
|
||||
+ pTpWaitForWork(work, TRUE);
|
||||
+ ok((userdata & 0xffff) < 100, "expected userdata & 0xffff < 100, got %u\n", userdata & 0xffff);
|
||||
+ ok(userdata >> 16 == 10, "expected userdata >> 16 == 10, got %u\n", userdata >> 16);
|
||||
+
|
||||
+ /* Cleanup */
|
||||
+ pTpReleaseWork(work);
|
||||
+ pTpReleaseCleanupGroup(group);
|
||||
+ pTpReleasePool(pool);
|
||||
+}
|
||||
+
|
||||
+static void CALLBACK instance_cb(TP_CALLBACK_INSTANCE *instance, void *userdata)
|
||||
+{
|
||||
+ trace("Running instance callback\n");
|
||||
+ pTpCallbackMayRunLong(instance);
|
||||
+ Sleep(100);
|
||||
+ InterlockedIncrement( (LONG *)userdata );
|
||||
+}
|
||||
+
|
||||
+static void CALLBACK instance2_cb(TP_CALLBACK_INSTANCE *instance, void *userdata)
|
||||
+{
|
||||
+ trace("Running instance2 callback\n");
|
||||
+ ok(*(LONG *)userdata == 1, "expected *userdata = 1, got %u\n", *(LONG *)userdata);
|
||||
+ InterlockedIncrement( (LONG *)userdata );
|
||||
+}
|
||||
+
|
||||
+static void CALLBACK instance3_cb(TP_CALLBACK_INSTANCE *instance, void *userdata)
|
||||
+{
|
||||
+ trace("Running instance3 callback\n");
|
||||
+ pTpDisassociateCallback(instance);
|
||||
+ Sleep(100);
|
||||
+ InterlockedIncrement( (LONG *)userdata );
|
||||
+}
|
||||
+
|
||||
+static void CALLBACK instance4_cb(TP_CALLBACK_INSTANCE *instance, void *userdata)
|
||||
+{
|
||||
+ trace("Running instance4 callback\n");
|
||||
+ pTpCallbackReleaseSemaphoreOnCompletion(instance, userdata, 1);
|
||||
+}
|
||||
+
|
||||
+static HANDLE instance_finalization_semaphore;
|
||||
+
|
||||
+static void CALLBACK instance_finalization_cb(TP_CALLBACK_INSTANCE *instance, void *userdata)
|
||||
+{
|
||||
+ DWORD ret;
|
||||
+ trace("Running instance finalization callback\n");
|
||||
+ ok(*(LONG *)userdata == 1, "expected *userdata = 1, got %u\n", *(LONG *)userdata);
|
||||
+
|
||||
+ /* Make sure that this callback is called before the regular instance cleanup tasks */
|
||||
+ ret = WaitForSingleObject(instance_finalization_semaphore, 100);
|
||||
+ ok(ret == WAIT_TIMEOUT, "expected ret = WAIT_TIMEOUT, got %u\n", ret);
|
||||
+
|
||||
+ InterlockedIncrement( (LONG *)userdata );
|
||||
+}
|
||||
+
|
||||
+static void CALLBACK instance5_cb(TP_CALLBACK_INSTANCE *instance, void *userdata)
|
||||
+{
|
||||
+ trace("Running instance5 callback\n");
|
||||
+ pTpCallbackReleaseSemaphoreOnCompletion(instance, instance_finalization_semaphore, 1);
|
||||
+ InterlockedIncrement( (LONG *)userdata );
|
||||
+}
|
||||
+
|
||||
+
|
||||
+static void test_tp_instance(void)
|
||||
+{
|
||||
+ TP_CALLBACK_ENVIRON environment;
|
||||
+ TP_CLEANUP_GROUP *group;
|
||||
+ TP_POOL *pool;
|
||||
+ NTSTATUS status;
|
||||
+ LONG userdata;
|
||||
+ HANDLE semaphore;
|
||||
+ DWORD ret;
|
||||
+
|
||||
+ /* Allocate new threadpool */
|
||||
+ pool = NULL;
|
||||
+ status = pTpAllocPool(&pool, NULL);
|
||||
+ ok(!status, "TpAllocPool failed with status %x\n", status);
|
||||
+ ok(pool != NULL, "expected pool != NULL\n");
|
||||
+
|
||||
+ /* We limit the pool to a single thread */
|
||||
+ pTpSetPoolMaxThreads(pool, 1);
|
||||
+
|
||||
+ /* Test behaviour of TpCallbackMayRunLong when the max number of threads is reached */
|
||||
+ userdata = 0;
|
||||
+ memset(&environment, 0, sizeof(environment));
|
||||
+ environment.Version = 1;
|
||||
+ environment.Pool = pool;
|
||||
+ status = pTpSimpleTryPost(instance_cb, &userdata, &environment);
|
||||
+ ok(!status, "TpSimpleTryPost failed with status %x\n", status);
|
||||
+ status = pTpSimpleTryPost(instance2_cb, &userdata, &environment);
|
||||
+ ok(!status, "TpSimpleTryPost failed with status %x\n", status);
|
||||
+ while (userdata != 2) Sleep(10);
|
||||
+
|
||||
+ /* Test behaviour of TpDisassociateCallback on wait functions */
|
||||
+ group = NULL;
|
||||
+ status = pTpAllocCleanupGroup(&group);
|
||||
+ ok(!status, "TpAllocCleanupGroup failed with status %x\n", status);
|
||||
+ ok(group != NULL, "expected pool != NULL\n");
|
||||
+
|
||||
+ userdata = 0;
|
||||
+ memset(&environment, 0, sizeof(environment));
|
||||
+ environment.Version = 1;
|
||||
+ environment.Pool = pool;
|
||||
+ environment.CleanupGroup = group;
|
||||
+ status = pTpSimpleTryPost(instance3_cb, &userdata, &environment);
|
||||
+ ok(!status, "TpSimpleTryPost failed with status %x\n", status);
|
||||
+
|
||||
+ pTpReleaseCleanupGroupMembers(group, FALSE, NULL);
|
||||
+ todo_wine /* behaviour contradicts the MSDN description? */
|
||||
+ ok(userdata == 1, "expected userdata = 1, got %u\n", userdata);
|
||||
+ while (userdata == 0) Sleep(10);
|
||||
+
|
||||
+ pTpReleaseCleanupGroup(group);
|
||||
+
|
||||
+ /* Test for TpCallbackReleaseSemaphoreOnCompletion */
|
||||
+ semaphore = CreateSemaphoreW(NULL, 0, 1, NULL);
|
||||
+ ok(semaphore != NULL, "failed to create semaphore\n");
|
||||
+ memset(&environment, 0, sizeof(environment));
|
||||
+ environment.Version = 1;
|
||||
+ environment.Pool = pool;
|
||||
+ status = pTpSimpleTryPost(instance4_cb, semaphore, &environment);
|
||||
+ ok(!status, "TpSimpleTryPost failed with status %x\n", status);
|
||||
+ ret = WaitForSingleObject(semaphore, 1000);
|
||||
+ ok(ret == WAIT_OBJECT_0, "expected ret = WAIT_OBJECT_0, got %u\n", ret);
|
||||
+
|
||||
+ /* Test for finalization callback */
|
||||
+ userdata = 0;
|
||||
+ instance_finalization_semaphore = semaphore;
|
||||
+ memset(&environment, 0, sizeof(environment));
|
||||
+ environment.Version = 1;
|
||||
+ environment.Pool = pool;
|
||||
+ environment.FinalizationCallback = instance_finalization_cb;
|
||||
+ status = pTpSimpleTryPost(instance5_cb, &userdata, &environment);
|
||||
+ ok(!status, "TpSimpleTryPost failed with status %x\n", status);
|
||||
+ ret = WaitForSingleObject(semaphore, 1000);
|
||||
+ ok(ret == WAIT_OBJECT_0, "expected ret = WAIT_OBJECT_0, got %u\n", ret);
|
||||
+ while (userdata != 2) Sleep(10);
|
||||
+
|
||||
+ CloseHandle(semaphore);
|
||||
+
|
||||
+ /* Cleanup */
|
||||
+ pTpReleasePool(pool);
|
||||
+}
|
||||
+
|
||||
+static DWORD group_cancel_tid;
|
||||
+
|
||||
+static void CALLBACK group_cancel_cleanup_cb(void *object, void *userdata)
|
||||
+{
|
||||
+ trace("Running group cancel cleanup callback\n");
|
||||
+ InterlockedIncrement( (LONG *)userdata );
|
||||
+ group_cancel_tid = GetCurrentThreadId();
|
||||
+}
|
||||
+
|
||||
+static void CALLBACK group_cancel_cb(TP_CALLBACK_INSTANCE *instance, void *userdata)
|
||||
+{
|
||||
+ trace("Running group cancel callback\n");
|
||||
+ pTpCallbackMayRunLong(instance);
|
||||
+ Sleep(100);
|
||||
+ ok(*(LONG *)userdata == 1, "expected *userdata = 1, got %u\n", *(LONG *)userdata);
|
||||
+ InterlockedIncrement( (LONG *)userdata );
|
||||
+}
|
||||
+
|
||||
+static void CALLBACK dummy_cb(TP_CALLBACK_INSTANCE *instance, void *userdata)
|
||||
+{
|
||||
+ ok(0, "Unexpected call to dummy_cb function\n");
|
||||
+}
|
||||
+
|
||||
+static void test_tp_group_cancel(void)
|
||||
+{
|
||||
+ TP_CALLBACK_ENVIRON environment;
|
||||
+ TP_CLEANUP_GROUP *group;
|
||||
+ TP_WORK *work;
|
||||
+ TP_POOL *pool;
|
||||
+ NTSTATUS status;
|
||||
+ LONG userdata, userdata2;
|
||||
+ int i;
|
||||
+
|
||||
+ /* Allocate new threadpool */
|
||||
+ pool = NULL;
|
||||
+ status = pTpAllocPool(&pool, NULL);
|
||||
+ ok(!status, "TpAllocPool failed with status %x\n", status);
|
||||
+ ok(pool != NULL, "expected pool != NULL\n");
|
||||
+
|
||||
+ /* We limit the pool to a single thread */
|
||||
+ pTpSetPoolMaxThreads(pool, 1);
|
||||
+
|
||||
+ /* Allocate a cleanup group */
|
||||
+ group = NULL;
|
||||
+ status = pTpAllocCleanupGroup(&group);
|
||||
+ ok(!status, "TpAllocCleanupGroup failed with status %x\n", status);
|
||||
+ ok(group != NULL, "expected pool != NULL\n");
|
||||
+
|
||||
+ /* Test execution of cancellation callback */
|
||||
+ userdata = 0;
|
||||
+ memset(&environment, 0, sizeof(environment));
|
||||
+ environment.Version = 1;
|
||||
+ environment.Pool = pool;
|
||||
+ status = pTpSimpleTryPost(group_cancel_cb, &userdata, &environment);
|
||||
+ ok(!status, "TpSimpleTryPost failed with status %x\n", status);
|
||||
+
|
||||
+ memset(&environment, 0, sizeof(environment));
|
||||
+ environment.Version = 1;
|
||||
+ environment.Pool = pool;
|
||||
+ environment.CleanupGroup = group;
|
||||
+ environment.CleanupGroupCancelCallback = group_cancel_cleanup_cb;
|
||||
+ status = pTpSimpleTryPost(dummy_cb, NULL, &environment);
|
||||
+ ok(!status, "TpSimpleTryPost failed with status %x\n", status);
|
||||
+
|
||||
+ group_cancel_tid = 0;
|
||||
+ pTpReleaseCleanupGroupMembers(group, TRUE, &userdata);
|
||||
+ ok(userdata == 1, "expected userdata = 1, got %u\n", userdata);
|
||||
+ ok(group_cancel_tid == GetCurrentThreadId(), "expected tid %x, got %x\n",
|
||||
+ GetCurrentThreadId(), group_cancel_tid);
|
||||
+ while (userdata != 2) Sleep(10);
|
||||
+
|
||||
+ /* Test cancellation callback for elements with multiple instances */
|
||||
+ /* Allocate new work item */
|
||||
+ work = NULL;
|
||||
+ memset(&environment, 0, sizeof(environment));
|
||||
+ environment.Version = 1;
|
||||
+ environment.Pool = pool;
|
||||
+ environment.CleanupGroup = group;
|
||||
+ environment.CleanupGroupCancelCallback = group_cancel_cleanup_cb;
|
||||
+ status = pTpAllocWork(&work, work_cb, &userdata, &environment);
|
||||
+ ok(!status, "TpAllocWork failed with status %x\n", status);
|
||||
+ ok(work != NULL, "expected work != NULL\n");
|
||||
+
|
||||
+ /* Post 10 identical work items at once */
|
||||
+ userdata = userdata2 = 0;
|
||||
+ for (i = 0; i < 10; i++)
|
||||
+ pTpPostWork(work);
|
||||
+
|
||||
+ /* Check if we get multiple cancellation callbacks */
|
||||
+ group_cancel_tid = 0;
|
||||
+ pTpReleaseCleanupGroupMembers(group, TRUE, &userdata2);
|
||||
+ ok(userdata < 10, "expected userdata < 10, got %u\n", userdata);
|
||||
+ ok(userdata2 == 1, "expected only one cancellation callback, got %u\n", userdata2);
|
||||
+ ok(group_cancel_tid == GetCurrentThreadId(), "expected tid %x, got %x\n",
|
||||
+ GetCurrentThreadId(), group_cancel_tid);
|
||||
+
|
||||
+ /* Cleanup */
|
||||
+ pTpReleaseCleanupGroup(group);
|
||||
+ pTpReleasePool(pool);
|
||||
+}
|
||||
+
|
||||
+static void CALLBACK timer_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, TP_TIMER *timer)
|
||||
+{
|
||||
+ trace("Running timer callback\n");
|
||||
+ InterlockedIncrement( (LONG *)userdata );
|
||||
+}
|
||||
+
|
||||
+static void test_tp_timer(void)
|
||||
+{
|
||||
+ TP_CALLBACK_ENVIRON environment;
|
||||
+ TP_TIMER *timer;
|
||||
+ TP_POOL *pool;
|
||||
+ NTSTATUS status;
|
||||
+ LONG userdata;
|
||||
+ BOOL success;
|
||||
+ LARGE_INTEGER when;
|
||||
+ DWORD ticks;
|
||||
+
|
||||
+ /* Allocate new threadpool */
|
||||
+ pool = NULL;
|
||||
+ status = pTpAllocPool(&pool, NULL);
|
||||
+ ok(!status, "TpAllocPool failed with status %x\n", status);
|
||||
+ ok(pool != NULL, "expected pool != NULL\n");
|
||||
+
|
||||
+ /* Allocate new timer item */
|
||||
+ timer = NULL;
|
||||
+ memset(&environment, 0, sizeof(environment));
|
||||
+ environment.Version = 1;
|
||||
+ environment.Pool = pool;
|
||||
+ status = pTpAllocTimer(&timer, timer_cb, &userdata, &environment);
|
||||
+ ok(!status, "TpAllocTimer failed with status %x\n", status);
|
||||
+ ok(timer != NULL, "expected timer != NULL\n");
|
||||
+
|
||||
+ success = pTpIsTimerSet(timer);
|
||||
+ ok(!success, "expected TpIsTimerSet(...) = FALSE\n");
|
||||
+
|
||||
+ /* Set a relative timeout */
|
||||
+ userdata = 0;
|
||||
+ when.QuadPart = (ULONGLONG)500 * -10000;
|
||||
+ pTpSetTimer(timer, &when, 0, 0);
|
||||
+ success = pTpIsTimerSet(timer);
|
||||
+ ok(success, "expected TpIsTimerSet(...) = TRUE\n");
|
||||
+
|
||||
+ /* Wait until timer has triggered */
|
||||
+ pTpWaitForTimer(timer, FALSE);
|
||||
+ Sleep(250);
|
||||
+ ok(userdata == 0, "expected userdata = 0, got %u\n", userdata);
|
||||
+ while (userdata == 0) Sleep(10);
|
||||
+ ok(userdata == 1, "expected userdata = 1, got %u\n", userdata);
|
||||
+ success = pTpIsTimerSet(timer);
|
||||
+ ok(success, "expected TpIsTimerSet(...) = TRUE\n");
|
||||
+
|
||||
+ /* Set an absolute timeout */
|
||||
+ userdata = 0;
|
||||
+ NtQuerySystemTime( &when );
|
||||
+ when.QuadPart += (ULONGLONG)500 * 10000;
|
||||
+ pTpSetTimer(timer, &when, 0, 0);
|
||||
+ success = pTpIsTimerSet(timer);
|
||||
+ ok(success, "expected TpIsTimerSet(...) = TRUE\n");
|
||||
+
|
||||
+ /* Wait until timer has triggered */
|
||||
+ pTpWaitForTimer(timer, FALSE);
|
||||
+ Sleep(250);
|
||||
+ ok(userdata == 0, "expected userdata = 0, got %u\n", userdata);
|
||||
+ while (userdata == 0) Sleep(10);
|
||||
+ ok(userdata == 1, "expected userdata = 1, got %u\n", userdata);
|
||||
+ success = pTpIsTimerSet(timer);
|
||||
+ ok(success, "expected TpIsTimerSet(...) = TRUE\n");
|
||||
+
|
||||
+ /* Test a relative timeout repeated periodically */
|
||||
+ userdata = 0;
|
||||
+ when.QuadPart = (ULONGLONG)50 * -10000;
|
||||
+ pTpSetTimer(timer, &when, 50, 0);
|
||||
+ success = pTpIsTimerSet(timer);
|
||||
+ ok(success, "expected TpIsTimerSet(...) = TRUE\n");
|
||||
+
|
||||
+ /* Wait until the timer was triggered a couple of times */
|
||||
+ ticks = GetTickCount();
|
||||
+ while (userdata < 100) Sleep(10);
|
||||
+ ticks = GetTickCount() - ticks;
|
||||
+ pTpSetTimer(timer, NULL, 0, 0);
|
||||
+ pTpWaitForTimer(timer, TRUE);
|
||||
+ ok(ticks >= 4500 && ticks <= 5500, "expected approximately 5000 ticks, got %u\n", ticks);
|
||||
+ success = pTpIsTimerSet(timer);
|
||||
+ ok(!success, "expected TpIsTimerSet(...) = FALSE\n");
|
||||
+
|
||||
+ /* Test with zero timeout, we expect a call immediately */
|
||||
+ userdata = 0;
|
||||
+ when.QuadPart = 0;
|
||||
+ pTpSetTimer(timer, &when, 0, 0);
|
||||
+ success = pTpIsTimerSet(timer);
|
||||
+ ok(success, "expected TpIsTimerSet(...) = TRUE\n");
|
||||
+
|
||||
+ /* Wait until timer has triggered */
|
||||
+ pTpWaitForTimer(timer, FALSE);
|
||||
+ ok(userdata == 1 || broken(userdata == 0) /* Win 8 */,
|
||||
+ "expected userdata = 1, got %u\n", userdata);
|
||||
+ while (userdata == 0) Sleep(10);
|
||||
+ success = pTpIsTimerSet(timer);
|
||||
+ ok(success, "expected TpIsTimerSet(...) = TRUE\n");
|
||||
+
|
||||
+ /* Unset the timer again */
|
||||
+ pTpSetTimer(timer, NULL, 0, 0);
|
||||
+ success = pTpIsTimerSet(timer);
|
||||
+ ok(!success, "expected TpIsTimerSet(...) = FALSE\n");
|
||||
+
|
||||
+ /* Cleanup */
|
||||
+ pTpReleaseTimer(timer);
|
||||
+ pTpReleasePool(pool);
|
||||
+}
|
||||
+
|
||||
+static void CALLBACK window_length_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, TP_TIMER *timer)
|
||||
+{
|
||||
+ trace("Running window length callback\n");
|
||||
+ (*(DWORD *)userdata) = GetTickCount();
|
||||
+}
|
||||
+
|
||||
+static void test_tp_window_length(void)
|
||||
+{
|
||||
+ TP_CALLBACK_ENVIRON environment;
|
||||
+ TP_TIMER *timer, *timer2;
|
||||
+ DWORD ticks, ticks2;
|
||||
+ TP_POOL *pool;
|
||||
+ NTSTATUS status;
|
||||
+ LARGE_INTEGER when;
|
||||
+
|
||||
+ /* Allocate new threadpool */
|
||||
+ pool = NULL;
|
||||
+ status = pTpAllocPool(&pool, NULL);
|
||||
+ ok(!status, "TpAllocPool failed with status %x\n", status);
|
||||
+ ok(pool != NULL, "expected pool != NULL\n");
|
||||
+
|
||||
+ /* Allocate two identical timers */
|
||||
+ timer = NULL;
|
||||
+ memset(&environment, 0, sizeof(environment));
|
||||
+ environment.Version = 1;
|
||||
+ environment.Pool = pool;
|
||||
+ status = pTpAllocTimer(&timer, window_length_cb, &ticks, &environment);
|
||||
+ ok(!status, "TpAllocTimer failed with status %x\n", status);
|
||||
+ ok(timer != NULL, "expected timer != NULL\n");
|
||||
+
|
||||
+ status = pTpAllocTimer(&timer2, window_length_cb, &ticks2, &environment);
|
||||
+ ok(!status, "TpAllocTimer failed with status %x\n", status);
|
||||
+ ok(timer2 != NULL, "expected timer2 != NULL\n");
|
||||
+
|
||||
+ /* Choose parameters so that timers are not merged */
|
||||
+ ticks = ticks2 = 0;
|
||||
+ NtQuerySystemTime( &when );
|
||||
+ when.QuadPart += (ULONGLONG)500 * 10000;
|
||||
+ pTpSetTimer(timer2, &when, 0, 0);
|
||||
+ Sleep(50);
|
||||
+ when.QuadPart -= (ULONGLONG)400 * 10000;
|
||||
+ pTpSetTimer(timer, &when, 0, 200);
|
||||
+ while (!ticks || !ticks2) Sleep(10);
|
||||
+ ok(ticks2 >= ticks + 150, "expected that timers are not merged\n");
|
||||
+
|
||||
+ /* On Windows the timers also get merged in this case */
|
||||
+ ticks = ticks2 = 0;
|
||||
+ NtQuerySystemTime( &when );
|
||||
+ when.QuadPart += (ULONGLONG)100 * 10000;
|
||||
+ pTpSetTimer(timer, &when, 0, 450);
|
||||
+ Sleep(50);
|
||||
+ when.QuadPart += (ULONGLONG)400 * 10000;
|
||||
+ pTpSetTimer(timer2, &when, 0, 0);
|
||||
+ while (!ticks || !ticks2) Sleep(10);
|
||||
+ todo_wine
|
||||
+ ok(ticks2 >= ticks - 50 && ticks2 <= ticks + 50, "expected that timers are merged\n");
|
||||
+
|
||||
+ /* Timers will be merged */
|
||||
+ ticks = ticks2 = 0;
|
||||
+ NtQuerySystemTime( &when );
|
||||
+ when.QuadPart += (ULONGLONG)500 * 10000;
|
||||
+ pTpSetTimer(timer2, &when, 0, 0);
|
||||
+ Sleep(50);
|
||||
+ when.QuadPart -= (ULONGLONG)400 * 10000;
|
||||
+ pTpSetTimer(timer, &when, 0, 450);
|
||||
+ while (!ticks || !ticks2) Sleep(10);
|
||||
+ ok(ticks2 >= ticks - 50 && ticks2 <= ticks + 50, "expected that timers are merged\n");
|
||||
+
|
||||
+ /* Cleanup */
|
||||
+ pTpReleaseTimer(timer);
|
||||
+ pTpReleaseTimer(timer2);
|
||||
+ pTpReleasePool(pool);
|
||||
+}
|
||||
+
|
||||
+START_TEST(threadpool)
|
||||
+{
|
||||
+ if(!init_threadpool())
|
||||
+ return;
|
||||
+
|
||||
+ test_default_threadpool();
|
||||
+ test_tp_simple();
|
||||
+ test_tp_work();
|
||||
+ test_tp_work_scheduler();
|
||||
+ test_tp_instance();
|
||||
+ test_tp_group_cancel();
|
||||
+ test_tp_timer();
|
||||
+ test_tp_window_length();
|
||||
+
|
||||
+ /* FIXME: Make sure worker threads have terminated before. */
|
||||
+ Sleep(100);
|
||||
+}
|
||||
--
|
||||
2.2.2
|
||||
|
@ -0,0 +1,342 @@
|
||||
From 3477bdc9e48312f6188f9c9beb8d062291f8b81f Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Lackner <sebastian@fds-team.de>
|
||||
Date: Sun, 1 Feb 2015 19:41:13 +0100
|
||||
Subject: kernel32: Forward various threadpool functions to ntdll.
|
||||
|
||||
---
|
||||
dlls/kernel32/kernel32.spec | 48 ++++++++++----------
|
||||
dlls/kernel32/tests/thread.c | 6 +--
|
||||
dlls/kernel32/thread.c | 101 +++++++++++++++++++++++++++++++++++++++++++
|
||||
include/winternl.h | 27 ++++++++++++
|
||||
4 files changed, 155 insertions(+), 27 deletions(-)
|
||||
|
||||
diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec
|
||||
index 3719505..4d170d5 100644
|
||||
--- a/dlls/kernel32/kernel32.spec
|
||||
+++ b/dlls/kernel32/kernel32.spec
|
||||
@@ -204,7 +204,7 @@
|
||||
@ stdcall BuildCommDCBAndTimeoutsA(str ptr ptr)
|
||||
@ stdcall BuildCommDCBAndTimeoutsW(wstr ptr ptr)
|
||||
@ stdcall BuildCommDCBW(wstr ptr)
|
||||
-# @ stub CallbackMayRunLong
|
||||
+@ stdcall CallbackMayRunLong(ptr) ntdll.TpCallbackMayRunLong
|
||||
@ stdcall CallNamedPipeA(str ptr long ptr long ptr long)
|
||||
@ stdcall CallNamedPipeW(wstr ptr long ptr long ptr long)
|
||||
@ stub CancelDeviceWakeupRequest
|
||||
@@ -228,13 +228,13 @@
|
||||
# @ stub ClosePrivateNamespace
|
||||
@ stdcall CloseProfileUserMapping()
|
||||
@ stub CloseSystemHandle
|
||||
-# @ stub CloseThreadpool
|
||||
-# @ stub CloseThreadpoolCleanupGroup
|
||||
-# @ stub CloseThreadpoolCleanupGroupMembers
|
||||
+@ stdcall CloseThreadpool(ptr) ntdll.TpReleasePool
|
||||
+@ stdcall CloseThreadpoolCleanupGroup(ptr) ntdll.TpReleaseCleanupGroup
|
||||
+@ stdcall CloseThreadpoolCleanupGroupMembers(ptr long ptr) ntdll.TpReleaseCleanupGroupMembers
|
||||
# @ stub CloseThreadpoolIo
|
||||
-# @ stub CloseThreadpoolTimer
|
||||
+@ stdcall CloseThreadpoolTimer(ptr) ntdll.TpReleaseTimer
|
||||
# @ stub CloseThreadpoolWait
|
||||
-# @ stub CloseThreadpoolWork
|
||||
+@ stdcall CloseThreadpoolWork(ptr) ntdll.TpReleaseWork
|
||||
@ stdcall CmdBatNotification(long)
|
||||
@ stdcall CommConfigDialogA(str long ptr)
|
||||
@ stdcall CommConfigDialogW(wstr long ptr)
|
||||
@@ -331,12 +331,12 @@
|
||||
@ stdcall CreateSymbolicLinkW(wstr wstr long)
|
||||
@ stdcall CreateTapePartition(long long long long)
|
||||
@ stdcall CreateThread(ptr long ptr long long ptr)
|
||||
-# @ stub CreateThreadpool
|
||||
-# @ stub CreateThreadpoolCleanupGroup
|
||||
+@ stdcall CreateThreadpool(ptr)
|
||||
+@ stdcall CreateThreadpoolCleanupGroup()
|
||||
# @ stub CreateThreadpoolIo
|
||||
-# @ stub CreateThreadpoolTimer
|
||||
+@ stdcall CreateThreadpoolTimer(ptr ptr ptr)
|
||||
# @ stub CreateThreadpoolWait
|
||||
-# @ stub CreateThreadpoolWork
|
||||
+@ stdcall CreateThreadpoolWork(ptr ptr ptr)
|
||||
@ stdcall CreateTimerQueue ()
|
||||
@ stdcall CreateTimerQueueTimer(ptr long ptr ptr long long long)
|
||||
@ stdcall CreateToolhelp32Snapshot(long long)
|
||||
@@ -369,7 +369,7 @@
|
||||
@ stdcall DeleteFileW(wstr)
|
||||
# @ stub DeleteProcThreadAttributeList
|
||||
# @ stub DisableThreadProfiling
|
||||
-# @ stub DisassociateCurrentThreadFromCallback
|
||||
+@ stdcall DisassociateCurrentThreadFromCallback(ptr) ntdll.TpDisassociateCallback
|
||||
@ stdcall DeleteTimerQueue(long)
|
||||
@ stdcall DeleteTimerQueueEx (long long)
|
||||
@ stdcall DeleteTimerQueueTimer(long long long)
|
||||
@@ -495,7 +495,6 @@
|
||||
@ stdcall FindFirstVolumeMountPointA(str ptr long)
|
||||
@ stdcall FindFirstVolumeMountPointW(wstr ptr long)
|
||||
@ stdcall FindFirstVolumeW(ptr long)
|
||||
-# @ stub FreeLibraryWhenCallbackReturns
|
||||
@ stdcall FindNextChangeNotification(long)
|
||||
@ stdcall FindNextFileA(long ptr)
|
||||
# @ stub FindNextFileNameW
|
||||
@@ -533,6 +532,7 @@
|
||||
@ stub -i386 FreeLSCallback
|
||||
@ stdcall FreeLibrary(long)
|
||||
@ stdcall FreeLibraryAndExitThread(long long)
|
||||
+@ stdcall FreeLibraryWhenCallbackReturns(ptr ptr) ntdll.TpCallbackUnloadDllOnCompletion
|
||||
@ stdcall FreeResource(long)
|
||||
@ stdcall -i386 -private FreeSLCallback(long) krnl386.exe16.FreeSLCallback
|
||||
@ stub FreeUserPhysicalPages
|
||||
@@ -980,7 +980,7 @@
|
||||
@ stub -i386 IsSLCallback
|
||||
@ stdcall IsSystemResumeAutomatic()
|
||||
@ stdcall IsThreadAFiber()
|
||||
-# @ stub IsThreadpoolTimerSet
|
||||
+@ stdcall IsThreadpoolTimerSet(ptr) ntdll.TpIsTimerSet
|
||||
# @ stub IsTimeZoneRedirectionEnabled
|
||||
# @ stub IsValidCalDateTime
|
||||
@ stdcall IsValidCodePage(long)
|
||||
@@ -1034,7 +1034,7 @@
|
||||
@ stdcall LZSeek(long long long)
|
||||
@ stdcall LZStart()
|
||||
@ stdcall LeaveCriticalSection(ptr) ntdll.RtlLeaveCriticalSection
|
||||
-# @ stub LeaveCriticalSectionWhenCallbackReturns
|
||||
+@ stdcall LeaveCriticalSectionWhenCallbackReturns(ptr ptr) ntdll.TpCallbackLeaveCriticalSectionOnCompletion
|
||||
# @ stub LoadAppInitDlls
|
||||
@ stdcall LoadLibraryA(str)
|
||||
@ stdcall LoadLibraryExA( str long long)
|
||||
@@ -1251,9 +1251,9 @@
|
||||
@ stdcall ReinitializeCriticalSection(ptr)
|
||||
@ stdcall ReleaseActCtx(ptr)
|
||||
@ stdcall ReleaseMutex(long)
|
||||
-# @ stub ReleaseMutexWhenCallbackReturns
|
||||
+@ stdcall ReleaseMutexWhenCallbackReturns(ptr long) ntdll.TpCallbackReleaseMutexOnCompletion
|
||||
@ stdcall ReleaseSemaphore(long long ptr)
|
||||
-# @ stub ReleaseSemaphoreWhenCallbackReturns
|
||||
+@ stdcall ReleaseSemaphoreWhenCallbackReturns(ptr long long) ntdll.TpCallbackReleaseSemaphoreOnCompletion
|
||||
@ stdcall ReleaseSRWLockExclusive(ptr) ntdll.RtlReleaseSRWLockExclusive
|
||||
@ stdcall ReleaseSRWLockShared(ptr) ntdll.RtlReleaseSRWLockShared
|
||||
@ stdcall RemoveDirectoryA(str)
|
||||
@@ -1384,7 +1384,7 @@
|
||||
@ stdcall SetEnvironmentVariableW(wstr wstr)
|
||||
@ stdcall SetErrorMode(long)
|
||||
@ stdcall SetEvent(long)
|
||||
-# @ stub SetEventWhenCallbackReturns
|
||||
+@ stdcall SetEventWhenCallbackReturns(ptr long) ntdll.TpCallbackSetEventOnCompletion
|
||||
@ stdcall SetFileApisToANSI()
|
||||
@ stdcall SetFileApisToOEM()
|
||||
@ stdcall SetFileAttributesA(str long)
|
||||
@@ -1453,9 +1453,9 @@
|
||||
# @ stub SetThreadToken
|
||||
@ stdcall SetThreadUILanguage(long)
|
||||
# @ stub SetThreadpoolStackInformation
|
||||
-# @ stub SetThreadpoolThreadMaximum
|
||||
-# @ stub SetThreadpoolThreadMinimum
|
||||
-# @ stub SetThreadpoolTimer
|
||||
+@ stdcall SetThreadpoolThreadMaximum(ptr long) ntdll.TpSetPoolMaxThreads
|
||||
+@ stdcall SetThreadpoolThreadMinimum(ptr long) ntdll.TpSetPoolMinThreads
|
||||
+@ stdcall SetThreadpoolTimer(ptr ptr long long)
|
||||
# @ stub SetThreadpoolWait
|
||||
@ stdcall SetTimeZoneInformation(ptr)
|
||||
@ stub SetTimerQueueTimer
|
||||
@@ -1481,7 +1481,7 @@
|
||||
# @ stub SortCloseHandle
|
||||
# @ stub SortGetHandle
|
||||
# @ stub StartThreadpoolIo
|
||||
-# @ stub SubmitThreadpoolWork
|
||||
+@ stdcall SubmitThreadpoolWork(ptr) ntdll.TpPostWork
|
||||
@ stdcall SuspendThread(long)
|
||||
@ stdcall SwitchToFiber(ptr)
|
||||
@ stdcall SwitchToThread()
|
||||
@@ -1508,7 +1508,7 @@
|
||||
@ stdcall TryAcquireSRWLockExclusive(ptr) ntdll.RtlTryAcquireSRWLockExclusive
|
||||
@ stdcall TryAcquireSRWLockShared(ptr) ntdll.RtlTryAcquireSRWLockShared
|
||||
@ stdcall TryEnterCriticalSection(ptr) ntdll.RtlTryEnterCriticalSection
|
||||
-# @ stub TrySubmitThreadpoolCallback
|
||||
+@ stdcall TrySubmitThreadpoolCallback(ptr ptr ptr) ntdll.TpSimpleTryPost
|
||||
@ stdcall TzSpecificLocalTimeToSystemTime(ptr ptr ptr)
|
||||
# @ stub TzSpecificLocalTimeToSystemTimeEx
|
||||
# @ stub -arch=x86_64 uaw_lstrcmpW
|
||||
@@ -1570,9 +1570,9 @@
|
||||
@ stdcall WaitForSingleObject(long long)
|
||||
@ stdcall WaitForSingleObjectEx(long long long)
|
||||
# @ stub WaitForThreadpoolIoCallbacks
|
||||
-# @ stub WaitForThreadpoolTimerCallbacks
|
||||
+@ stdcall WaitForThreadpoolTimerCallbacks(ptr long) ntdll.TpWaitForTimer
|
||||
# @ stub WaitForThreadpoolWaitCallbacks
|
||||
-# @ stub WaitForThreadpoolWorkCallbacks
|
||||
+@ stdcall WaitForThreadpoolWorkCallbacks(ptr long) ntdll.TpWaitForWork
|
||||
@ stdcall WaitNamedPipeA (str long)
|
||||
@ stdcall WaitNamedPipeW (wstr long)
|
||||
@ stdcall WakeAllConditionVariable(ptr) ntdll.RtlWakeAllConditionVariable
|
||||
diff --git a/dlls/kernel32/tests/thread.c b/dlls/kernel32/tests/thread.c
|
||||
index d3ecd2a..25801b2 100644
|
||||
--- a/dlls/kernel32/tests/thread.c
|
||||
+++ b/dlls/kernel32/tests/thread.c
|
||||
@@ -1627,8 +1627,8 @@ static void test_threadpool(void)
|
||||
int workcalled = 0;
|
||||
|
||||
if (!pCreateThreadpool) {
|
||||
- todo_wine win_skip("thread pool apis not supported.\n");
|
||||
- return;
|
||||
+ win_skip("thread pool apis not supported.\n");
|
||||
+ return;
|
||||
}
|
||||
|
||||
work = pCreateThreadpoolWork(threadpool_workcallback, &workcalled, NULL);
|
||||
@@ -1640,7 +1640,7 @@ static void test_threadpool(void)
|
||||
ok (workcalled == 1, "expected work to be called once, got %d\n", workcalled);
|
||||
|
||||
pool = pCreateThreadpool(NULL);
|
||||
- todo_wine ok (pool != NULL, "CreateThreadpool failed\n");
|
||||
+ ok (pool != NULL, "CreateThreadpool failed\n");
|
||||
}
|
||||
|
||||
static void test_reserved_tls(void)
|
||||
diff --git a/dlls/kernel32/thread.c b/dlls/kernel32/thread.c
|
||||
index 0abfdf1..085b011 100644
|
||||
--- a/dlls/kernel32/thread.c
|
||||
+++ b/dlls/kernel32/thread.c
|
||||
@@ -831,3 +831,104 @@ BOOL WINAPI GetThreadPreferredUILanguages( DWORD flags, PULONG count, PCZZWSTR b
|
||||
*buffersize = 0;
|
||||
return TRUE;
|
||||
}
|
||||
+
|
||||
+/***********************************************************************
|
||||
+ * CreateThreadpool (KERNEL32.@)
|
||||
+ */
|
||||
+PTP_POOL WINAPI CreateThreadpool( PVOID reserved )
|
||||
+{
|
||||
+ TP_POOL *pool;
|
||||
+ NTSTATUS status;
|
||||
+
|
||||
+ TRACE( "%p\n", reserved );
|
||||
+
|
||||
+ status = TpAllocPool( &pool, reserved );
|
||||
+ if (status)
|
||||
+ {
|
||||
+ SetLastError( RtlNtStatusToDosError(status) );
|
||||
+ return NULL;
|
||||
+ }
|
||||
+
|
||||
+ return pool;
|
||||
+}
|
||||
+
|
||||
+/***********************************************************************
|
||||
+ * CreateThreadpoolCleanupGroup (KERNEL32.@)
|
||||
+ */
|
||||
+PTP_CLEANUP_GROUP WINAPI CreateThreadpoolCleanupGroup( void )
|
||||
+{
|
||||
+ TP_CLEANUP_GROUP *group;
|
||||
+ NTSTATUS status;
|
||||
+
|
||||
+ TRACE( "\n" );
|
||||
+
|
||||
+ status = TpAllocCleanupGroup( &group );
|
||||
+ if (status)
|
||||
+ {
|
||||
+ SetLastError( RtlNtStatusToDosError(status) );
|
||||
+ return NULL;
|
||||
+ }
|
||||
+
|
||||
+ return group;
|
||||
+}
|
||||
+
|
||||
+/***********************************************************************
|
||||
+ * CreateThreadpoolTimer (KERNEL32.@)
|
||||
+ */
|
||||
+PTP_TIMER WINAPI CreateThreadpoolTimer( PTP_TIMER_CALLBACK callback, PVOID userdata,
|
||||
+ TP_CALLBACK_ENVIRON *environment )
|
||||
+{
|
||||
+ TP_TIMER *timer;
|
||||
+ NTSTATUS status;
|
||||
+
|
||||
+ TRACE( "%p, %p, %p\n", callback, userdata, environment );
|
||||
+
|
||||
+ status = TpAllocTimer( &timer, callback, userdata, environment );
|
||||
+ if (status)
|
||||
+ {
|
||||
+ SetLastError( RtlNtStatusToDosError(status) );
|
||||
+ return NULL;
|
||||
+ }
|
||||
+
|
||||
+ return timer;
|
||||
+}
|
||||
+
|
||||
+/***********************************************************************
|
||||
+ * CreateThreadpoolWork (KERNEL32.@)
|
||||
+ */
|
||||
+PTP_WORK WINAPI CreateThreadpoolWork( PTP_WORK_CALLBACK callback, PVOID userdata,
|
||||
+ TP_CALLBACK_ENVIRON *environment )
|
||||
+{
|
||||
+ TP_WORK *work;
|
||||
+ NTSTATUS status;
|
||||
+
|
||||
+ TRACE( "%p, %p, %p\n", callback, userdata, environment );
|
||||
+
|
||||
+ status = TpAllocWork( &work, callback, userdata, environment );
|
||||
+ if (status)
|
||||
+ {
|
||||
+ SetLastError( RtlNtStatusToDosError(status) );
|
||||
+ return NULL;
|
||||
+ }
|
||||
+
|
||||
+ return work;
|
||||
+}
|
||||
+
|
||||
+/***********************************************************************
|
||||
+ * SetThreadpoolTimer (KERNEL32.@)
|
||||
+ */
|
||||
+VOID WINAPI SetThreadpoolTimer( TP_TIMER *timer, FILETIME *due_time,
|
||||
+ DWORD period, DWORD window_length )
|
||||
+{
|
||||
+ LARGE_INTEGER timeout;
|
||||
+
|
||||
+ TRACE( "%p, %p, %u, %u\n", timer, due_time, period, window_length );
|
||||
+
|
||||
+ if (due_time)
|
||||
+ {
|
||||
+ timeout.u.LowPart = due_time->dwLowDateTime;
|
||||
+ timeout.u.HighPart = due_time->dwHighDateTime;
|
||||
+ }
|
||||
+
|
||||
+ TpSetTimer( timer, due_time ? &timeout : NULL, period, window_length );
|
||||
+}
|
||||
diff --git a/include/winternl.h b/include/winternl.h
|
||||
index 1a694da..a534bd1 100644
|
||||
--- a/include/winternl.h
|
||||
+++ b/include/winternl.h
|
||||
@@ -2599,6 +2599,33 @@ NTSYSAPI LONGLONG WINAPI RtlLargeIntegerSubtract(LONGLONG,LONGLONG);
|
||||
NTSYSAPI NTSTATUS WINAPI RtlLargeIntegerToChar(const ULONGLONG *,ULONG,ULONG,PCHAR);
|
||||
#endif
|
||||
|
||||
+/* Threadpool functions */
|
||||
+
|
||||
+NTSYSAPI NTSTATUS WINAPI TpAllocCleanupGroup(TP_CLEANUP_GROUP **);
|
||||
+NTSYSAPI NTSTATUS WINAPI TpAllocPool(TP_POOL **,PVOID);
|
||||
+NTSYSAPI NTSTATUS WINAPI TpAllocTimer(TP_TIMER **,PTP_TIMER_CALLBACK,PVOID,TP_CALLBACK_ENVIRON *);
|
||||
+NTSYSAPI NTSTATUS WINAPI TpAllocWork(TP_WORK **,PTP_WORK_CALLBACK,PVOID,TP_CALLBACK_ENVIRON *);
|
||||
+NTSYSAPI void WINAPI TpCallbackLeaveCriticalSectionOnCompletion(TP_CALLBACK_INSTANCE *,RTL_CRITICAL_SECTION *);
|
||||
+NTSYSAPI NTSTATUS WINAPI TpCallbackMayRunLong(TP_CALLBACK_INSTANCE *);
|
||||
+NTSYSAPI void WINAPI TpCallbackReleaseMutexOnCompletion(TP_CALLBACK_INSTANCE *,HANDLE);
|
||||
+NTSYSAPI void WINAPI TpCallbackReleaseSemaphoreOnCompletion(TP_CALLBACK_INSTANCE *,HANDLE,DWORD);
|
||||
+NTSYSAPI void WINAPI TpCallbackSetEventOnCompletion(TP_CALLBACK_INSTANCE *,HANDLE);
|
||||
+NTSYSAPI void WINAPI TpCallbackUnloadDllOnCompletion(TP_CALLBACK_INSTANCE *,HMODULE);
|
||||
+NTSYSAPI void WINAPI TpDisassociateCallback(TP_CALLBACK_INSTANCE *);
|
||||
+NTSYSAPI BOOL WINAPI TpIsTimerSet(TP_TIMER *);
|
||||
+NTSYSAPI void WINAPI TpPostWork(TP_WORK *);
|
||||
+NTSYSAPI void WINAPI TpReleaseCleanupGroup(TP_CLEANUP_GROUP *);
|
||||
+NTSYSAPI void WINAPI TpReleaseCleanupGroupMembers(TP_CLEANUP_GROUP *,BOOL,PVOID);
|
||||
+NTSYSAPI void WINAPI TpReleasePool(TP_POOL *);
|
||||
+NTSYSAPI void WINAPI TpReleaseTimer(TP_TIMER *);
|
||||
+NTSYSAPI void WINAPI TpReleaseWork(TP_WORK *);
|
||||
+NTSYSAPI void WINAPI TpSetPoolMaxThreads(TP_POOL *,DWORD);
|
||||
+NTSYSAPI BOOL WINAPI TpSetPoolMinThreads(TP_POOL *,DWORD);
|
||||
+NTSYSAPI void WINAPI TpSetTimer(TP_TIMER *, LARGE_INTEGER *,LONG,LONG);
|
||||
+NTSYSAPI NTSTATUS WINAPI TpSimpleTryPost(PTP_SIMPLE_CALLBACK,PVOID,TP_CALLBACK_ENVIRON *);
|
||||
+NTSYSAPI void WINAPI TpWaitForTimer(TP_TIMER *,BOOL);
|
||||
+NTSYSAPI void WINAPI TpWaitForWork(TP_WORK *,BOOL);
|
||||
+
|
||||
/* Wine internal functions */
|
||||
|
||||
NTSYSAPI NTSTATUS CDECL wine_nt_to_unix_file_name( const UNICODE_STRING *nameW, ANSI_STRING *unix_name_ret,
|
||||
--
|
||||
2.2.2
|
||||
|
3
patches/ntdll-Vista_Threadpool/definition
Normal file
3
patches/ntdll-Vista_Threadpool/definition
Normal file
@ -0,0 +1,3 @@
|
||||
Fixes: [35192] Add implementation for CreateThreadpool
|
||||
Fixes: [32531] Implement threadpool work items
|
||||
Fixes: [37306] Implement threadpool timers
|
@ -14,15 +14,11 @@ diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec
|
||||
index 51de6e7..44dfc22 100644
|
||||
--- a/dlls/ntdll/ntdll.spec
|
||||
+++ b/dlls/ntdll/ntdll.spec
|
||||
@@ -968,6 +968,8 @@
|
||||
@ stdcall RtlxUnicodeStringToAnsiSize(ptr) RtlUnicodeStringToAnsiSize
|
||||
@ stdcall RtlxUnicodeStringToOemSize(ptr) RtlUnicodeStringToOemSize
|
||||
@@ -970,2 +970,4 @@
|
||||
@ stdcall -ret64 VerSetConditionMask(int64 long long)
|
||||
+@ stdcall WinSqmEndSession(ptr)
|
||||
+@ stdcall WinSqmStartSession(ptr long long)
|
||||
@ stdcall ZwAcceptConnectPort(ptr long ptr long long ptr) NtAcceptConnectPort
|
||||
@ stdcall ZwAccessCheck(ptr long long ptr ptr ptr ptr ptr) NtAccessCheck
|
||||
@ stdcall ZwAccessCheckAndAuditAlarm(ptr long ptr ptr ptr long ptr long ptr ptr ptr) NtAccessCheckAndAuditAlarm
|
||||
diff --git a/dlls/ntdll/rtl.c b/dlls/ntdll/rtl.c
|
||||
index 8f6f386..2e87beb 100644
|
||||
--- a/dlls/ntdll/rtl.c
|
||||
|
@ -132,6 +132,7 @@ patch_enable_all ()
|
||||
enable_ntdll_RtlUnwindEx="$1"
|
||||
enable_ntdll_ThreadTime="$1"
|
||||
enable_ntdll_User_Shared_Data="$1"
|
||||
enable_ntdll_Vista_Threadpool="$1"
|
||||
enable_ntdll_WRITECOPY="$1"
|
||||
enable_ntdll_WinSqm="$1"
|
||||
enable_ntoskrnl_DriverTest="$1"
|
||||
@ -417,6 +418,9 @@ patch_enable ()
|
||||
ntdll-User_Shared_Data)
|
||||
enable_ntdll_User_Shared_Data="$2"
|
||||
;;
|
||||
ntdll-Vista_Threadpool)
|
||||
enable_ntdll_Vista_Threadpool="$2"
|
||||
;;
|
||||
ntdll-WRITECOPY)
|
||||
enable_ntdll_WRITECOPY="$2"
|
||||
;;
|
||||
@ -2212,6 +2216,35 @@ if test "$enable_ntdll_User_Shared_Data" -eq 1; then
|
||||
) >> "$patchlist"
|
||||
fi
|
||||
|
||||
# Patchset ntdll-Vista_Threadpool
|
||||
# |
|
||||
# | This patchset fixes the following Wine bugs:
|
||||
# | * [#35192] Add implementation for CreateThreadpool
|
||||
# | * [#32531] Implement threadpool work items
|
||||
# | * [#37306] Implement threadpool timers
|
||||
# |
|
||||
# | Modified files:
|
||||
# | * dlls/kernel32/kernel32.spec, dlls/kernel32/tests/thread.c, dlls/kernel32/thread.c, dlls/ntdll/Makefile.in,
|
||||
# | dlls/ntdll/ntdll.spec, dlls/ntdll/tests/Makefile.in, dlls/ntdll/tests/threadpool.c, dlls/ntdll/threadpool2.c,
|
||||
# | include/winternl.h
|
||||
# |
|
||||
if test "$enable_ntdll_Vista_Threadpool" -eq 1; then
|
||||
patch_apply ntdll-Vista_Threadpool/0001-ntdll-Add-threadpool-stub-functions-to-specfile.patch
|
||||
patch_apply ntdll-Vista_Threadpool/0002-ntdll-Implement-threadpool-cleanup-group-and-callbac.patch
|
||||
patch_apply ntdll-Vista_Threadpool/0003-ntdll-Implement-additional-threadpool-work-item-func.patch
|
||||
patch_apply ntdll-Vista_Threadpool/0004-ntdll-Implement-threadpool-timer-functions.patch
|
||||
patch_apply ntdll-Vista_Threadpool/0005-ntdll-tests-Add-tests-for-Tp-threadpool-functions.patch
|
||||
patch_apply ntdll-Vista_Threadpool/0006-kernel32-Forward-various-threadpool-functions-to-ntd.patch
|
||||
(
|
||||
echo '+ { "Sebastian Lackner", "ntdll: Add threadpool stub functions to specfile.", 1 },';
|
||||
echo '+ { "Sebastian Lackner", "ntdll: Implement threadpool, cleanup group and callback instance functions.", 1 },';
|
||||
echo '+ { "Sebastian Lackner", "ntdll: Implement additional threadpool work item functions.", 1 },';
|
||||
echo '+ { "Sebastian Lackner", "ntdll: Implement threadpool timer functions.", 1 },';
|
||||
echo '+ { "Sebastian Lackner", "ntdll/tests: Add tests for Tp* threadpool functions.", 1 },';
|
||||
echo '+ { "Sebastian Lackner", "kernel32: Forward various threadpool functions to ntdll.", 1 },';
|
||||
) >> "$patchlist"
|
||||
fi
|
||||
|
||||
# Patchset ntdll-WinSqm
|
||||
# |
|
||||
# | This patchset fixes the following Wine bugs:
|
||||
|
Loading…
Reference in New Issue
Block a user