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