view src/thread/SDL_thread.c @ 1662:782fd950bd46 SDL-1.3

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.
author Sam Lantinga <slouken@libsdl.org>
date Sun, 28 May 2006 13:04:16 +0000
parents 14717b52abc0
children 4da1ee79c9af
line wrap: on
line source

/*
    SDL - Simple DirectMedia Layer
    Copyright (C) 1997-2006 Sam Lantinga

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

    Sam Lantinga
    slouken@libsdl.org
*/
#include "SDL_config.h"

/* System independent thread management routines for SDL */

#include "SDL_mutex.h"
#include "SDL_thread.h"
#include "SDL_thread_c.h"
#include "SDL_systhread.h"

#define ARRAY_CHUNKSIZE	32
/* The array of threads currently active in the application
   (except the main thread)
   The manipulation of an array here is safer than using a linked list.
*/
static int SDL_maxthreads = 0;
static int SDL_numthreads = 0;
static SDL_Thread **SDL_Threads = NULL;
static SDL_mutex *thread_lock = NULL;

int
SDL_ThreadsInit (void)
{
    int retval;

    retval = 0;
    thread_lock = SDL_CreateMutex ();
    if (thread_lock == NULL) {
        retval = -1;
    }
    return (retval);
}

/* This should never be called...
   If this is called by SDL_Quit(), we don't know whether or not we should
   clean up threads here.  If any threads are still running after this call,
   they will no longer have access to any per-thread data.
 */
void
SDL_ThreadsQuit (void)
{
    SDL_mutex *mutex;

    mutex = thread_lock;
    thread_lock = NULL;
    if (mutex != NULL) {
        SDL_DestroyMutex (mutex);
    }
}

/* Routines for manipulating the thread list */
static void
SDL_AddThread (SDL_Thread * thread)
{
    /* WARNING:
       If the very first threads are created simultaneously, then
       there could be a race condition causing memory corruption.
       In practice, this isn't a problem because by definition there
       is only one thread running the first time this is called.
     */
    if (!thread_lock) {
        if (SDL_ThreadsInit () < 0) {
            return;
        }
    }
    SDL_mutexP (thread_lock);

    /* Expand the list of threads, if necessary */
#ifdef DEBUG_THREADS
    printf ("Adding thread (%d already - %d max)\n",
            SDL_numthreads, SDL_maxthreads);
#endif
    if (SDL_numthreads == SDL_maxthreads) {
        SDL_Thread **threads;
        threads = (SDL_Thread **) SDL_realloc (SDL_Threads,
                                               (SDL_maxthreads +
                                                ARRAY_CHUNKSIZE) *
                                               (sizeof *threads));
        if (threads == NULL) {
            SDL_OutOfMemory ();
            goto done;
        }
        SDL_maxthreads += ARRAY_CHUNKSIZE;
        SDL_Threads = threads;
    }
    SDL_Threads[SDL_numthreads++] = thread;
  done:
    SDL_mutexV (thread_lock);
}

static void
SDL_DelThread (SDL_Thread * thread)
{
    int i;

    if (!thread_lock) {
        return;
    }
    SDL_mutexP (thread_lock);
    for (i = 0; i < SDL_numthreads; ++i) {
        if (thread == SDL_Threads[i]) {
            break;
        }
    }
    if (i < SDL_numthreads) {
        if (--SDL_numthreads > 0) {
            while (i < SDL_numthreads) {
                SDL_Threads[i] = SDL_Threads[i + 1];
                ++i;
            }
        } else {
            SDL_maxthreads = 0;
            SDL_free (SDL_Threads);
            SDL_Threads = NULL;
        }
#ifdef DEBUG_THREADS
        printf ("Deleting thread (%d left - %d max)\n",
                SDL_numthreads, SDL_maxthreads);
#endif
    }
    SDL_mutexV (thread_lock);

    if (SDL_Threads == NULL) {
        SDL_ThreadsQuit ();
    }
}

/* The default (non-thread-safe) global error variable */
static SDL_error SDL_global_error;

/* Routine to get the thread-specific error variable */
SDL_error *
SDL_GetErrBuf (void)
{
    SDL_error *errbuf;

    errbuf = &SDL_global_error;
    if (SDL_Threads) {
        int i;
        Uint32 this_thread;

        this_thread = SDL_ThreadID ();
        SDL_mutexP (thread_lock);
        for (i = 0; i < SDL_numthreads; ++i) {
            if (this_thread == SDL_Threads[i]->threadid) {
                errbuf = &SDL_Threads[i]->errbuf;
                break;
            }
        }
        SDL_mutexV (thread_lock);
    }
    return (errbuf);
}


/* Arguments and callback to setup and run the user thread function */
typedef struct
{
    int (SDLCALL * func) (void *);
    void *data;
    SDL_Thread *info;
    SDL_sem *wait;
} thread_args;

void
SDL_RunThread (void *data)
{
    thread_args *args;
    int (SDLCALL * userfunc) (void *);
    void *userdata;
    int *statusloc;

    /* Perform any system-dependent setup
       - this function cannot fail, and cannot use SDL_SetError()
     */
    SDL_SYS_SetupThread ();

    /* Get the thread id */
    args = (thread_args *) data;
    args->info->threadid = SDL_ThreadID ();

    /* Figure out what function to run */
    userfunc = args->func;
    userdata = args->data;
    statusloc = &args->info->status;

    /* Wake up the parent thread */
    SDL_SemPost (args->wait);

    /* Run the function */
    *statusloc = userfunc (userdata);
}

#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
#undef SDL_CreateThread
DECLSPEC SDL_Thread *SDLCALL
SDL_CreateThread (int (SDLCALL * fn) (void *), void *data,
                  pfnSDL_CurrentBeginThread pfnBeginThread,
                  pfnSDL_CurrentEndThread pfnEndThread)
#else
DECLSPEC SDL_Thread *SDLCALL
SDL_CreateThread (int (SDLCALL * fn) (void *), void *data)
#endif
{
    SDL_Thread *thread;
    thread_args *args;
    int ret;

    /* Allocate memory for the thread info structure */
    thread = (SDL_Thread *) SDL_malloc (sizeof (*thread));
    if (thread == NULL) {
        SDL_OutOfMemory ();
        return (NULL);
    }
    SDL_memset (thread, 0, (sizeof *thread));
    thread->status = -1;

    /* Set up the arguments for the thread */
    args = (thread_args *) SDL_malloc (sizeof (*args));
    if (args == NULL) {
        SDL_OutOfMemory ();
        SDL_free (thread);
        return (NULL);
    }
    args->func = fn;
    args->data = data;
    args->info = thread;
    args->wait = SDL_CreateSemaphore (0);
    if (args->wait == NULL) {
        SDL_free (thread);
        SDL_free (args);
        return (NULL);
    }

    /* Add the thread to the list of available threads */
    SDL_AddThread (thread);

    /* Create the thread and go! */
#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
    ret = SDL_SYS_CreateThread (thread, args, pfnBeginThread, pfnEndThread);
#else
    ret = SDL_SYS_CreateThread (thread, args);
#endif
    if (ret >= 0) {
        /* Wait for the thread function to use arguments */
        SDL_SemWait (args->wait);
    } else {
        /* Oops, failed.  Gotta free everything */
        SDL_DelThread (thread);
        SDL_free (thread);
        thread = NULL;
    }
    SDL_DestroySemaphore (args->wait);
    SDL_free (args);

    /* Everything is running now */
    return (thread);
}

void
SDL_WaitThread (SDL_Thread * thread, int *status)
{
    if (thread) {
        SDL_SYS_WaitThread (thread);
        if (status) {
            *status = thread->status;
        }
        SDL_DelThread (thread);
        SDL_free (thread);
    }
}

Uint32
SDL_GetThreadID (SDL_Thread * thread)
{
    Uint32 id;

    if (thread) {
        id = thread->threadid;
    } else {
        id = SDL_ThreadID ();
    }
    return (id);
}

void
SDL_KillThread (SDL_Thread * thread)
{
    if (thread) {
        SDL_SYS_KillThread (thread);
        SDL_WaitThread (thread, NULL);
    }
}

/* vi: set ts=4 sw=4 expandtab: */