src/thread/SDL_thread.c
author Ryan C. Gordon
Sun, 02 Oct 2011 00:29:16 -0400
changeset 5969 3a041d215edc
parent 5535 96594ac5fd1a
child 6044 35448a5ea044
permissions -rw-r--r--
1.3 API CHANGE: Add support for naming threads.
slouken@0
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@5535
     3
  Copyright (C) 1997-2011 Sam Lantinga <slouken@libsdl.org>
slouken@0
     4
slouken@5535
     5
  This software is provided 'as-is', without any express or implied
slouken@5535
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@5535
     7
  arising from the use of this software.
slouken@0
     8
slouken@5535
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@5535
    10
  including commercial applications, and to alter it and redistribute it
slouken@5535
    11
  freely, subject to the following restrictions:
slouken@0
    12
slouken@5535
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@5535
    14
     claim that you wrote the original software. If you use this software
slouken@5535
    15
     in a product, an acknowledgment in the product documentation would be
slouken@5535
    16
     appreciated but is not required.
slouken@5535
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@5535
    18
     misrepresented as being the original software.
slouken@5535
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@0
    20
*/
slouken@1402
    21
#include "SDL_config.h"
slouken@0
    22
slouken@0
    23
/* System independent thread management routines for SDL */
slouken@0
    24
slouken@0
    25
#include "SDL_mutex.h"
slouken@0
    26
#include "SDL_thread.h"
slouken@0
    27
#include "SDL_thread_c.h"
slouken@0
    28
#include "SDL_systhread.h"
slouken@0
    29
slouken@0
    30
#define ARRAY_CHUNKSIZE	32
slouken@0
    31
/* The array of threads currently active in the application
slouken@0
    32
   (except the main thread)
slouken@0
    33
   The manipulation of an array here is safer than using a linked list.
slouken@0
    34
*/
slouken@0
    35
static int SDL_maxthreads = 0;
slouken@0
    36
static int SDL_numthreads = 0;
slouken@0
    37
static SDL_Thread **SDL_Threads = NULL;
slouken@0
    38
static SDL_mutex *thread_lock = NULL;
slouken@0
    39
slouken@4472
    40
static int
slouken@1895
    41
SDL_ThreadsInit(void)
slouken@0
    42
{
slouken@1895
    43
    int retval;
slouken@0
    44
slouken@1895
    45
    retval = 0;
slouken@1895
    46
    thread_lock = SDL_CreateMutex();
slouken@1895
    47
    if (thread_lock == NULL) {
slouken@1895
    48
        retval = -1;
slouken@1895
    49
    }
slouken@1895
    50
    return (retval);
slouken@0
    51
}
slouken@0
    52
slouken@0
    53
/* This should never be called...
slouken@0
    54
   If this is called by SDL_Quit(), we don't know whether or not we should
slouken@0
    55
   clean up threads here.  If any threads are still running after this call,
slouken@0
    56
   they will no longer have access to any per-thread data.
slouken@0
    57
 */
slouken@5425
    58
#if 0
slouken@4472
    59
static void
slouken@1895
    60
SDL_ThreadsQuit(void)
slouken@0
    61
{
slouken@1895
    62
    SDL_mutex *mutex;
slouken@0
    63
slouken@1895
    64
    mutex = thread_lock;
slouken@1895
    65
    thread_lock = NULL;
slouken@1895
    66
    if (mutex != NULL) {
slouken@1895
    67
        SDL_DestroyMutex(mutex);
slouken@1895
    68
    }
slouken@0
    69
}
slouken@5425
    70
#endif
slouken@0
    71
slouken@0
    72
/* Routines for manipulating the thread list */
slouken@1895
    73
static void
slouken@1895
    74
SDL_AddThread(SDL_Thread * thread)
slouken@0
    75
{
slouken@1895
    76
    /* WARNING:
slouken@1895
    77
       If the very first threads are created simultaneously, then
slouken@1895
    78
       there could be a race condition causing memory corruption.
slouken@1895
    79
       In practice, this isn't a problem because by definition there
slouken@1895
    80
       is only one thread running the first time this is called.
slouken@1895
    81
     */
slouken@1895
    82
    if (!thread_lock) {
slouken@1895
    83
        if (SDL_ThreadsInit() < 0) {
slouken@1895
    84
            return;
slouken@1895
    85
        }
slouken@1895
    86
    }
slouken@1895
    87
    SDL_mutexP(thread_lock);
slouken@0
    88
slouken@1895
    89
    /* Expand the list of threads, if necessary */
slouken@0
    90
#ifdef DEBUG_THREADS
slouken@1895
    91
    printf("Adding thread (%d already - %d max)\n",
slouken@1895
    92
           SDL_numthreads, SDL_maxthreads);
slouken@0
    93
#endif
slouken@1895
    94
    if (SDL_numthreads == SDL_maxthreads) {
slouken@1895
    95
        SDL_Thread **threads;
slouken@1895
    96
        threads = (SDL_Thread **) SDL_realloc(SDL_Threads,
slouken@1895
    97
                                              (SDL_maxthreads +
slouken@1895
    98
                                               ARRAY_CHUNKSIZE) *
slouken@1895
    99
                                              (sizeof *threads));
slouken@1895
   100
        if (threads == NULL) {
slouken@1895
   101
            SDL_OutOfMemory();
slouken@1895
   102
            goto done;
slouken@1895
   103
        }
slouken@1895
   104
        SDL_maxthreads += ARRAY_CHUNKSIZE;
slouken@1895
   105
        SDL_Threads = threads;
slouken@1895
   106
    }
slouken@1895
   107
    SDL_Threads[SDL_numthreads++] = thread;
slouken@1895
   108
  done:
slouken@1895
   109
    SDL_mutexV(thread_lock);
slouken@0
   110
}
slouken@0
   111
slouken@1895
   112
static void
slouken@1895
   113
SDL_DelThread(SDL_Thread * thread)
slouken@0
   114
{
slouken@1895
   115
    int i;
slouken@0
   116
slouken@1895
   117
    if (!thread_lock) {
slouken@1895
   118
        return;
slouken@1895
   119
    }
slouken@1895
   120
    SDL_mutexP(thread_lock);
slouken@1895
   121
    for (i = 0; i < SDL_numthreads; ++i) {
slouken@1895
   122
        if (thread == SDL_Threads[i]) {
slouken@1895
   123
            break;
slouken@1895
   124
        }
slouken@1895
   125
    }
slouken@1895
   126
    if (i < SDL_numthreads) {
slouken@1895
   127
        if (--SDL_numthreads > 0) {
slouken@1895
   128
            while (i < SDL_numthreads) {
slouken@1895
   129
                SDL_Threads[i] = SDL_Threads[i + 1];
slouken@1895
   130
                ++i;
slouken@1895
   131
            }
slouken@1895
   132
        } else {
slouken@1895
   133
            SDL_maxthreads = 0;
slouken@1895
   134
            SDL_free(SDL_Threads);
slouken@1895
   135
            SDL_Threads = NULL;
slouken@1895
   136
        }
slouken@0
   137
#ifdef DEBUG_THREADS
slouken@1895
   138
        printf("Deleting thread (%d left - %d max)\n",
slouken@1895
   139
               SDL_numthreads, SDL_maxthreads);
slouken@0
   140
#endif
slouken@1895
   141
    }
slouken@1895
   142
    SDL_mutexV(thread_lock);
slouken@1499
   143
slouken@3300
   144
#if 0   /* There could be memory corruption if another thread is starting */
slouken@1895
   145
    if (SDL_Threads == NULL) {
slouken@1895
   146
        SDL_ThreadsQuit();
slouken@1895
   147
    }
slouken@3300
   148
#endif
slouken@0
   149
}
slouken@0
   150
slouken@0
   151
/* The default (non-thread-safe) global error variable */
slouken@0
   152
static SDL_error SDL_global_error;
slouken@0
   153
slouken@0
   154
/* Routine to get the thread-specific error variable */
slouken@1895
   155
SDL_error *
slouken@1895
   156
SDL_GetErrBuf(void)
slouken@0
   157
{
slouken@1895
   158
    SDL_error *errbuf;
slouken@0
   159
slouken@1895
   160
    errbuf = &SDL_global_error;
slouken@1895
   161
    if (SDL_Threads) {
slouken@1895
   162
        int i;
slouken@3578
   163
        SDL_threadID this_thread;
slouken@0
   164
slouken@1895
   165
        this_thread = SDL_ThreadID();
slouken@1895
   166
        SDL_mutexP(thread_lock);
slouken@1895
   167
        for (i = 0; i < SDL_numthreads; ++i) {
slouken@1895
   168
            if (this_thread == SDL_Threads[i]->threadid) {
slouken@1895
   169
                errbuf = &SDL_Threads[i]->errbuf;
slouken@1895
   170
                break;
slouken@1895
   171
            }
slouken@1895
   172
        }
slouken@1895
   173
        SDL_mutexV(thread_lock);
slouken@1895
   174
    }
slouken@1895
   175
    return (errbuf);
slouken@0
   176
}
slouken@0
   177
slouken@0
   178
slouken@0
   179
/* Arguments and callback to setup and run the user thread function */
slouken@1895
   180
typedef struct
slouken@1895
   181
{
slouken@1895
   182
    int (SDLCALL * func) (void *);
slouken@1895
   183
    void *data;
slouken@1895
   184
    SDL_Thread *info;
slouken@1895
   185
    SDL_sem *wait;
slouken@0
   186
} thread_args;
slouken@0
   187
slouken@1895
   188
void
slouken@1895
   189
SDL_RunThread(void *data)
slouken@0
   190
{
icculus@5969
   191
    thread_args *args = (thread_args *) data;
icculus@5969
   192
    int (SDLCALL * userfunc) (void *) = args->func;
icculus@5969
   193
    void *userdata = args->data;
icculus@5969
   194
    int *statusloc = &args->info->status;
slouken@0
   195
slouken@1895
   196
    /* Perform any system-dependent setup
slouken@1895
   197
       - this function cannot fail, and cannot use SDL_SetError()
slouken@1895
   198
     */
icculus@5969
   199
    SDL_SYS_SetupThread(args->info->name);
slouken@0
   200
slouken@1895
   201
    /* Get the thread id */
slouken@1895
   202
    args->info->threadid = SDL_ThreadID();
slouken@0
   203
slouken@1895
   204
    /* Wake up the parent thread */
slouken@1895
   205
    SDL_SemPost(args->wait);
slouken@0
   206
slouken@1895
   207
    /* Run the function */
slouken@1895
   208
    *statusloc = userfunc(userdata);
slouken@0
   209
}
slouken@0
   210
slouken@1471
   211
#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
slouken@1330
   212
#undef SDL_CreateThread
slouken@1895
   213
DECLSPEC SDL_Thread *SDLCALL
icculus@5969
   214
SDL_CreateThread(int (SDLCALL * fn) (void *),
icculus@5969
   215
                 const char *name, void *data,
slouken@1895
   216
                 pfnSDL_CurrentBeginThread pfnBeginThread,
slouken@1895
   217
                 pfnSDL_CurrentEndThread pfnEndThread)
icculus@1190
   218
#else
slouken@1895
   219
DECLSPEC SDL_Thread *SDLCALL
icculus@5969
   220
SDL_CreateThread(int (SDLCALL * fn) (void *),
icculus@5969
   221
                 const char *name, void *data)
icculus@1190
   222
#endif
slouken@0
   223
{
slouken@1895
   224
    SDL_Thread *thread;
slouken@1895
   225
    thread_args *args;
slouken@1895
   226
    int ret;
slouken@0
   227
slouken@1895
   228
    /* Allocate memory for the thread info structure */
slouken@1895
   229
    thread = (SDL_Thread *) SDL_malloc(sizeof(*thread));
slouken@1895
   230
    if (thread == NULL) {
slouken@1895
   231
        SDL_OutOfMemory();
slouken@1895
   232
        return (NULL);
slouken@1895
   233
    }
slouken@1895
   234
    SDL_memset(thread, 0, (sizeof *thread));
slouken@1895
   235
    thread->status = -1;
slouken@0
   236
slouken@1895
   237
    /* Set up the arguments for the thread */
icculus@5969
   238
    if (name != NULL) {
icculus@5969
   239
        thread->name = SDL_strdup(name);
icculus@5969
   240
        if (thread->name == NULL) {
icculus@5969
   241
            SDL_OutOfMemory();
icculus@5969
   242
            SDL_free(thread);
icculus@5969
   243
            return (NULL);
icculus@5969
   244
        }
icculus@5969
   245
    }
icculus@5969
   246
icculus@5969
   247
    /* Set up the arguments for the thread */
slouken@1895
   248
    args = (thread_args *) SDL_malloc(sizeof(*args));
slouken@1895
   249
    if (args == NULL) {
slouken@1895
   250
        SDL_OutOfMemory();
icculus@5969
   251
        SDL_free(thread->name);
slouken@1895
   252
        SDL_free(thread);
slouken@1895
   253
        return (NULL);
slouken@1895
   254
    }
slouken@1895
   255
    args->func = fn;
slouken@1895
   256
    args->data = data;
slouken@1895
   257
    args->info = thread;
slouken@1895
   258
    args->wait = SDL_CreateSemaphore(0);
slouken@1895
   259
    if (args->wait == NULL) {
icculus@5969
   260
        SDL_free(thread->name);
slouken@1895
   261
        SDL_free(thread);
slouken@1895
   262
        SDL_free(args);
slouken@1895
   263
        return (NULL);
slouken@1895
   264
    }
slouken@0
   265
slouken@1895
   266
    /* Add the thread to the list of available threads */
slouken@1895
   267
    SDL_AddThread(thread);
slouken@0
   268
slouken@1895
   269
    /* Create the thread and go! */
slouken@1471
   270
#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
slouken@1895
   271
    ret = SDL_SYS_CreateThread(thread, args, pfnBeginThread, pfnEndThread);
icculus@1190
   272
#else
slouken@1895
   273
    ret = SDL_SYS_CreateThread(thread, args);
icculus@1190
   274
#endif
slouken@1895
   275
    if (ret >= 0) {
slouken@1895
   276
        /* Wait for the thread function to use arguments */
slouken@1895
   277
        SDL_SemWait(args->wait);
slouken@1895
   278
    } else {
slouken@1895
   279
        /* Oops, failed.  Gotta free everything */
slouken@1895
   280
        SDL_DelThread(thread);
icculus@5969
   281
        SDL_free(thread->name);
slouken@1895
   282
        SDL_free(thread);
slouken@1895
   283
        thread = NULL;
slouken@1895
   284
    }
slouken@1895
   285
    SDL_DestroySemaphore(args->wait);
slouken@1895
   286
    SDL_free(args);
slouken@0
   287
slouken@1895
   288
    /* Everything is running now */
slouken@1895
   289
    return (thread);
slouken@0
   290
}
slouken@0
   291
slouken@5506
   292
SDL_threadID
slouken@5506
   293
SDL_GetThreadID(SDL_Thread * thread)
slouken@5506
   294
{
slouken@5506
   295
    SDL_threadID id;
slouken@5506
   296
slouken@5506
   297
    if (thread) {
slouken@5506
   298
        id = thread->threadid;
slouken@5506
   299
    } else {
slouken@5506
   300
        id = SDL_ThreadID();
slouken@5506
   301
    }
slouken@5506
   302
    return id;
slouken@5506
   303
}
slouken@5506
   304
icculus@5969
   305
const char *
icculus@5969
   306
SDL_GetThreadName(SDL_Thread * thread)
icculus@5969
   307
{
icculus@5969
   308
    return thread->name;
icculus@5969
   309
}
icculus@5969
   310
slouken@5506
   311
int
slouken@5509
   312
SDL_SetThreadPriority(SDL_ThreadPriority priority)
slouken@5506
   313
{
slouken@5509
   314
    return SDL_SYS_SetThreadPriority(priority);
slouken@5506
   315
}
slouken@5506
   316
slouken@1895
   317
void
slouken@1895
   318
SDL_WaitThread(SDL_Thread * thread, int *status)
slouken@0
   319
{
slouken@1895
   320
    if (thread) {
slouken@1895
   321
        SDL_SYS_WaitThread(thread);
slouken@1895
   322
        if (status) {
slouken@1895
   323
            *status = thread->status;
slouken@1895
   324
        }
slouken@1895
   325
        SDL_DelThread(thread);
icculus@5969
   326
        SDL_free(thread->name);
slouken@1895
   327
        SDL_free(thread);
slouken@1895
   328
    }
slouken@0
   329
}
slouken@0
   330
slouken@1895
   331
/* vi: set ts=4 sw=4 expandtab: */