Rebase against f6653a93eb6796b733c6b0e9e289746692ab0a69.

This commit is contained in:
Sebastian Lackner 2015-07-06 19:01:52 +02:00
parent fae66ca721
commit afb95f8dde
10 changed files with 3 additions and 1393 deletions

View File

@ -187,7 +187,7 @@ for more details.*
* Implement stubs for ntoskrnl.Ex{Acquire,Release}FastMutexUnsafe
* Implement stubs for ntoskrnl.ObReferenceObjectByPointer and ntoskrnl.ObDereferenceObject
* ~~Implement threadpool timers~~ ([Wine Bug #37306](https://bugs.winehq.org/show_bug.cgi?id=37306))
* Implement threadpool wait objects
* ~~Implement threadpool wait objects~~
* ~~Implement threadpool work items~~ ([Wine Bug #32531](https://bugs.winehq.org/show_bug.cgi?id=32531))
* Improve ReadDataAvailable handling in FilePipeLocalInformation class
* Improve stub for AEV_GetVolumeRange ([Wine Bug #35658](https://bugs.winehq.org/show_bug.cgi?id=35658))

3
debian/changelog vendored
View File

@ -3,8 +3,7 @@ wine-staging (1.7.47) UNRELEASED; urgency=low
upstream).
* Removed patch to initialize *end with NULL on failure in msvcrt.strtod
(accepted upstream).
* Partially removed patchset for new Threadpool implementation (accepted
upstream).
* Removed patchset for new Threadpool implementation (accepted upstream).
-- Sebastian Lackner <sebastian@fds-team.de> Mon, 29 Jun 2015 19:39:04 +0200
wine-staging (1.7.46) unstable; urgency=low

View File

@ -1,146 +0,0 @@
From f4397c4e7c279c41b12b03dfa1b368044de436f7 Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian@fds-team.de>
Date: Sat, 4 Jul 2015 04:11:31 +0200
Subject: ntdll: Implement TpAllocWait and TpReleaseWait.
---
dlls/ntdll/ntdll.spec | 2 ++
dlls/ntdll/threadpool.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 68 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec
index ce9d1bb..78814cd 100644
--- a/dlls/ntdll/ntdll.spec
+++ b/dlls/ntdll/ntdll.spec
@@ -973,6 +973,7 @@
@ stdcall TpAllocCleanupGroup(ptr)
@ stdcall TpAllocPool(ptr ptr)
@ stdcall TpAllocTimer(ptr ptr ptr ptr)
+@ stdcall TpAllocWait(ptr ptr ptr ptr)
@ stdcall TpAllocWork(ptr ptr ptr ptr)
@ stdcall TpCallbackLeaveCriticalSectionOnCompletion(ptr ptr)
@ stdcall TpCallbackMayRunLong(ptr)
@@ -987,6 +988,7 @@
@ stdcall TpReleaseCleanupGroupMembers(ptr long ptr)
@ stdcall TpReleasePool(ptr)
@ stdcall TpReleaseTimer(ptr)
+@ stdcall TpReleaseWait(ptr)
@ stdcall TpReleaseWork(ptr)
@ stdcall TpSetPoolMaxThreads(ptr long)
@ stdcall TpSetPoolMinThreads(ptr long)
diff --git a/dlls/ntdll/threadpool.c b/dlls/ntdll/threadpool.c
index ef502ba..b9aece4 100644
--- a/dlls/ntdll/threadpool.c
+++ b/dlls/ntdll/threadpool.c
@@ -159,7 +159,8 @@ enum threadpool_objtype
{
TP_OBJECT_TYPE_SIMPLE,
TP_OBJECT_TYPE_WORK,
- TP_OBJECT_TYPE_TIMER
+ TP_OBJECT_TYPE_TIMER,
+ TP_OBJECT_TYPE_WAIT
};
/* internal threadpool object representation */
@@ -209,6 +210,10 @@ struct threadpool_object
LONG period;
LONG window_length;
} timer;
+ struct
+ {
+ PTP_WAIT_CALLBACK callback;
+ } wait;
} u;
};
@@ -286,6 +291,13 @@ static inline struct threadpool_object *impl_from_TP_TIMER( TP_TIMER *timer )
return object;
}
+static inline struct threadpool_object *impl_from_TP_WAIT( TP_WAIT *wait )
+{
+ struct threadpool_object *object = (struct threadpool_object *)wait;
+ assert( object->type == TP_OBJECT_TYPE_WAIT );
+ return object;
+}
+
static inline struct threadpool_group *impl_from_TP_CLEANUP_GROUP( TP_CLEANUP_GROUP *group )
{
return (struct threadpool_group *)group;
@@ -1907,6 +1919,15 @@ static void CALLBACK threadpool_worker_proc( void *param )
break;
}
+ case TP_OBJECT_TYPE_WAIT:
+ {
+ TRACE( "executing wait callback %p(%p, %p, %p, %u)\n",
+ object->u.wait.callback, callback_instance, object->userdata, object, WAIT_OBJECT_0 );
+ object->u.wait.callback( callback_instance, object->userdata, (TP_WAIT *)object, WAIT_OBJECT_0 );
+ TRACE( "callback %p returned\n", object->u.wait.callback );
+ break;
+ }
+
default:
assert(0);
break;
@@ -2052,6 +2073,37 @@ NTSTATUS WINAPI TpAllocTimer( TP_TIMER **out, PTP_TIMER_CALLBACK callback, PVOID
}
/***********************************************************************
+ * TpAllocWait (NTDLL.@)
+ */
+NTSTATUS WINAPI TpAllocWait( TP_WAIT **out, PTP_WAIT_CALLBACK callback, PVOID userdata,
+ TP_CALLBACK_ENVIRON *environment )
+{
+ struct threadpool_object *object;
+ struct threadpool *pool;
+ NTSTATUS status;
+
+ TRACE( "%p %p %p %p\n", out, callback, userdata, environment );
+
+ object = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*object) );
+ if (!object)
+ return STATUS_NO_MEMORY;
+
+ status = tp_threadpool_lock( &pool, environment );
+ if (status)
+ {
+ RtlFreeHeap( GetProcessHeap(), 0, object );
+ return status;
+ }
+
+ object->type = TP_OBJECT_TYPE_WAIT;
+ object->u.wait.callback = callback;
+ tp_object_initialize( object, pool, userdata, environment );
+
+ *out = (TP_WAIT *)object;
+ return STATUS_SUCCESS;
+}
+
+/***********************************************************************
* TpAllocWork (NTDLL.@)
*/
NTSTATUS WINAPI TpAllocWork( TP_WORK **out, PTP_WORK_CALLBACK callback, PVOID userdata,
@@ -2356,6 +2408,19 @@ VOID WINAPI TpReleaseTimer( TP_TIMER *timer )
}
/***********************************************************************
+ * TpReleaseWait (NTDLL.@)
+ */
+VOID WINAPI TpReleaseWait( TP_WAIT *wait )
+{
+ struct threadpool_object *this = impl_from_TP_WAIT( wait );
+
+ TRACE( "%p\n", wait );
+
+ tp_object_shutdown( this );
+ tp_object_release( this );
+}
+
+/***********************************************************************
* TpReleaseWork (NTDLL.@)
*/
VOID WINAPI TpReleaseWork( TP_WORK *work )
--
2.4.4

View File

@ -1,525 +0,0 @@
From 4371c3cd5674720b230ec584199b7d5cbb67e2fc Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian@fds-team.de>
Date: Sat, 4 Jul 2015 04:32:31 +0200
Subject: ntdll: Implement threadpool wait queues.
---
dlls/ntdll/ntdll.spec | 2 +
dlls/ntdll/threadpool.c | 367 ++++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 361 insertions(+), 8 deletions(-)
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec
index 78814cd..75dc647 100644
--- a/dlls/ntdll/ntdll.spec
+++ b/dlls/ntdll/ntdll.spec
@@ -993,8 +993,10 @@
@ stdcall TpSetPoolMaxThreads(ptr long)
@ stdcall TpSetPoolMinThreads(ptr long)
@ stdcall TpSetTimer(ptr ptr long long)
+@ stdcall TpSetWait(ptr long ptr)
@ stdcall TpSimpleTryPost(ptr ptr ptr)
@ stdcall TpWaitForTimer(ptr long)
+@ stdcall TpWaitForWait(ptr long)
@ stdcall TpWaitForWork(ptr long)
@ stdcall -ret64 VerSetConditionMask(int64 long long)
@ stdcall WinSqmIsOptedIn()
diff --git a/dlls/ntdll/threadpool.c b/dlls/ntdll/threadpool.c
index b9aece4..f86e965 100644
--- a/dlls/ntdll/threadpool.c
+++ b/dlls/ntdll/threadpool.c
@@ -137,6 +137,7 @@ struct timer_queue
*/
#define THREADPOOL_WORKER_TIMEOUT 5000
+#define MAXIMUM_WAITQUEUE_OBJECTS (MAXIMUM_WAIT_OBJECTS - 1)
/* internal threadpool representation */
struct threadpool
@@ -213,6 +214,13 @@ struct threadpool_object
struct
{
PTP_WAIT_CALLBACK callback;
+ LONG signaled;
+ /* information about the wait, locked via waitqueue.cs */
+ struct waitqueue_bucket *bucket;
+ BOOL wait_pending;
+ struct list wait_entry;
+ ULONGLONG timeout;
+ HANDLE handle;
} wait;
} u;
};
@@ -272,6 +280,38 @@ static RTL_CRITICAL_SECTION_DEBUG timerqueue_debug =
0, 0, { (DWORD_PTR)(__FILE__ ": timerqueue.cs") }
};
+/* global waitqueue object */
+static RTL_CRITICAL_SECTION_DEBUG waitqueue_debug;
+
+static struct
+{
+ CRITICAL_SECTION cs;
+ LONG num_buckets;
+ struct list buckets;
+}
+waitqueue =
+{
+ { &waitqueue_debug, -1, 0, 0, 0, 0 }, /* cs */
+ 0, /* num_buckets */
+ LIST_INIT( waitqueue.buckets ) /* buckets */
+};
+
+static RTL_CRITICAL_SECTION_DEBUG waitqueue_debug =
+{
+ 0, 0, &waitqueue.cs,
+ { &waitqueue_debug.ProcessLocksList, &waitqueue_debug.ProcessLocksList },
+ 0, 0, { (DWORD_PTR)(__FILE__ ": waitqueue.cs") }
+};
+
+struct waitqueue_bucket
+{
+ struct list bucket_entry;
+ LONG objcount;
+ struct list reserved;
+ struct list waiting;
+ HANDLE update_event;
+};
+
static inline struct threadpool *impl_from_TP_POOL( TP_POOL *pool )
{
return (struct threadpool *)pool;
@@ -309,7 +349,7 @@ static inline struct threadpool_instance *impl_from_TP_CALLBACK_INSTANCE( TP_CAL
}
static void CALLBACK threadpool_worker_proc( void *param );
-static void tp_object_submit( struct threadpool_object *object );
+static void tp_object_submit( struct threadpool_object *object, BOOL success );
static void tp_object_shutdown( struct threadpool_object *object );
static BOOL tp_object_release( struct threadpool_object *object );
static struct threadpool *default_threadpool = NULL;
@@ -1261,7 +1301,7 @@ static void CALLBACK timerqueue_thread_proc( void *param )
/* Queue a new callback in one of the worker threads. */
list_remove( &timer->u.timer.timer_entry );
timer->u.timer.timer_pending = FALSE;
- tp_object_submit( timer );
+ tp_object_submit( timer, FALSE );
/* Insert the timer back into the queue, except its marked for shutdown. */
if (timer->u.timer.period && !timer->shutdown)
@@ -1398,6 +1438,216 @@ static void tp_timerqueue_unlock( struct threadpool_object *timer )
}
/***********************************************************************
+ * waitqueue_thread_proc (internal)
+ */
+static void CALLBACK waitqueue_thread_proc( void *param )
+{
+ struct threadpool_object *objects[MAXIMUM_WAITQUEUE_OBJECTS];
+ HANDLE handles[MAXIMUM_WAITQUEUE_OBJECTS + 1];
+ struct waitqueue_bucket *bucket = param;
+ struct threadpool_object *wait, *next;
+ LARGE_INTEGER now, timeout;
+ DWORD num_handles;
+ NTSTATUS status;
+
+ TRACE( "starting wait queue thread\n" );
+
+ RtlEnterCriticalSection( &waitqueue.cs );
+
+ for (;;)
+ {
+ NtQuerySystemTime( &now );
+ timeout.QuadPart = TIMEOUT_INFINITE;
+ num_handles = 0;
+
+ /* Check for expired waits. */
+ LIST_FOR_EACH_ENTRY_SAFE( wait, next, &bucket->waiting, struct threadpool_object, u.wait.wait_entry )
+ {
+ assert( wait->type == TP_OBJECT_TYPE_WAIT );
+ if (wait->u.wait.timeout <= now.QuadPart)
+ {
+ /* Wait expired. */
+ list_remove( &wait->u.wait.wait_entry );
+ list_add_tail( &bucket->reserved, &wait->u.wait.wait_entry );
+ tp_object_submit( wait, FALSE );
+ }
+ else
+ {
+ if (wait->u.wait.timeout < timeout.QuadPart)
+ timeout.QuadPart = wait->u.wait.timeout;
+
+ /* Prepare wait, keep a reference to make sure the object doesn't get destroyed. */
+ assert( num_handles < MAXIMUM_WAITQUEUE_OBJECTS );
+ interlocked_inc( &wait->refcount );
+ objects[num_handles] = wait;
+ handles[num_handles] = wait->u.wait.handle;
+ num_handles++;
+ }
+ }
+
+ if (!bucket->objcount)
+ {
+ /* All wait objects have been destroyed, if no new wait objects are created
+ * within some amount of time, then we can shutdown this thread. */
+ assert( num_handles == 0 );
+ RtlLeaveCriticalSection( &waitqueue.cs );
+ timeout.QuadPart = (ULONGLONG)THREADPOOL_WORKER_TIMEOUT * -10000;
+ status = NtWaitForMultipleObjects( 1, &bucket->update_event, TRUE, FALSE, &timeout );
+ RtlEnterCriticalSection( &waitqueue.cs );
+
+ if (status == STATUS_TIMEOUT && !bucket->objcount)
+ break;
+ }
+ else
+ {
+ /* Wait for the next event. */
+ handles[num_handles] = bucket->update_event;
+ RtlLeaveCriticalSection( &waitqueue.cs );
+ status = NtWaitForMultipleObjects( num_handles + 1, handles, TRUE, FALSE, &timeout );
+ RtlEnterCriticalSection( &waitqueue.cs );
+
+ if (status >= STATUS_WAIT_0 && status < STATUS_WAIT_0 + num_handles)
+ {
+ wait = objects[status - STATUS_WAIT_0];
+ assert( wait->type == TP_OBJECT_TYPE_WAIT );
+ if (wait->u.wait.bucket)
+ {
+ /* Wait object signaled. */
+ assert( wait->u.wait.bucket == bucket );
+ list_remove( &wait->u.wait.wait_entry );
+ list_add_tail( &bucket->reserved, &wait->u.wait.wait_entry );
+ tp_object_submit( wait, TRUE );
+ }
+ else
+ ERR("wait object %p triggered while object was destroyed\n", wait);
+ }
+ }
+
+ /* Release locked wait objects (if any). */
+ while (num_handles)
+ {
+ wait = objects[--num_handles];
+ assert( wait->type == TP_OBJECT_TYPE_WAIT );
+ tp_object_release( wait );
+ }
+ }
+
+ /* Remove this bucket from the list. */
+ list_remove( &bucket->bucket_entry );
+ if (!--waitqueue.num_buckets)
+ assert( list_empty( &waitqueue.buckets ) );
+
+ RtlLeaveCriticalSection( &waitqueue.cs );
+
+ TRACE( "terminating wait queue thread\n" );
+
+ assert( bucket->objcount == 0 );
+ assert( list_empty( &bucket->reserved ) );
+ assert( list_empty( &bucket->waiting ) );
+ NtClose( bucket->update_event );
+
+ RtlFreeHeap( GetProcessHeap(), 0, bucket );
+}
+
+/***********************************************************************
+ * tp_waitqueue_lock (internal)
+ */
+static NTSTATUS tp_waitqueue_lock( struct threadpool_object *wait )
+{
+ struct waitqueue_bucket *bucket;
+ NTSTATUS status;
+ HANDLE thread;
+ assert( wait->type = TP_OBJECT_TYPE_WAIT );
+
+ wait->u.wait.signaled = 0;
+ wait->u.wait.bucket = NULL;
+ wait->u.wait.wait_pending = FALSE;
+ wait->u.wait.timeout = 0;
+ wait->u.wait.handle = INVALID_HANDLE_VALUE;
+
+ RtlEnterCriticalSection( &waitqueue.cs );
+
+ /* Try to assign to existing bucket if possible. */
+ LIST_FOR_EACH_ENTRY( bucket, &waitqueue.buckets, struct waitqueue_bucket, bucket_entry )
+ {
+ if (bucket->objcount < MAXIMUM_WAITQUEUE_OBJECTS)
+ {
+ list_add_tail( &bucket->reserved, &wait->u.wait.wait_entry );
+ wait->u.wait.bucket = bucket;
+ bucket->objcount++;
+
+ status = STATUS_SUCCESS;
+ goto out;
+ }
+ }
+
+ /* Create a new bucket and corresponding worker thread. */
+ bucket = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*bucket) );
+ if (!bucket)
+ {
+ status = STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ bucket->objcount = 0;
+ list_init( &bucket->reserved );
+ list_init( &bucket->waiting );
+
+ status = NtCreateEvent( &bucket->update_event, EVENT_ALL_ACCESS,
+ NULL, SynchronizationEvent, FALSE );
+ if (status)
+ {
+ RtlFreeHeap( GetProcessHeap(), 0, bucket );
+ goto out;
+ }
+
+ status = RtlCreateUserThread( GetCurrentProcess(), NULL, FALSE, NULL, 0, 0,
+ waitqueue_thread_proc, bucket, &thread, NULL );
+ if (status == STATUS_SUCCESS)
+ {
+ list_add_tail( &waitqueue.buckets, &bucket->bucket_entry );
+ waitqueue.num_buckets++;
+
+ list_add_tail( &bucket->reserved, &wait->u.wait.wait_entry );
+ wait->u.wait.bucket = bucket;
+ bucket->objcount++;
+
+ NtClose( thread );
+ }
+ else
+ {
+ NtClose( bucket->update_event );
+ RtlFreeHeap( GetProcessHeap(), 0, bucket );
+ }
+
+out:
+ RtlLeaveCriticalSection( &waitqueue.cs );
+ return status;
+}
+
+/***********************************************************************
+ * tp_waitqueue_unlock (internal)
+ */
+static void tp_waitqueue_unlock( struct threadpool_object *wait )
+{
+ assert( wait->type == TP_OBJECT_TYPE_WAIT );
+
+ RtlEnterCriticalSection( &waitqueue.cs );
+ if (wait->u.wait.bucket)
+ {
+ struct waitqueue_bucket *bucket = wait->u.wait.bucket;
+ assert( bucket->objcount > 0 );
+
+ list_remove( &wait->u.wait.wait_entry );
+ wait->u.wait.bucket = NULL;
+ bucket->objcount--;
+
+ NtSetEvent( bucket->update_event, NULL );
+ }
+ RtlLeaveCriticalSection( &waitqueue.cs );
+}
+
+/***********************************************************************
* tp_threadpool_alloc (internal)
*
* Allocates a new threadpool object.
@@ -1666,7 +1916,7 @@ static void tp_object_initialize( struct threadpool_object *object, struct threa
* will be set, and tp_object_submit would fail with an assertion. */
if (is_simple_callback)
- tp_object_submit( object );
+ tp_object_submit( object, FALSE );
if (object->group)
{
@@ -1692,7 +1942,7 @@ static void tp_object_initialize( struct threadpool_object *object, struct threa
* Submits a threadpool object to the associcated threadpool. This
* function has to be VOID because TpPostWork can never fail on Windows.
*/
-static void tp_object_submit( struct threadpool_object *object )
+static void tp_object_submit( struct threadpool_object *object, BOOL success )
{
struct threadpool *pool = object->pool;
NTSTATUS status = STATUS_UNSUCCESSFUL;
@@ -1722,6 +1972,10 @@ static void tp_object_submit( struct threadpool_object *object )
if (!object->num_pending_callbacks++)
list_add_tail( &pool->pool, &object->pool_entry );
+ /* Count how often the object was signaled. */
+ if (object->type == TP_OBJECT_TYPE_WAIT && success)
+ object->u.wait.signaled++;
+
/* No new thread started - wake up one existing thread. */
if (status != STATUS_SUCCESS)
{
@@ -1748,6 +2002,9 @@ static void tp_object_cancel( struct threadpool_object *object, BOOL group_cance
pending_callbacks = object->num_pending_callbacks;
object->num_pending_callbacks = 0;
list_remove( &object->pool_entry );
+
+ if (object->type == TP_OBJECT_TYPE_WAIT)
+ object->u.wait.signaled = 0;
}
RtlLeaveCriticalSection( &pool->cs );
@@ -1797,6 +2054,8 @@ static void tp_object_shutdown( struct threadpool_object *object )
{
if (object->type == TP_OBJECT_TYPE_TIMER)
tp_timerqueue_unlock( object );
+ else if (object->type == TP_OBJECT_TYPE_WAIT)
+ tp_waitqueue_unlock( object );
object->shutdown = TRUE;
}
@@ -1851,6 +2110,7 @@ static void CALLBACK threadpool_worker_proc( void *param )
TP_CALLBACK_INSTANCE *callback_instance;
struct threadpool_instance instance;
struct threadpool *pool = param;
+ TP_WAIT_RESULT wait_result;
LARGE_INTEGER timeout;
struct list *ptr;
NTSTATUS status;
@@ -1871,6 +2131,13 @@ static void CALLBACK threadpool_worker_proc( void *param )
if (--object->num_pending_callbacks)
list_add_tail( &pool->pool, &object->pool_entry );
+ /* For wait objects determine if the operation was successful. */
+ if (object->type == TP_OBJECT_TYPE_WAIT)
+ {
+ wait_result = object->u.wait.signaled ? WAIT_OBJECT_0 : WAIT_TIMEOUT;
+ if (wait_result == WAIT_OBJECT_0) object->u.wait.signaled--;
+ }
+
/* Leave critical section and do the actual callback. */
object->num_associated_callbacks++;
object->num_running_callbacks++;
@@ -1922,8 +2189,8 @@ static void CALLBACK threadpool_worker_proc( void *param )
case TP_OBJECT_TYPE_WAIT:
{
TRACE( "executing wait callback %p(%p, %p, %p, %u)\n",
- object->u.wait.callback, callback_instance, object->userdata, object, WAIT_OBJECT_0 );
- object->u.wait.callback( callback_instance, object->userdata, (TP_WAIT *)object, WAIT_OBJECT_0 );
+ object->u.wait.callback, callback_instance, object->userdata, object, wait_result );
+ object->u.wait.callback( callback_instance, object->userdata, (TP_WAIT *)object, wait_result );
TRACE( "callback %p returned\n", object->u.wait.callback );
break;
}
@@ -2097,6 +2364,15 @@ NTSTATUS WINAPI TpAllocWait( TP_WAIT **out, PTP_WAIT_CALLBACK callback, PVOID us
object->type = TP_OBJECT_TYPE_WAIT;
object->u.wait.callback = callback;
+
+ status = tp_waitqueue_lock( object );
+ if (status)
+ {
+ tp_threadpool_unlock( pool );
+ RtlFreeHeap( GetProcessHeap(), 0, object );
+ return status;
+ }
+
tp_object_initialize( object, pool, userdata, environment );
*out = (TP_WAIT *)object;
@@ -2304,7 +2580,7 @@ VOID WINAPI TpPostWork( TP_WORK *work )
TRACE( "%p\n", work );
- tp_object_submit( this );
+ tp_object_submit( this, FALSE );
}
/***********************************************************************
@@ -2558,7 +2834,68 @@ VOID WINAPI TpSetTimer( TP_TIMER *timer, LARGE_INTEGER *timeout, LONG period, LO
RtlLeaveCriticalSection( &timerqueue.cs );
if (submit_timer)
- tp_object_submit( this );
+ tp_object_submit( this, FALSE );
+}
+
+/***********************************************************************
+ * TpSetWait (KERNEL32.@)
+ */
+VOID WINAPI TpSetWait( TP_WAIT *wait, HANDLE handle, LARGE_INTEGER *timeout )
+{
+ struct threadpool_object *this = impl_from_TP_WAIT( wait );
+ ULONGLONG timestamp = TIMEOUT_INFINITE;
+ BOOL submit_wait = FALSE;
+
+ TRACE( "%p %p %p\n", wait, handle, timeout );
+
+ RtlEnterCriticalSection( &waitqueue.cs );
+
+ assert( this->u.wait.bucket );
+ this->u.wait.handle = handle;
+
+ if (handle || this->u.wait.wait_pending)
+ {
+ struct waitqueue_bucket *bucket = this->u.wait.bucket;
+ list_remove( &this->u.wait.wait_entry );
+
+ /* Convert relative timeout to absolute timestamp. */
+ if (handle && timeout)
+ {
+ timestamp = timeout->QuadPart;
+ if ((LONGLONG)timestamp < 0)
+ {
+ LARGE_INTEGER now;
+ NtQuerySystemTime( &now );
+ timestamp = now.QuadPart - timestamp;
+ }
+ else if (!timestamp)
+ {
+ submit_wait = TRUE;
+ handle = NULL;
+ }
+ }
+
+ /* Add wait object back into one of the queues. */
+ if (handle)
+ {
+ list_add_tail( &bucket->waiting, &this->u.wait.wait_entry );
+ this->u.wait.wait_pending = TRUE;
+ this->u.wait.timeout = timestamp;
+ }
+ else
+ {
+ list_add_tail( &bucket->reserved, &this->u.wait.wait_entry );
+ this->u.wait.wait_pending = FALSE;
+ }
+
+ /* Wake up the wait queue thread. */
+ NtSetEvent( bucket->update_event, NULL );
+ }
+
+ RtlLeaveCriticalSection( &waitqueue.cs );
+
+ if (submit_wait)
+ tp_object_submit( this, FALSE );
}
/***********************************************************************
@@ -2606,6 +2943,20 @@ VOID WINAPI TpWaitForTimer( TP_TIMER *timer, BOOL cancel_pending )
}
/***********************************************************************
+ * TpWaitForWait (KERNEL32.@)
+ */
+VOID WINAPI TpWaitForWait( TP_WAIT *wait, BOOL cancel_pending )
+{
+ struct threadpool_object *this = impl_from_TP_WAIT( wait );
+
+ TRACE( "%p %d\n", wait, cancel_pending );
+
+ if (cancel_pending)
+ tp_object_cancel( this, FALSE, NULL );
+ tp_object_wait( this, FALSE );
+}
+
+/***********************************************************************
* TpWaitForWork (NTDLL.@)
*/
VOID WINAPI TpWaitForWork( TP_WORK *work, BOOL cancel_pending )
--
2.4.4

View File

@ -1,334 +0,0 @@
From ef697e2435d35ad1a53fd60209da7a7d4100dcf2 Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian@fds-team.de>
Date: Sat, 4 Jul 2015 04:36:23 +0200
Subject: ntdll/tests: Add basic tests for threadpool wait objects.
---
dlls/ntdll/tests/threadpool.c | 266 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 266 insertions(+)
diff --git a/dlls/ntdll/tests/threadpool.c b/dlls/ntdll/tests/threadpool.c
index 0671202..c808400 100644
--- a/dlls/ntdll/tests/threadpool.c
+++ b/dlls/ntdll/tests/threadpool.c
@@ -24,11 +24,13 @@ static HMODULE hntdll = 0;
static NTSTATUS (WINAPI *pTpAllocCleanupGroup)(TP_CLEANUP_GROUP **);
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 NTSTATUS (WINAPI *pTpCallbackMayRunLong)(TP_CALLBACK_INSTANCE *);
static VOID (WINAPI *pTpCallbackReleaseSemaphoreOnCompletion)(TP_CALLBACK_INSTANCE *,HANDLE,DWORD);
static VOID (WINAPI *pTpDisassociateCallback)(TP_CALLBACK_INSTANCE *);
static BOOL (WINAPI *pTpIsTimerSet)(TP_TIMER *);
+static VOID (WINAPI *pTpReleaseWait)(TP_WAIT *);
static VOID (WINAPI *pTpPostWork)(TP_WORK *);
static VOID (WINAPI *pTpReleaseCleanupGroup)(TP_CLEANUP_GROUP *);
static VOID (WINAPI *pTpReleaseCleanupGroupMembers)(TP_CLEANUP_GROUP *,BOOL,PVOID);
@@ -37,8 +39,10 @@ static VOID (WINAPI *pTpReleaseTimer)(TP_TIMER *);
static VOID (WINAPI *pTpReleaseWork)(TP_WORK *);
static VOID (WINAPI *pTpSetPoolMaxThreads)(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 *pTpWaitForTimer)(TP_TIMER *,BOOL);
+static VOID (WINAPI *pTpWaitForWait)(TP_WAIT *,BOOL);
static VOID (WINAPI *pTpWaitForWork)(TP_WORK *,BOOL);
#define NTDLL_GET_PROC(func) \
@@ -61,6 +65,7 @@ static BOOL init_threadpool(void)
NTDLL_GET_PROC(TpAllocCleanupGroup);
NTDLL_GET_PROC(TpAllocPool);
NTDLL_GET_PROC(TpAllocTimer);
+ NTDLL_GET_PROC(TpAllocWait);
NTDLL_GET_PROC(TpAllocWork);
NTDLL_GET_PROC(TpCallbackMayRunLong);
NTDLL_GET_PROC(TpCallbackReleaseSemaphoreOnCompletion);
@@ -71,11 +76,14 @@ static BOOL init_threadpool(void)
NTDLL_GET_PROC(TpReleaseCleanupGroupMembers);
NTDLL_GET_PROC(TpReleasePool);
NTDLL_GET_PROC(TpReleaseTimer);
+ NTDLL_GET_PROC(TpReleaseWait);
NTDLL_GET_PROC(TpReleaseWork);
NTDLL_GET_PROC(TpSetPoolMaxThreads);
NTDLL_GET_PROC(TpSetTimer);
+ NTDLL_GET_PROC(TpSetWait);
NTDLL_GET_PROC(TpSimpleTryPost);
NTDLL_GET_PROC(TpWaitForTimer);
+ NTDLL_GET_PROC(TpWaitForWait);
NTDLL_GET_PROC(TpWaitForWork);
if (!pTpAllocPool)
@@ -906,6 +914,263 @@ static void test_tp_window_length(void)
CloseHandle(semaphore);
}
+struct wait_info
+{
+ HANDLE semaphore;
+ LONG userdata;
+};
+
+static void CALLBACK wait_cb(TP_CALLBACK_INSTANCE *instance, void *userdata,
+ TP_WAIT *wait, TP_WAIT_RESULT result)
+{
+ struct wait_info *info = userdata;
+ trace("Running wait callback\n");
+
+ if (result == WAIT_OBJECT_0)
+ InterlockedIncrement(&info->userdata);
+ else if (result == WAIT_TIMEOUT)
+ InterlockedExchangeAdd(&info->userdata, 0x10000);
+ else
+ ok(0, "unexpected result %u\n", result);
+ ReleaseSemaphore(info->semaphore, 1, NULL);
+}
+
+static void test_tp_wait(void)
+{
+ TP_CALLBACK_ENVIRON environment;
+ TP_WAIT *wait1, *wait2;
+ struct wait_info info;
+ HANDLE semaphores[2];
+ LARGE_INTEGER when;
+ NTSTATUS status;
+ TP_POOL *pool;
+ DWORD result;
+
+ semaphores[0] = CreateSemaphoreW(NULL, 0, 2, NULL);
+ ok(semaphores[0] != NULL, "failed to create semaphore\n");
+ semaphores[1] = CreateSemaphoreW(NULL, 0, 1, NULL);
+ ok(semaphores[1] != NULL, "failed to create semaphore\n");
+ info.semaphore = semaphores[0];
+
+ /* 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 wait items */
+ memset(&environment, 0, sizeof(environment));
+ environment.Version = 1;
+ environment.Pool = pool;
+
+ wait1 = NULL;
+ status = pTpAllocWait(&wait1, wait_cb, &info, &environment);
+ ok(!status, "TpAllocWait failed with status %x\n", status);
+ ok(wait1 != NULL, "expected wait1 != NULL\n");
+
+ wait2 = NULL;
+ status = pTpAllocWait(&wait2, wait_cb, &info, &environment);
+ ok(!status, "TpAllocWait failed with status %x\n", status);
+ ok(wait2 != NULL, "expected wait2 != NULL\n");
+
+ /* infinite timeout, signal the semaphore immediately */
+ info.userdata = 0;
+ pTpSetWait(wait1, semaphores[1], NULL);
+ ReleaseSemaphore(semaphores[1], 1, NULL);
+ result = WaitForSingleObject(semaphores[0], 100);
+ ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
+ ok(info.userdata == 1, "expected info.userdata = 1, got %u\n", info.userdata);
+ result = WaitForSingleObject(semaphores[1], 0);
+ ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
+
+ /* relative timeout, no event */
+ info.userdata = 0;
+ when.QuadPart = (ULONGLONG)200 * -10000;
+ pTpSetWait(wait1, semaphores[1], &when);
+ result = WaitForSingleObject(semaphores[0], 100);
+ ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
+ ok(info.userdata == 0, "expected info.userdata = 0, got %u\n", info.userdata);
+ result = WaitForSingleObject(semaphores[0], 200);
+ ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
+ ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %u\n", info.userdata);
+ result = WaitForSingleObject(semaphores[1], 0);
+ ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
+
+ /* repeat test with call to TpWaitForWait(..., TRUE) */
+ info.userdata = 0;
+ when.QuadPart = (ULONGLONG)200 * -10000;
+ pTpSetWait(wait1, semaphores[1], &when);
+ result = WaitForSingleObject(semaphores[0], 100);
+ ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
+ pTpWaitForWait(wait1, TRUE);
+ ok(info.userdata == 0, "expected info.userdata = 0, got %u\n", info.userdata);
+ result = WaitForSingleObject(semaphores[0], 200);
+ ok(result == WAIT_OBJECT_0 || broken(result == WAIT_TIMEOUT) /* Win 8 */,
+ "WaitForSingleObject returned %u\n", result);
+ if (result == WAIT_OBJECT_0)
+ ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %u\n", info.userdata);
+ else
+ ok(info.userdata == 0, "expected info.userdata = 0, got %u\n", info.userdata);
+ result = WaitForSingleObject(semaphores[1], 0);
+ ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
+
+ /* relative timeout, with event */
+ info.userdata = 0;
+ when.QuadPart = (ULONGLONG)200 * -10000;
+ pTpSetWait(wait1, semaphores[1], &when);
+ result = WaitForSingleObject(semaphores[0], 100);
+ ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
+ ok(info.userdata == 0, "expected info.userdata = 0, got %u\n", info.userdata);
+ ReleaseSemaphore(semaphores[1], 1, NULL);
+ result = WaitForSingleObject(semaphores[0], 100);
+ ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
+ ok(info.userdata == 1, "expected info.userdata = 1, got %u\n", info.userdata);
+ result = WaitForSingleObject(semaphores[1], 0);
+ ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
+
+ /* repeat test with call to TpWaitForWait(..., TRUE) */
+ info.userdata = 0;
+ when.QuadPart = (ULONGLONG)200 * -10000;
+ pTpSetWait(wait1, semaphores[1], &when);
+ result = WaitForSingleObject(semaphores[0], 100);
+ ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
+ pTpWaitForWait(wait1, TRUE);
+ ok(info.userdata == 0, "expected info.userdata = 0, got %u\n", info.userdata);
+ ReleaseSemaphore(semaphores[1], 1, NULL);
+ result = WaitForSingleObject(semaphores[0], 100);
+ ok(result == WAIT_OBJECT_0 || broken(result == WAIT_TIMEOUT) /* Win 8 */,
+ "WaitForSingleObject returned %u\n", result);
+ if (result == WAIT_OBJECT_0)
+ {
+ ok(info.userdata == 1, "expected info.userdata = 1, got %u\n", info.userdata);
+ result = WaitForSingleObject(semaphores[1], 0);
+ ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
+ }
+ else
+ {
+ ok(info.userdata == 0, "expected info.userdata = 0, got %u\n", info.userdata);
+ result = WaitForSingleObject(semaphores[1], 0);
+ ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
+ }
+
+ /* absolute timeout, no event */
+ info.userdata = 0;
+ NtQuerySystemTime( &when );
+ when.QuadPart += (ULONGLONG)200 * 10000;
+ pTpSetWait(wait1, semaphores[1], &when);
+ result = WaitForSingleObject(semaphores[0], 100);
+ ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
+ ok(info.userdata == 0, "expected info.userdata = 0, got %u\n", info.userdata);
+ result = WaitForSingleObject(semaphores[0], 200);
+ ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
+ ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %u\n", info.userdata);
+ result = WaitForSingleObject(semaphores[1], 0);
+ ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
+
+ /* absolute timeout, with event */
+ info.userdata = 0;
+ NtQuerySystemTime( &when );
+ when.QuadPart += (ULONGLONG)200 * 10000;
+ pTpSetWait(wait1, semaphores[1], &when);
+ result = WaitForSingleObject(semaphores[0], 100);
+ ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
+ ok(info.userdata == 0, "expected info.userdata = 0, got %u\n", info.userdata);
+ ReleaseSemaphore(semaphores[1], 1, NULL);
+ result = WaitForSingleObject(semaphores[0], 100);
+ ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
+ ok(info.userdata == 1, "expected info.userdata = 1, got %u\n", info.userdata);
+ result = WaitForSingleObject(semaphores[1], 0);
+ ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
+
+ /* test timeout of zero */
+ info.userdata = 0;
+ when.QuadPart = 0;
+ pTpSetWait(wait1, semaphores[1], &when);
+ result = WaitForSingleObject(semaphores[0], 100);
+ ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
+ ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %u\n", info.userdata);
+ result = WaitForSingleObject(semaphores[1], 0);
+ ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
+
+ /* cancel a pending wait */
+ info.userdata = 0;
+ when.QuadPart = (ULONGLONG)250 * -10000;
+ pTpSetWait(wait1, semaphores[1], &when);
+ result = WaitForSingleObject(semaphores[0], 100);
+ ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
+ pTpSetWait(wait1, NULL, (void *)0xdeadbeef);
+ Sleep(50);
+ ReleaseSemaphore(semaphores[1], 1, NULL);
+ result = WaitForSingleObject(semaphores[0], 100);
+ ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
+ ok(info.userdata == 0, "expected info.userdata = 0, got %u\n", info.userdata);
+ result = WaitForSingleObject(semaphores[1], 0);
+ ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
+
+ /* test with INVALID_HANDLE_VALUE */
+ info.userdata = 0;
+ when.QuadPart = 0;
+ pTpSetWait(wait1, INVALID_HANDLE_VALUE, &when);
+ result = WaitForSingleObject(semaphores[0], 100);
+ ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
+ ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %u\n", info.userdata);
+
+ /* cancel a pending wait with INVALID_HANDLE_VALUE */
+ info.userdata = 0;
+ when.QuadPart = (ULONGLONG)250 * -10000;
+ pTpSetWait(wait1, semaphores[1], &when);
+ result = WaitForSingleObject(semaphores[0], 100);
+ ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
+ when.QuadPart = 0;
+ pTpSetWait(wait1, INVALID_HANDLE_VALUE, &when);
+ Sleep(50);
+ ReleaseSemaphore(semaphores[1], 1, NULL);
+ result = WaitForSingleObject(semaphores[0], 100);
+ ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
+ ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %u\n", info.userdata);
+ result = WaitForSingleObject(semaphores[1], 0);
+ ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
+
+ CloseHandle(semaphores[1]);
+ semaphores[1] = CreateSemaphoreW(NULL, 0, 2, NULL);
+ ok(semaphores[1] != NULL, "failed to create semaphore\n");
+
+ /* add two wait objects with the same semaphore */
+ info.userdata = 0;
+ pTpSetWait(wait1, semaphores[1], NULL);
+ pTpSetWait(wait2, semaphores[1], NULL);
+ Sleep(50);
+ ReleaseSemaphore(semaphores[1], 1, NULL);
+ result = WaitForSingleObject(semaphores[0], 100);
+ ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
+ result = WaitForSingleObject(semaphores[0], 100);
+ ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
+ ok(info.userdata == 1, "expected info.userdata = 1, got %u\n", info.userdata);
+ result = WaitForSingleObject(semaphores[1], 0);
+ ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
+
+ /* repeat test above, but with a semaphore of count 2 */
+ info.userdata = 0;
+ pTpSetWait(wait1, semaphores[1], NULL);
+ pTpSetWait(wait2, semaphores[1], NULL);
+ Sleep(50);
+ result = ReleaseSemaphore(semaphores[1], 2, NULL);
+ result = WaitForSingleObject(semaphores[0], 100);
+ ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
+ result = WaitForSingleObject(semaphores[0], 100);
+ ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
+ ok(info.userdata == 2, "expected info.userdata = 2, got %u\n", info.userdata);
+ result = WaitForSingleObject(semaphores[1], 0);
+ ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
+
+ /* cleanup */
+ pTpReleaseWait(wait1);
+ pTpReleaseWait(wait2);
+ pTpReleasePool(pool);
+ CloseHandle(semaphores[0]);
+ CloseHandle(semaphores[1]);
+}
+
START_TEST(threadpool)
{
if (!init_threadpool())
@@ -919,4 +1184,5 @@ START_TEST(threadpool)
test_tp_disassociate();
test_tp_timer();
test_tp_window_length();
+ test_tp_wait();
}
--
2.4.4

View File

@ -1,147 +0,0 @@
From fa0a0443778b2c4730a66ee4909681e0379673f4 Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian@fds-team.de>
Date: Sat, 4 Jul 2015 13:29:38 +0200
Subject: ntdll/tests: Add highly multithreaded wait tests.
---
dlls/ntdll/tests/threadpool.c | 119 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 119 insertions(+)
diff --git a/dlls/ntdll/tests/threadpool.c b/dlls/ntdll/tests/threadpool.c
index c808400..7235dfd 100644
--- a/dlls/ntdll/tests/threadpool.c
+++ b/dlls/ntdll/tests/threadpool.c
@@ -1171,6 +1171,124 @@ static void test_tp_wait(void)
CloseHandle(semaphores[1]);
}
+static struct
+{
+ HANDLE semaphore;
+ DWORD result;
+} multi_wait_info;
+
+static void CALLBACK multi_wait_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, TP_WAIT *wait, TP_WAIT_RESULT result)
+{
+ DWORD index = (DWORD)(DWORD_PTR)userdata;
+
+ if (result == WAIT_OBJECT_0)
+ multi_wait_info.result = index;
+ else if (result == WAIT_TIMEOUT)
+ multi_wait_info.result = 0x10000 | index;
+ else
+ ok(0, "unexpected result %u\n", result);
+ ReleaseSemaphore(multi_wait_info.semaphore, 1, NULL);
+}
+
+static void test_tp_multi_wait(void)
+{
+ TP_CALLBACK_ENVIRON environment;
+ HANDLE semaphores[512];
+ TP_WAIT *waits[512];
+ LARGE_INTEGER when;
+ HANDLE semaphore;
+ NTSTATUS status;
+ TP_POOL *pool;
+ DWORD result;
+ int i;
+
+ semaphore = CreateSemaphoreW(NULL, 0, 512, NULL);
+ ok(semaphore != NULL, "failed to create semaphore\n");
+ multi_wait_info.semaphore = semaphore;
+
+ /* 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");
+
+ memset(&environment, 0, sizeof(environment));
+ environment.Version = 1;
+ environment.Pool = pool;
+
+ /* create semaphores, wait objects and enable them */
+ for (i = 0; i < sizeof(semaphores)/sizeof(semaphores[0]); i++)
+ {
+ semaphores[i] = CreateSemaphoreW(NULL, 0, 1, NULL);
+ ok(semaphores[i] != NULL, "failed to create semaphore %i\n", i);
+
+ waits[i] = NULL;
+ status = pTpAllocWait(&waits[i], multi_wait_cb, (void *)(DWORD_PTR)i, &environment);
+ ok(!status, "TpAllocWait failed with status %x\n", status);
+ ok(waits[i] != NULL, "expected waits[%d] != NULL\n", i);
+
+ pTpSetWait(waits[i], semaphores[i], NULL);
+ }
+
+ /* test releasing each semaphore */
+ for (i = 0; i < sizeof(semaphores)/sizeof(semaphores[0]); i++)
+ {
+ multi_wait_info.result = 0;
+ ReleaseSemaphore(semaphores[i], 1, NULL);
+
+ result = WaitForSingleObject(semaphore, 100);
+ ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
+ ok(multi_wait_info.result == i, "expected result %d, got %u\n", i, multi_wait_info.result);
+
+ pTpSetWait(waits[i], semaphores[i], NULL);
+ }
+
+ /* repeat the same test in reverse order */
+ for (i = sizeof(semaphores)/sizeof(semaphores[0]) - 1; i >= 0; i--)
+ {
+ multi_wait_info.result = 0;
+ ReleaseSemaphore(semaphores[i], 1, NULL);
+
+ result = WaitForSingleObject(semaphore, 100);
+ ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
+ ok(multi_wait_info.result == i, "expected result %d, got %u\n", i, multi_wait_info.result);
+
+ pTpSetWait(waits[i], semaphores[i], NULL);
+ }
+
+ /* now test with a timeout */
+ multi_wait_info.result = 0;
+ for (i = 0; i < sizeof(semaphores)/sizeof(semaphores[0]); i++)
+ {
+ when.QuadPart = (ULONGLONG)50 * -10000;
+ pTpSetWait(waits[i], semaphores[i], &when);
+ }
+ for (i = 0; i < sizeof(semaphores)/sizeof(semaphores[0]); i++)
+ {
+ result = WaitForSingleObject(semaphore, 100);
+ ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
+ }
+ ok(multi_wait_info.result >> 16, "expected multi_wait_info.result >> 16 != 0\n");
+
+ /* destroy the wait objects and semaphores while waiting -
+ * broken applications might do that too. */
+ for (i = 0; i < sizeof(semaphores)/sizeof(semaphores[0]); i++)
+ {
+ pTpSetWait(waits[i], semaphores[i], NULL);
+ }
+
+ Sleep(50);
+
+ for (i = 0; i < sizeof(semaphores)/sizeof(semaphores[0]); i++)
+ {
+ pTpReleaseWait(waits[i]);
+ NtClose(semaphores[i]);
+ }
+
+ pTpReleasePool(pool);
+ CloseHandle(semaphore);
+}
+
START_TEST(threadpool)
{
if (!init_threadpool())
@@ -1185,4 +1303,5 @@ START_TEST(threadpool)
test_tp_timer();
test_tp_window_length();
test_tp_wait();
+ test_tp_multi_wait();
}
--
2.4.4

View File

@ -1,63 +0,0 @@
From 6cdcda50c44c46c50feccc486f96a2e4e4818bfb Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian@fds-team.de>
Date: Sat, 4 Jul 2015 15:54:45 +0200
Subject: ntdll: Try to merge threadpool wait queue buckets if possible.
---
dlls/ntdll/threadpool.c | 40 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/dlls/ntdll/threadpool.c b/dlls/ntdll/threadpool.c
index f86e965..d6a57c3 100644
--- a/dlls/ntdll/threadpool.c
+++ b/dlls/ntdll/threadpool.c
@@ -1530,6 +1530,46 @@ static void CALLBACK waitqueue_thread_proc( void *param )
assert( wait->type == TP_OBJECT_TYPE_WAIT );
tp_object_release( wait );
}
+
+ /* Try to merge bucket with other threads. */
+ if (waitqueue.num_buckets > 1 && bucket->objcount &&
+ bucket->objcount <= MAXIMUM_WAITQUEUE_OBJECTS * 1 / 3)
+ {
+ struct waitqueue_bucket *other_bucket;
+ LIST_FOR_EACH_ENTRY( other_bucket, &waitqueue.buckets, struct waitqueue_bucket, bucket_entry )
+ {
+ if (other_bucket != bucket && other_bucket->objcount &&
+ other_bucket->objcount + bucket->objcount <= MAXIMUM_WAITQUEUE_OBJECTS * 2 / 3)
+ {
+ other_bucket->objcount += bucket->objcount;
+ bucket->objcount = 0;
+
+ /* Update reserved list. */
+ LIST_FOR_EACH_ENTRY( wait, &bucket->reserved, struct threadpool_object, u.wait.wait_entry )
+ {
+ assert( wait->type == TP_OBJECT_TYPE_WAIT );
+ wait->u.wait.bucket = other_bucket;
+ }
+ list_move_tail( &other_bucket->reserved, &bucket->reserved );
+
+ /* Update waiting list. */
+ LIST_FOR_EACH_ENTRY( wait, &bucket->waiting, struct threadpool_object, u.wait.wait_entry )
+ {
+ assert( wait->type == TP_OBJECT_TYPE_WAIT );
+ wait->u.wait.bucket = other_bucket;
+ }
+ list_move_tail( &other_bucket->waiting, &bucket->waiting );
+
+ /* Move bucket to the end, to keep the probability of
+ * newly added wait objects as small as possible. */
+ list_remove( &bucket->bucket_entry );
+ list_add_tail( &waitqueue.buckets, &bucket->bucket_entry );
+
+ NtSetEvent( other_bucket->update_event, NULL );
+ break;
+ }
+ }
+ }
}
/* Remove this bucket from the list. */
--
2.4.4

View File

@ -1,143 +0,0 @@
From 25310b36af718466fc095b6dd9b86ce15d42e0ee Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian@fds-team.de>
Date: Fri, 6 Feb 2015 20:24:27 +0100
Subject: kernel32: Forward threadpool wait functions to ntdll.
---
dlls/kernel32/kernel32.spec | 8 ++++----
dlls/kernel32/thread.c | 43 +++++++++++++++++++++++++++++++++++++++++++
include/winternl.h | 4 ++++
3 files changed, 51 insertions(+), 4 deletions(-)
diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec
index a14d03b..77e55e1 100644
--- a/dlls/kernel32/kernel32.spec
+++ b/dlls/kernel32/kernel32.spec
@@ -233,7 +233,7 @@
@ stdcall CloseThreadpoolCleanupGroupMembers(ptr long ptr) ntdll.TpReleaseCleanupGroupMembers
# @ stub CloseThreadpoolIo
@ stdcall CloseThreadpoolTimer(ptr) ntdll.TpReleaseTimer
-# @ stub CloseThreadpoolWait
+@ stdcall CloseThreadpoolWait(ptr) ntdll.TpReleaseWait
@ stdcall CloseThreadpoolWork(ptr) ntdll.TpReleaseWork
@ stdcall CmdBatNotification(long)
@ stdcall CommConfigDialogA(str long ptr)
@@ -335,7 +335,7 @@
@ stdcall CreateThreadpoolCleanupGroup()
# @ stub CreateThreadpoolIo
@ stdcall CreateThreadpoolTimer(ptr ptr ptr)
-# @ stub CreateThreadpoolWait
+@ stdcall CreateThreadpoolWait(ptr ptr ptr)
@ stdcall CreateThreadpoolWork(ptr ptr ptr)
@ stdcall CreateTimerQueue ()
@ stdcall CreateTimerQueueTimer(ptr long ptr ptr long long long)
@@ -1457,7 +1457,7 @@
@ stdcall SetThreadpoolThreadMaximum(ptr long) ntdll.TpSetPoolMaxThreads
@ stdcall SetThreadpoolThreadMinimum(ptr long) ntdll.TpSetPoolMinThreads
@ stdcall SetThreadpoolTimer(ptr ptr long long)
-# @ stub SetThreadpoolWait
+@ stdcall SetThreadpoolWait(ptr long ptr)
@ stdcall SetTimeZoneInformation(ptr)
@ stub SetTimerQueueTimer
# @ stub -arch=x86_64 SetUmsThreadInformation
@@ -1572,7 +1572,7 @@
@ stdcall WaitForSingleObjectEx(long long long)
# @ stub WaitForThreadpoolIoCallbacks
@ stdcall WaitForThreadpoolTimerCallbacks(ptr long) ntdll.TpWaitForTimer
-# @ stub WaitForThreadpoolWaitCallbacks
+@ stdcall WaitForThreadpoolWaitCallbacks(ptr long) ntdll.TpWaitForWait
@ stdcall WaitForThreadpoolWorkCallbacks(ptr long) ntdll.TpWaitForWork
@ stdcall WaitNamedPipeA (str long)
@ stdcall WaitNamedPipeW (wstr long)
diff --git a/dlls/kernel32/thread.c b/dlls/kernel32/thread.c
index 21ec276..c992e0d 100644
--- a/dlls/kernel32/thread.c
+++ b/dlls/kernel32/thread.c
@@ -942,6 +942,27 @@ PTP_TIMER WINAPI CreateThreadpoolTimer( PTP_TIMER_CALLBACK callback, PVOID userd
}
/***********************************************************************
+ * CreateThreadpoolWait (KERNEL32.@)
+ */
+PTP_WAIT WINAPI CreateThreadpoolWait( PTP_WAIT_CALLBACK callback, PVOID userdata,
+ TP_CALLBACK_ENVIRON *environment )
+{
+ TP_WAIT *wait;
+ NTSTATUS status;
+
+ TRACE( "%p, %p, %p\n", callback, userdata, environment );
+
+ status = TpAllocWait( &wait, callback, userdata, environment );
+ if (status)
+ {
+ SetLastError( RtlNtStatusToDosError(status) );
+ return NULL;
+ }
+
+ return wait;
+}
+
+/***********************************************************************
* CreateThreadpoolWork (KERNEL32.@)
*/
PTP_WORK WINAPI CreateThreadpoolWork( PTP_WORK_CALLBACK callback, PVOID userdata,
@@ -982,6 +1003,28 @@ VOID WINAPI SetThreadpoolTimer( TP_TIMER *timer, FILETIME *due_time,
}
/***********************************************************************
+ * SetThreadpoolWait (KERNEL32.@)
+ */
+VOID WINAPI SetThreadpoolWait( TP_WAIT *wait, HANDLE handle, FILETIME *due_time )
+{
+ LARGE_INTEGER timeout;
+
+ TRACE( "%p, %p, %p\n", wait, handle, due_time );
+
+ if (!handle)
+ {
+ due_time = NULL;
+ }
+ else if (due_time)
+ {
+ timeout.u.LowPart = due_time->dwLowDateTime;
+ timeout.u.HighPart = due_time->dwHighDateTime;
+ }
+
+ TpSetWait( wait, handle, due_time ? &timeout : NULL );
+}
+
+/***********************************************************************
* TrySubmitThreadpoolCallback (KERNEL32.@)
*/
BOOL WINAPI TrySubmitThreadpoolCallback( PTP_SIMPLE_CALLBACK callback, PVOID userdata,
diff --git a/include/winternl.h b/include/winternl.h
index e1707fd..7ab1bd8 100644
--- a/include/winternl.h
+++ b/include/winternl.h
@@ -2621,6 +2621,7 @@ NTSYSAPI NTSTATUS WINAPI RtlLargeIntegerToChar(const ULONGLONG *,ULONG,ULONG,PC
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 TpAllocWait(TP_WAIT **,PTP_WAIT_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 *);
@@ -2635,12 +2636,15 @@ 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 TpReleaseWait(TP_WAIT *);
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 void WINAPI TpSetWait(TP_WAIT *,HANDLE,LARGE_INTEGER *);
NTSYSAPI NTSTATUS WINAPI TpSimpleTryPost(PTP_SIMPLE_CALLBACK,PVOID,TP_CALLBACK_ENVIRON *);
NTSYSAPI void WINAPI TpWaitForTimer(TP_TIMER *,BOOL);
+NTSYSAPI void WINAPI TpWaitForWait(TP_WAIT *,BOOL);
NTSYSAPI void WINAPI TpWaitForWork(TP_WORK *,BOOL);
/* Wine internal functions */
--
2.4.4

View File

@ -1,4 +0,0 @@
# Fixes: [35192] Add implementation for CreateThreadpool
# Fixes: [32531] Implement threadpool work items
# Fixes: [37306] Implement threadpool timers
Fixes: Implement threadpool wait objects

View File

@ -55,7 +55,7 @@ version()
echo "Copyright (C) 2014-2015 the Wine Staging project authors."
echo ""
echo "Patchset to be applied on upstream Wine:"
echo " commit 8b566b1da71a412fe58c3cc1988d610c2aba5f2d"
echo " commit f6653a93eb6796b733c6b0e9e289746692ab0a69"
echo ""
}
@ -175,7 +175,6 @@ patch_enable_all ()
enable_ntdll_ThreadTime="$1"
enable_ntdll_Threading="$1"
enable_ntdll_User_Shared_Data="$1"
enable_ntdll_Vista_Threadpool="$1"
enable_ntdll_WRITECOPY="$1"
enable_ntdll_WinSqm="$1"
enable_ntdll_WriteWatches="$1"
@ -599,9 +598,6 @@ 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"
;;
@ -3689,29 +3685,6 @@ if test "$enable_ntdll_User_Shared_Data" -eq 1; then
) >> "$patchlist"
fi
# Patchset ntdll-Vista_Threadpool
# |
# | Modified files:
# | * dlls/kernel32/kernel32.spec, dlls/kernel32/thread.c, dlls/ntdll/ntdll.spec, dlls/ntdll/tests/threadpool.c,
# | dlls/ntdll/threadpool.c, include/winternl.h
# |
if test "$enable_ntdll_Vista_Threadpool" -eq 1; then
patch_apply ntdll-Vista_Threadpool/0001-ntdll-Implement-TpAllocWait-and-TpReleaseWait.patch
patch_apply ntdll-Vista_Threadpool/0002-ntdll-Implement-threadpool-wait-queues.patch
patch_apply ntdll-Vista_Threadpool/0003-ntdll-tests-Add-basic-tests-for-threadpool-wait-obje.patch
patch_apply ntdll-Vista_Threadpool/0004-ntdll-tests-Add-highly-multithreaded-wait-tests.patch
patch_apply ntdll-Vista_Threadpool/0005-ntdll-Try-to-merge-threadpool-wait-queue-buckets-if-.patch
patch_apply ntdll-Vista_Threadpool/0006-kernel32-Forward-threadpool-wait-functions-to-ntdll.patch
(
echo '+ { "Sebastian Lackner", "ntdll: Implement TpAllocWait and TpReleaseWait.", 1 },';
echo '+ { "Sebastian Lackner", "ntdll: Implement threadpool wait queues.", 1 },';
echo '+ { "Sebastian Lackner", "ntdll/tests: Add basic tests for threadpool wait objects.", 1 },';
echo '+ { "Sebastian Lackner", "ntdll/tests: Add highly multithreaded wait tests.", 1 },';
echo '+ { "Sebastian Lackner", "ntdll: Try to merge threadpool wait queue buckets if possible.", 1 },';
echo '+ { "Sebastian Lackner", "kernel32: Forward threadpool wait functions to ntdll.", 1 },';
) >> "$patchlist"
fi
# Patchset ntdll-WinSqm
# |
# | This patchset fixes the following Wine bugs: