new 818051de2c8769029049ce3d36c6b856f47496c9
[wine/hacks.git] / dlls / ntdll / threadpool.c
blob72ea0cf9be11222846a0e223838a68bf43a70280
1 /*
2 * Thread pooling
4 * Copyright (c) 2006 Robert Shearman
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include "config.h"
22 #include "wine/port.h"
24 #include <stdarg.h>
25 #include <limits.h>
27 #define NONAMELESSUNION
28 #include "ntstatus.h"
29 #define WIN32_NO_STATUS
30 #include "winternl.h"
32 #include "wine/debug.h"
33 #include "wine/list.h"
35 #include "ntdll_misc.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(threadpool);
39 #define WORKER_TIMEOUT 30000 /* 30 seconds */
41 static LONG num_workers;
42 static LONG num_work_items;
43 static LONG num_busy_workers;
45 static struct list work_item_list = LIST_INIT(work_item_list);
46 static HANDLE work_item_event;
48 static RTL_CRITICAL_SECTION threadpool_cs;
49 static RTL_CRITICAL_SECTION_DEBUG critsect_debug =
51 0, 0, &threadpool_cs,
52 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
53 0, 0, { (DWORD_PTR)(__FILE__ ": threadpool_cs") }
55 static RTL_CRITICAL_SECTION threadpool_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
57 struct work_item
59 struct list entry;
60 PRTL_WORK_ITEM_ROUTINE function;
61 PVOID context;
64 static inline LONG interlocked_inc( PLONG dest )
66 return interlocked_xchg_add( (int *)dest, 1 ) + 1;
69 static inline LONG interlocked_dec( PLONG dest )
71 return interlocked_xchg_add( (int *)dest, -1 ) - 1;
74 static void WINAPI worker_thread_proc(void * param)
76 interlocked_inc(&num_workers);
78 /* free the work item memory sooner to reduce memory usage */
79 while (TRUE)
81 if (num_work_items > 0)
83 struct list *item;
84 RtlEnterCriticalSection(&threadpool_cs);
85 item = list_head(&work_item_list);
86 if (item)
88 struct work_item *work_item_ptr = LIST_ENTRY(item, struct work_item, entry);
89 struct work_item work_item;
90 list_remove(&work_item_ptr->entry);
91 interlocked_dec(&num_work_items);
93 RtlLeaveCriticalSection(&threadpool_cs);
95 work_item = *work_item_ptr;
96 RtlFreeHeap(GetProcessHeap(), 0, work_item_ptr);
98 TRACE("executing %p(%p)\n", work_item.function, work_item.context);
100 interlocked_inc(&num_busy_workers);
102 /* do the work */
103 work_item.function(work_item.context);
105 interlocked_dec(&num_busy_workers);
107 else
108 RtlLeaveCriticalSection(&threadpool_cs);
110 else
112 NTSTATUS status;
113 LARGE_INTEGER timeout;
114 timeout.QuadPart = -(WORKER_TIMEOUT * (ULONGLONG)10000);
115 status = NtWaitForSingleObject(work_item_event, FALSE, &timeout);
116 if (status != STATUS_WAIT_0)
117 break;
121 interlocked_dec(&num_workers);
123 RtlExitUserThread(0);
125 /* never reached */
128 static NTSTATUS add_work_item_to_queue(struct work_item *work_item)
130 NTSTATUS status;
132 RtlEnterCriticalSection(&threadpool_cs);
133 list_add_tail(&work_item_list, &work_item->entry);
134 num_work_items++;
135 RtlLeaveCriticalSection(&threadpool_cs);
137 if (!work_item_event)
139 HANDLE sem;
140 status = NtCreateSemaphore(&sem, SEMAPHORE_ALL_ACCESS, NULL, 1, LONG_MAX);
141 if (interlocked_cmpxchg_ptr( (PVOID *)&work_item_event, (PVOID)sem, 0 ))
142 NtClose(sem); /* somebody beat us to it */
144 else
145 status = NtReleaseSemaphore(work_item_event, 1, NULL);
147 return status;
150 /***********************************************************************
151 * RtlQueueWorkItem (NTDLL.@)
153 * Queues a work item into a thread in the thread pool.
155 * PARAMS
156 * Function [I] Work function to execute.
157 * Context [I] Context to pass to the work function when it is executed.
158 * Flags [I] Flags. See notes.
160 * RETURNS
161 * Success: STATUS_SUCCESS.
162 * Failure: Any NTSTATUS code.
164 * NOTES
165 * Flags can be one or more of the following:
166 *|WT_EXECUTEDEFAULT - Executes the work item in a non-I/O worker thread.
167 *|WT_EXECUTEINIOTHREAD - Executes the work item in an I/O worker thread.
168 *|WT_EXECUTEINPERSISTENTTHREAD - Executes the work item in a thread that is persistent.
169 *|WT_EXECUTELONGFUNCTION - Hints that the execution can take a long time.
170 *|WT_TRANSFER_IMPERSONATION - Executes the function with the current access token.
172 NTSTATUS WINAPI RtlQueueWorkItem(PRTL_WORK_ITEM_ROUTINE Function, PVOID Context, ULONG Flags)
174 HANDLE thread;
175 NTSTATUS status;
176 struct work_item *work_item = RtlAllocateHeap(GetProcessHeap(), 0, sizeof(struct work_item));
178 if (!work_item)
179 return STATUS_NO_MEMORY;
181 work_item->function = Function;
182 work_item->context = Context;
184 if (Flags & ~WT_EXECUTELONGFUNCTION)
185 FIXME("Flags 0x%x not supported\n", Flags);
187 status = add_work_item_to_queue(work_item);
189 /* FIXME: tune this algorithm to not be as aggressive with creating threads
190 * if WT_EXECUTELONGFUNCTION isn't specified */
191 if ((status == STATUS_SUCCESS) &&
192 ((num_workers == 0) || (num_workers == num_busy_workers)))
194 status = RtlCreateUserThread( GetCurrentProcess(), NULL, FALSE,
195 NULL, 0, 0,
196 worker_thread_proc, NULL, &thread, NULL );
197 if (status == STATUS_SUCCESS)
198 NtClose( thread );
200 /* NOTE: we don't care if we couldn't create the thread if there is at
201 * least one other available to process the request */
202 if ((num_workers > 0) && (status != STATUS_SUCCESS))
203 status = STATUS_SUCCESS;
206 if (status != STATUS_SUCCESS)
208 RtlEnterCriticalSection(&threadpool_cs);
210 interlocked_dec(&num_work_items);
211 list_remove(&work_item->entry);
212 RtlFreeHeap(GetProcessHeap(), 0, work_item);
214 RtlLeaveCriticalSection(&threadpool_cs);
216 return status;
219 return STATUS_SUCCESS;