Coverage Report

Created: 2026-03-04 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/winpr/libwinpr/pool/pool.c
Line
Count
Source
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * Thread Pool API (Pool)
4
 *
5
 * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
20
#include <winpr/config.h>
21
22
#include <winpr/crt.h>
23
#include <winpr/sysinfo.h>
24
#include <winpr/pool.h>
25
#include <winpr/library.h>
26
27
#include "pool.h"
28
29
#ifdef WINPR_THREAD_POOL
30
31
#ifdef _WIN32
32
static INIT_ONCE init_once_module = INIT_ONCE_STATIC_INIT;
33
static PTP_POOL(WINAPI* pCreateThreadpool)(PVOID reserved);
34
static VOID(WINAPI* pCloseThreadpool)(PTP_POOL ptpp);
35
static BOOL(WINAPI* pSetThreadpoolThreadMinimum)(PTP_POOL ptpp, DWORD cthrdMic);
36
static VOID(WINAPI* pSetThreadpoolThreadMaximum)(PTP_POOL ptpp, DWORD cthrdMost);
37
38
static BOOL CALLBACK init_module(PINIT_ONCE once, PVOID param, PVOID* context)
39
{
40
  HMODULE kernel32 = LoadLibraryA("kernel32.dll");
41
  if (kernel32)
42
  {
43
    pCreateThreadpool = GetProcAddressAs(kernel32, "CreateThreadpool", void*);
44
    pCloseThreadpool = GetProcAddressAs(kernel32, "CloseThreadpool", void*);
45
    pSetThreadpoolThreadMinimum =
46
        GetProcAddressAs(kernel32, "SetThreadpoolThreadMinimum", void*);
47
    pSetThreadpoolThreadMaximum =
48
        GetProcAddressAs(kernel32, "SetThreadpoolThreadMaximum", void*);
49
  }
50
  return TRUE;
51
}
52
#endif
53
54
static TP_POOL DEFAULT_POOL = {
55
  0,       /* DWORD Minimum */
56
  500,     /* DWORD Maximum */
57
  nullptr, /* wArrayList* Threads */
58
  nullptr, /* wQueue* PendingQueue */
59
  nullptr, /* HANDLE TerminateEvent */
60
  nullptr, /* wCountdownEvent* WorkComplete */
61
};
62
63
static DWORD WINAPI thread_pool_work_func(LPVOID arg)
64
0
{
65
0
  DWORD status = 0;
66
0
  PTP_POOL pool = nullptr;
67
0
  PTP_WORK work = nullptr;
68
0
  HANDLE events[2];
69
0
  PTP_CALLBACK_INSTANCE callbackInstance = nullptr;
70
71
0
  pool = (PTP_POOL)arg;
72
73
0
  events[0] = pool->TerminateEvent;
74
0
  events[1] = Queue_Event(pool->PendingQueue);
75
76
0
  while (1)
77
0
  {
78
0
    status = WaitForMultipleObjects(2, events, FALSE, INFINITE);
79
80
0
    if (status == WAIT_OBJECT_0)
81
0
      break;
82
83
0
    if (status != (WAIT_OBJECT_0 + 1))
84
0
      break;
85
86
0
    callbackInstance = (PTP_CALLBACK_INSTANCE)Queue_Dequeue(pool->PendingQueue);
87
88
0
    if (callbackInstance)
89
0
    {
90
0
      work = callbackInstance->Work;
91
0
      work->WorkCallback(callbackInstance, work->CallbackParameter, work);
92
0
      CountdownEvent_Signal(pool->WorkComplete, 1);
93
0
      free(callbackInstance);
94
0
    }
95
0
  }
96
97
0
  ExitThread(0);
98
0
  return 0;
99
0
}
100
101
static void threads_close(void* thread)
102
0
{
103
0
  (void)WaitForSingleObject(thread, INFINITE);
104
0
  (void)CloseHandle(thread);
105
0
}
106
107
static BOOL InitializeThreadpool(PTP_POOL pool)
108
0
{
109
0
  BOOL rc = FALSE;
110
0
  wObject* obj = nullptr;
111
112
0
  if (pool->Threads)
113
0
    return TRUE;
114
115
0
  if (!(pool->PendingQueue = Queue_New(TRUE, -1, -1)))
116
0
    goto fail;
117
118
0
  if (!(pool->WorkComplete = CountdownEvent_New(0)))
119
0
    goto fail;
120
121
0
  if (!(pool->TerminateEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
122
0
    goto fail;
123
124
0
  if (!(pool->Threads = ArrayList_New(TRUE)))
125
0
    goto fail;
126
127
0
  obj = ArrayList_Object(pool->Threads);
128
0
  obj->fnObjectFree = threads_close;
129
130
#if !defined(WINPR_THREADPOOL_DEFAULT_MIN_COUNT)
131
#error "WINPR_THREADPOOL_DEFAULT_MIN_COUNT must be defined"
132
#endif
133
#if !defined(WINPR_THREADPOOL_DEFAULT_MAX_COUNT)
134
#error "WINPR_THREADPOOL_DEFAULT_MAX_COUNT must be defined"
135
#endif
136
137
0
  {
138
0
    SYSTEM_INFO info = WINPR_C_ARRAY_INIT;
139
0
    GetSystemInfo(&info);
140
141
0
    DWORD min = info.dwNumberOfProcessors;
142
0
    DWORD max = info.dwNumberOfProcessors;
143
0
    if (info.dwNumberOfProcessors < WINPR_THREADPOOL_DEFAULT_MIN_COUNT)
144
0
      min = WINPR_THREADPOOL_DEFAULT_MIN_COUNT;
145
0
    if (info.dwNumberOfProcessors > WINPR_THREADPOOL_DEFAULT_MAX_COUNT)
146
0
      max = WINPR_THREADPOOL_DEFAULT_MAX_COUNT;
147
0
    if (min > max)
148
0
      min = max;
149
150
0
    if (!SetThreadpoolThreadMinimum(pool, min))
151
0
      goto fail;
152
153
0
    SetThreadpoolThreadMaximum(pool, max);
154
0
  }
155
156
0
  rc = TRUE;
157
158
0
fail:
159
0
  return rc;
160
0
}
161
162
PTP_POOL GetDefaultThreadpool(void)
163
0
{
164
0
  PTP_POOL pool = &DEFAULT_POOL;
165
166
0
  if (!InitializeThreadpool(pool))
167
0
    return nullptr;
168
169
0
  return pool;
170
0
}
171
172
PTP_POOL winpr_CreateThreadpool(PVOID reserved)
173
0
{
174
0
  PTP_POOL pool = nullptr;
175
#ifdef _WIN32
176
  if (!InitOnceExecuteOnce(&init_once_module, init_module, nullptr, nullptr))
177
    return nullptr;
178
  if (pCreateThreadpool)
179
    return pCreateThreadpool(reserved);
180
#else
181
0
  WINPR_UNUSED(reserved);
182
0
#endif
183
0
  if (!(pool = (PTP_POOL)calloc(1, sizeof(TP_POOL))))
184
0
    return nullptr;
185
186
0
  if (!InitializeThreadpool(pool))
187
0
  {
188
0
    winpr_CloseThreadpool(pool);
189
0
    return nullptr;
190
0
  }
191
192
0
  return pool;
193
0
}
194
195
VOID winpr_CloseThreadpool(PTP_POOL ptpp)
196
0
{
197
#ifdef _WIN32
198
  if (!InitOnceExecuteOnce(&init_once_module, init_module, nullptr, nullptr))
199
    return;
200
  if (pCloseThreadpool)
201
  {
202
    pCloseThreadpool(ptpp);
203
    return;
204
  }
205
#endif
206
0
  (void)SetEvent(ptpp->TerminateEvent);
207
208
0
  ArrayList_Free(ptpp->Threads);
209
0
  Queue_Free(ptpp->PendingQueue);
210
0
  CountdownEvent_Free(ptpp->WorkComplete);
211
0
  (void)CloseHandle(ptpp->TerminateEvent);
212
213
0
  {
214
0
    TP_POOL empty = WINPR_C_ARRAY_INIT;
215
0
    *ptpp = empty;
216
0
  }
217
218
0
  if (ptpp != &DEFAULT_POOL)
219
0
    free(ptpp);
220
0
}
221
222
BOOL winpr_SetThreadpoolThreadMinimum(PTP_POOL ptpp, DWORD cthrdMic)
223
0
{
224
0
  BOOL rc = FALSE;
225
#ifdef _WIN32
226
  if (!InitOnceExecuteOnce(&init_once_module, init_module, nullptr, nullptr))
227
    return FALSE;
228
  if (pSetThreadpoolThreadMinimum)
229
    return pSetThreadpoolThreadMinimum(ptpp, cthrdMic);
230
#endif
231
0
  ptpp->Minimum = cthrdMic;
232
233
0
  ArrayList_Lock(ptpp->Threads);
234
0
  while (ArrayList_Count(ptpp->Threads) < ptpp->Minimum)
235
0
  {
236
0
    HANDLE thread = CreateThread(nullptr, 0, thread_pool_work_func, (void*)ptpp, 0, nullptr);
237
0
    if (!thread)
238
0
      goto fail;
239
240
0
    if (!ArrayList_Append(ptpp->Threads, thread))
241
0
    {
242
0
      (void)CloseHandle(thread);
243
0
      goto fail;
244
0
    }
245
0
  }
246
247
0
  rc = TRUE;
248
0
fail:
249
0
  ArrayList_Unlock(ptpp->Threads);
250
251
0
  return rc;
252
0
}
253
254
VOID winpr_SetThreadpoolThreadMaximum(PTP_POOL ptpp, DWORD cthrdMost)
255
0
{
256
#ifdef _WIN32
257
  if (!InitOnceExecuteOnce(&init_once_module, init_module, nullptr, nullptr))
258
    return;
259
  if (pSetThreadpoolThreadMaximum)
260
  {
261
    pSetThreadpoolThreadMaximum(ptpp, cthrdMost);
262
    return;
263
  }
264
#endif
265
0
  ptpp->Maximum = cthrdMost;
266
267
0
  ArrayList_Lock(ptpp->Threads);
268
0
  if (ArrayList_Count(ptpp->Threads) > ptpp->Maximum)
269
0
  {
270
0
    (void)SetEvent(ptpp->TerminateEvent);
271
0
    ArrayList_Clear(ptpp->Threads);
272
0
    (void)ResetEvent(ptpp->TerminateEvent);
273
0
  }
274
0
  ArrayList_Unlock(ptpp->Threads);
275
0
  winpr_SetThreadpoolThreadMinimum(ptpp, ptpp->Minimum);
276
0
}
277
278
#endif /* WINPR_THREAD_POOL defined */