src/thread/SDL_thread.c
author Sam Lantinga
Fri, 25 Mar 2011 12:44:06 -0700
changeset 5509 5b1b4d820d10
parent 5506 82a09d5d0f07
child 5535 96594ac5fd1a
permissions -rw-r--r--
The API sets the priority for the current thread, not an arbitrary thread.

Implemented thread priority as the 'nice' value on Linux. High priority threads require root permissions (you shouldn't give your game root permissions though!)
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@5509
   298
SDL_SetThreadPriority(SDL_ThreadPriority priority)
slouken@5506
   299
{
slouken@5509
   300
    return SDL_SYS_SetThreadPriority(priority);
slouken@5506
   301
}
slouken@5506
   302
slouken@1895
   303
void
slouken@1895
   304
SDL_WaitThread(SDL_Thread * thread, int *status)
slouken@0
   305
{
slouken@1895
   306
    if (thread) {
slouken@1895
   307
        SDL_SYS_WaitThread(thread);
slouken@1895
   308
        if (status) {
slouken@1895
   309
            *status = thread->status;
slouken@1895
   310
        }
slouken@1895
   311
        SDL_DelThread(thread);
slouken@1895
   312
        SDL_free(thread);
slouken@1895
   313
    }
slouken@0
   314
}
slouken@0
   315
slouken@1895
   316
/* vi: set ts=4 sw=4 expandtab: */