SDL

Форк
0
/
SDL_thread.c 
518 строк · 15.1 Кб
1
/*
2
  Simple DirectMedia Layer
3
  Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
4

5
  This software is provided 'as-is', without any express or implied
6
  warranty.  In no event will the authors be held liable for any damages
7
  arising from the use of this software.
8

9
  Permission is granted to anyone to use this software for any purpose,
10
  including commercial applications, and to alter it and redistribute it
11
  freely, subject to the following restrictions:
12

13
  1. The origin of this software must not be misrepresented; you must not
14
     claim that you wrote the original software. If you use this software
15
     in a product, an acknowledgment in the product documentation would be
16
     appreciated but is not required.
17
  2. Altered source versions must be plainly marked as such, and must not be
18
     misrepresented as being the original software.
19
  3. This notice may not be removed or altered from any source distribution.
20
*/
21
#include "SDL_internal.h"
22

23
// System independent thread management routines for SDL
24

25
#include "SDL_thread_c.h"
26
#include "SDL_systhread.h"
27
#include "../SDL_error_c.h"
28

29
// The storage is local to the thread, but the IDs are global for the process
30

31
static SDL_AtomicInt SDL_tls_allocated;
32
static SDL_AtomicInt SDL_tls_id;
33

34
void SDL_InitTLSData(void)
35
{
36
    SDL_SYS_InitTLSData();
37
}
38

39
void *SDL_GetTLS(SDL_TLSID *id)
40
{
41
    SDL_TLSData *storage;
42
    int storage_index;
43

44
    if (id == NULL) {
45
        SDL_InvalidParamError("id");
46
        return NULL;
47
    }
48

49
    storage_index = SDL_AtomicGet(id) - 1;
50
    storage = SDL_SYS_GetTLSData();
51
    if (!storage || storage_index < 0 || storage_index >= storage->limit) {
52
        return NULL;
53
    }
54
    return storage->array[storage_index].data;
55
}
56

57
SDL_bool SDL_SetTLS(SDL_TLSID *id, const void *value, SDL_TLSDestructorCallback destructor)
58
{
59
    SDL_TLSData *storage;
60
    int storage_index;
61

62
    if (id == NULL) {
63
        return SDL_InvalidParamError("id");
64
    }
65

66
    /* Make sure TLS is initialized.
67
     * There's a race condition here if you are calling this from non-SDL threads
68
     * and haven't called SDL_Init() on your main thread, but such is life.
69
     */
70
    SDL_InitTLSData();
71

72
    // Get the storage index associated with the ID in a thread-safe way
73
    storage_index = SDL_AtomicGet(id) - 1;
74
    if (storage_index < 0) {
75
        int new_id = (SDL_AtomicIncRef(&SDL_tls_id) + 1);
76

77
        SDL_AtomicCompareAndSwap(id, 0, new_id);
78

79
        /* If there was a race condition we'll have wasted an ID, but every thread
80
         * will have the same storage index for this id.
81
         */
82
        storage_index = SDL_AtomicGet(id) - 1;
83
    }
84

85
    // Get the storage for the current thread
86
    storage = SDL_SYS_GetTLSData();
87
    if (!storage || storage_index >= storage->limit) {
88
        unsigned int i, oldlimit, newlimit;
89
        SDL_TLSData *new_storage;
90

91
        oldlimit = storage ? storage->limit : 0;
92
        newlimit = (storage_index + TLS_ALLOC_CHUNKSIZE);
93
        new_storage = (SDL_TLSData *)SDL_realloc(storage, sizeof(*storage) + (newlimit - 1) * sizeof(storage->array[0]));
94
        if (!new_storage) {
95
            return false;
96
        }
97
        storage = new_storage;
98
        storage->limit = newlimit;
99
        for (i = oldlimit; i < newlimit; ++i) {
100
            storage->array[i].data = NULL;
101
            storage->array[i].destructor = NULL;
102
        }
103
        if (!SDL_SYS_SetTLSData(storage)) {
104
            SDL_free(storage);
105
            return false;
106
        }
107
        SDL_AtomicIncRef(&SDL_tls_allocated);
108
    }
109

110
    storage->array[storage_index].data = SDL_const_cast(void *, value);
111
    storage->array[storage_index].destructor = destructor;
112
    return true;
113
}
114

115
void SDL_CleanupTLS(void)
116
{
117
    SDL_TLSData *storage;
118

119
    // Cleanup the storage for the current thread
120
    storage = SDL_SYS_GetTLSData();
121
    if (storage) {
122
        int i;
123
        for (i = 0; i < storage->limit; ++i) {
124
            if (storage->array[i].destructor) {
125
                storage->array[i].destructor(storage->array[i].data);
126
            }
127
        }
128
        SDL_SYS_SetTLSData(NULL);
129
        SDL_free(storage);
130
        (void)SDL_AtomicDecRef(&SDL_tls_allocated);
131
    }
132
}
133

134
void SDL_QuitTLSData(void)
135
{
136
    SDL_CleanupTLS();
137

138
    if (SDL_AtomicGet(&SDL_tls_allocated) == 0) {
139
        SDL_SYS_QuitTLSData();
140
    } else {
141
        // Some thread hasn't called SDL_CleanupTLS()
142
    }
143
}
144

145
/* This is a generic implementation of thread-local storage which doesn't
146
   require additional OS support.
147

148
   It is not especially efficient and doesn't clean up thread-local storage
149
   as threads exit.  If there is a real OS that doesn't support thread-local
150
   storage this implementation should be improved to be production quality.
151
*/
152

153
typedef struct SDL_TLSEntry
154
{
155
    SDL_ThreadID thread;
156
    SDL_TLSData *storage;
157
    struct SDL_TLSEntry *next;
158
} SDL_TLSEntry;
159

160
static SDL_Mutex *SDL_generic_TLS_mutex;
161
static SDL_TLSEntry *SDL_generic_TLS;
162

163
void SDL_Generic_InitTLSData(void)
164
{
165
    if (!SDL_generic_TLS_mutex) {
166
        SDL_generic_TLS_mutex = SDL_CreateMutex();
167
    }
168
}
169

170
SDL_TLSData *SDL_Generic_GetTLSData(void)
171
{
172
    SDL_ThreadID thread = SDL_GetCurrentThreadID();
173
    SDL_TLSEntry *entry;
174
    SDL_TLSData *storage = NULL;
175

176
    SDL_LockMutex(SDL_generic_TLS_mutex);
177
    for (entry = SDL_generic_TLS; entry; entry = entry->next) {
178
        if (entry->thread == thread) {
179
            storage = entry->storage;
180
            break;
181
        }
182
    }
183
    SDL_UnlockMutex(SDL_generic_TLS_mutex);
184

185
    return storage;
186
}
187

188
bool SDL_Generic_SetTLSData(SDL_TLSData *data)
189
{
190
    SDL_ThreadID thread = SDL_GetCurrentThreadID();
191
    SDL_TLSEntry *prev, *entry;
192
    bool result = true;
193

194
    SDL_LockMutex(SDL_generic_TLS_mutex);
195
    prev = NULL;
196
    for (entry = SDL_generic_TLS; entry; entry = entry->next) {
197
        if (entry->thread == thread) {
198
            if (data) {
199
                entry->storage = data;
200
            } else {
201
                if (prev) {
202
                    prev->next = entry->next;
203
                } else {
204
                    SDL_generic_TLS = entry->next;
205
                }
206
                SDL_free(entry);
207
            }
208
            break;
209
        }
210
        prev = entry;
211
    }
212
    if (!entry && data) {
213
        entry = (SDL_TLSEntry *)SDL_malloc(sizeof(*entry));
214
        if (entry) {
215
            entry->thread = thread;
216
            entry->storage = data;
217
            entry->next = SDL_generic_TLS;
218
            SDL_generic_TLS = entry;
219
        } else {
220
            result = false;
221
        }
222
    }
223
    SDL_UnlockMutex(SDL_generic_TLS_mutex);
224

225
    return result;
226
}
227

228
void SDL_Generic_QuitTLSData(void)
229
{
230
    SDL_TLSEntry *entry;
231

232
    // This should have been cleaned up by the time we get here
233
    SDL_assert(!SDL_generic_TLS);
234
    if (SDL_generic_TLS) {
235
        SDL_LockMutex(SDL_generic_TLS_mutex);
236
        for (entry = SDL_generic_TLS; entry; ) {
237
            SDL_TLSEntry *next = entry->next;
238
            SDL_free(entry->storage);
239
            SDL_free(entry);
240
            entry = next;
241
        }
242
        SDL_generic_TLS = NULL;
243
        SDL_UnlockMutex(SDL_generic_TLS_mutex);
244
    }
245

246
    if (SDL_generic_TLS_mutex) {
247
        SDL_DestroyMutex(SDL_generic_TLS_mutex);
248
        SDL_generic_TLS_mutex = NULL;
249
    }
250
}
251

252
// Non-thread-safe global error variable
253
static SDL_error *SDL_GetStaticErrBuf(void)
254
{
255
    static SDL_error SDL_global_error;
256
    static char SDL_global_error_str[128];
257
    SDL_global_error.str = SDL_global_error_str;
258
    SDL_global_error.len = sizeof(SDL_global_error_str);
259
    return &SDL_global_error;
260
}
261

262
#ifndef SDL_THREADS_DISABLED
263
static void SDLCALL SDL_FreeErrBuf(void *data)
264
{
265
    SDL_error *errbuf = (SDL_error *)data;
266

267
    if (errbuf->str) {
268
        errbuf->free_func(errbuf->str);
269
    }
270
    errbuf->free_func(errbuf);
271
}
272
#endif
273

274
// Routine to get the thread-specific error variable
275
SDL_error *SDL_GetErrBuf(bool create)
276
{
277
#ifdef SDL_THREADS_DISABLED
278
    return SDL_GetStaticErrBuf();
279
#else
280
    static SDL_TLSID tls_errbuf;
281
    SDL_error *errbuf;
282

283
    errbuf = (SDL_error *)SDL_GetTLS(&tls_errbuf);
284
    if (!errbuf) {
285
        if (!create) {
286
            return NULL;
287
        }
288

289
        /* Get the original memory functions for this allocation because the lifetime
290
         * of the error buffer may span calls to SDL_SetMemoryFunctions() by the app
291
         */
292
        SDL_realloc_func realloc_func;
293
        SDL_free_func free_func;
294
        SDL_GetOriginalMemoryFunctions(NULL, NULL, &realloc_func, &free_func);
295

296
        errbuf = (SDL_error *)realloc_func(NULL, sizeof(*errbuf));
297
        if (!errbuf) {
298
            return SDL_GetStaticErrBuf();
299
        }
300
        SDL_zerop(errbuf);
301
        errbuf->realloc_func = realloc_func;
302
        errbuf->free_func = free_func;
303
        SDL_SetTLS(&tls_errbuf, errbuf, SDL_FreeErrBuf);
304
    }
305
    return errbuf;
306
#endif // SDL_THREADS_DISABLED
307
}
308

309
void SDL_RunThread(SDL_Thread *thread)
310
{
311
    void *userdata = thread->userdata;
312
    int(SDLCALL * userfunc)(void *) = thread->userfunc;
313

314
    int *statusloc = &thread->status;
315

316
    // Perform any system-dependent setup - this function may not fail
317
    SDL_SYS_SetupThread(thread->name);
318

319
    // Get the thread id
320
    thread->threadid = SDL_GetCurrentThreadID();
321

322
    // Run the function
323
    *statusloc = userfunc(userdata);
324

325
    // Clean up thread-local storage
326
    SDL_CleanupTLS();
327

328
    // Mark us as ready to be joined (or detached)
329
    if (!SDL_AtomicCompareAndSwap(&thread->state, SDL_THREAD_STATE_ALIVE, SDL_THREAD_STATE_ZOMBIE)) {
330
        // Clean up if something already detached us.
331
        if (SDL_AtomicCompareAndSwap(&thread->state, SDL_THREAD_STATE_DETACHED, SDL_THREAD_STATE_CLEANED)) {
332
            SDL_free(thread->name); // Can't free later, we've already cleaned up TLS
333
            SDL_free(thread);
334
        }
335
    }
336
}
337

338
SDL_Thread *SDL_CreateThreadWithPropertiesRuntime(SDL_PropertiesID props,
339
                              SDL_FunctionPointer pfnBeginThread,
340
                              SDL_FunctionPointer pfnEndThread)
341
{
342
    // rather than check this in every backend, just make sure it's correct upfront. Only allow non-NULL if non-WinRT Windows, or Microsoft GDK.
343
    #if (!defined(SDL_PLATFORM_WIN32) && !defined(SDL_PLATFORM_GDK)) || defined(SDL_PLATFORM_WINRT)
344
    if (pfnBeginThread || pfnEndThread) {
345
        SDL_SetError("_beginthreadex/_endthreadex not supported on this platform");
346
        return NULL;
347
    }
348
    #endif
349

350
    SDL_ThreadFunction fn = (SDL_ThreadFunction) SDL_GetPointerProperty(props, SDL_PROP_THREAD_CREATE_ENTRY_FUNCTION_POINTER, NULL);
351
    const char *name = SDL_GetStringProperty(props, SDL_PROP_THREAD_CREATE_NAME_STRING, NULL);
352
    const size_t stacksize = (size_t) SDL_GetNumberProperty(props, SDL_PROP_THREAD_CREATE_STACKSIZE_NUMBER, 0);
353
    void *userdata = SDL_GetPointerProperty(props, SDL_PROP_THREAD_CREATE_USERDATA_POINTER, NULL);
354

355
    if (!fn) {
356
        SDL_SetError("Thread entry function is NULL");
357
        return NULL;
358
    }
359

360
    SDL_InitMainThread();
361

362
    SDL_Thread *thread = (SDL_Thread *)SDL_calloc(1, sizeof(*thread));
363
    if (!thread) {
364
        return NULL;
365
    }
366
    thread->status = -1;
367
    SDL_AtomicSet(&thread->state, SDL_THREAD_STATE_ALIVE);
368

369
    // Set up the arguments for the thread
370
    if (name) {
371
        thread->name = SDL_strdup(name);
372
        if (!thread->name) {
373
            SDL_free(thread);
374
            return NULL;
375
        }
376
    }
377

378
    thread->userfunc = fn;
379
    thread->userdata = userdata;
380
    thread->stacksize = stacksize;
381

382
    // Create the thread and go!
383
    if (!SDL_SYS_CreateThread(thread, pfnBeginThread, pfnEndThread)) {
384
        // Oops, failed.  Gotta free everything
385
        SDL_free(thread->name);
386
        SDL_free(thread);
387
        thread = NULL;
388
    }
389

390
    // Everything is running now
391
    return thread;
392
}
393

394
SDL_Thread *SDL_CreateThreadRuntime(SDL_ThreadFunction fn,
395
                 const char *name, void *userdata,
396
                 SDL_FunctionPointer pfnBeginThread,
397
                 SDL_FunctionPointer pfnEndThread)
398
{
399
    const SDL_PropertiesID props = SDL_CreateProperties();
400
    SDL_SetPointerProperty(props, SDL_PROP_THREAD_CREATE_ENTRY_FUNCTION_POINTER, (void *) fn);
401
    SDL_SetStringProperty(props, SDL_PROP_THREAD_CREATE_NAME_STRING, name);
402
    SDL_SetPointerProperty(props, SDL_PROP_THREAD_CREATE_USERDATA_POINTER, userdata);
403
    SDL_Thread *thread = SDL_CreateThreadWithPropertiesRuntime(props, pfnBeginThread, pfnEndThread);
404
    SDL_DestroyProperties(props);
405
    return thread;
406
}
407

408
// internal helper function, not in the public API.
409
SDL_Thread *SDL_CreateThreadWithStackSize(SDL_ThreadFunction fn, const char *name, size_t stacksize, void *userdata)
410
{
411
    const SDL_PropertiesID props = SDL_CreateProperties();
412
    SDL_SetPointerProperty(props, SDL_PROP_THREAD_CREATE_ENTRY_FUNCTION_POINTER, (void *) fn);
413
    SDL_SetStringProperty(props, SDL_PROP_THREAD_CREATE_NAME_STRING, name);
414
    SDL_SetPointerProperty(props, SDL_PROP_THREAD_CREATE_USERDATA_POINTER, userdata);
415
    SDL_SetNumberProperty(props, SDL_PROP_THREAD_CREATE_STACKSIZE_NUMBER, (Sint64) stacksize);
416
    SDL_Thread *thread = SDL_CreateThreadWithProperties(props);
417
    SDL_DestroyProperties(props);
418
    return thread;
419
}
420

421
SDL_ThreadID SDL_GetThreadID(SDL_Thread *thread)
422
{
423
    SDL_ThreadID id;
424

425
    if (thread) {
426
        id = thread->threadid;
427
    } else {
428
        id = SDL_GetCurrentThreadID();
429
    }
430
    return id;
431
}
432

433
const char *SDL_GetThreadName(SDL_Thread *thread)
434
{
435
    if (thread) {
436
        return SDL_GetPersistentString(thread->name);
437
    } else {
438
        return NULL;
439
    }
440
}
441

442
SDL_bool SDL_SetThreadPriority(SDL_ThreadPriority priority)
443
{
444
    return SDL_SYS_SetThreadPriority(priority);
445
}
446

447
void SDL_WaitThread(SDL_Thread *thread, int *status)
448
{
449
    if (thread) {
450
        SDL_SYS_WaitThread(thread);
451
        if (status) {
452
            *status = thread->status;
453
        }
454
        SDL_free(thread->name);
455
        SDL_free(thread);
456
    }
457
}
458

459
void SDL_DetachThread(SDL_Thread *thread)
460
{
461
    if (!thread) {
462
        return;
463
    }
464

465
    // Grab dibs if the state is alive+joinable.
466
    if (SDL_AtomicCompareAndSwap(&thread->state, SDL_THREAD_STATE_ALIVE, SDL_THREAD_STATE_DETACHED)) {
467
        SDL_SYS_DetachThread(thread);
468
    } else {
469
        // all other states are pretty final, see where we landed.
470
        const int thread_state = SDL_AtomicGet(&thread->state);
471
        if ((thread_state == SDL_THREAD_STATE_DETACHED) || (thread_state == SDL_THREAD_STATE_CLEANED)) {
472
            return; // already detached (you shouldn't call this twice!)
473
        } else if (thread_state == SDL_THREAD_STATE_ZOMBIE) {
474
            SDL_WaitThread(thread, NULL); // already done, clean it up.
475
        } else {
476
            SDL_assert(0 && "Unexpected thread state");
477
        }
478
    }
479
}
480

481
void SDL_WaitSemaphore(SDL_Semaphore *sem)
482
{
483
    SDL_WaitSemaphoreTimeoutNS(sem, -1);
484
}
485

486
SDL_bool SDL_TryWaitSemaphore(SDL_Semaphore *sem)
487
{
488
    return SDL_WaitSemaphoreTimeoutNS(sem, 0);
489
}
490

491
SDL_bool SDL_WaitSemaphoreTimeout(SDL_Semaphore *sem, Sint32 timeoutMS)
492
{
493
    Sint64 timeoutNS;
494

495
    if (timeoutMS >= 0) {
496
        timeoutNS = SDL_MS_TO_NS(timeoutMS);
497
    } else {
498
        timeoutNS = -1;
499
    }
500
    return SDL_WaitSemaphoreTimeoutNS(sem, timeoutNS);
501
}
502

503
void SDL_WaitCondition(SDL_Condition *cond, SDL_Mutex *mutex)
504
{
505
    SDL_WaitConditionTimeoutNS(cond, mutex, -1);
506
}
507

508
SDL_bool SDL_WaitConditionTimeout(SDL_Condition *cond, SDL_Mutex *mutex, Sint32 timeoutMS)
509
{
510
    Sint64 timeoutNS;
511

512
    if (timeoutMS >= 0) {
513
        timeoutNS = SDL_MS_TO_NS(timeoutMS);
514
    } else {
515
        timeoutNS = -1;
516
    }
517
    return SDL_WaitConditionTimeoutNS(cond, mutex, timeoutNS);
518
}
519

520

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.