FreeRDP
Loading...
Searching...
No Matches
StreamPool.c
1
20#include <winpr/config.h>
21#include <winpr/interlocked.h>
22
23#include <winpr/crt.h>
24#include <winpr/wlog.h>
25
26#include <winpr/collections.h>
27
28#include "../stream.h"
29#include "../log.h"
30#define XTAG WINPR_TAG("utils.streampool")
31
32#if !defined(STREAMPOOL_SIZE_LIMIT)
33#error "CMake must define STREAMPOOL_SIZE_LIMIT=<unsigned>"
34#endif
35static const size_t POOL_COMMON_LIMIT = STREAMPOOL_SIZE_LIMIT;
36
37struct s_StreamPoolEntry
38{
39#if defined(WITH_DEBUG_STREAMPOOL)
40 char** msg;
41 size_t lines;
42#endif
43 wStream* s;
44};
45
46struct s_wStreamPool
47{
48 size_t aSize;
49 size_t aCapacity;
50 struct s_StreamPoolEntry* aArray;
51
52 size_t uSize;
53 size_t uCapacity;
54 struct s_StreamPoolEntry* uArray;
55
57 BOOL synchronized;
58 size_t defaultSize;
59 wLog* log;
60};
61
62static void discard_entry(struct s_StreamPoolEntry* entry, BOOL discardStream)
63{
64 if (!entry)
65 return;
66
67#if defined(WITH_DEBUG_STREAMPOOL)
68 free((void*)entry->msg);
69#endif
70
71 if (discardStream && entry->s)
72 Stream_Free(entry->s, entry->s->isAllocatedStream);
73
74 const struct s_StreamPoolEntry empty = WINPR_C_ARRAY_INIT;
75 *entry = empty;
76}
77
78static struct s_StreamPoolEntry add_entry(wStream* s)
79{
80 struct s_StreamPoolEntry entry = WINPR_C_ARRAY_INIT;
81
82#if defined(WITH_DEBUG_STREAMPOOL)
83 void* stack = winpr_backtrace(20);
84 if (stack)
85 entry.msg = winpr_backtrace_symbols(stack, &entry.lines);
86 winpr_backtrace_free(stack);
87#endif
88
89 entry.s = s;
90 return entry;
91}
92
97static inline void StreamPool_Lock(wStreamPool* pool)
98{
99 WINPR_ASSERT(pool);
100 if (pool->synchronized)
101 EnterCriticalSection(&pool->lock);
102}
103
108static inline void StreamPool_Unlock(wStreamPool* pool)
109{
110 WINPR_ASSERT(pool);
111 if (pool->synchronized)
112 LeaveCriticalSection(&pool->lock);
113}
114
115static BOOL StreamPool_ShrinkToCommonLimit(wStream* s)
116{
117 if (Stream_Capacity(s) <= POOL_COMMON_LIMIT)
118 return TRUE;
119
120 return Stream_ResizeToCapacity(s, POOL_COMMON_LIMIT);
121}
122
123static BOOL StreamPool_EnsureCapacity(wStreamPool* pool, size_t count, BOOL usedOrAvailable)
124{
125 WINPR_ASSERT(pool);
126
127 size_t* cap = (usedOrAvailable) ? &pool->uCapacity : &pool->aCapacity;
128 size_t* size = (usedOrAvailable) ? &pool->uSize : &pool->aSize;
129 struct s_StreamPoolEntry** array = (usedOrAvailable) ? &pool->uArray : &pool->aArray;
130
131 size_t new_cap = 0;
132 if (*cap == 0)
133 new_cap = *size + count;
134 else if (*size + count > *cap)
135 new_cap = (*size + count + 2) / 2 * 3;
136 else if ((*size + count) < *cap / 3)
137 new_cap = *cap / 2;
138
139 if (new_cap > 0)
140 {
141 struct s_StreamPoolEntry* new_arr = nullptr;
142
143 if (*cap < *size + count)
144 *cap += count;
145
146 new_arr =
147 (struct s_StreamPoolEntry*)realloc(*array, sizeof(struct s_StreamPoolEntry) * new_cap);
148 if (!new_arr)
149 return FALSE;
150 *cap = new_cap;
151 *array = new_arr;
152 }
153 return TRUE;
154}
155
160static void StreamPool_ShiftUsed(wStreamPool* pool, size_t index)
161{
162 WINPR_ASSERT(pool);
163
164 const size_t pcount = 1;
165 const size_t off = index + pcount;
166 if (pool->uSize >= off)
167 {
168 for (size_t x = 0; x < pcount; x++)
169 {
170 struct s_StreamPoolEntry* cur = &pool->uArray[index + x];
171 discard_entry(cur, FALSE);
172 }
173 MoveMemory(&pool->uArray[index], &pool->uArray[index + pcount],
174 (pool->uSize - index - pcount) * sizeof(struct s_StreamPoolEntry));
175 pool->uSize -= pcount;
176 }
177}
178
183static void StreamPool_AddUsed(wStreamPool* pool, wStream* s)
184{
185 StreamPool_EnsureCapacity(pool, 1, TRUE);
186 pool->uArray[pool->uSize] = add_entry(s);
187 pool->uSize++;
188}
189
194static void StreamPool_RemoveUsed(wStreamPool* pool, wStream* s)
195{
196 WINPR_ASSERT(pool);
197 for (size_t index = 0; index < pool->uSize; index++)
198 {
199 struct s_StreamPoolEntry* cur = &pool->uArray[index];
200 if (cur->s == s)
201 {
202 StreamPool_ShiftUsed(pool, index);
203 break;
204 }
205 }
206}
207
208static void StreamPool_ShiftAvailable(wStreamPool* pool, size_t index)
209{
210 WINPR_ASSERT(pool);
211
212 const size_t pcount = 1;
213 const size_t off = index + pcount;
214 if (pool->aSize >= off)
215 {
216 for (size_t x = 0; x < pcount; x++)
217 {
218 struct s_StreamPoolEntry* cur = &pool->aArray[index + x];
219 discard_entry(cur, FALSE);
220 }
221
222 MoveMemory(&pool->aArray[index], &pool->aArray[index + pcount],
223 (pool->aSize - index - pcount) * sizeof(struct s_StreamPoolEntry));
224 pool->aSize -= pcount;
225 }
226}
227
232wStream* StreamPool_Take(wStreamPool* pool, size_t size)
233{
234 BOOL found = FALSE;
235 size_t foundIndex = 0;
236 wStream* s = nullptr;
237
238 StreamPool_Lock(pool);
239
240 if (size == 0)
241 size = pool->defaultSize;
242
243 for (size_t index = 0; index < pool->aSize; index++)
244 {
245 struct s_StreamPoolEntry* cur = &pool->aArray[index];
246 s = cur->s;
247
248 if (Stream_Capacity(s) >= size)
249 {
250 found = TRUE;
251 foundIndex = index;
252 break;
253 }
254 }
255
256 if (!found)
257 {
258 s = Stream_New(nullptr, size);
259 if (!s)
260 goto out_fail;
261 }
262 else if (s)
263 {
264 Stream_ResetPosition(s);
265 if (!Stream_SetLength(s, Stream_Capacity(s)))
266 goto out_fail;
267 StreamPool_ShiftAvailable(pool, foundIndex);
268 }
269
270 if (s)
271 {
272 s->pool = pool;
273 s->count = 1;
274 StreamPool_AddUsed(pool, s);
275 }
276
277out_fail:
278 StreamPool_Unlock(pool);
279
280 return s;
281}
282
287static void StreamPool_Remove(wStreamPool* pool, wStream* s)
288{
289 StreamPool_EnsureCapacity(pool, 1, FALSE);
290 Stream_EnsureValidity(s);
291 StreamPool_ShrinkToCommonLimit(s);
292 for (size_t x = 0; x < pool->aSize; x++)
293 {
294 wStream* cs = pool->aArray[x].s;
295 if (cs == s)
296 return;
297 }
298 pool->aArray[(pool->aSize)++] = add_entry(s);
299 StreamPool_RemoveUsed(pool, s);
300}
301
302static void StreamPool_ReleaseOrReturn(wStreamPool* pool, wStream* s)
303{
304 StreamPool_Lock(pool);
305 StreamPool_Remove(pool, s);
306 StreamPool_Unlock(pool);
307}
308
309void StreamPool_Return(wStreamPool* pool, wStream* s)
310{
311 WINPR_ASSERT(pool);
312 if (!s)
313 return;
314
315 StreamPool_Lock(pool);
316 StreamPool_Remove(pool, s);
317 StreamPool_Unlock(pool);
318}
319
324void Stream_AddRef(wStream* s)
325{
326 WINPR_ASSERT(s);
327 InterlockedIncrement(&s->count);
328}
329
334void Stream_Release(wStream* s)
335{
336 WINPR_ASSERT(s);
337
338 const LONG val = InterlockedDecrement(&s->count);
339 if (val <= 0)
340 {
341 if (s->pool)
342 StreamPool_ReleaseOrReturn(s->pool, s);
343 else
344 Stream_Free(s, TRUE);
345 }
346}
347
352wStream* StreamPool_Find(wStreamPool* pool, const BYTE* ptr)
353{
354 wStream* s = nullptr;
355
356 StreamPool_Lock(pool);
357
358 for (size_t index = 0; index < pool->uSize; index++)
359 {
360 struct s_StreamPoolEntry* cur = &pool->uArray[index];
361
362 if ((ptr >= Stream_Buffer(cur->s)) &&
363 (ptr < (Stream_Buffer(cur->s) + Stream_Capacity(cur->s))))
364 {
365 s = cur->s;
366 break;
367 }
368 }
369
370 StreamPool_Unlock(pool);
371
372 return s;
373}
374
379void StreamPool_Clear(wStreamPool* pool)
380{
381 StreamPool_Lock(pool);
382
383 for (size_t x = 0; x < pool->aSize; x++)
384 {
385 struct s_StreamPoolEntry* cur = &pool->aArray[x];
386 discard_entry(cur, TRUE);
387 }
388 pool->aSize = 0;
389
390 if (pool->uSize > 0)
391 {
392 WLog_Print(pool->log, WLOG_WARN,
393 "Clearing StreamPool, but there are %" PRIuz " streams currently in use",
394 pool->uSize);
395 for (size_t x = 0; x < pool->uSize; x++)
396 {
397 struct s_StreamPoolEntry* cur = &pool->uArray[x];
398 discard_entry(cur, TRUE);
399 }
400 pool->uSize = 0;
401 }
402
403 StreamPool_Unlock(pool);
404}
405
406size_t StreamPool_UsedCount(wStreamPool* pool)
407{
408 StreamPool_Lock(pool);
409 size_t usize = pool->uSize;
410 StreamPool_Unlock(pool);
411 return usize;
412}
413
418wStreamPool* StreamPool_New(BOOL synchronized, size_t defaultSize)
419{
420 wStreamPool* pool = calloc(1, sizeof(wStreamPool));
421
422 if (!pool)
423 return nullptr;
424
425 pool->log = WLog_Create(XTAG, WLog_GetRoot());
426 if (!pool->log)
427 goto fail;
428
429 pool->synchronized = synchronized;
430 pool->defaultSize = defaultSize;
431
432 if (!StreamPool_EnsureCapacity(pool, 32, FALSE))
433 goto fail;
434 if (!StreamPool_EnsureCapacity(pool, 32, TRUE))
435 goto fail;
436
437 if (!InitializeCriticalSectionAndSpinCount(&pool->lock, 4000))
438 goto fail;
439
440 return pool;
441fail:
442 WINPR_PRAGMA_DIAG_PUSH
443 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
444 StreamPool_Free(pool);
445 WINPR_PRAGMA_DIAG_POP
446 return nullptr;
447}
448
449void StreamPool_Free(wStreamPool* pool)
450{
451 if (!pool)
452 return;
453
454 StreamPool_Clear(pool);
455
456 DeleteCriticalSection(&pool->lock);
457
458 free(pool->aArray);
459 free(pool->uArray);
460
461 WLog_Discard(pool->log);
462 free(pool);
463}
464
465char* StreamPool_GetStatistics(wStreamPool* pool, char* buffer, size_t size)
466{
467 WINPR_ASSERT(pool);
468
469 if (!buffer || (size < 1))
470 return nullptr;
471
472 size_t used = 0;
473 int offset = _snprintf(buffer, size - 1,
474 "aSize =%" PRIuz ", uSize =%" PRIuz ", aCapacity=%" PRIuz
475 ", uCapacity=%" PRIuz,
476 pool->aSize, pool->uSize, pool->aCapacity, pool->uCapacity);
477 if ((offset > 0) && ((size_t)offset < size))
478 used += (size_t)offset;
479
480#if defined(WITH_DEBUG_STREAMPOOL)
481 StreamPool_Lock(pool);
482
483 offset = _snprintf(&buffer[used], size - 1 - used, "\n-- dump used array take locations --\n");
484 if ((offset > 0) && ((size_t)offset < size - used))
485 used += (size_t)offset;
486 for (size_t x = 0; x < pool->uSize; x++)
487 {
488 const struct s_StreamPoolEntry* cur = &pool->uArray[x];
489 WINPR_ASSERT(cur->msg || (cur->lines == 0));
490
491 for (size_t y = 0; y < cur->lines; y++)
492 {
493 offset = _snprintf(&buffer[used], size - 1 - used, "[%" PRIuz " | %" PRIuz "]: %s\n", x,
494 y, cur->msg[y]);
495 if ((offset > 0) && ((size_t)offset < size - used))
496 used += (size_t)offset;
497 }
498 }
499
500 offset = _snprintf(&buffer[used], size - 1 - used, "\n-- statistics called from --\n");
501 if ((offset > 0) && ((size_t)offset < size - used))
502 used += (size_t)offset;
503
504 struct s_StreamPoolEntry entry = WINPR_C_ARRAY_INIT;
505 void* stack = winpr_backtrace(20);
506 if (stack)
507 entry.msg = winpr_backtrace_symbols(stack, &entry.lines);
508 winpr_backtrace_free(stack);
509
510 for (size_t x = 0; x < entry.lines; x++)
511 {
512 const char* msg = entry.msg[x];
513 offset = _snprintf(&buffer[used], size - 1 - used, "[%" PRIuz "]: %s\n", x, msg);
514 if ((offset > 0) && ((size_t)offset < size - used))
515 used += (size_t)offset;
516 }
517 free((void*)entry.msg);
518 StreamPool_Unlock(pool);
519#endif
520 buffer[used] = '\0';
521 return buffer;
522}
523
524BOOL StreamPool_WaitForReturn(wStreamPool* pool, UINT32 timeoutMS)
525{
526 /* HACK: We disconnected the transport above, now wait without a read or write lock until all
527 * streams in use have been returned to the pool. */
528 while (timeoutMS > 0)
529 {
530 const size_t used = StreamPool_UsedCount(pool);
531 if (used == 0)
532 return TRUE;
533 WLog_Print(pool->log, WLOG_DEBUG, "%" PRIuz " streams still in use, sleeping...", used);
534
535 char buffer[4096] = WINPR_C_ARRAY_INIT;
536 StreamPool_GetStatistics(pool, buffer, sizeof(buffer));
537 WLog_Print(pool->log, WLOG_TRACE, "Pool statistics: %s", buffer);
538
539 UINT32 diff = 10;
540 if (timeoutMS != INFINITE)
541 {
542 diff = timeoutMS > 10 ? 10 : timeoutMS;
543 timeoutMS -= diff;
544 }
545 Sleep(diff);
546 }
547
548 return FALSE;
549}