src/thread/SDL_thread.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 28 May 2006 13:04:16 +0000
branchSDL-1.3
changeset 1662 782fd950bd46
parent 1659 14717b52abc0
child 1668 4da1ee79c9af
permissions -rw-r--r--
Revamp of the video system in progress - adding support for multiple displays, multiple windows, and a full video mode selection API.

WARNING: None of the video drivers have been updated for the new API yet! The API is still under design and very fluid.

The code is now run through a consistent indent format:
indent -i4 -nut -nsc -br -ce

The headers are being converted to automatically generate doxygen documentation.
slouken@0
     1
/*
slouken@0
     2
    SDL - Simple DirectMedia Layer
slouken@1312
     3
    Copyright (C) 1997-2006 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@1662
    41
int
slouken@1662
    42
SDL_ThreadsInit (void)
slouken@0
    43
{
slouken@1662
    44
    int retval;
slouken@0
    45
slouken@1662
    46
    retval = 0;
slouken@1662
    47
    thread_lock = SDL_CreateMutex ();
slouken@1662
    48
    if (thread_lock == NULL) {
slouken@1662
    49
        retval = -1;
slouken@1662
    50
    }
slouken@1662
    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@1662
    59
void
slouken@1662
    60
SDL_ThreadsQuit (void)
slouken@0
    61
{
slouken@1662
    62
    SDL_mutex *mutex;
slouken@0
    63
slouken@1662
    64
    mutex = thread_lock;
slouken@1662
    65
    thread_lock = NULL;
slouken@1662
    66
    if (mutex != NULL) {
slouken@1662
    67
        SDL_DestroyMutex (mutex);
slouken@1662
    68
    }
slouken@0
    69
}
slouken@0
    70
slouken@0
    71
/* Routines for manipulating the thread list */
slouken@1662
    72
static void
slouken@1662
    73
SDL_AddThread (SDL_Thread * thread)
slouken@0
    74
{
slouken@1662
    75
    /* WARNING:
slouken@1662
    76
       If the very first threads are created simultaneously, then
slouken@1662
    77
       there could be a race condition causing memory corruption.
slouken@1662
    78
       In practice, this isn't a problem because by definition there
slouken@1662
    79
       is only one thread running the first time this is called.
slouken@1662
    80
     */
slouken@1662
    81
    if (!thread_lock) {
slouken@1662
    82
        if (SDL_ThreadsInit () < 0) {
slouken@1662
    83
            return;
slouken@1662
    84
        }
slouken@1662
    85
    }
slouken@1662
    86
    SDL_mutexP (thread_lock);
slouken@0
    87
slouken@1662
    88
    /* Expand the list of threads, if necessary */
slouken@0
    89
#ifdef DEBUG_THREADS
slouken@1662
    90
    printf ("Adding thread (%d already - %d max)\n",
slouken@1662
    91
            SDL_numthreads, SDL_maxthreads);
slouken@0
    92
#endif
slouken@1662
    93
    if (SDL_numthreads == SDL_maxthreads) {
slouken@1662
    94
        SDL_Thread **threads;
slouken@1662
    95
        threads = (SDL_Thread **) SDL_realloc (SDL_Threads,
slouken@1662
    96
                                               (SDL_maxthreads +
slouken@1662
    97
                                                ARRAY_CHUNKSIZE) *
slouken@1662
    98
                                               (sizeof *threads));
slouken@1662
    99
        if (threads == NULL) {
slouken@1662
   100
            SDL_OutOfMemory ();
slouken@1662
   101
            goto done;
slouken@1662
   102
        }
slouken@1662
   103
        SDL_maxthreads += ARRAY_CHUNKSIZE;
slouken@1662
   104
        SDL_Threads = threads;
slouken@1662
   105
    }
slouken@1662
   106
    SDL_Threads[SDL_numthreads++] = thread;
slouken@1662
   107
  done:
slouken@1662
   108
    SDL_mutexV (thread_lock);
slouken@0
   109
}
slouken@0
   110
slouken@1662
   111
static void
slouken@1662
   112
SDL_DelThread (SDL_Thread * thread)
slouken@0
   113
{
slouken@1662
   114
    int i;
slouken@0
   115
slouken@1662
   116
    if (!thread_lock) {
slouken@1662
   117
        return;
slouken@1662
   118
    }
slouken@1662
   119
    SDL_mutexP (thread_lock);
slouken@1662
   120
    for (i = 0; i < SDL_numthreads; ++i) {
slouken@1662
   121
        if (thread == SDL_Threads[i]) {
slouken@1662
   122
            break;
slouken@1662
   123
        }
slouken@1662
   124
    }
slouken@1662
   125
    if (i < SDL_numthreads) {
slouken@1662
   126
        if (--SDL_numthreads > 0) {
slouken@1662
   127
            while (i < SDL_numthreads) {
slouken@1662
   128
                SDL_Threads[i] = SDL_Threads[i + 1];
slouken@1662
   129
                ++i;
slouken@1662
   130
            }
slouken@1662
   131
        } else {
slouken@1662
   132
            SDL_maxthreads = 0;
slouken@1662
   133
            SDL_free (SDL_Threads);
slouken@1662
   134
            SDL_Threads = NULL;
slouken@1662
   135
        }
slouken@0
   136
#ifdef DEBUG_THREADS
slouken@1662
   137
        printf ("Deleting thread (%d left - %d max)\n",
slouken@1662
   138
                SDL_numthreads, SDL_maxthreads);
slouken@0
   139
#endif
slouken@1662
   140
    }
slouken@1662
   141
    SDL_mutexV (thread_lock);
slouken@1499
   142
slouken@1662
   143
    if (SDL_Threads == NULL) {
slouken@1662
   144
        SDL_ThreadsQuit ();
slouken@1662
   145
    }
slouken@0
   146
}
slouken@0
   147
slouken@0
   148
/* The default (non-thread-safe) global error variable */
slouken@0
   149
static SDL_error SDL_global_error;
slouken@0
   150
slouken@0
   151
/* Routine to get the thread-specific error variable */
slouken@1662
   152
SDL_error *
slouken@1662
   153
SDL_GetErrBuf (void)
slouken@0
   154
{
slouken@1662
   155
    SDL_error *errbuf;
slouken@0
   156
slouken@1662
   157
    errbuf = &SDL_global_error;
slouken@1662
   158
    if (SDL_Threads) {
slouken@1662
   159
        int i;
slouken@1662
   160
        Uint32 this_thread;
slouken@0
   161
slouken@1662
   162
        this_thread = SDL_ThreadID ();
slouken@1662
   163
        SDL_mutexP (thread_lock);
slouken@1662
   164
        for (i = 0; i < SDL_numthreads; ++i) {
slouken@1662
   165
            if (this_thread == SDL_Threads[i]->threadid) {
slouken@1662
   166
                errbuf = &SDL_Threads[i]->errbuf;
slouken@1662
   167
                break;
slouken@1662
   168
            }
slouken@1662
   169
        }
slouken@1662
   170
        SDL_mutexV (thread_lock);
slouken@1662
   171
    }
slouken@1662
   172
    return (errbuf);
slouken@0
   173
}
slouken@0
   174
slouken@0
   175
slouken@0
   176
/* Arguments and callback to setup and run the user thread function */
slouken@1662
   177
typedef struct
slouken@1662
   178
{
slouken@1662
   179
    int (SDLCALL * func) (void *);
slouken@1662
   180
    void *data;
slouken@1662
   181
    SDL_Thread *info;
slouken@1662
   182
    SDL_sem *wait;
slouken@0
   183
} thread_args;
slouken@0
   184
slouken@1662
   185
void
slouken@1662
   186
SDL_RunThread (void *data)
slouken@0
   187
{
slouken@1662
   188
    thread_args *args;
slouken@1662
   189
    int (SDLCALL * userfunc) (void *);
slouken@1662
   190
    void *userdata;
slouken@1662
   191
    int *statusloc;
slouken@0
   192
slouken@1662
   193
    /* Perform any system-dependent setup
slouken@1662
   194
       - this function cannot fail, and cannot use SDL_SetError()
slouken@1662
   195
     */
slouken@1662
   196
    SDL_SYS_SetupThread ();
slouken@0
   197
slouken@1662
   198
    /* Get the thread id */
slouken@1662
   199
    args = (thread_args *) data;
slouken@1662
   200
    args->info->threadid = SDL_ThreadID ();
slouken@0
   201
slouken@1662
   202
    /* Figure out what function to run */
slouken@1662
   203
    userfunc = args->func;
slouken@1662
   204
    userdata = args->data;
slouken@1662
   205
    statusloc = &args->info->status;
slouken@0
   206
slouken@1662
   207
    /* Wake up the parent thread */
slouken@1662
   208
    SDL_SemPost (args->wait);
slouken@0
   209
slouken@1662
   210
    /* Run the function */
slouken@1662
   211
    *statusloc = userfunc (userdata);
slouken@0
   212
}
slouken@0
   213
slouken@1471
   214
#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
slouken@1330
   215
#undef SDL_CreateThread
slouken@1662
   216
DECLSPEC SDL_Thread *SDLCALL
slouken@1662
   217
SDL_CreateThread (int (SDLCALL * fn) (void *), void *data,
slouken@1662
   218
                  pfnSDL_CurrentBeginThread pfnBeginThread,
slouken@1662
   219
                  pfnSDL_CurrentEndThread pfnEndThread)
icculus@1190
   220
#else
slouken@1662
   221
DECLSPEC SDL_Thread *SDLCALL
slouken@1662
   222
SDL_CreateThread (int (SDLCALL * fn) (void *), void *data)
icculus@1190
   223
#endif
slouken@0
   224
{
slouken@1662
   225
    SDL_Thread *thread;
slouken@1662
   226
    thread_args *args;
slouken@1662
   227
    int ret;
slouken@0
   228
slouken@1662
   229
    /* Allocate memory for the thread info structure */
slouken@1662
   230
    thread = (SDL_Thread *) SDL_malloc (sizeof (*thread));
slouken@1662
   231
    if (thread == NULL) {
slouken@1662
   232
        SDL_OutOfMemory ();
slouken@1662
   233
        return (NULL);
slouken@1662
   234
    }
slouken@1662
   235
    SDL_memset (thread, 0, (sizeof *thread));
slouken@1662
   236
    thread->status = -1;
slouken@0
   237
slouken@1662
   238
    /* Set up the arguments for the thread */
slouken@1662
   239
    args = (thread_args *) SDL_malloc (sizeof (*args));
slouken@1662
   240
    if (args == NULL) {
slouken@1662
   241
        SDL_OutOfMemory ();
slouken@1662
   242
        SDL_free (thread);
slouken@1662
   243
        return (NULL);
slouken@1662
   244
    }
slouken@1662
   245
    args->func = fn;
slouken@1662
   246
    args->data = data;
slouken@1662
   247
    args->info = thread;
slouken@1662
   248
    args->wait = SDL_CreateSemaphore (0);
slouken@1662
   249
    if (args->wait == NULL) {
slouken@1662
   250
        SDL_free (thread);
slouken@1662
   251
        SDL_free (args);
slouken@1662
   252
        return (NULL);
slouken@1662
   253
    }
slouken@0
   254
slouken@1662
   255
    /* Add the thread to the list of available threads */
slouken@1662
   256
    SDL_AddThread (thread);
slouken@0
   257
slouken@1662
   258
    /* Create the thread and go! */
slouken@1471
   259
#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
slouken@1662
   260
    ret = SDL_SYS_CreateThread (thread, args, pfnBeginThread, pfnEndThread);
icculus@1190
   261
#else
slouken@1662
   262
    ret = SDL_SYS_CreateThread (thread, args);
icculus@1190
   263
#endif
slouken@1662
   264
    if (ret >= 0) {
slouken@1662
   265
        /* Wait for the thread function to use arguments */
slouken@1662
   266
        SDL_SemWait (args->wait);
slouken@1662
   267
    } else {
slouken@1662
   268
        /* Oops, failed.  Gotta free everything */
slouken@1662
   269
        SDL_DelThread (thread);
slouken@1662
   270
        SDL_free (thread);
slouken@1662
   271
        thread = NULL;
slouken@1662
   272
    }
slouken@1662
   273
    SDL_DestroySemaphore (args->wait);
slouken@1662
   274
    SDL_free (args);
slouken@0
   275
slouken@1662
   276
    /* Everything is running now */
slouken@1662
   277
    return (thread);
slouken@0
   278
}
slouken@0
   279
slouken@1662
   280
void
slouken@1662
   281
SDL_WaitThread (SDL_Thread * thread, int *status)
slouken@0
   282
{
slouken@1662
   283
    if (thread) {
slouken@1662
   284
        SDL_SYS_WaitThread (thread);
slouken@1662
   285
        if (status) {
slouken@1662
   286
            *status = thread->status;
slouken@1662
   287
        }
slouken@1662
   288
        SDL_DelThread (thread);
slouken@1662
   289
        SDL_free (thread);
slouken@1662
   290
    }
slouken@0
   291
}
slouken@0
   292
slouken@1662
   293
Uint32
slouken@1662
   294
SDL_GetThreadID (SDL_Thread * thread)
slouken@0
   295
{
slouken@1662
   296
    Uint32 id;
slouken@0
   297
slouken@1662
   298
    if (thread) {
slouken@1662
   299
        id = thread->threadid;
slouken@1662
   300
    } else {
slouken@1662
   301
        id = SDL_ThreadID ();
slouken@1662
   302
    }
slouken@1662
   303
    return (id);
slouken@0
   304
}
slouken@0
   305
slouken@1662
   306
void
slouken@1662
   307
SDL_KillThread (SDL_Thread * thread)
slouken@0
   308
{
slouken@1662
   309
    if (thread) {
slouken@1662
   310
        SDL_SYS_KillThread (thread);
slouken@1662
   311
        SDL_WaitThread (thread, NULL);
slouken@1662
   312
    }
slouken@0
   313
}
slouken@0
   314
slouken@1662
   315
/* vi: set ts=4 sw=4 expandtab: */