Commit eb974bcd authored by Sebastian Lackner's avatar Sebastian Lackner Committed by Alexandre Julliard

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.
parent 8fc2430c
...@@ -973,6 +973,7 @@ ...@@ -973,6 +973,7 @@
@ stdcall TpAllocCleanupGroup(ptr) @ stdcall TpAllocCleanupGroup(ptr)
@ stdcall TpAllocPool(ptr ptr) @ stdcall TpAllocPool(ptr ptr)
@ stdcall TpAllocWork(ptr ptr ptr ptr) @ stdcall TpAllocWork(ptr ptr ptr ptr)
@ stdcall TpCallbackMayRunLong(ptr)
@ stdcall TpPostWork(ptr) @ stdcall TpPostWork(ptr)
@ stdcall TpReleaseCleanupGroup(ptr) @ stdcall TpReleaseCleanupGroup(ptr)
@ stdcall TpReleaseCleanupGroupMembers(ptr long ptr) @ stdcall TpReleaseCleanupGroupMembers(ptr long ptr)
......
...@@ -24,6 +24,7 @@ static HMODULE hntdll = 0; ...@@ -24,6 +24,7 @@ static HMODULE hntdll = 0;
static NTSTATUS (WINAPI *pTpAllocCleanupGroup)(TP_CLEANUP_GROUP **); static NTSTATUS (WINAPI *pTpAllocCleanupGroup)(TP_CLEANUP_GROUP **);
static NTSTATUS (WINAPI *pTpAllocPool)(TP_POOL **,PVOID); static NTSTATUS (WINAPI *pTpAllocPool)(TP_POOL **,PVOID);
static NTSTATUS (WINAPI *pTpAllocWork)(TP_WORK **,PTP_WORK_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 *pTpPostWork)(TP_WORK *); static VOID (WINAPI *pTpPostWork)(TP_WORK *);
static VOID (WINAPI *pTpReleaseCleanupGroup)(TP_CLEANUP_GROUP *); static VOID (WINAPI *pTpReleaseCleanupGroup)(TP_CLEANUP_GROUP *);
static VOID (WINAPI *pTpReleaseCleanupGroupMembers)(TP_CLEANUP_GROUP *,BOOL,PVOID); static VOID (WINAPI *pTpReleaseCleanupGroupMembers)(TP_CLEANUP_GROUP *,BOOL,PVOID);
...@@ -53,6 +54,7 @@ static BOOL init_threadpool(void) ...@@ -53,6 +54,7 @@ static BOOL init_threadpool(void)
NTDLL_GET_PROC(TpAllocCleanupGroup); NTDLL_GET_PROC(TpAllocCleanupGroup);
NTDLL_GET_PROC(TpAllocPool); NTDLL_GET_PROC(TpAllocPool);
NTDLL_GET_PROC(TpAllocWork); NTDLL_GET_PROC(TpAllocWork);
NTDLL_GET_PROC(TpCallbackMayRunLong);
NTDLL_GET_PROC(TpPostWork); NTDLL_GET_PROC(TpPostWork);
NTDLL_GET_PROC(TpReleaseCleanupGroup); NTDLL_GET_PROC(TpReleaseCleanupGroup);
NTDLL_GET_PROC(TpReleaseCleanupGroupMembers); NTDLL_GET_PROC(TpReleaseCleanupGroupMembers);
...@@ -312,8 +314,15 @@ static DWORD group_cancel_tid; ...@@ -312,8 +314,15 @@ static DWORD group_cancel_tid;
static void CALLBACK group_cancel_cb(TP_CALLBACK_INSTANCE *instance, void *userdata) static void CALLBACK group_cancel_cb(TP_CALLBACK_INSTANCE *instance, void *userdata)
{ {
HANDLE *semaphores = userdata; HANDLE *semaphores = userdata;
NTSTATUS status;
DWORD result; DWORD result;
trace("Running group cancel callback\n"); 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); result = WaitForSingleObject(semaphores[0], 1000);
ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
ReleaseSemaphore(semaphores[1], 1, NULL); ReleaseSemaphore(semaphores[1], 1, NULL);
......
...@@ -173,6 +173,7 @@ struct threadpool_object ...@@ -173,6 +173,7 @@ struct threadpool_object
PVOID userdata; PVOID userdata;
PTP_CLEANUP_GROUP_CANCEL_CALLBACK group_cancel_callback; PTP_CLEANUP_GROUP_CANCEL_CALLBACK group_cancel_callback;
PTP_SIMPLE_CALLBACK finalization_callback; PTP_SIMPLE_CALLBACK finalization_callback;
BOOL may_run_long;
HMODULE race_dll; HMODULE race_dll;
/* information about the group, locked via .group->cs */ /* information about the group, locked via .group->cs */
struct list group_entry; struct list group_entry;
...@@ -196,6 +197,14 @@ struct threadpool_object ...@@ -196,6 +197,14 @@ struct threadpool_object
} u; } u;
}; };
/* internal threadpool instance representation */
struct threadpool_instance
{
struct threadpool_object *object;
DWORD threadid;
BOOL may_run_long;
};
/* internal threadpool group representation */ /* internal threadpool group representation */
struct threadpool_group struct threadpool_group
{ {
...@@ -223,6 +232,11 @@ static inline struct threadpool_group *impl_from_TP_CLEANUP_GROUP( TP_CLEANUP_GR ...@@ -223,6 +232,11 @@ static inline struct threadpool_group *impl_from_TP_CLEANUP_GROUP( TP_CLEANUP_GR
return (struct threadpool_group *)group; 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 CALLBACK threadpool_worker_proc( void *param );
static void tp_object_submit( struct threadpool_object *object ); static void tp_object_submit( struct threadpool_object *object );
static void tp_object_shutdown( 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 ...@@ -1375,6 +1389,7 @@ static void tp_object_initialize( struct threadpool_object *object, struct threa
object->userdata = userdata; object->userdata = userdata;
object->group_cancel_callback = NULL; object->group_cancel_callback = NULL;
object->finalization_callback = NULL; object->finalization_callback = NULL;
object->may_run_long = 0;
object->race_dll = NULL; object->race_dll = NULL;
memset( &object->group_entry, 0, sizeof(object->group_entry) ); memset( &object->group_entry, 0, sizeof(object->group_entry) );
...@@ -1393,9 +1408,14 @@ static void tp_object_initialize( struct threadpool_object *object, struct threa ...@@ -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 = impl_from_TP_CLEANUP_GROUP( environment->CleanupGroup );
object->group_cancel_callback = environment->CleanupGroupCancelCallback; object->group_cancel_callback = environment->CleanupGroupCancelCallback;
object->finalization_callback = environment->FinalizationCallback; object->finalization_callback = environment->FinalizationCallback;
object->may_run_long = environment->u.s.LongFunction != 0;
object->race_dll = environment->RaceDll; 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) if (object->race_dll)
...@@ -1578,6 +1598,8 @@ static BOOL tp_object_release( struct threadpool_object *object ) ...@@ -1578,6 +1598,8 @@ static BOOL tp_object_release( struct threadpool_object *object )
*/ */
static void CALLBACK threadpool_worker_proc( void *param ) static void CALLBACK threadpool_worker_proc( void *param )
{ {
TP_CALLBACK_INSTANCE *callback_instance;
struct threadpool_instance instance;
struct threadpool *pool = param; struct threadpool *pool = param;
LARGE_INTEGER timeout; LARGE_INTEGER timeout;
struct list *ptr; struct list *ptr;
...@@ -1603,22 +1625,28 @@ static void CALLBACK threadpool_worker_proc( void *param ) ...@@ -1603,22 +1625,28 @@ static void CALLBACK threadpool_worker_proc( void *param )
pool->num_busy_workers++; pool->num_busy_workers++;
RtlLeaveCriticalSection( &pool->cs ); 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) switch (object->type)
{ {
case TP_OBJECT_TYPE_SIMPLE: case TP_OBJECT_TYPE_SIMPLE:
{ {
TRACE( "executing simple callback %p(NULL, %p)\n", TRACE( "executing simple callback %p(%p, %p)\n",
object->u.simple.callback, object->userdata ); object->u.simple.callback, callback_instance, object->userdata );
object->u.simple.callback( NULL, object->userdata ); object->u.simple.callback( callback_instance, object->userdata );
TRACE( "callback %p returned\n", object->u.simple.callback ); TRACE( "callback %p returned\n", object->u.simple.callback );
break; break;
} }
case TP_OBJECT_TYPE_WORK: case TP_OBJECT_TYPE_WORK:
{ {
TRACE( "executing work callback %p(NULL, %p, %p)\n", TRACE( "executing work callback %p(%p, %p, %p)\n",
object->u.work.callback, object->userdata, object ); object->u.work.callback, callback_instance, object->userdata, object );
object->u.work.callback( NULL, object->userdata, (TP_WORK *)object ); object->u.work.callback( callback_instance, object->userdata, (TP_WORK *)object );
TRACE( "callback %p returned\n", object->u.work.callback ); TRACE( "callback %p returned\n", object->u.work.callback );
break; break;
} }
...@@ -1631,9 +1659,9 @@ static void CALLBACK threadpool_worker_proc( void *param ) ...@@ -1631,9 +1659,9 @@ static void CALLBACK threadpool_worker_proc( void *param )
/* Execute finalization callback. */ /* Execute finalization callback. */
if (object->finalization_callback) if (object->finalization_callback)
{ {
TRACE( "executing finalization callback %p(NULL, %p)\n", TRACE( "executing finalization callback %p(%p, %p)\n",
object->finalization_callback, object->userdata ); object->finalization_callback, callback_instance, object->userdata );
object->finalization_callback( NULL, object->userdata ); object->finalization_callback( callback_instance, object->userdata );
TRACE( "callback %p returned\n", object->finalization_callback ); TRACE( "callback %p returned\n", object->finalization_callback );
} }
...@@ -1725,6 +1753,56 @@ NTSTATUS WINAPI TpAllocWork( TP_WORK **out, PTP_WORK_CALLBACK callback, PVOID us ...@@ -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.@) * TpPostWork (NTDLL.@)
*/ */
VOID WINAPI TpPostWork( TP_WORK *work ) VOID WINAPI TpPostWork( TP_WORK *work )
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment