Coverage Report

Created: 2024-05-20 06:11

/src/FreeRDP/winpr/libwinpr/pool/pool.c
Line
Count
Source (jump to first uncovered line)
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/pool.h>
24
#include <winpr/library.h>
25
26
#include "pool.h"
27
28
#ifdef WINPR_THREAD_POOL
29
30
#ifdef _WIN32
31
static INIT_ONCE init_once_module = INIT_ONCE_STATIC_INIT;
32
static PTP_POOL(WINAPI* pCreateThreadpool)(PVOID reserved);
33
static VOID(WINAPI* pCloseThreadpool)(PTP_POOL ptpp);
34
static BOOL(WINAPI* pSetThreadpoolThreadMinimum)(PTP_POOL ptpp, DWORD cthrdMic);
35
static VOID(WINAPI* pSetThreadpoolThreadMaximum)(PTP_POOL ptpp, DWORD cthrdMost);
36
37
static BOOL CALLBACK init_module(PINIT_ONCE once, PVOID param, PVOID* context)
38
{
39
  HMODULE kernel32 = LoadLibraryA("kernel32.dll");
40
  if (kernel32)
41
  {
42
    pCreateThreadpool = (void*)GetProcAddress(kernel32, "CreateThreadpool");
43
    pCloseThreadpool = (void*)GetProcAddress(kernel32, "CloseThreadpool");
44
    pSetThreadpoolThreadMinimum = (void*)GetProcAddress(kernel32, "SetThreadpoolThreadMinimum");
45
    pSetThreadpoolThreadMaximum = (void*)GetProcAddress(kernel32, "SetThreadpoolThreadMaximum");
46
  }
47
  return TRUE;
48
}
49
#endif
50
51
static TP_POOL DEFAULT_POOL = {
52
  0,    /* DWORD Minimum */
53
  500,  /* DWORD Maximum */
54
  NULL, /* wArrayList* Threads */
55
  NULL, /* wQueue* PendingQueue */
56
  NULL, /* HANDLE TerminateEvent */
57
  NULL, /* wCountdownEvent* WorkComplete */
58
};
59
60
static DWORD WINAPI thread_pool_work_func(LPVOID arg)
61
365k
{
62
365k
  DWORD status = 0;
63
365k
  PTP_POOL pool = NULL;
64
365k
  PTP_WORK work = NULL;
65
365k
  HANDLE events[2];
66
365k
  PTP_CALLBACK_INSTANCE callbackInstance = NULL;
67
68
365k
  pool = (PTP_POOL)arg;
69
70
365k
  events[0] = pool->TerminateEvent;
71
365k
  events[1] = Queue_Event(pool->PendingQueue);
72
73
366k
  while (1)
74
365k
  {
75
365k
    status = WaitForMultipleObjects(2, events, FALSE, INFINITE);
76
77
365k
    if (status == WAIT_OBJECT_0)
78
364k
      break;
79
80
1.18k
    if (status != (WAIT_OBJECT_0 + 1))
81
0
      break;
82
83
1.18k
    callbackInstance = (PTP_CALLBACK_INSTANCE)Queue_Dequeue(pool->PendingQueue);
84
85
1.18k
    if (callbackInstance)
86
0
    {
87
0
      work = callbackInstance->Work;
88
0
      work->WorkCallback(callbackInstance, work->CallbackParameter, work);
89
0
      CountdownEvent_Signal(pool->WorkComplete, 1);
90
0
      free(callbackInstance);
91
0
    }
92
1.18k
  }
93
94
365k
  ExitThread(0);
95
365k
  return 0;
96
365k
}
97
98
static void threads_close(void* thread)
99
365k
{
100
365k
  WaitForSingleObject(thread, INFINITE);
101
365k
  CloseHandle(thread);
102
365k
}
103
104
static BOOL InitializeThreadpool(PTP_POOL pool)
105
11.4k
{
106
11.4k
  BOOL rc = FALSE;
107
11.4k
  wObject* obj = NULL;
108
11.4k
  HANDLE thread = NULL;
109
110
11.4k
  if (pool->Threads)
111
0
    return TRUE;
112
113
11.4k
  pool->Minimum = 0;
114
11.4k
  pool->Maximum = 500;
115
116
11.4k
  if (!(pool->PendingQueue = Queue_New(TRUE, -1, -1)))
117
0
    goto fail;
118
119
11.4k
  if (!(pool->WorkComplete = CountdownEvent_New(0)))
120
0
    goto fail;
121
122
11.4k
  if (!(pool->TerminateEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
123
0
    goto fail;
124
125
11.4k
  if (!(pool->Threads = ArrayList_New(TRUE)))
126
0
    goto fail;
127
128
11.4k
  obj = ArrayList_Object(pool->Threads);
129
11.4k
  obj->fnObjectFree = threads_close;
130
131
57.1k
  for (int index = 0; index < 4; index++)
132
45.7k
  {
133
45.7k
    if (!(thread = CreateThread(NULL, 0, thread_pool_work_func, (void*)pool, 0, NULL)))
134
0
    {
135
0
      goto fail;
136
0
    }
137
138
45.7k
    if (!ArrayList_Append(pool->Threads, thread))
139
0
    {
140
0
      CloseHandle(thread);
141
0
      goto fail;
142
0
    }
143
45.7k
  }
144
145
11.4k
  rc = TRUE;
146
147
11.4k
fail:
148
11.4k
  return rc;
149
11.4k
}
150
151
PTP_POOL GetDefaultThreadpool(void)
152
0
{
153
0
  PTP_POOL pool = NULL;
154
155
0
  pool = &DEFAULT_POOL;
156
157
0
  if (!InitializeThreadpool(pool))
158
0
    return NULL;
159
160
0
  return pool;
161
0
}
162
163
PTP_POOL winpr_CreateThreadpool(PVOID reserved)
164
11.4k
{
165
11.4k
  PTP_POOL pool = NULL;
166
#ifdef _WIN32
167
  InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL);
168
  if (pCreateThreadpool)
169
    return pCreateThreadpool(reserved);
170
#else
171
11.4k
  WINPR_UNUSED(reserved);
172
11.4k
#endif
173
11.4k
  if (!(pool = (PTP_POOL)calloc(1, sizeof(TP_POOL))))
174
0
    return NULL;
175
176
11.4k
  if (!InitializeThreadpool(pool))
177
0
  {
178
0
    winpr_CloseThreadpool(pool);
179
0
    return NULL;
180
0
  }
181
182
11.4k
  return pool;
183
11.4k
}
184
185
VOID winpr_CloseThreadpool(PTP_POOL ptpp)
186
11.4k
{
187
#ifdef _WIN32
188
  InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL);
189
  if (pCloseThreadpool)
190
  {
191
    pCloseThreadpool(ptpp);
192
    return;
193
  }
194
#endif
195
11.4k
  SetEvent(ptpp->TerminateEvent);
196
197
11.4k
  ArrayList_Free(ptpp->Threads);
198
11.4k
  Queue_Free(ptpp->PendingQueue);
199
11.4k
  CountdownEvent_Free(ptpp->WorkComplete);
200
11.4k
  CloseHandle(ptpp->TerminateEvent);
201
202
11.4k
  {
203
11.4k
    TP_POOL empty = { 0 };
204
11.4k
    *ptpp = empty;
205
11.4k
  }
206
207
11.4k
  if (ptpp != &DEFAULT_POOL)
208
11.4k
    free(ptpp);
209
11.4k
}
210
211
BOOL winpr_SetThreadpoolThreadMinimum(PTP_POOL ptpp, DWORD cthrdMic)
212
11.4k
{
213
11.4k
  HANDLE thread = NULL;
214
#ifdef _WIN32
215
  InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL);
216
  if (pSetThreadpoolThreadMinimum)
217
    return pSetThreadpoolThreadMinimum(ptpp, cthrdMic);
218
#endif
219
11.4k
  ptpp->Minimum = cthrdMic;
220
221
331k
  while (ArrayList_Count(ptpp->Threads) < ptpp->Minimum)
222
319k
  {
223
319k
    if (!(thread = CreateThread(NULL, 0, thread_pool_work_func, (void*)ptpp, 0, NULL)))
224
0
    {
225
0
      return FALSE;
226
0
    }
227
228
319k
    if (!ArrayList_Append(ptpp->Threads, thread))
229
0
    {
230
0
      CloseHandle(thread);
231
0
      return FALSE;
232
0
    }
233
319k
  }
234
235
11.4k
  return TRUE;
236
11.4k
}
237
238
VOID winpr_SetThreadpoolThreadMaximum(PTP_POOL ptpp, DWORD cthrdMost)
239
0
{
240
#ifdef _WIN32
241
  InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL);
242
  if (pSetThreadpoolThreadMaximum)
243
  {
244
    pSetThreadpoolThreadMaximum(ptpp, cthrdMost);
245
    return;
246
  }
247
#endif
248
0
  ptpp->Maximum = cthrdMost;
249
0
}
250
251
#endif /* WINPR_THREAD_POOL defined */