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.
This commit is contained in:
Sebastian Lackner 2015-07-01 22:55:30 +02:00 committed by Alexandre Julliard
parent 8fc2430c2a
commit eb974bcd7a
3 changed files with 98 additions and 10 deletions

View File

@ -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)

View File

@ -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);

View File

@ -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 );
}
@ -1724,6 +1752,56 @@ NTSTATUS WINAPI TpAllocWork( TP_WORK **out, PTP_WORK_CALLBACK callback, PVOID us
return STATUS_SUCCESS;
}
/***********************************************************************
* 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.@)
*/