Sebastian Lackner : ntdll: Implement instance objects and the TpCallbackMayRunLong function.

Alexandre Julliard julliard at wine.codeweavers.com
Thu Jul 2 07:18:07 CDT 2015


Module: wine
Branch: master
Commit: eb974bcd7a4eb6e0205c6309f48b885701f3a56f
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=eb974bcd7a4eb6e0205c6309f48b885701f3a56f

Author: Sebastian Lackner <sebastian at fds-team.de>
Date:   Wed Jul  1 22:55:30 2015 +0200

ntdll: Implement instance objects and the TpCallbackMayRunLong function.

The instance is marked as long-running even if TpCallbackMayRunLong fails,
a second call will lead to an exception on Windows.

---

 dlls/ntdll/ntdll.spec         |  1 +
 dlls/ntdll/tests/threadpool.c |  9 ++++
 dlls/ntdll/threadpool.c       | 98 ++++++++++++++++++++++++++++++++++++++-----
 3 files changed, 98 insertions(+), 10 deletions(-)

diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec
index 4b5ece5..5a698c4 100644
--- a/dlls/ntdll/ntdll.spec
+++ b/dlls/ntdll/ntdll.spec
@@ -973,6 +973,7 @@
 @ stdcall TpAllocCleanupGroup(ptr)
 @ stdcall TpAllocPool(ptr ptr)
 @ stdcall TpAllocWork(ptr ptr ptr ptr)
+@ stdcall TpCallbackMayRunLong(ptr)
 @ stdcall TpPostWork(ptr)
 @ stdcall TpReleaseCleanupGroup(ptr)
 @ stdcall TpReleaseCleanupGroupMembers(ptr long ptr)
diff --git a/dlls/ntdll/tests/threadpool.c b/dlls/ntdll/tests/threadpool.c
index 2d64471..e920300 100644
--- a/dlls/ntdll/tests/threadpool.c
+++ b/dlls/ntdll/tests/threadpool.c
@@ -24,6 +24,7 @@ static HMODULE hntdll = 0;
 static NTSTATUS (WINAPI *pTpAllocCleanupGroup)(TP_CLEANUP_GROUP **);
 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 *pTpPostWork)(TP_WORK *);
 static VOID     (WINAPI *pTpReleaseCleanupGroup)(TP_CLEANUP_GROUP *);
 static VOID     (WINAPI *pTpReleaseCleanupGroupMembers)(TP_CLEANUP_GROUP *,BOOL,PVOID);
@@ -53,6 +54,7 @@ static BOOL init_threadpool(void)
     NTDLL_GET_PROC(TpAllocCleanupGroup);
     NTDLL_GET_PROC(TpAllocPool);
     NTDLL_GET_PROC(TpAllocWork);
+    NTDLL_GET_PROC(TpCallbackMayRunLong);
     NTDLL_GET_PROC(TpPostWork);
     NTDLL_GET_PROC(TpReleaseCleanupGroup);
     NTDLL_GET_PROC(TpReleaseCleanupGroupMembers);
@@ -312,8 +314,15 @@ static DWORD group_cancel_tid;
 static void CALLBACK group_cancel_cb(TP_CALLBACK_INSTANCE *instance, void *userdata)
 {
     HANDLE *semaphores = userdata;
+    NTSTATUS status;
     DWORD result;
+
     trace("Running group cancel callback\n");
+
+    status = pTpCallbackMayRunLong(instance);
+    ok(status == STATUS_TOO_MANY_THREADS || broken(status == 1) /* Win Vista / 2008 */,
+       "expected STATUS_TOO_MANY_THREADS, got %08x\n", status);
+
     result = WaitForSingleObject(semaphores[0], 1000);
     ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
     ReleaseSemaphore(semaphores[1], 1, NULL);
diff --git a/dlls/ntdll/threadpool.c b/dlls/ntdll/threadpool.c
index 4ae81b0..a0090c0 100644
--- a/dlls/ntdll/threadpool.c
+++ b/dlls/ntdll/threadpool.c
@@ -173,6 +173,7 @@ struct threadpool_object
     PVOID                   userdata;
     PTP_CLEANUP_GROUP_CANCEL_CALLBACK group_cancel_callback;
     PTP_SIMPLE_CALLBACK     finalization_callback;
+    BOOL                    may_run_long;
     HMODULE                 race_dll;
     /* information about the group, locked via .group->cs */
     struct list             group_entry;
@@ -196,6 +197,14 @@ struct threadpool_object
     } u;
 };
 
+/* internal threadpool instance representation */
+struct threadpool_instance
+{
+    struct threadpool_object *object;
+    DWORD                   threadid;
+    BOOL                    may_run_long;
+};
+
 /* internal threadpool group representation */
 struct threadpool_group
 {
@@ -223,6 +232,11 @@ static inline struct threadpool_group *impl_from_TP_CLEANUP_GROUP( TP_CLEANUP_GR
     return (struct threadpool_group *)group;
 }
 
+static inline struct threadpool_instance *impl_from_TP_CALLBACK_INSTANCE( TP_CALLBACK_INSTANCE *instance )
+{
+    return (struct threadpool_instance *)instance;
+}
+
 static void CALLBACK threadpool_worker_proc( void *param );
 static void tp_object_submit( struct threadpool_object *object );
 static void tp_object_shutdown( struct threadpool_object *object );
@@ -1375,6 +1389,7 @@ static void tp_object_initialize( struct threadpool_object *object, struct threa
     object->userdata                = userdata;
     object->group_cancel_callback   = NULL;
     object->finalization_callback   = NULL;
+    object->may_run_long            = 0;
     object->race_dll                = NULL;
 
     memset( &object->group_entry, 0, sizeof(object->group_entry) );
@@ -1393,9 +1408,14 @@ static void tp_object_initialize( struct threadpool_object *object, struct threa
         object->group = impl_from_TP_CLEANUP_GROUP( environment->CleanupGroup );
         object->group_cancel_callback   = environment->CleanupGroupCancelCallback;
         object->finalization_callback   = environment->FinalizationCallback;
+        object->may_run_long            = environment->u.s.LongFunction != 0;
         object->race_dll                = environment->RaceDll;
 
-        WARN( "environment not fully implemented yet\n" );
+        if (environment->ActivationContext)
+            FIXME( "activation context not supported yet\n" );
+
+        if (environment->u.s.Persistent)
+            FIXME( "persistent threads not supported yet\n" );
     }
 
     if (object->race_dll)
@@ -1578,6 +1598,8 @@ static BOOL tp_object_release( struct threadpool_object *object )
  */
 static void CALLBACK threadpool_worker_proc( void *param )
 {
+    TP_CALLBACK_INSTANCE *callback_instance;
+    struct threadpool_instance instance;
     struct threadpool *pool = param;
     LARGE_INTEGER timeout;
     struct list *ptr;
@@ -1603,22 +1625,28 @@ static void CALLBACK threadpool_worker_proc( void *param )
             pool->num_busy_workers++;
             RtlLeaveCriticalSection( &pool->cs );
 
+            /* Initialize threadpool instance struct. */
+            callback_instance = (TP_CALLBACK_INSTANCE *)&instance;
+            instance.object                     = object;
+            instance.threadid                   = GetCurrentThreadId();
+            instance.may_run_long               = object->may_run_long;
+
             switch (object->type)
             {
                 case TP_OBJECT_TYPE_SIMPLE:
                 {
-                    TRACE( "executing simple callback %p(NULL, %p)\n",
-                           object->u.simple.callback, object->userdata );
-                    object->u.simple.callback( NULL, object->userdata );
+                    TRACE( "executing simple callback %p(%p, %p)\n",
+                           object->u.simple.callback, callback_instance, object->userdata );
+                    object->u.simple.callback( callback_instance, object->userdata );
                     TRACE( "callback %p returned\n", object->u.simple.callback );
                     break;
                 }
 
                 case TP_OBJECT_TYPE_WORK:
                 {
-                    TRACE( "executing work callback %p(NULL, %p, %p)\n",
-                           object->u.work.callback, object->userdata, object );
-                    object->u.work.callback( NULL, object->userdata, (TP_WORK *)object );
+                    TRACE( "executing work callback %p(%p, %p, %p)\n",
+                           object->u.work.callback, callback_instance, object->userdata, object );
+                    object->u.work.callback( callback_instance, object->userdata, (TP_WORK *)object );
                     TRACE( "callback %p returned\n", object->u.work.callback );
                     break;
                 }
@@ -1631,9 +1659,9 @@ static void CALLBACK threadpool_worker_proc( void *param )
             /* Execute finalization callback. */
             if (object->finalization_callback)
             {
-                TRACE( "executing finalization callback %p(NULL, %p)\n",
-                       object->finalization_callback, object->userdata );
-                object->finalization_callback( NULL, object->userdata );
+                TRACE( "executing finalization callback %p(%p, %p)\n",
+                       object->finalization_callback, callback_instance, object->userdata );
+                object->finalization_callback( callback_instance, object->userdata );
                 TRACE( "callback %p returned\n", object->finalization_callback );
             }
 
@@ -1725,6 +1753,56 @@ NTSTATUS WINAPI TpAllocWork( TP_WORK **out, PTP_WORK_CALLBACK callback, PVOID us
 }
 
 /***********************************************************************
+ *           TpCallbackMayRunLong    (NTDLL.@)
+ */
+NTSTATUS WINAPI TpCallbackMayRunLong( TP_CALLBACK_INSTANCE *instance )
+{
+    struct threadpool_instance *this = impl_from_TP_CALLBACK_INSTANCE( instance );
+    struct threadpool_object *object = this->object;
+    struct threadpool *pool;
+    NTSTATUS status = STATUS_SUCCESS;
+
+    TRACE( "%p\n", instance );
+
+    if (this->threadid != GetCurrentThreadId())
+    {
+        ERR("called from wrong thread, ignoring\n");
+        return STATUS_UNSUCCESSFUL; /* FIXME */
+    }
+
+    if (this->may_run_long)
+        return STATUS_SUCCESS;
+
+    pool = object->pool;
+    RtlEnterCriticalSection( &pool->cs );
+
+    /* Start new worker threads if required. */
+    if (pool->num_busy_workers >= pool->num_workers)
+    {
+        if (pool->num_workers < pool->max_workers)
+        {
+            HANDLE thread;
+            status = RtlCreateUserThread( GetCurrentProcess(), NULL, FALSE, NULL, 0, 0,
+                                          threadpool_worker_proc, pool, &thread, NULL );
+            if (status == STATUS_SUCCESS)
+            {
+                interlocked_inc( &pool->refcount );
+                pool->num_workers++;
+                NtClose( thread );
+            }
+        }
+        else
+        {
+            status = STATUS_TOO_MANY_THREADS;
+        }
+    }
+
+    RtlLeaveCriticalSection( &pool->cs );
+    this->may_run_long = TRUE;
+    return status;
+}
+
+/***********************************************************************
  *           TpPostWork    (NTDLL.@)
  */
 VOID WINAPI TpPostWork( TP_WORK *work )




More information about the wine-cvs mailing list