Rebase against 8b566b1da71a412fe58c3cc1988d610c2aba5f2d.

This commit is contained in:
Sebastian Lackner 2015-07-04 04:01:01 +02:00
parent dfd86e0f91
commit cde323b772
14 changed files with 458 additions and 2065 deletions

View File

@ -50,7 +50,7 @@ for more details.*
* Add HTTP Host header in HttpSendRequest instead of HttpOpenRequest ([Wine Bug #28911](https://bugs.winehq.org/show_bug.cgi?id=28911))
* Add a ProfileList\<UserSID> registry subkey ([Wine Bug #15670](https://bugs.winehq.org/show_bug.cgi?id=15670))
* Add implementation for CreateThreadpool ([Wine Bug #35192](https://bugs.winehq.org/show_bug.cgi?id=35192))
* ~~Add implementation for CreateThreadpool~~ ([Wine Bug #35192](https://bugs.winehq.org/show_bug.cgi?id=35192))
* ~~Add implementation for kernel32.GetNumaProcessorNode~~ ([Wine Bug #38660](https://bugs.winehq.org/show_bug.cgi?id=38660))
* Add implementation for mfplat.MFTRegister ([Wine Bug #37811](https://bugs.winehq.org/show_bug.cgi?id=37811))
* Add implementation for shlwapi.AssocGetPerceivedType
@ -186,9 +186,9 @@ for more details.*
* Implement stub for ntoskrnl.KeDelayExecutionThread.
* 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 timers~~ ([Wine Bug #37306](https://bugs.winehq.org/show_bug.cgi?id=37306))
* Implement threadpool wait objects
* Implement threadpool work items ([Wine Bug #32531](https://bugs.winehq.org/show_bug.cgi?id=32531))
* ~~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))
* Improve stub for ID3DXEffectImpl_CloneEffect

View File

@ -1,175 +0,0 @@
From 1651eab61e449fef2119ff35703d6c9acc43f2b3 Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian@fds-team.de>
Date: Thu, 2 Jul 2015 17:15:41 +0200
Subject: ntdll: Implement TpDisassociateCallback and add separate group
finished event.
---
dlls/ntdll/ntdll.spec | 1 +
dlls/ntdll/threadpool.c | 67 ++++++++++++++++++++++++++++++++++++++++++++-----
2 files changed, 62 insertions(+), 6 deletions(-)
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec
index 134a3f7..ee90705 100644
--- a/dlls/ntdll/ntdll.spec
+++ b/dlls/ntdll/ntdll.spec
@@ -979,6 +979,7 @@
@ stdcall TpCallbackReleaseSemaphoreOnCompletion(ptr long long)
@ stdcall TpCallbackSetEventOnCompletion(ptr long)
@ stdcall TpCallbackUnloadDllOnCompletion(ptr ptr)
+@ stdcall TpDisassociateCallback(ptr)
@ stdcall TpPostWork(ptr)
@ stdcall TpReleaseCleanupGroup(ptr)
@ stdcall TpReleaseCleanupGroupMembers(ptr long ptr)
diff --git a/dlls/ntdll/threadpool.c b/dlls/ntdll/threadpool.c
index eeb275f..0e09d0f 100644
--- a/dlls/ntdll/threadpool.c
+++ b/dlls/ntdll/threadpool.c
@@ -181,8 +181,10 @@ struct threadpool_object
/* information about the pool, locked via .pool->cs */
struct list pool_entry;
RTL_CONDITION_VARIABLE finished_event;
+ RTL_CONDITION_VARIABLE group_finished_event;
LONG num_pending_callbacks;
LONG num_running_callbacks;
+ LONG num_associated_callbacks;
/* arguments for callback */
union
{
@@ -202,6 +204,7 @@ struct threadpool_instance
{
struct threadpool_object *object;
DWORD threadid;
+ BOOL associated;
BOOL may_run_long;
struct
{
@@ -1406,8 +1409,10 @@ static void tp_object_initialize( struct threadpool_object *object, struct threa
memset( &object->pool_entry, 0, sizeof(object->pool_entry) );
RtlInitializeConditionVariable( &object->finished_event );
+ RtlInitializeConditionVariable( &object->group_finished_event );
object->num_pending_callbacks = 0;
object->num_running_callbacks = 0;
+ object->num_associated_callbacks = 0;
if (environment)
{
@@ -1540,13 +1545,21 @@ static void tp_object_cancel( struct threadpool_object *object, BOOL group_cance
* Waits until all pending and running callbacks of a specific object
* have been processed.
*/
-static void tp_object_wait( struct threadpool_object *object )
+static void tp_object_wait( struct threadpool_object *object, BOOL group_wait )
{
struct threadpool *pool = object->pool;
RtlEnterCriticalSection( &pool->cs );
- while (object->num_pending_callbacks || object->num_running_callbacks)
- RtlSleepConditionVariableCS( &object->finished_event, &pool->cs, NULL );
+ if (group_wait)
+ {
+ while (object->num_pending_callbacks || object->num_running_callbacks)
+ RtlSleepConditionVariableCS( &object->group_finished_event, &pool->cs, NULL );
+ }
+ else
+ {
+ while (object->num_pending_callbacks || object->num_associated_callbacks)
+ RtlSleepConditionVariableCS( &object->finished_event, &pool->cs, NULL );
+ }
RtlLeaveCriticalSection( &pool->cs );
}
@@ -1631,6 +1644,7 @@ static void CALLBACK threadpool_worker_proc( void *param )
list_add_tail( &pool->pool, &object->pool_entry );
/* Leave critical section and do the actual callback. */
+ object->num_associated_callbacks++;
object->num_running_callbacks++;
pool->num_busy_workers++;
RtlLeaveCriticalSection( &pool->cs );
@@ -1639,6 +1653,7 @@ static void CALLBACK threadpool_worker_proc( void *param )
callback_instance = (TP_CALLBACK_INSTANCE *)&instance;
instance.object = object;
instance.threadid = GetCurrentThreadId();
+ instance.associated = TRUE;
instance.may_run_long = object->may_run_long;
instance.cleanup.critical_section = NULL;
instance.cleanup.mutex = NULL;
@@ -1709,9 +1724,18 @@ static void CALLBACK threadpool_worker_proc( void *param )
skip_cleanup:
RtlEnterCriticalSection( &pool->cs );
pool->num_busy_workers--;
+
object->num_running_callbacks--;
if (!object->num_pending_callbacks && !object->num_running_callbacks)
- RtlWakeAllConditionVariable( &object->finished_event );
+ RtlWakeAllConditionVariable( &object->group_finished_event );
+
+ if (instance.associated)
+ {
+ object->num_associated_callbacks--;
+ if (!object->num_pending_callbacks && !object->num_associated_callbacks)
+ RtlWakeAllConditionVariable( &object->finished_event );
+ }
+
tp_object_release( object );
}
@@ -1913,6 +1937,37 @@ VOID WINAPI TpCallbackUnloadDllOnCompletion( TP_CALLBACK_INSTANCE *instance, HMO
}
/***********************************************************************
+ * TpDisassociateCallback (NTDLL.@)
+ */
+VOID WINAPI TpDisassociateCallback( TP_CALLBACK_INSTANCE *instance )
+{
+ struct threadpool_instance *this = impl_from_TP_CALLBACK_INSTANCE( instance );
+ struct threadpool_object *object = this->object;
+ struct threadpool *pool;
+
+ TRACE( "%p\n", instance );
+
+ if (this->threadid != GetCurrentThreadId())
+ {
+ ERR("called from wrong thread, ignoring\n");
+ return;
+ }
+
+ if (!this->associated)
+ return;
+
+ pool = object->pool;
+ RtlEnterCriticalSection( &pool->cs );
+
+ object->num_associated_callbacks--;
+ if (!object->num_pending_callbacks && !object->num_associated_callbacks)
+ RtlWakeAllConditionVariable( &object->finished_event );
+
+ RtlLeaveCriticalSection( &pool->cs );
+ this->associated = FALSE;
+}
+
+/***********************************************************************
* TpPostWork (NTDLL.@)
*/
VOID WINAPI TpPostWork( TP_WORK *work )
@@ -1993,7 +2048,7 @@ VOID WINAPI TpReleaseCleanupGroupMembers( TP_CLEANUP_GROUP *group, BOOL cancel_p
/* Wait for remaining callbacks to finish */
LIST_FOR_EACH_ENTRY_SAFE( object, next, &members, struct threadpool_object, group_entry )
{
- tp_object_wait( object );
+ tp_object_wait( object, TRUE );
tp_object_release( object );
}
}
@@ -2115,5 +2170,5 @@ VOID WINAPI TpWaitForWork( TP_WORK *work, BOOL cancel_pending )
if (cancel_pending)
tp_object_cancel( this, FALSE, NULL );
- tp_object_wait( this );
+ tp_object_wait( this, FALSE );
}
--
2.4.4

View File

@ -1,55 +1,43 @@
From 5dd5ac0fa0ef0845cc8611ce664e48e8b25f290b Mon Sep 17 00:00:00 2001
From 102fb5ddc55779ac9c95937faef0cc063774612f Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian@fds-team.de>
Date: Wed, 4 Mar 2015 13:33:25 +0100
Subject: ntdll: Implement threadpool wait objects.
---
dlls/ntdll/ntdll.spec | 8 +-
dlls/ntdll/threadpool.c | 472 +++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 469 insertions(+), 11 deletions(-)
dlls/ntdll/ntdll.spec | 4 +
dlls/ntdll/threadpool.c | 474 +++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 471 insertions(+), 7 deletions(-)
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec
index 0f4374e..ffcf58d 100644
index ce9d1bb..75dc647 100644
--- a/dlls/ntdll/ntdll.spec
+++ b/dlls/ntdll/ntdll.spec
@@ -977,7 +977,7 @@
# @ stub TpAllocJobNotification
@@ -973,6 +973,7 @@
@ stdcall TpAllocCleanupGroup(ptr)
@ stdcall TpAllocPool(ptr ptr)
@ stdcall TpAllocTimer(ptr ptr ptr)
-# @ stub TpAllocWait
@ stdcall TpAllocTimer(ptr ptr ptr ptr)
+@ stdcall TpAllocWait(ptr ptr ptr ptr)
@ stdcall TpAllocWork(ptr ptr ptr ptr)
# @ stub TpAlpcRegisterCompletionList
# @ stub TpAlpcUnregisterCompletionList
@@ -1008,7 +1008,7 @@
# @ stub TpReleaseJobNotification
@ stdcall TpCallbackLeaveCriticalSectionOnCompletion(ptr ptr)
@ stdcall TpCallbackMayRunLong(ptr)
@@ -987,12 +988,15 @@
@ stdcall TpReleaseCleanupGroupMembers(ptr long ptr)
@ stdcall TpReleasePool(ptr)
@ stdcall TpReleaseTimer(ptr)
-# @ stub TpReleaseWait
+@ stdcall TpReleaseWait(ptr)
@ stdcall TpReleaseWork(ptr)
# @ stub TpSetDefaultPoolMaxThreads
# @ stub TpSetDefaultPoolStackInformation
@@ -1020,7 +1020,7 @@
# @ stub TpSetPoolWorkerThreadIdleTimeout
@ stdcall TpSetPoolMaxThreads(ptr long)
@ stdcall TpSetPoolMinThreads(ptr long)
@ stdcall TpSetTimer(ptr ptr long long)
# @ stub TpSetTimerEx
-# @ stub TpSetWait
+@ stdcall TpSetWait(ptr long ptr)
# @ stub TpSetWaitEx
@ stdcall TpSimpleTryPost(ptr ptr ptr)
# @ stub TpStartAsyncIoOperation
@@ -1030,7 +1030,7 @@
# @ stub TpWaitForIoCompletion
# @ stub TpWaitForJobNotification
@ stdcall TpWaitForTimer(ptr long)
-# @ stub TpWaitForWait
+@ 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 c44988c..b8d761b 100644
index ef502ba..773c3d6 100644
--- a/dlls/ntdll/threadpool.c
+++ b/dlls/ntdll/threadpool.c
@@ -137,6 +137,7 @@ struct timer_queue
@ -89,18 +77,9 @@ index c44988c..b8d761b 100644
};
@@ -267,6 +280,38 @@ static RTL_CRITICAL_SECTION_DEBUG timerqueue_debug =
0, 0, { (DWORD_PTR)(__FILE__ ": timerqueue.cs") }
0, 0, { (DWORD_PTR)(__FILE__ ": timerqueue.cs") }
};
+struct waitqueue_bucket
+{
+ struct list bucket_entry;
+ LONG num_waits;
+ struct list reserved;
+ struct list waits;
+ HANDLE update_event;
+};
+
+/* global waitqueue object */
+static RTL_CRITICAL_SECTION_DEBUG waitqueue_debug;
+
@ -112,16 +91,25 @@ index c44988c..b8d761b 100644
+}
+waitqueue =
+{
+ { &waitqueue_debug, -1, 0, 0, 0, 0 },
+ 0,
+ LIST_INIT( waitqueue.buckets )
+ { &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") }
+ 0, 0, { (DWORD_PTR)(__FILE__ ": waitqueue.cs") }
+};
+
+struct waitqueue_bucket
+{
+ struct list bucket_entry;
+ LONG objcount;
+ struct list reserved;
+ struct list waits;
+ HANDLE update_event;
+};
+
static inline struct threadpool *impl_from_TP_POOL( TP_POOL *pool )
@ -141,39 +129,182 @@ index c44988c..b8d761b 100644
static inline struct threadpool_group *impl_from_TP_CLEANUP_GROUP( TP_CLEANUP_GROUP *group )
{
return (struct threadpool_group *)group;
@@ -298,7 +350,8 @@ static inline struct threadpool_instance *impl_from_TP_CALLBACK_INSTANCE( TP_CAL
@@ -297,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 CALLBACK timerqueue_thread_proc( void *param );
-static void tp_object_submit( struct threadpool_object *object );
+static void CALLBACK waitqueue_thread_proc( void *param );
+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;
@@ -1352,7 +1405,7 @@ update_timer:
RtlLeaveCriticalSection( &timerqueue.cs );
if (submit_timer)
- tp_object_submit( new_timer );
+ tp_object_submit( new_timer, FALSE );
}
static void CALLBACK timerqueue_thread_proc( void *param )
@@ -1379,7 +1432,7 @@ static void CALLBACK timerqueue_thread_proc( void *param )
/* Queue a new callback in one of the worker threads */
@@ -1249,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 );
/* Requeue the timer, except its marked for shutdown */
if (!timer->shutdown && timer->u.timer.period)
@@ -1445,6 +1498,301 @@ static void CALLBACK timerqueue_thread_proc( void *param )
RtlLeaveCriticalSection( &timerqueue.cs );
/* Insert the timer back into the queue, except its marked for shutdown. */
if (timer->u.timer.period && !timer->shutdown)
@@ -1386,6 +1438,255 @@ static void tp_timerqueue_unlock( struct threadpool_object *timer )
}
+static NTSTATUS tp_waitqueue_acquire( struct threadpool_object *wait )
/***********************************************************************
+ * 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->waits, 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 a wait queue update event or until an event is triggered */
+ 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 fulfilled. */
+ 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 );
+ }
+
+ /* Try to merge with other threads. */
+ if (waitqueue.num_buckets > 1 && bucket->objcount &&
+ bucket->objcount < MAXIMUM_WAITQUEUE_OBJECTS / 2)
+ {
+ 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)
+ {
+ 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 wait list. */
+ LIST_FOR_EACH_ENTRY( wait, &bucket->waits, 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->waits, &bucket->waits );
+
+ /* Move bucket to the end to keep 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. */
+ 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->waits ) );
+
+ 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;
@ -183,25 +314,26 @@ index c44988c..b8d761b 100644
+ wait->u.wait.signaled = 0;
+ wait->u.wait.bucket = NULL;
+ wait->u.wait.wait_pending = FALSE;
+ memset( &wait->u.wait.wait_entry, 0, sizeof(wait->u.wait.wait_entry) );
+ 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->num_waits < MAXIMUM_WAITQUEUE_OBJECTS)
+ if (bucket->objcount < MAXIMUM_WAITQUEUE_OBJECTS)
+ {
+ bucket->num_waits++;
+ wait->u.wait.bucket = bucket;
+ 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)
+ {
@ -209,11 +341,12 @@ index c44988c..b8d761b 100644
+ goto out;
+ }
+
+ bucket->num_waits = 1;
+ bucket->objcount = 0;
+ list_init( &bucket->reserved );
+ list_init( &bucket->waits );
+
+ status = NtCreateEvent( &bucket->update_event, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE );
+ status = NtCreateEvent( &bucket->update_event, EVENT_ALL_ACCESS,
+ NULL, SynchronizationEvent, FALSE );
+ if (status)
+ {
+ RtlFreeHeap( GetProcessHeap(), 0, bucket );
@ -224,11 +357,12 @@ index c44988c..b8d761b 100644
+ waitqueue_thread_proc, bucket, &thread, NULL );
+ if (status == STATUS_SUCCESS)
+ {
+ waitqueue.num_buckets++;
+ list_add_tail( &waitqueue.buckets, &bucket->bucket_entry );
+ waitqueue.num_buckets++;
+
+ wait->u.wait.bucket = bucket;
+ list_add_tail( &bucket->reserved, &wait->u.wait.wait_entry );
+ wait->u.wait.bucket = bucket;
+ bucket->objcount++;
+
+ NtClose( thread );
+ }
@ -243,235 +377,33 @@ index c44988c..b8d761b 100644
+ return status;
+}
+
+/* Decrement the refcount of a timer queue. */
+static void tp_waitqueue_release( struct threadpool_object *wait )
+/***********************************************************************
+ * tp_waitqueue_unlock (internal)
+ */
+static void tp_waitqueue_unlock( struct threadpool_object *wait )
+{
+ assert( wait->type == TP_OBJECT_TYPE_WAIT );
+ RtlEnterCriticalSection( &waitqueue.cs );
+
+ RtlEnterCriticalSection( &waitqueue.cs );
+ if (wait->u.wait.bucket)
+ {
+ struct waitqueue_bucket *bucket = wait->u.wait.bucket;
+ assert( bucket->num_waits > 0 );
+ assert( bucket->objcount > 0 );
+
+ bucket->num_waits--;
+ list_remove( &wait->u.wait.wait_entry );
+ NtSetEvent( bucket->update_event, NULL );
+
+ wait->u.wait.bucket = NULL;
+ }
+
+ RtlLeaveCriticalSection( &waitqueue.cs );
+}
+
+static void tp_waitqueue_update_wait( struct threadpool_object *new_wait, HANDLE handle, LARGE_INTEGER *timeout )
+{
+ BOOL submit_wait = FALSE;
+
+ assert( new_wait->type == TP_OBJECT_TYPE_WAIT );
+ RtlEnterCriticalSection( &waitqueue.cs );
+ assert( new_wait->u.wait.bucket );
+
+ /* update wait handle */
+ new_wait->u.wait.handle = handle;
+
+ /* for performance reasons we only wake up when something has changed */
+ if (handle || new_wait->u.wait.wait_pending)
+ {
+ struct waitqueue_bucket *bucket = new_wait->u.wait.bucket;
+ list_remove( &new_wait->u.wait.wait_entry );
+
+ if (handle)
+ {
+ ULONGLONG when = TIMEOUT_INFINITE;
+
+ if (timeout)
+ {
+ when = timeout->QuadPart;
+
+ /* A timeout of zero means that the wait should be submitted immediately */
+ if (when == 0)
+ {
+ submit_wait = TRUE;
+ goto remove_wait;
+ }
+
+ /* Convert relative timeout to absolute */
+ if ((LONGLONG)when < 0)
+ {
+ LARGE_INTEGER now;
+ NtQuerySystemTime( &now );
+ when = now.QuadPart - when;
+ }
+ }
+
+ list_add_tail( &bucket->waits, &new_wait->u.wait.wait_entry );
+ new_wait->u.wait.wait_pending = TRUE;
+ new_wait->u.wait.timeout = when;
+ }
+ else
+ {
+remove_wait:
+ list_add_tail( &bucket->reserved, &new_wait->u.wait.wait_entry );
+ new_wait->u.wait.wait_pending = FALSE;
+ }
+ bucket->objcount--;
+
+ NtSetEvent( bucket->update_event, NULL );
+ }
+
+ RtlLeaveCriticalSection( &waitqueue.cs );
+
+ if (submit_wait)
+ tp_object_submit( new_wait, FALSE );
+}
+
+static void CALLBACK waitqueue_thread_proc( void *param )
+{
+ HANDLE handles[MAXIMUM_WAITQUEUE_OBJECTS + 1];
+ struct threadpool_object *objects[MAXIMUM_WAITQUEUE_OBJECTS];
+ struct waitqueue_bucket *bucket = param;
+ LARGE_INTEGER now, timeout;
+ struct threadpool_object *wait, *next;
+ DWORD num_handles;
+ NTSTATUS status;
+
+ RtlEnterCriticalSection( &waitqueue.cs );
+
+ for (;;)
+ {
+ NtQuerySystemTime( &now );
+ timeout.QuadPart = TIMEOUT_INFINITE;
+ num_handles = 0;
+
+ LIST_FOR_EACH_ENTRY_SAFE( wait, next, &bucket->waits, struct threadpool_object, u.wait.wait_entry )
+ {
+ assert( wait->type == TP_OBJECT_TYPE_WAIT );
+
+ /* Timeout expired or object was signaled */
+ if (wait->u.wait.timeout <= now.QuadPart)
+ {
+ 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;
+
+ /* We will have to wait for this object - keep a reference to make sure it 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->num_waits)
+ {
+ assert( num_handles == 0 );
+
+ /* All wait objects have been destroyed, if there are no new wait objects within some
+ * amount of time, then we can shutdown this thread. */
+ 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->num_waits)
+ break;
+ }
+ else
+ {
+ handles[num_handles] = bucket->update_event;
+
+ /* Wait for a wait queue update event or until an event is triggered */
+ 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)
+ {
+ 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
+ FIXME("Wait object triggered while object was destroyed, race-condition.\n");
+ }
+
+ while (num_handles)
+ {
+ wait = objects[--num_handles];
+ assert( wait->type == TP_OBJECT_TYPE_WAIT );
+ tp_object_release( wait );
+ }
+ }
+
+ /* Try to merge with other buckets */
+ if (waitqueue.num_buckets > 1 && bucket->num_waits && bucket->num_waits < MAXIMUM_WAITQUEUE_OBJECTS / 2)
+ {
+ struct waitqueue_bucket *other_bucket;
+ LIST_FOR_EACH_ENTRY( other_bucket, &waitqueue.buckets, struct waitqueue_bucket, bucket_entry )
+ {
+ if (other_bucket != bucket && other_bucket->num_waits &&
+ other_bucket->num_waits + bucket->num_waits <= MAXIMUM_WAITQUEUE_OBJECTS)
+ {
+ other_bucket->num_waits += bucket->num_waits;
+ bucket->num_waits = 0;
+
+ 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 );
+
+ LIST_FOR_EACH_ENTRY( wait, &bucket->waits, 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->waits, &bucket->waits );
+
+ /* we will not terminate immediately, but instead after a timeout. Make sure that this
+ * bucket appears as the last one in the list, otherwise there is a high risk that
+ * elements will be added again. */
+ list_remove( &bucket->bucket_entry );
+ list_add_tail( &waitqueue.buckets, &bucket->bucket_entry );
+
+ NtSetEvent( other_bucket->update_event, NULL );
+ break;
+ }
+ }
+ }
+ }
+
+ waitqueue.num_buckets--;
+ list_remove( &bucket->bucket_entry );
+ if (!waitqueue.num_buckets)
+ assert( list_empty( &waitqueue.buckets ) );
+ RtlLeaveCriticalSection( &waitqueue.cs );
+
+ assert( bucket->num_waits == 0 );
+ assert( list_empty( &bucket->reserved ) );
+ assert( list_empty( &bucket->waits ) );
+
+ NtClose( bucket->update_event );
+ RtlFreeHeap( GetProcessHeap(), 0, bucket );
+}
+
/***********************************************************************
+/***********************************************************************
* tp_threadpool_alloc (internal)
*
@@ -1714,7 +2062,7 @@ static void tp_object_initialize( struct threadpool_object *object, struct threa
* Allocates a new threadpool object.
@@ -1654,7 +1955,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)
@ -480,7 +412,7 @@ index c44988c..b8d761b 100644
if (object->group)
{
@@ -1740,7 +2088,7 @@ static void tp_object_initialize( struct threadpool_object *object, struct threa
@@ -1680,7 +1981,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.
*/
@ -489,37 +421,37 @@ index c44988c..b8d761b 100644
{
struct threadpool *pool = object->pool;
NTSTATUS status = STATUS_UNSUCCESSFUL;
@@ -1770,6 +2118,10 @@ static void tp_object_submit( struct threadpool_object *object )
@@ -1710,6 +2011,10 @@ static void tp_object_submit( struct threadpool_object *object )
if (!object->num_pending_callbacks++)
list_add_tail( &pool->pool, &object->pool_entry );
+ /* increment success counter by one */
+ /* Remember how often the wait was fulfilled. */
+ 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)
{
@@ -1797,6 +2149,9 @@ static void tp_object_cancel( struct threadpool_object *object, BOOL group_cance
@@ -1736,6 +2041,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;
+ if (object->type == TP_OBJECT_TYPE_WAIT)
+ object->u.wait.signaled = 0;
}
RtlLeaveCriticalSection( &pool->cs );
/* Execute group cancellation callback if defined, and if this was actually a group cancel. */
@@ -1845,6 +2200,8 @@ static void tp_object_shutdown( struct threadpool_object *object )
@@ -1785,6 +2093,8 @@ static void tp_object_shutdown( struct threadpool_object *object )
{
if (object->type == TP_OBJECT_TYPE_TIMER)
tp_timerqueue_release( object );
tp_timerqueue_unlock( object );
+ else if (object->type == TP_OBJECT_TYPE_WAIT)
+ tp_waitqueue_release( object );
+ tp_waitqueue_unlock( object );
object->shutdown = TRUE;
}
@@ -1898,6 +2255,7 @@ static void CALLBACK threadpool_worker_proc( void *param )
@@ -1839,6 +2149,7 @@ static void CALLBACK threadpool_worker_proc( void *param )
TP_CALLBACK_INSTANCE *callback_instance;
struct threadpool_instance instance;
struct threadpool *pool = param;
@ -527,26 +459,21 @@ index c44988c..b8d761b 100644
LARGE_INTEGER timeout;
struct list *ptr;
NTSTATUS status;
@@ -1918,6 +2276,18 @@ static void CALLBACK threadpool_worker_proc( void *param )
@@ -1859,6 +2170,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 object was signaled or if this
+ * is a timeout. */
+ /* For wait objects determine if the operation was successful. */
+ if (object->type == TP_OBJECT_TYPE_WAIT)
+ {
+ if (object->u.wait.signaled > 0)
+ {
+ wait_result = WAIT_OBJECT_0;
+ object->u.wait.signaled--;
+ }
+ else wait_result = WAIT_TIMEOUT;
+ 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++;
@@ -1966,6 +2336,15 @@ static void CALLBACK threadpool_worker_proc( void *param )
@@ -1907,6 +2225,15 @@ static void CALLBACK threadpool_worker_proc( void *param )
break;
}
@ -562,7 +489,7 @@ index c44988c..b8d761b 100644
default:
assert(0);
break;
@@ -2112,6 +2491,46 @@ NTSTATUS WINAPI TpAllocTimer( TP_TIMER **out, PTP_TIMER_CALLBACK callback, PVOID
@@ -2052,6 +2379,46 @@ NTSTATUS WINAPI TpAllocTimer( TP_TIMER **out, PTP_TIMER_CALLBACK callback, PVOID
}
/***********************************************************************
@ -591,7 +518,7 @@ index c44988c..b8d761b 100644
+ object->type = TP_OBJECT_TYPE_WAIT;
+ object->u.wait.callback = callback;
+
+ status = tp_waitqueue_acquire( object );
+ status = tp_waitqueue_lock( object );
+ if (status)
+ {
+ tp_threadpool_unlock( pool );
@ -609,7 +536,7 @@ index c44988c..b8d761b 100644
* TpAllocWork (NTDLL.@)
*/
NTSTATUS WINAPI TpAllocWork( TP_WORK **out, PTP_WORK_CALLBACK callback, PVOID userdata,
@@ -2311,7 +2730,7 @@ VOID WINAPI TpPostWork( TP_WORK *work )
@@ -2252,7 +2619,7 @@ VOID WINAPI TpPostWork( TP_WORK *work )
TRACE( "%p\n", work );
@ -618,7 +545,7 @@ index c44988c..b8d761b 100644
}
/***********************************************************************
@@ -2415,6 +2834,19 @@ VOID WINAPI TpReleaseTimer( TP_TIMER *timer )
@@ -2356,6 +2723,19 @@ VOID WINAPI TpReleaseTimer( TP_TIMER *timer )
}
/***********************************************************************
@ -638,26 +565,82 @@ index c44988c..b8d761b 100644
* TpReleaseWork (NTDLL.@)
*/
VOID WINAPI TpReleaseWork( TP_WORK *work )
@@ -2490,6 +2922,18 @@ VOID WINAPI TpSetTimer( TP_TIMER *timer, LARGE_INTEGER *timeout, LONG period, LO
}
@@ -2493,7 +2873,73 @@ 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 );
+ BOOL submit_wait = FALSE;
+
+ TRACE( "%p %p %p\n", wait, handle, timeout );
+
+ tp_waitqueue_update_wait( this, handle, timeout );
+}
+ RtlEnterCriticalSection( &waitqueue.cs );
+ assert( this->u.wait.bucket );
+
+/***********************************************************************
* TpSimpleTryPost (NTDLL.@)
*/
NTSTATUS WINAPI TpSimpleTryPost( PTP_SIMPLE_CALLBACK callback, PVOID userdata,
@@ -2534,6 +2978,20 @@ VOID WINAPI TpWaitForTimer( TP_TIMER *timer, BOOL cancel_pending )
+ /* update wait handle */
+ this->u.wait.handle = handle;
+
+ /* for performance reasons we only wake up when something has changed */
+ if (handle || this->u.wait.wait_pending)
+ {
+ struct waitqueue_bucket *bucket = this->u.wait.bucket;
+ list_remove( &this->u.wait.wait_entry );
+
+ if (handle)
+ {
+ ULONGLONG when = TIMEOUT_INFINITE;
+
+ if (timeout)
+ {
+ when = timeout->QuadPart;
+
+ /* A timeout of zero means that the wait should be submitted immediately */
+ if (when == 0)
+ {
+ submit_wait = TRUE;
+ goto remove_wait;
+ }
+
+ /* Convert relative timeout to absolute */
+ if ((LONGLONG)when < 0)
+ {
+ LARGE_INTEGER now;
+ NtQuerySystemTime( &now );
+ when = now.QuadPart - when;
+ }
+ }
+
+ list_add_tail( &bucket->waits, &this->u.wait.wait_entry );
+ this->u.wait.wait_pending = TRUE;
+ this->u.wait.timeout = when;
+ }
+ else
+ {
+remove_wait:
+ list_add_tail( &bucket->reserved, &this->u.wait.wait_entry );
+ this->u.wait.wait_pending = FALSE;
+ }
+
+ NtSetEvent( bucket->update_event, NULL );
+ }
+
+ RtlLeaveCriticalSection( &waitqueue.cs );
+
+ if (submit_wait)
+ tp_object_submit( this, FALSE );
}
/***********************************************************************
@@ -2541,6 +2987,20 @@ VOID WINAPI TpWaitForTimer( TP_TIMER *timer, BOOL cancel_pending )
}
/***********************************************************************

View File

@ -1,191 +0,0 @@
From 8041e8ccf5ed9021525a95b7c6bcedfa7d945cb4 Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian@fds-team.de>
Date: Thu, 2 Jul 2015 17:18:00 +0200
Subject: ntdll/tests: Add tests for TpDisassociateCallback.
---
dlls/ntdll/tests/threadpool.c | 149 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 149 insertions(+)
diff --git a/dlls/ntdll/tests/threadpool.c b/dlls/ntdll/tests/threadpool.c
index 62bd064..a75b622 100644
--- a/dlls/ntdll/tests/threadpool.c
+++ b/dlls/ntdll/tests/threadpool.c
@@ -26,6 +26,7 @@ static NTSTATUS (WINAPI *pTpAllocPool)(TP_POOL **,PVOID);
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 VOID (WINAPI *pTpPostWork)(TP_WORK *);
static VOID (WINAPI *pTpReleaseCleanupGroup)(TP_CLEANUP_GROUP *);
static VOID (WINAPI *pTpReleaseCleanupGroupMembers)(TP_CLEANUP_GROUP *,BOOL,PVOID);
@@ -57,6 +58,7 @@ static BOOL init_threadpool(void)
NTDLL_GET_PROC(TpAllocWork);
NTDLL_GET_PROC(TpCallbackMayRunLong);
NTDLL_GET_PROC(TpCallbackReleaseSemaphoreOnCompletion);
+ NTDLL_GET_PROC(TpDisassociateCallback);
NTDLL_GET_PROC(TpPostWork);
NTDLL_GET_PROC(TpReleaseCleanupGroup);
NTDLL_GET_PROC(TpReleaseCleanupGroupMembers);
@@ -498,6 +500,152 @@ static void test_tp_instance(void)
CloseHandle(semaphores[1]);
}
+static void CALLBACK disassociate_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, TP_WORK *work)
+{
+ HANDLE *semaphores = userdata;
+ DWORD result;
+
+ trace("Running disassociate callback\n");
+
+ pTpDisassociateCallback(instance);
+ result = WaitForSingleObject(semaphores[0], 1000);
+ ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
+ ReleaseSemaphore(semaphores[1], 1, NULL);
+}
+
+static void CALLBACK disassociate2_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, TP_WORK *work)
+{
+ HANDLE *semaphores = userdata;
+ DWORD result;
+
+ trace("Running disassociate2 callback\n");
+
+ pTpDisassociateCallback(instance);
+ result = WaitForSingleObject(semaphores[0], 100);
+ ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
+ ReleaseSemaphore(semaphores[1], 1, NULL);
+}
+
+static void CALLBACK disassociate3_cb(TP_CALLBACK_INSTANCE *instance, void *userdata)
+{
+ HANDLE *semaphores = userdata;
+ DWORD result;
+
+ trace("Running disassociate3 callback\n");
+
+ pTpDisassociateCallback(instance);
+ result = WaitForSingleObject(semaphores[0], 100);
+ ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
+ ReleaseSemaphore(semaphores[1], 1, NULL);
+}
+
+static void test_tp_disassociate(void)
+{
+ TP_CALLBACK_ENVIRON environment;
+ TP_CLEANUP_GROUP *group;
+ HANDLE semaphores[2];
+ NTSTATUS status;
+ TP_POOL *pool;
+ TP_WORK *work;
+ DWORD result;
+
+ semaphores[0] = CreateSemaphoreW(NULL, 0, 1, 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");
+
+ /* allocate new threadpool and cleanup group */
+ pool = NULL;
+ status = pTpAllocPool(&pool, NULL);
+ ok(!status, "TpAllocPool failed with status %x\n", status);
+ ok(pool != NULL, "expected pool != NULL\n");
+
+ group = NULL;
+ status = pTpAllocCleanupGroup(&group);
+ ok(!status, "TpAllocCleanupGroup failed with status %x\n", status);
+ ok(group != NULL, "expected pool != NULL\n");
+
+ /* test TpDisassociateCallback on work objects without group */
+ work = NULL;
+ memset(&environment, 0, sizeof(environment));
+ environment.Version = 1;
+ environment.Pool = pool;
+ status = pTpAllocWork(&work, disassociate_cb, semaphores, &environment);
+ ok(!status, "TpAllocWork failed with status %x\n", status);
+ ok(work != NULL, "expected work != NULL\n");
+
+ pTpPostWork(work);
+ pTpWaitForWork(work, FALSE);
+
+ result = WaitForSingleObject(semaphores[1], 100);
+ ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
+ ReleaseSemaphore(semaphores[0], 1, NULL);
+ result = WaitForSingleObject(semaphores[1], 1000);
+ ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
+ pTpReleaseWork(work);
+
+ /* test TpDisassociateCallback on work objects with group (1) */
+ work = NULL;
+ memset(&environment, 0, sizeof(environment));
+ environment.Version = 1;
+ environment.Pool = pool;
+ environment.CleanupGroup = group;
+ status = pTpAllocWork(&work, disassociate_cb, semaphores, &environment);
+ ok(!status, "TpAllocWork failed with status %x\n", status);
+ ok(work != NULL, "expected work != NULL\n");
+
+ pTpPostWork(work);
+ pTpWaitForWork(work, FALSE);
+
+ result = WaitForSingleObject(semaphores[1], 100);
+ ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
+ ReleaseSemaphore(semaphores[0], 1, NULL);
+ result = WaitForSingleObject(semaphores[1], 1000);
+ ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
+ pTpReleaseCleanupGroupMembers(group, FALSE, NULL);
+
+ /* test TpDisassociateCallback on work objects with group (2) */
+ work = NULL;
+ memset(&environment, 0, sizeof(environment));
+ environment.Version = 1;
+ environment.Pool = pool;
+ environment.CleanupGroup = group;
+ status = pTpAllocWork(&work, disassociate2_cb, semaphores, &environment);
+ ok(!status, "TpAllocWork failed with status %x\n", status);
+ ok(work != NULL, "expected work != NULL\n");
+
+ pTpPostWork(work);
+ pTpReleaseCleanupGroupMembers(group, FALSE, NULL);
+
+ ReleaseSemaphore(semaphores[0], 1, NULL);
+ result = WaitForSingleObject(semaphores[1], 1000);
+ ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
+ result = WaitForSingleObject(semaphores[0], 1000);
+ ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
+
+ /* test TpDisassociateCallback on simple callbacks */
+ memset(&environment, 0, sizeof(environment));
+ environment.Version = 1;
+ environment.Pool = pool;
+ environment.CleanupGroup = group;
+ status = pTpSimpleTryPost(disassociate3_cb, semaphores, &environment);
+ ok(!status, "TpSimpleTryPost failed with status %x\n", status);
+
+ pTpReleaseCleanupGroupMembers(group, FALSE, NULL);
+
+ ReleaseSemaphore(semaphores[0], 1, NULL);
+ result = WaitForSingleObject(semaphores[1], 1000);
+ ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
+ result = WaitForSingleObject(semaphores[0], 1000);
+ ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
+
+ /* cleanup */
+ pTpReleaseCleanupGroup(group);
+ pTpReleasePool(pool);
+ CloseHandle(semaphores[0]);
+ CloseHandle(semaphores[1]);
+}
+
START_TEST(threadpool)
{
if (!init_threadpool())
@@ -508,4 +656,5 @@ START_TEST(threadpool)
test_tp_work_scheduler();
test_tp_group_cancel();
test_tp_instance();
+ test_tp_disassociate();
}
--
2.4.4

View File

@ -1,18 +1,66 @@
From 0aa4ad9970796cbbbe4fc5f81209e6d5d505a385 Mon Sep 17 00:00:00 2001
From c8a3a30a9d4f4a3609d59298d486b691f682390e Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian@fds-team.de>
Date: Fri, 6 Feb 2015 20:09:41 +0100
Subject: ntdll/tests: Add tests for threadpool wait objects.
---
dlls/ntdll/tests/threadpool.c | 287 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 287 insertions(+)
dlls/ntdll/tests/threadpool.c | 295 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 295 insertions(+)
diff --git a/dlls/ntdll/tests/threadpool.c b/dlls/ntdll/tests/threadpool.c
index 4be3a8b..fd587ba 100644
index 0671202..9904d46 100644
--- a/dlls/ntdll/tests/threadpool.c
+++ b/dlls/ntdll/tests/threadpool.c
@@ -869,6 +869,291 @@ static void test_tp_window_length(void)
pTpReleasePool(pool);
@@ -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,291 @@ static void test_tp_window_length(void)
CloseHandle(semaphore);
}
+static void CALLBACK wait_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, TP_WAIT *wait, TP_WAIT_RESULT result)
@ -303,15 +351,13 @@ index 4be3a8b..fd587ba 100644
START_TEST(threadpool)
{
if (!init_threadpool())
@@ -882,6 +1167,8 @@ START_TEST(threadpool)
@@ -919,4 +1212,6 @@ START_TEST(threadpool)
test_tp_disassociate();
test_tp_timer();
test_tp_window_length();
+ test_tp_wait();
+ test_tp_multi_wait();
/* FIXME: Make sure worker threads have terminated before. */
Sleep(100);
}
--
2.4.4

View File

@ -1,4 +1,4 @@
From b32a93ef14e23672df3a11ef1e9718857c933ff9 Mon Sep 17 00:00:00 2001
From 898045c05b21aa7cbdfb168f4b4ce8ffb06b1ae0 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.
@ -10,7 +10,7 @@ Subject: kernel32: Forward threadpool wait functions to ntdll.
3 files changed, 51 insertions(+), 4 deletions(-)
diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec
index 337b339..146db0f 100644
index a14d03b..77e55e1 100644
--- a/dlls/kernel32/kernel32.spec
+++ b/dlls/kernel32/kernel32.spec
@@ -233,7 +233,7 @@
@ -50,10 +50,10 @@ index 337b339..146db0f 100644
@ stdcall WaitNamedPipeA (str long)
@ stdcall WaitNamedPipeW (wstr long)
diff --git a/dlls/kernel32/thread.c b/dlls/kernel32/thread.c
index 945fe0b..89b0b60 100644
index 21ec276..d592692 100644
--- a/dlls/kernel32/thread.c
+++ b/dlls/kernel32/thread.c
@@ -923,6 +923,27 @@ PTP_TIMER WINAPI CreateThreadpoolTimer( PTP_TIMER_CALLBACK callback, PVOID userd
@@ -942,6 +942,27 @@ PTP_TIMER WINAPI CreateThreadpoolTimer( PTP_TIMER_CALLBACK callback, PVOID userd
}
/***********************************************************************
@ -81,9 +81,9 @@ index 945fe0b..89b0b60 100644
* CreateThreadpoolWork (KERNEL32.@)
*/
PTP_WORK WINAPI CreateThreadpoolWork( PTP_WORK_CALLBACK callback, PVOID userdata,
@@ -961,3 +982,25 @@ VOID WINAPI SetThreadpoolTimer( TP_TIMER *timer, FILETIME *due_time,
@@ -1000,3 +1021,25 @@ BOOL WINAPI TrySubmitThreadpoolCallback( PTP_SIMPLE_CALLBACK callback, PVOID use
TpSetTimer( timer, due_time ? &timeout : NULL, period, window_length );
return TRUE;
}
+
+/***********************************************************************

View File

@ -1,84 +0,0 @@
From aef4ab9059bc29f2449c7ec19cb6562896ac1708 Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian@fds-team.de>
Date: Wed, 4 Mar 2015 08:01:00 +0100
Subject: ntdll: Add remaining threadpool functions to specfile.
---
dlls/ntdll/ntdll.spec | 43 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 43 insertions(+)
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec
index ee90705..d75efdc 100644
--- a/dlls/ntdll/ntdll.spec
+++ b/dlls/ntdll/ntdll.spec
@@ -970,24 +970,67 @@
@ stdcall RtlxOemStringToUnicodeSize(ptr) RtlOemStringToUnicodeSize
@ stdcall RtlxUnicodeStringToAnsiSize(ptr) RtlUnicodeStringToAnsiSize
@ stdcall RtlxUnicodeStringToOemSize(ptr) RtlUnicodeStringToOemSize
+# @ stub TpAllocAlpcCompletion
+# @ stub TpAllocAlpcCompletionEx
@ stdcall TpAllocCleanupGroup(ptr)
+# @ stub TpAllocIoCompletion
+# @ stub TpAllocJobNotification
@ stdcall TpAllocPool(ptr ptr)
+# @ stub TpAllocTimer
+# @ stub TpAllocWait
@ stdcall TpAllocWork(ptr ptr ptr ptr)
+# @ stub TpAlpcRegisterCompletionList
+# @ stub TpAlpcUnregisterCompletionList
+# @ stub TpCallbackDetectedUnrecoverableError
+# @ stub TpCallbackIndependent
@ stdcall TpCallbackLeaveCriticalSectionOnCompletion(ptr ptr)
@ stdcall TpCallbackMayRunLong(ptr)
@ stdcall TpCallbackReleaseMutexOnCompletion(ptr long)
@ stdcall TpCallbackReleaseSemaphoreOnCompletion(ptr long long)
+# @ stub TpCallbackSendAlpcMessageOnCompletion
+# @ stub TpCallbackSendPendingAlpcMessage
@ stdcall TpCallbackSetEventOnCompletion(ptr long)
@ stdcall TpCallbackUnloadDllOnCompletion(ptr ptr)
+# @ stub TpCancelAsyncIoOperation
+# @ stub TpCaptureCaller
+# @ stub TpCheckTerminateWorker
+# @ stub TpDbgDumpHeapUsage
+# @ stub TpDbgSetLogRoutine
+# @ stub TpDisablePoolCallbackChecks
@ stdcall TpDisassociateCallback(ptr)
+# @ stub TpIsTimerSet
@ stdcall TpPostWork(ptr)
+# @ stub TpQueryPoolStackInformation
+# @ stub TpReleaseAlpcCompletion
@ stdcall TpReleaseCleanupGroup(ptr)
@ stdcall TpReleaseCleanupGroupMembers(ptr long ptr)
+# @ stub TpReleaseIoCompletion
+# @ stub TpReleaseJobNotification
@ stdcall TpReleasePool(ptr)
+# @ stub TpReleaseTimer
+# @ stub TpReleaseWait
@ stdcall TpReleaseWork(ptr)
+# @ stub TpSetDefaultPoolMaxThreads
+# @ stub TpSetDefaultPoolStackInformation
@ stdcall TpSetPoolMaxThreads(ptr long)
+# @ stub TpSetPoolMaxThreadsSoftLimit
@ stdcall TpSetPoolMinThreads(ptr long)
+# @ stub TpSetPoolStackInformation
+# @ stub TpSetPoolThreadBasePriority
+# @ stub TpSetPoolWorkerThreadIdleTimeout
+# @ stub TpSetTimer
+# @ stub TpSetTimerEx
+# @ stub TpSetWait
+# @ stub TpSetWaitEx
@ stdcall TpSimpleTryPost(ptr ptr ptr)
+# @ stub TpStartAsyncIoOperation
+# @ stub TpTimerOutstandingCallbackCount
+# @ stub TpTrimPools
+# @ stub TpWaitForAlpcCompletion
+# @ stub TpWaitForIoCompletion
+# @ stub TpWaitForJobNotification
+# @ stub TpWaitForTimer
+# @ stub TpWaitForWait
@ stdcall TpWaitForWork(ptr long)
@ stdcall -ret64 VerSetConditionMask(int64 long long)
@ stdcall WinSqmIsOptedIn()
--
2.4.4

View File

@ -1,533 +0,0 @@
From e138d4b3eedeb471f70f2e48cdbb8c4c55207554 Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian@fds-team.de>
Date: Wed, 4 Mar 2015 08:19:47 +0100
Subject: ntdll: Implement threadpool timer functions. (rev 2)
---
dlls/ntdll/ntdll.spec | 10 +-
dlls/ntdll/threadpool.c | 376 +++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 380 insertions(+), 6 deletions(-)
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec
index d75efdc..0f4374e 100644
--- a/dlls/ntdll/ntdll.spec
+++ b/dlls/ntdll/ntdll.spec
@@ -976,7 +976,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
@@ -998,7 +998,7 @@
# @ stub TpDbgSetLogRoutine
# @ stub TpDisablePoolCallbackChecks
@ stdcall TpDisassociateCallback(ptr)
-# @ stub TpIsTimerSet
+@ stdcall TpIsTimerSet(ptr)
@ stdcall TpPostWork(ptr)
# @ stub TpQueryPoolStackInformation
# @ stub TpReleaseAlpcCompletion
@@ -1007,7 +1007,7 @@
# @ stub TpReleaseIoCompletion
# @ stub TpReleaseJobNotification
@ stdcall TpReleasePool(ptr)
-# @ stub TpReleaseTimer
+@ stdcall TpReleaseTimer(ptr)
# @ stub TpReleaseWait
@ stdcall TpReleaseWork(ptr)
# @ stub TpSetDefaultPoolMaxThreads
@@ -1018,7 +1018,7 @@
# @ stub TpSetPoolStackInformation
# @ stub TpSetPoolThreadBasePriority
# @ stub TpSetPoolWorkerThreadIdleTimeout
-# @ stub TpSetTimer
+@ stdcall TpSetTimer(ptr ptr long long)
# @ stub TpSetTimerEx
# @ stub TpSetWait
# @ stub TpSetWaitEx
@@ -1029,7 +1029,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/threadpool.c b/dlls/ntdll/threadpool.c
index 0e09d0f..c44988c 100644
--- a/dlls/ntdll/threadpool.c
+++ b/dlls/ntdll/threadpool.c
@@ -158,7 +158,8 @@ struct threadpool
enum threadpool_objtype
{
TP_OBJECT_TYPE_SIMPLE,
- TP_OBJECT_TYPE_WORK
+ TP_OBJECT_TYPE_WORK,
+ TP_OBJECT_TYPE_TIMER
};
/* internal threadpool object representation */
@@ -196,6 +197,18 @@ struct threadpool_object
{
PTP_WORK_CALLBACK callback;
} work;
+ struct
+ {
+ PTP_TIMER_CALLBACK callback;
+ /* information about the timer, locked via timerqueue.cs */
+ BOOL timer_initialized;
+ BOOL timer_pending;
+ struct list timer_entry;
+ BOOL timer_set;
+ ULONGLONG timeout;
+ LONG period;
+ LONG window_length;
+ } timer;
} u;
};
@@ -227,6 +240,33 @@ struct threadpool_group
struct list members;
};
+/* global timerqueue object */
+static RTL_CRITICAL_SECTION_DEBUG timerqueue_debug;
+
+static struct
+{
+ CRITICAL_SECTION cs;
+ BOOL thread_running;
+ LONG num_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;
@@ -239,6 +279,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->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;
@@ -250,6 +297,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 void tp_object_submit( struct threadpool_object *object );
static void tp_object_shutdown( struct threadpool_object *object );
static BOOL tp_object_release( struct threadpool_object *object );
@@ -1173,6 +1221,230 @@ NTSTATUS WINAPI RtlDeleteTimer(HANDLE TimerQueue, HANDLE Timer,
return status;
}
+static NTSTATUS tp_timerqueue_acquire( struct threadpool_object *timer )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ assert( timer->type == TP_OBJECT_TYPE_TIMER );
+
+ timer->u.timer.timer_initialized = TRUE;
+ timer->u.timer.timer_pending = FALSE;
+ memset( &timer->u.timer.timer_entry, 0, sizeof(timer->u.timer.timer_entry) );
+ timer->u.timer.timer_set = FALSE;
+ timer->u.timer.timeout = 0;
+ timer->u.timer.period = 0;
+ timer->u.timer.window_length = 0;
+
+ 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( struct threadpool_object *timer )
+{
+ assert( timer->type == TP_OBJECT_TYPE_TIMER );
+ RtlEnterCriticalSection( &timerqueue.cs );
+ if (timer->u.timer.timer_initialized)
+ {
+
+ if (timer->u.timer.timer_pending)
+ {
+ list_remove( &timer->u.timer.timer_entry );
+ timer->u.timer.timer_pending = FALSE;
+ }
+
+ if (!--timerqueue.num_timers)
+ {
+ assert( list_empty( &timerqueue.pending_timers ) );
+ RtlWakeAllConditionVariable( &timerqueue.update_event );
+ }
+
+ timer->u.timer.timer_initialized = FALSE;
+ }
+ RtlLeaveCriticalSection( &timerqueue.cs );
+}
+
+static void tp_timerqueue_update_timer( struct threadpool_object *new_timer, LARGE_INTEGER *timeout,
+ LONG period, LONG window_length )
+{
+ BOOL submit_timer = FALSE;
+ struct threadpool_object *timer;
+ ULONGLONG when;
+
+ assert( new_timer->type == TP_OBJECT_TYPE_TIMER );
+ RtlEnterCriticalSection( &timerqueue.cs );
+ assert( new_timer->u.timer.timer_initialized );
+
+ /* Remember if the timer is set or unset */
+ new_timer->u.timer.timer_set = timeout != NULL;
+
+ if (timeout)
+ {
+ when = timeout->QuadPart;
+
+ /* A timeout of zero means that the timer should be submitted immediately */
+ if (when == 0)
+ {
+ submit_timer = TRUE;
+ if (!period)
+ {
+ timeout = NULL;
+ goto update_timer;
+ }
+ when = (ULONGLONG)period * -10000;
+ }
+
+ /* Convert relative timeout to absolute */
+ 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 );
+ memset( &new_timer->u.timer.timer_entry, 0, sizeof(new_timer->u.timer.timer_entry) );
+ new_timer->u.timer.timer_pending = FALSE;
+ }
+
+ /* Timer should be enabled again, add it to the queue */
+ if (timeout)
+ {
+ 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 (submit_timer)
+ tp_object_submit( 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
+ {
+ /* The element is no longer queued */
+ timer->u.timer.timer_pending = FALSE;
+ }
+ }
+
+ /* 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 );
+}
+
/***********************************************************************
* tp_threadpool_alloc (internal)
*
@@ -1571,6 +1843,9 @@ static void tp_object_wait( struct threadpool_object *object, BOOL group_wait )
*/
static void tp_object_shutdown( struct threadpool_object *object )
{
+ if (object->type == TP_OBJECT_TYPE_TIMER)
+ tp_timerqueue_release( object );
+
object->shutdown = TRUE;
}
@@ -1682,6 +1957,15 @@ static void CALLBACK threadpool_worker_proc( void *param )
break;
}
+ case TP_OBJECT_TYPE_TIMER:
+ {
+ TRACE( "executing timer callback %p(%p, %p, %p)\n",
+ object->u.timer.callback, callback_instance, object->userdata, object );
+ object->u.timer.callback( callback_instance, object->userdata, (TP_TIMER *)object );
+ TRACE( "callback %p returned\n", object->u.timer.callback );
+ break;
+ }
+
default:
assert(0);
break;
@@ -1788,6 +2072,46 @@ 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 )
+{
+ 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_TIMER;
+ object->u.timer.callback = callback;
+
+ status = tp_timerqueue_acquire( object );
+ if (status)
+ {
+ tp_threadpool_unlock( pool );
+ RtlFreeHeap( GetProcessHeap(), 0, object );
+ return status;
+ }
+
+ tp_object_initialize( object, pool, userdata, environment );
+
+ *out = (TP_TIMER *)object;
+ return STATUS_SUCCESS;
+}
+
+/***********************************************************************
* TpAllocWork (NTDLL.@)
*/
NTSTATUS WINAPI TpAllocWork( TP_WORK **out, PTP_WORK_CALLBACK callback, PVOID userdata,
@@ -1968,6 +2292,17 @@ 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->u.timer.timer_set;
+}
+
+/***********************************************************************
* TpPostWork (NTDLL.@)
*/
VOID WINAPI TpPostWork( TP_WORK *work )
@@ -2067,6 +2402,19 @@ 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 );
+
+ tp_object_shutdown( this );
+ tp_object_release( this );
+}
+
+/***********************************************************************
* TpReleaseWork (NTDLL.@)
*/
VOID WINAPI TpReleaseWork( TP_WORK *work )
@@ -2130,6 +2478,18 @@ 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 );
+
+ tp_timerqueue_update_timer( this, timeout, period, window_length );
+}
+
+/***********************************************************************
* TpSimpleTryPost (NTDLL.@)
*/
NTSTATUS WINAPI TpSimpleTryPost( PTP_SIMPLE_CALLBACK callback, PVOID userdata,
@@ -2160,6 +2520,20 @@ NTSTATUS WINAPI TpSimpleTryPost( PTP_SIMPLE_CALLBACK callback, PVOID userdata,
}
/***********************************************************************
+ * 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 (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,294 +0,0 @@
From 8e03c37240d2a0fafbe6347bc8d509ce5555757a Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian@fds-team.de>
Date: Wed, 4 Mar 2015 13:17:04 +0100
Subject: ntdll/tests: Add tests for Tp* threadpool functions.
---
dlls/ntdll/tests/threadpool.c | 228 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 228 insertions(+)
diff --git a/dlls/ntdll/tests/threadpool.c b/dlls/ntdll/tests/threadpool.c
index a75b622..4be3a8b 100644
--- a/dlls/ntdll/tests/threadpool.c
+++ b/dlls/ntdll/tests/threadpool.c
@@ -22,18 +22,37 @@
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) \
@@ -54,18 +73,37 @@ static BOOL init_threadpool(void)
}
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)
@@ -646,6 +684,191 @@ static void test_tp_disassociate(void)
CloseHandle(semaphores[1]);
}
+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");
+
+ timer2 = NULL;
+ 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())
@@ -657,4 +880,9 @@ START_TEST(threadpool)
test_tp_group_cancel();
test_tp_instance();
test_tp_disassociate();
+ test_tp_timer();
+ test_tp_window_length();
+
+ /* FIXME: Make sure worker threads have terminated before. */
+ Sleep(100);
}
--
2.4.4

View File

@ -1,342 +0,0 @@
From 37906069933d6bd73b8c6a691292288c57b09e22 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 3c40975..337b339 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
@@ -981,7 +981,7 @@
@ stub -i386 IsSLCallback
@ stdcall IsSystemResumeAutomatic()
@ stdcall IsThreadAFiber()
-# @ stub IsThreadpoolTimerSet
+@ stdcall IsThreadpoolTimerSet(ptr) ntdll.TpIsTimerSet
# @ stub IsTimeZoneRedirectionEnabled
# @ stub IsValidCalDateTime
@ stdcall IsValidCodePage(long)
@@ -1035,7 +1035,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)
@@ -1252,9 +1252,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)
@@ -1385,7 +1385,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)
@@ -1454,9 +1454,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
@@ -1482,7 +1482,7 @@
# @ stub SortCloseHandle
# @ stub SortGetHandle
# @ stub StartThreadpoolIo
-# @ stub SubmitThreadpoolWork
+@ stdcall SubmitThreadpoolWork(ptr) ntdll.TpPostWork
@ stdcall SuspendThread(long)
@ stdcall SwitchToFiber(ptr)
@ stdcall SwitchToThread()
@@ -1509,7 +1509,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
@@ -1571,9 +1571,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 3dabf94..945fe0b 100644
--- a/dlls/kernel32/thread.c
+++ b/dlls/kernel32/thread.c
@@ -860,3 +860,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 a84c6d4..e1707fd 100644
--- a/include/winternl.h
+++ b/include/winternl.h
@@ -2616,6 +2616,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.4.4

View File

@ -1,4 +1,4 @@
Fixes: [35192] Add implementation for CreateThreadpool
Fixes: [32531] Implement threadpool work items
Fixes: [37306] Implement threadpool timers
# 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 3d8353fe182f3e7742cd7dab3420635e45ace129"
echo " commit 8b566b1da71a412fe58c3cc1988d610c2aba5f2d"
echo ""
}
@ -3691,32 +3691,15 @@ 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/ntdll.spec,
# | dlls/ntdll/tests/threadpool.c, dlls/ntdll/threadpool.c, include/winternl.h
# | * 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-TpDisassociateCallback-and-add-separ.patch
patch_apply ntdll-Vista_Threadpool/0002-ntdll-tests-Add-tests-for-TpDisassociateCallback.patch
patch_apply ntdll-Vista_Threadpool/0003-ntdll-Add-remaining-threadpool-functions-to-specfile.patch
patch_apply ntdll-Vista_Threadpool/0004-ntdll-Implement-threadpool-timer-functions.-rev-2.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
patch_apply ntdll-Vista_Threadpool/0007-ntdll-Implement-threadpool-wait-objects.patch
patch_apply ntdll-Vista_Threadpool/0008-ntdll-tests-Add-tests-for-threadpool-wait-objects.patch
patch_apply ntdll-Vista_Threadpool/0009-kernel32-Forward-threadpool-wait-functions-to-ntdll.patch
patch_apply ntdll-Vista_Threadpool/0001-ntdll-Implement-threadpool-wait-objects.patch
patch_apply ntdll-Vista_Threadpool/0002-ntdll-tests-Add-tests-for-threadpool-wait-objects.patch
patch_apply ntdll-Vista_Threadpool/0003-kernel32-Forward-threadpool-wait-functions-to-ntdll.patch
(
echo '+ { "Sebastian Lackner", "ntdll: Implement TpDisassociateCallback and add separate group finished event.", 1 },';
echo '+ { "Sebastian Lackner", "ntdll/tests: Add tests for TpDisassociateCallback.", 1 },';
echo '+ { "Sebastian Lackner", "ntdll: Add remaining threadpool functions to specfile.", 1 },';
echo '+ { "Sebastian Lackner", "ntdll: Implement threadpool timer functions.", 2 },';
echo '+ { "Sebastian Lackner", "ntdll/tests: Add tests for Tp* threadpool functions.", 1 },';
echo '+ { "Sebastian Lackner", "kernel32: Forward various threadpool functions to ntdll.", 1 },';
echo '+ { "Sebastian Lackner", "ntdll: Implement threadpool wait objects.", 1 },';
echo '+ { "Sebastian Lackner", "ntdll/tests: Add tests for threadpool wait objects.", 1 },';
echo '+ { "Sebastian Lackner", "kernel32: Forward threadpool wait functions to ntdll.", 1 },';

View File

@ -1,4 +1,4 @@
From 449e8ea8593261284351648eb19504e8e8769dde Mon Sep 17 00:00:00 2001
From 37df70888f1e9c12f63d5fc3a511ec34fce4b10d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20D=C3=B6singer?= <stefan@codeweavers.com>
Date: Tue, 1 Oct 2013 14:31:56 +0200
Subject: wined3d: Hackily introduce a multithreaded command stream
@ -10,7 +10,7 @@ Subject: wined3d: Hackily introduce a multithreaded command stream
3 files changed, 368 insertions(+), 39 deletions(-)
diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c
index 639dc4f..9247d98 100644
index daebc94..40c04db 100644
--- a/dlls/wined3d/cs.c
+++ b/dlls/wined3d/cs.c
@@ -24,8 +24,19 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d);
@ -467,7 +467,7 @@ index 639dc4f..9247d98 100644
{
const struct wined3d_d3d_info *d3d_info = &cs->device->adapter->d3d_info;
const struct wined3d_cs_set_texture *op = data;
@@ -708,6 +885,8 @@ static void wined3d_cs_exec_set_texture(struct wined3d_cs *cs, const void *data)
@@ -710,6 +887,8 @@ static void wined3d_cs_exec_set_texture(struct wined3d_cs *cs, const void *data)
if (new_use_color_key)
device_invalidate_state(cs->device, STATE_COLOR_KEY);
@ -476,7 +476,7 @@ index 639dc4f..9247d98 100644
}
void wined3d_cs_emit_set_texture(struct wined3d_cs *cs, UINT stage, struct wined3d_texture *texture)
@@ -722,12 +901,14 @@ void wined3d_cs_emit_set_texture(struct wined3d_cs *cs, UINT stage, struct wined
@@ -724,12 +903,14 @@ void wined3d_cs_emit_set_texture(struct wined3d_cs *cs, UINT stage, struct wined
cs->ops->submit(cs);
}
@ -492,7 +492,7 @@ index 639dc4f..9247d98 100644
}
void wined3d_cs_emit_set_shader_resource_view(struct wined3d_cs *cs, enum wined3d_shader_type type,
@@ -744,12 +925,14 @@ void wined3d_cs_emit_set_shader_resource_view(struct wined3d_cs *cs, enum wined3
@@ -746,12 +927,14 @@ void wined3d_cs_emit_set_shader_resource_view(struct wined3d_cs *cs, enum wined3
cs->ops->submit(cs);
}
@ -508,7 +508,7 @@ index 639dc4f..9247d98 100644
}
void wined3d_cs_emit_set_sampler(struct wined3d_cs *cs, enum wined3d_shader_type type,
@@ -766,13 +949,15 @@ void wined3d_cs_emit_set_sampler(struct wined3d_cs *cs, enum wined3d_shader_type
@@ -768,13 +951,15 @@ void wined3d_cs_emit_set_sampler(struct wined3d_cs *cs, enum wined3d_shader_type
cs->ops->submit(cs);
}
@ -525,7 +525,7 @@ index 639dc4f..9247d98 100644
}
void wined3d_cs_emit_set_shader(struct wined3d_cs *cs, enum wined3d_shader_type type, struct wined3d_shader *shader)
@@ -787,12 +972,14 @@ void wined3d_cs_emit_set_shader(struct wined3d_cs *cs, enum wined3d_shader_type
@@ -789,12 +974,14 @@ void wined3d_cs_emit_set_shader(struct wined3d_cs *cs, enum wined3d_shader_type
cs->ops->submit(cs);
}
@ -541,7 +541,7 @@ index 639dc4f..9247d98 100644
}
void wined3d_cs_emit_set_render_state(struct wined3d_cs *cs, enum wined3d_render_state state, DWORD value)
@@ -807,12 +994,14 @@ void wined3d_cs_emit_set_render_state(struct wined3d_cs *cs, enum wined3d_render
@@ -809,12 +996,14 @@ void wined3d_cs_emit_set_render_state(struct wined3d_cs *cs, enum wined3d_render
cs->ops->submit(cs);
}
@ -557,7 +557,7 @@ index 639dc4f..9247d98 100644
}
void wined3d_cs_emit_set_texture_state(struct wined3d_cs *cs, UINT stage,
@@ -829,12 +1018,14 @@ void wined3d_cs_emit_set_texture_state(struct wined3d_cs *cs, UINT stage,
@@ -831,12 +1020,14 @@ void wined3d_cs_emit_set_texture_state(struct wined3d_cs *cs, UINT stage,
cs->ops->submit(cs);
}
@ -573,7 +573,7 @@ index 639dc4f..9247d98 100644
}
void wined3d_cs_emit_set_sampler_state(struct wined3d_cs *cs, UINT sampler_idx,
@@ -851,13 +1042,15 @@ void wined3d_cs_emit_set_sampler_state(struct wined3d_cs *cs, UINT sampler_idx,
@@ -853,13 +1044,15 @@ void wined3d_cs_emit_set_sampler_state(struct wined3d_cs *cs, UINT sampler_idx,
cs->ops->submit(cs);
}
@ -584,14 +584,14 @@ index 639dc4f..9247d98 100644
- cs->state.transforms[op->state] = *op->matrix;
+ cs->state.transforms[op->state] = op->matrix;
if (op->state < WINED3D_TS_WORLD_MATRIX(cs->device->adapter->gl_info.limits.blends))
if (op->state < WINED3D_TS_WORLD_MATRIX(cs->device->adapter->d3d_info.limits.ffp_vertex_blend_matrices))
device_invalidate_state(cs->device, STATE_TRANSFORM(op->state));
+
+ return sizeof(*op);
}
void wined3d_cs_emit_set_transform(struct wined3d_cs *cs, enum wined3d_transform_state state,
@@ -868,17 +1061,19 @@ void wined3d_cs_emit_set_transform(struct wined3d_cs *cs, enum wined3d_transform
@@ -870,17 +1063,19 @@ void wined3d_cs_emit_set_transform(struct wined3d_cs *cs, enum wined3d_transform
op = cs->ops->require_space(cs, sizeof(*op));
op->opcode = WINED3D_CS_OP_SET_TRANSFORM;
op->state = state;
@ -614,7 +614,7 @@ index 639dc4f..9247d98 100644
}
void wined3d_cs_emit_set_clip_plane(struct wined3d_cs *cs, UINT plane_idx, const struct wined3d_vec4 *plane)
@@ -888,12 +1083,12 @@ void wined3d_cs_emit_set_clip_plane(struct wined3d_cs *cs, UINT plane_idx, const
@@ -890,12 +1085,12 @@ void wined3d_cs_emit_set_clip_plane(struct wined3d_cs *cs, UINT plane_idx, const
op = cs->ops->require_space(cs, sizeof(*op));
op->opcode = WINED3D_CS_OP_SET_CLIP_PLANE;
op->plane_idx = plane_idx;
@ -629,7 +629,7 @@ index 639dc4f..9247d98 100644
{
const struct wined3d_cs_set_color_key *op = data;
struct wined3d_texture *texture = op->texture;
@@ -954,6 +1149,8 @@ static void wined3d_cs_exec_set_color_key(struct wined3d_cs *cs, const void *dat
@@ -956,6 +1151,8 @@ static void wined3d_cs_exec_set_color_key(struct wined3d_cs *cs, const void *dat
break;
}
}
@ -638,7 +638,7 @@ index 639dc4f..9247d98 100644
}
void wined3d_cs_emit_set_color_key(struct wined3d_cs *cs, struct wined3d_texture *texture,
@@ -976,12 +1173,14 @@ void wined3d_cs_emit_set_color_key(struct wined3d_cs *cs, struct wined3d_texture
@@ -978,12 +1175,14 @@ void wined3d_cs_emit_set_color_key(struct wined3d_cs *cs, struct wined3d_texture
cs->ops->submit(cs);
}
@ -655,7 +655,7 @@ index 639dc4f..9247d98 100644
}
void wined3d_cs_emit_set_material(struct wined3d_cs *cs, const struct wined3d_material *material)
@@ -990,12 +1189,12 @@ void wined3d_cs_emit_set_material(struct wined3d_cs *cs, const struct wined3d_ma
@@ -992,12 +1191,12 @@ void wined3d_cs_emit_set_material(struct wined3d_cs *cs, const struct wined3d_ma
op = cs->ops->require_space(cs, sizeof(*op));
op->opcode = WINED3D_CS_OP_SET_MATERIAL;
@ -670,7 +670,7 @@ index 639dc4f..9247d98 100644
{
struct wined3d_adapter *adapter = cs->device->adapter;
HRESULT hr;
@@ -1005,6 +1204,8 @@ static void wined3d_cs_exec_reset_state(struct wined3d_cs *cs, const void *data)
@@ -1007,6 +1206,8 @@ static void wined3d_cs_exec_reset_state(struct wined3d_cs *cs, const void *data)
if (FAILED(hr = state_init(&cs->state, &adapter->gl_info, &adapter->d3d_info,
WINED3D_STATE_NO_REF | WINED3D_STATE_INIT_DEFAULT)))
ERR("Failed to initialize CS state, hr %#x.\n", hr);
@ -679,7 +679,7 @@ index 639dc4f..9247d98 100644
}
void wined3d_cs_emit_reset_state(struct wined3d_cs *cs)
@@ -1017,8 +1218,9 @@ void wined3d_cs_emit_reset_state(struct wined3d_cs *cs)
@@ -1019,8 +1220,9 @@ void wined3d_cs_emit_reset_state(struct wined3d_cs *cs)
cs->ops->submit(cs);
}
@ -690,7 +690,7 @@ index 639dc4f..9247d98 100644
/* WINED3D_CS_OP_PRESENT */ wined3d_cs_exec_present,
/* WINED3D_CS_OP_CLEAR */ wined3d_cs_exec_clear,
/* WINED3D_CS_OP_DRAW */ wined3d_cs_exec_draw,
@@ -1077,6 +1279,58 @@ static const struct wined3d_cs_ops wined3d_cs_st_ops =
@@ -1079,6 +1281,58 @@ static const struct wined3d_cs_ops wined3d_cs_st_ops =
wined3d_cs_st_submit,
};
@ -749,7 +749,7 @@ index 639dc4f..9247d98 100644
struct wined3d_cs *wined3d_cs_create(struct wined3d_device *device)
{
const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
@@ -1102,12 +1356,60 @@ struct wined3d_cs *wined3d_cs_create(struct wined3d_device *device)
@@ -1104,12 +1358,60 @@ struct wined3d_cs *wined3d_cs_create(struct wined3d_device *device)
return NULL;
}
@ -838,10 +838,10 @@ index 08021a2..088e59a 100644
if (appkey) RegCloseKey( appkey );
diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
index 089faa4..2addced 100644
index 59f9772..01c045b 100644
--- a/dlls/wined3d/wined3d_private.h
+++ b/dlls/wined3d/wined3d_private.h
@@ -278,6 +278,7 @@ struct wined3d_settings
@@ -279,6 +279,7 @@ struct wined3d_settings
unsigned int max_sm_gs;
unsigned int max_sm_ps;
BOOL no_3d;
@ -849,7 +849,7 @@ index 089faa4..2addced 100644
};
extern struct wined3d_settings wined3d_settings DECLSPEC_HIDDEN;
@@ -2573,6 +2574,18 @@ HRESULT state_init(struct wined3d_state *state, const struct wined3d_gl_info *gl
@@ -2589,6 +2590,18 @@ HRESULT state_init(struct wined3d_state *state, const struct wined3d_gl_info *gl
const struct wined3d_d3d_info *d3d_info, DWORD flags) DECLSPEC_HIDDEN;
void state_unbind_resources(struct wined3d_state *state) DECLSPEC_HIDDEN;
@ -868,7 +868,7 @@ index 089faa4..2addced 100644
struct wined3d_cs_ops
{
void *(*require_space)(struct wined3d_cs *cs, size_t size);
@@ -2584,9 +2597,14 @@ struct wined3d_cs
@@ -2600,9 +2613,14 @@ struct wined3d_cs
const struct wined3d_cs_ops *ops;
struct wined3d_device *device;
struct wined3d_state state;
@ -884,5 +884,5 @@ index 089faa4..2addced 100644
struct wined3d_cs *wined3d_cs_create(struct wined3d_device *device) DECLSPEC_HIDDEN;
--
2.3.5
2.4.4

View File

@ -408,7 +408,7 @@ diff --git a/dlls/wined3d/state.c b/dlls/wined3d/state.c
float scale;
union
@@ -4174,9 +4206,15 @@
@@ -4178,9 +4210,15 @@
}
}
} else {
@ -424,7 +424,7 @@ diff --git a/dlls/wined3d/state.c b/dlls/wined3d/state.c
WARN("unsupported blending in openGl\n");
}
}
@@ -4530,7 +4568,11 @@
@@ -4534,7 +4572,11 @@
static void viewport_miscpart(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id)
{
@ -436,7 +436,7 @@ diff --git a/dlls/wined3d/state.c b/dlls/wined3d/state.c
const struct wined3d_gl_info *gl_info = context->gl_info;
struct wined3d_viewport vp = state->viewport;
@@ -4708,7 +4750,11 @@
@@ -4712,7 +4754,11 @@
}
else
{
@ -448,7 +448,7 @@ diff --git a/dlls/wined3d/state.c b/dlls/wined3d/state.c
UINT height;
UINT width;
@@ -4772,7 +4818,11 @@
@@ -4776,7 +4822,11 @@
void state_srgbwrite(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id)
{
@ -1099,7 +1099,7 @@ diff --git a/dlls/wined3d/volume.c b/dlls/wined3d/volume.c
diff --git a/dlls/wined3d/utils.c b/dlls/wined3d/utils.c
--- a/dlls/wined3d/utils.c
+++ b/dlls/wined3d/utils.c
@@ -3514,7 +3514,11 @@
@@ -3518,7 +3518,11 @@
float y_offset = context->render_offscreen
? (center_offset - (2.0f * y) - h) / h
: (center_offset - (2.0f * y) - h) / -h;
@ -1111,7 +1111,7 @@ diff --git a/dlls/wined3d/utils.c b/dlls/wined3d/utils.c
state->render_states[WINED3D_RS_ZENABLE] : WINED3D_ZB_FALSE;
float z_scale = zenable ? 2.0f : 0.0f;
float z_offset = zenable ? -1.0f : 0.0f;
@@ -3637,6 +3641,7 @@
@@ -3641,6 +3645,7 @@
/* case WINED3D_TTFF_COUNT1: Won't ever get here. */
case WINED3D_TTFF_COUNT2:
mat._13 = mat._23 = mat._33 = mat._43 = 0.0f;
@ -1119,7 +1119,7 @@ diff --git a/dlls/wined3d/utils.c b/dlls/wined3d/utils.c
/* OpenGL divides the first 3 vertex coord by the 4th by default,
* which is essentially the same as D3DTTFF_PROJECTED. Make sure that
* the 4th coord evaluates to 1.0 to eliminate that.
@@ -3649,6 +3654,20 @@
@@ -3653,6 +3658,20 @@
* A more serious problem occurs if the app passes 4 coordinates in, and the
* 4th is != 1.0(opengl default). This would have to be fixed in draw_strided_slow
* or a replacement shader. */
@ -1140,7 +1140,7 @@ diff --git a/dlls/wined3d/utils.c b/dlls/wined3d/utils.c
default:
mat._14 = mat._24 = mat._34 = 0.0f; mat._44 = 1.0f;
}
@@ -4104,7 +4123,11 @@
@@ -4108,7 +4127,11 @@
unsigned int i;
DWORD ttff;
DWORD cop, aop, carg0, carg1, carg2, aarg0, aarg1, aarg2;
@ -1747,7 +1747,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
#include <stdarg.h>
#include <math.h>
#include <limits.h>
@@ -279,8 +307,10 @@
@@ -280,8 +308,10 @@
unsigned int max_sm_gs;
unsigned int max_sm_ps;
BOOL no_3d;
@ -1758,7 +1758,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
};
extern struct wined3d_settings wined3d_settings DECLSPEC_HIDDEN;
@@ -1012,9 +1042,14 @@
@@ -1014,9 +1044,14 @@
WORD use_map; /* MAX_ATTRIBS, 16 */
};
@ -1773,7 +1773,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
DWORD get_flexible_vertex_size(DWORD d3dvtVertexType) DECLSPEC_HIDDEN;
#define eps 1e-8f
@@ -1102,8 +1137,10 @@
@@ -1104,8 +1139,10 @@
struct list entry;
GLuint id;
struct wined3d_context *context;
@ -1784,7 +1784,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
};
union wined3d_gl_query_object
@@ -1139,6 +1176,7 @@
@@ -1141,6 +1178,7 @@
struct list entry;
GLuint id;
struct wined3d_context *context;
@ -1792,7 +1792,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
UINT64 timestamp;
};
@@ -1174,6 +1212,12 @@
@@ -1176,6 +1214,12 @@
for (i = 0; i < min(dst->rt_size, src->rt_size); i++)
dst->render_targets[i] = src->render_targets[i];
}
@ -1805,7 +1805,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
struct wined3d_context
{
@@ -1189,7 +1233,9 @@
@@ -1191,7 +1235,9 @@
DWORD dirtyArray[STATE_HIGHEST + 1]; /* Won't get bigger than that, a state is never marked dirty 2 times */
DWORD numDirtyEntries;
DWORD isStateDirty[STATE_HIGHEST / (sizeof(DWORD) * CHAR_BIT) + 1]; /* Bitmap to find out quickly if a state is dirty */
@ -1815,7 +1815,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
struct wined3d_swapchain *swapchain;
struct wined3d_surface *current_rt;
@@ -1289,8 +1335,17 @@
@@ -1291,8 +1337,17 @@
GLfloat fog_coord_value;
GLfloat color[4], fogstart, fogend, fogcolor[4];
GLuint dummy_arbfp_prog;
@ -1833,7 +1833,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
};
typedef void (*APPLYSTATEFUNC)(struct wined3d_context *ctx, const struct wined3d_state *state, DWORD state_id);
@@ -1425,8 +1480,12 @@
@@ -1427,8 +1482,12 @@
void context_apply_blit_state(struct wined3d_context *context, const struct wined3d_device *device) DECLSPEC_HIDDEN;
BOOL context_apply_clear_state(struct wined3d_context *context, const struct wined3d_device *device,
UINT rt_count, const struct wined3d_fb_state *fb) DECLSPEC_HIDDEN;
@ -1846,7 +1846,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
void context_apply_fbo_state_blit(struct wined3d_context *context, GLenum target,
struct wined3d_surface *render_target, struct wined3d_surface *depth_stencil, DWORD location) DECLSPEC_HIDDEN;
void context_active_texture(struct wined3d_context *context, const struct wined3d_gl_info *gl_info,
@@ -1980,7 +2039,11 @@
@@ -1984,7 +2043,11 @@
struct wined3d_state
{
DWORD flags;
@ -1858,7 +1858,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
struct wined3d_vertex_declaration *vertex_declaration;
struct wined3d_stream_output stream_output[MAX_STREAM_OUT];
@@ -2025,6 +2088,7 @@
@@ -2029,6 +2092,7 @@
DWORD render_states[WINEHIGHEST_RENDER_STATE + 1];
};
@ -1866,7 +1866,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
struct wined3d_gl_bo
{
GLuint name;
@@ -2033,6 +2097,7 @@
@@ -2037,6 +2101,7 @@
UINT size;
};
@ -1874,7 +1874,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
#define WINED3D_UNMAPPED_STAGE ~0U
/* Multithreaded flag. Removed from the public header to signal that
@@ -2088,11 +2153,23 @@
@@ -2092,11 +2157,23 @@
struct wined3d_rendertarget_view *back_buffer_view;
struct wined3d_swapchain **swapchains;
UINT swapchain_count;
@ -1898,7 +1898,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
/* For rendering to a texture using glCopyTexImage */
GLuint depth_blt_texture;
@@ -2103,6 +2180,9 @@
@@ -2107,6 +2184,9 @@
UINT xScreenSpace;
UINT yScreenSpace;
UINT cursorWidth, cursorHeight;
@ -1908,7 +1908,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
HCURSOR hardwareCursor;
/* The Wine logo texture */
@@ -2134,6 +2214,7 @@
@@ -2138,6 +2218,7 @@
UINT message, WPARAM wparam, LPARAM lparam, WNDPROC proc) DECLSPEC_HIDDEN;
void device_resource_add(struct wined3d_device *device, struct wined3d_resource *resource) DECLSPEC_HIDDEN;
void device_resource_released(struct wined3d_device *device, struct wined3d_resource *resource) DECLSPEC_HIDDEN;
@ -1916,7 +1916,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
void device_invalidate_state(const struct wined3d_device *device, DWORD state) DECLSPEC_HIDDEN;
void device_invalidate_shader_constants(const struct wined3d_device *device, DWORD mask) DECLSPEC_HIDDEN;
void device_exec_update_texture(struct wined3d_context *context, struct wined3d_texture *src_texture,
@@ -2145,6 +2226,11 @@
@@ -2149,6 +2230,11 @@
void device_create_dummy_textures(struct wined3d_device *device, struct wined3d_context *context) DECLSPEC_HIDDEN;
void device_delete_opengl_contexts_cs(struct wined3d_device *device,
struct wined3d_swapchain *swapchain) DECLSPEC_HIDDEN;
@ -1928,7 +1928,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
static inline BOOL isStateDirty(const struct wined3d_context *context, DWORD state)
{
@@ -2161,9 +2247,11 @@
@@ -2165,9 +2251,11 @@
ULONG (*resource_incref)(struct wined3d_resource *resource);
ULONG (*resource_decref)(struct wined3d_resource *resource);
void (*resource_unload)(struct wined3d_resource *resource);
@ -1940,7 +1940,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
};
struct wined3d_resource
@@ -2188,6 +2276,7 @@
@@ -2192,6 +2280,7 @@
UINT depth;
UINT size;
DWORD priority;
@ -1948,7 +1948,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
void *heap_memory, *map_heap_memory, *user_memory, *bitmap_data;
UINT custom_row_pitch, custom_slice_pitch;
struct wined3d_gl_bo *buffer, *map_buffer;
@@ -2195,6 +2284,11 @@
@@ -2199,6 +2288,11 @@
DWORD locations;
LONG access_fence;
BOOL unmap_dirtify;
@ -1960,7 +1960,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
void *parent;
const struct wined3d_parent_ops *parent_ops;
@@ -2219,6 +2313,7 @@
@@ -2223,6 +2317,7 @@
void *parent, const struct wined3d_parent_ops *parent_ops,
const struct wined3d_resource_ops *resource_ops) DECLSPEC_HIDDEN;
void resource_unload(struct wined3d_resource *resource) DECLSPEC_HIDDEN;
@ -1968,7 +1968,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
DWORD wined3d_resource_access_from_location(DWORD location) DECLSPEC_HIDDEN;
BOOL wined3d_resource_allocate_sysmem(struct wined3d_resource *resource) DECLSPEC_HIDDEN;
void wined3d_resource_changed(struct wined3d_resource *resource,
@@ -2265,6 +2360,15 @@
@@ -2269,6 +2364,15 @@
{
while(InterlockedCompareExchange(&resource->access_fence, 0, 0));
}
@ -1984,7 +1984,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
/* Tests show that the start address of resources is 32 byte aligned */
#define RESOURCE_ALIGNMENT 16
@@ -2349,7 +2453,9 @@
@@ -2353,7 +2457,9 @@
void wined3d_texture_apply_sampler_desc(struct wined3d_texture *texture,
const struct wined3d_sampler_desc *sampler_desc, const struct wined3d_gl_info *gl_info) DECLSPEC_HIDDEN;
@ -1994,7 +1994,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
void wined3d_texture_bind(struct wined3d_texture *texture,
struct wined3d_context *context, BOOL srgb) DECLSPEC_HIDDEN;
void wined3d_texture_bind_and_dirtify(struct wined3d_texture *texture,
@@ -2383,9 +2489,16 @@
@@ -2387,9 +2493,16 @@
struct wined3d_resource resource;
struct wined3d_texture *container;
@ -2011,7 +2011,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
};
static inline struct wined3d_volume *volume_from_resource(struct wined3d_resource *resource)
@@ -2393,6 +2506,7 @@
@@ -2397,6 +2510,7 @@
return CONTAINING_RECORD(resource, struct wined3d_volume, resource);
}
@ -2019,7 +2019,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
HRESULT wined3d_volume_create(struct wined3d_texture *container, const struct wined3d_resource_desc *desc,
unsigned int level, struct wined3d_volume **volume) DECLSPEC_HIDDEN;
void wined3d_volume_destroy(struct wined3d_volume *volume) DECLSPEC_HIDDEN;
@@ -2405,6 +2519,23 @@
@@ -2409,6 +2523,23 @@
struct wined3d_surface_dib
{
HBITMAP DIBsection;
@ -2043,7 +2043,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
UINT bitmap_size;
};
@@ -2430,7 +2561,11 @@
@@ -2434,7 +2565,11 @@
struct wined3d_surface_ops
{
HRESULT (*surface_private_setup)(struct wined3d_surface *surface);
@ -2055,7 +2055,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
};
struct wined3d_surface
@@ -2438,12 +2573,25 @@
@@ -2442,12 +2577,25 @@
struct wined3d_resource resource;
const struct wined3d_surface_ops *surface_ops;
struct wined3d_texture *container;
@ -2081,7 +2081,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
GLuint rb_multisample;
GLuint rb_resolved;
GLenum texture_target;
@@ -2487,10 +2635,19 @@
@@ -2491,10 +2639,19 @@
GLenum surface_get_gl_buffer(const struct wined3d_surface *surface) DECLSPEC_HIDDEN;
void surface_get_drawable_size(const struct wined3d_surface *surface, const struct wined3d_context *context,
unsigned int *width, unsigned int *height) DECLSPEC_HIDDEN;
@ -2101,7 +2101,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
void surface_modify_ds_location(struct wined3d_surface *surface, DWORD location, UINT w, UINT h) DECLSPEC_HIDDEN;
void surface_prepare_rb(struct wined3d_surface *surface,
const struct wined3d_gl_info *gl_info, BOOL multisample) DECLSPEC_HIDDEN;
@@ -2502,6 +2659,7 @@
@@ -2506,6 +2663,7 @@
const struct wined3d_gl_info *gl_info, void *mem, unsigned int pitch) DECLSPEC_HIDDEN;
HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
struct wined3d_surface *src_surface, const RECT *src_rect) DECLSPEC_HIDDEN;
@ -2109,7 +2109,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
HRESULT wined3d_surface_create(struct wined3d_texture *container, const struct wined3d_resource_desc *desc,
GLenum target, unsigned int level, unsigned int layer, DWORD flags,
struct wined3d_surface **surface) DECLSPEC_HIDDEN;
@@ -2520,6 +2678,21 @@
@@ -2524,6 +2682,21 @@
void draw_textured_quad(const struct wined3d_surface *src_surface, struct wined3d_context *context,
const RECT *src_rect, const RECT *dst_rect, enum wined3d_texture_filter_type filter) DECLSPEC_HIDDEN;
void surface_flip(struct wined3d_surface *front, struct wined3d_surface *back) DECLSPEC_HIDDEN;
@ -2131,7 +2131,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
/* Surface flags: */
#define SFLAG_DIBSECTION 0x00000001 /* Has a DIB section attached for GetDC. */
@@ -2567,8 +2740,10 @@
@@ -2571,8 +2744,10 @@
BOOL half_float_conv_needed;
};
@ -2142,7 +2142,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
struct wined3d_saved_states
{
DWORD transform[(HIGHEST_TRANSFORMSTATE >> 5) + 1];
@@ -2636,6 +2811,7 @@
@@ -2640,6 +2815,7 @@
void stateblock_init_contained_states(struct wined3d_stateblock *stateblock) DECLSPEC_HIDDEN;
void state_cleanup(struct wined3d_state *state) DECLSPEC_HIDDEN;
@ -2150,7 +2150,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
HRESULT state_init(struct wined3d_state *state, const struct wined3d_gl_info *gl_info,
const struct wined3d_d3d_info *d3d_info, DWORD flags) DECLSPEC_HIDDEN;
void state_unbind_resources(struct wined3d_state *state) DECLSPEC_HIDDEN;
@@ -2686,6 +2862,32 @@
@@ -2690,6 +2866,32 @@
void wined3d_cs_destroy(struct wined3d_cs *cs) DECLSPEC_HIDDEN;
void wined3d_cs_switch_onscreen_ds(struct wined3d_cs *cs, struct wined3d_context *context,
struct wined3d_surface *depth_stencil) DECLSPEC_HIDDEN;
@ -2183,7 +2183,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
void wined3d_cs_emit_clear(struct wined3d_cs *cs, DWORD rect_count, const RECT *rects,
DWORD flags, const struct wined3d_color *color, float depth, DWORD stencil) DECLSPEC_HIDDEN;
@@ -2735,6 +2937,7 @@
@@ -2739,6 +2941,7 @@
void wined3d_cs_emit_set_vertex_declaration(struct wined3d_cs *cs,
struct wined3d_vertex_declaration *declaration) DECLSPEC_HIDDEN;
void wined3d_cs_emit_set_viewport(struct wined3d_cs *cs, const struct wined3d_viewport *viewport) DECLSPEC_HIDDEN;
@ -2191,7 +2191,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
void wined3d_cs_emit_set_consts_f(struct wined3d_cs *cs, UINT start_register, const float *constants,
UINT vector4f_count, enum wined3d_shader_type type) DECLSPEC_HIDDEN;
void wined3d_cs_emit_set_consts_b(struct wined3d_cs *cs, UINT start_register,
@@ -2794,6 +2997,7 @@
@@ -2798,6 +3001,7 @@
struct wined3d_swapchain *swapchain) DECLSPEC_HIDDEN;
void wined3d_cs_emit_getdc(struct wined3d_cs *cs, struct wined3d_surface *surface) DECLSPEC_HIDDEN;
void wined3d_cs_emit_releasedc(struct wined3d_cs *cs, struct wined3d_surface *surface) DECLSPEC_HIDDEN;
@ -2199,7 +2199,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
/* Direct3D terminology with little modifications. We do not have an issued state
* because only the driver knows about it, but we have a created state because d3d
@@ -2808,8 +3012,12 @@
@@ -2812,8 +3016,12 @@
struct wined3d_query_ops
{
HRESULT (*query_get_data)(struct wined3d_query *query, void *data, DWORD data_size, DWORD flags);
@ -2212,7 +2212,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
};
struct wined3d_query
@@ -2823,12 +3031,16 @@
@@ -2827,12 +3035,16 @@
enum wined3d_query_type type;
DWORD data_size;
void *extendedData;
@ -2229,7 +2229,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
/* TODO: Add tests and support for FLOAT16_4 POSITIONT, D3DCOLOR position, other
* fixed function semantics as D3DCOLOR or FLOAT16 */
@@ -2855,7 +3067,9 @@
@@ -2859,7 +3071,9 @@
GLenum buffer_object_usage;
GLenum buffer_type_hint;
DWORD flags;
@ -2239,7 +2239,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
void *map_ptr;
struct wined3d_map_range *maps;
@@ -2880,11 +3094,15 @@
@@ -2884,11 +3098,15 @@
BYTE *buffer_get_sysmem(struct wined3d_buffer *This, struct wined3d_context *context) DECLSPEC_HIDDEN;
void buffer_internal_preload(struct wined3d_buffer *buffer, struct wined3d_context *context,
const struct wined3d_state *state) DECLSPEC_HIDDEN;
@ -2255,7 +2255,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
struct wined3d_rendertarget_view
{
@@ -2923,8 +3141,10 @@
@@ -2927,8 +3145,10 @@
return surface_from_resource(resource);
}
@ -2266,7 +2266,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
struct wined3d_shader_resource_view
{
LONG refcount;
@@ -2937,8 +3157,12 @@
@@ -2941,8 +3161,12 @@
struct wined3d_swapchain_ops
{
void (*swapchain_present)(struct wined3d_swapchain *swapchain, const RECT *src_rect,
@ -2279,7 +2279,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
};
struct wined3d_swapchain
@@ -2978,8 +3202,10 @@
@@ -2982,8 +3206,10 @@
HDC swapchain_get_backup_dc(struct wined3d_swapchain *swapchain) DECLSPEC_HIDDEN;
void swapchain_update_draw_bindings(struct wined3d_swapchain *swapchain) DECLSPEC_HIDDEN;
void swapchain_update_render_to_fbo(struct wined3d_swapchain *swapchain) DECLSPEC_HIDDEN;
@ -2290,7 +2290,7 @@ diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
/*****************************************************************************
* Utility function prototypes
@@ -3183,7 +3409,9 @@
@@ -3187,7 +3413,9 @@
void shader_generate_main(const struct wined3d_shader *shader, struct wined3d_string_buffer *buffer,
const struct wined3d_shader_reg_maps *reg_maps, const DWORD *byte_code, void *backend_ctx) DECLSPEC_HIDDEN;
BOOL shader_match_semantic(const char *semantic_name, enum wined3d_decl_usage usage) DECLSPEC_HIDDEN;
@ -4512,7 +4512,7 @@ diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c
diff --git a/dlls/wined3d/glsl_shader.c b/dlls/wined3d/glsl_shader.c
--- a/dlls/wined3d/glsl_shader.c
+++ b/dlls/wined3d/glsl_shader.c
@@ -1528,9 +1528,17 @@
@@ -1546,9 +1546,17 @@
const struct wined3d_shader_reg_maps *reg_maps, const struct shader_glsl_ctx_priv *ctx_priv)
{
const struct wined3d_shader_version *version = &reg_maps->shader_version;
@ -4530,7 +4530,7 @@ diff --git a/dlls/wined3d/glsl_shader.c b/dlls/wined3d/glsl_shader.c
unsigned int i, extra_constants_needed = 0;
const struct wined3d_shader_lconst *lconst;
const char *prefix;
@@ -1790,7 +1798,11 @@
@@ -1808,7 +1816,11 @@
{
UINT in_count = min(vec4_varyings(version->major, gl_info), shader->limits->packed_input);
@ -4542,7 +4542,7 @@ diff --git a/dlls/wined3d/glsl_shader.c b/dlls/wined3d/glsl_shader.c
shader_addline(buffer, "varying vec4 %s_link[%u];\n", prefix, in_count);
shader_addline(buffer, "vec4 %s_in[%u];\n", prefix, in_count);
}
@@ -1831,6 +1843,7 @@
@@ -1849,6 +1861,7 @@
}
else
{
@ -4550,7 +4550,7 @@ diff --git a/dlls/wined3d/glsl_shader.c b/dlls/wined3d/glsl_shader.c
/* This happens because we do not have proper tracking of the
* constant registers that are actually used, only the max
* limit of the shader version.
@@ -1839,6 +1852,23 @@
@@ -1857,6 +1870,23 @@
* it and just create the uniform.
*/
FIXME("Cannot find a free uniform for vpos correction params\n");
@ -5463,7 +5463,7 @@ diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c
+ const struct wined3d_cs_set_transform *op = data;
+
+ cs->state.transforms[op->state] = *op->matrix;
+ if (op->state < WINED3D_TS_WORLD_MATRIX(cs->device->adapter->gl_info.limits.blends))
+ if (op->state < WINED3D_TS_WORLD_MATRIX(cs->device->adapter->d3d_info.limits.ffp_vertex_blend_matrices))
+ device_invalidate_state(cs->device, STATE_TRANSFORM(op->state));
+}
+
@ -9081,7 +9081,7 @@ diff --git a/dlls/wined3d/surface.c b/dlls/wined3d/surface.c
diff --git a/dlls/wined3d/shader.c b/dlls/wined3d/shader.c
--- a/dlls/wined3d/shader.c
+++ b/dlls/wined3d/shader.c
@@ -1873,7 +1873,11 @@
@@ -1867,7 +1867,11 @@
}
}
@ -9093,7 +9093,7 @@ diff --git a/dlls/wined3d/shader.c b/dlls/wined3d/shader.c
{
HeapFree(GetProcessHeap(), 0, shader->output_signature.elements);
HeapFree(GetProcessHeap(), 0, shader->input_signature.elements);
@@ -2132,10 +2136,16 @@
@@ -2125,10 +2129,16 @@
if (!refcount)
{
@ -9110,7 +9110,7 @@ diff --git a/dlls/wined3d/shader.c b/dlls/wined3d/shader.c
}
return refcount;
@@ -2412,7 +2422,11 @@
@@ -2405,7 +2415,11 @@
memset(args, 0, sizeof(*args)); /* FIXME: Make sure all bits are set. */
if (!gl_info->supported[ARB_FRAMEBUFFER_SRGB] && state->render_states[WINED3D_RS_SRGBWRITEENABLE])
{
@ -9614,7 +9614,7 @@ diff --git a/dlls/wined3d/buffer.c b/dlls/wined3d/buffer.c
diff --git a/dlls/wined3d/directx.c b/dlls/wined3d/directx.c
--- a/dlls/wined3d/directx.c
+++ b/dlls/wined3d/directx.c
@@ -5341,9 +5341,15 @@
@@ -5343,9 +5343,15 @@
DebugBreak();
}