src/thread/SDL_thread.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 22 Jun 2015 23:36:06 -0700
changeset 9776 952ff8a5076f
parent 9619 b94b6d0bff0f
child 9998 f67cf37e9cd4
permissions -rw-r--r--
Fixed bug 3030 - SDL_RecreateWindow fails to restore title, icon, etc.

Adam M.

It loses the title and icon when window recreation fails. For instance, this may happen when trying to create an OpenGL ES window on a system that doesn't support it. But at that point, the title and icon have already been lost.
slouken@0
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@9619
     3
  Copyright (C) 1997-2015 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
*/
icculus@8093
    21
#include "../SDL_internal.h"
slouken@0
    22
slouken@0
    23
/* System independent thread management routines for SDL */
slouken@0
    24
icculus@7978
    25
#include "SDL_assert.h"
slouken@0
    26
#include "SDL_thread.h"
slouken@0
    27
#include "SDL_thread_c.h"
slouken@0
    28
#include "SDL_systhread.h"
slouken@6044
    29
#include "../SDL_error_c.h"
slouken@0
    30
slouken@0
    31
slouken@7393
    32
SDL_TLSID
slouken@7393
    33
SDL_TLSCreate()
slouken@7393
    34
{
slouken@7393
    35
    static SDL_atomic_t SDL_tls_id;
slouken@7393
    36
    return SDL_AtomicIncRef(&SDL_tls_id)+1;
slouken@7393
    37
}
slouken@7393
    38
slouken@7393
    39
void *
slouken@7393
    40
SDL_TLSGet(SDL_TLSID id)
slouken@7393
    41
{
slouken@7393
    42
    SDL_TLSData *storage;
slouken@7393
    43
slouken@7393
    44
    storage = SDL_SYS_GetTLSData();
slouken@7393
    45
    if (!storage || id == 0 || id > storage->limit) {
slouken@7393
    46
        return NULL;
slouken@7393
    47
    }
slouken@7393
    48
    return storage->array[id-1].data;
slouken@7393
    49
}
slouken@7393
    50
slouken@7393
    51
int
slouken@7393
    52
SDL_TLSSet(SDL_TLSID id, const void *value, void (*destructor)(void *))
slouken@7393
    53
{
slouken@7393
    54
    SDL_TLSData *storage;
slouken@7393
    55
slouken@7393
    56
    if (id == 0) {
slouken@7393
    57
        return SDL_InvalidParamError("id");
slouken@7393
    58
    }
slouken@7393
    59
slouken@7393
    60
    storage = SDL_SYS_GetTLSData();
icculus@7482
    61
    if (!storage || (id > storage->limit)) {
icculus@7482
    62
        unsigned int i, oldlimit, newlimit;
slouken@7393
    63
slouken@7393
    64
        oldlimit = storage ? storage->limit : 0;
slouken@7393
    65
        newlimit = (id + TLS_ALLOC_CHUNKSIZE);
slouken@7393
    66
        storage = (SDL_TLSData *)SDL_realloc(storage, sizeof(*storage)+(newlimit-1)*sizeof(storage->array[0]));
slouken@7393
    67
        if (!storage) {
slouken@7393
    68
            return SDL_OutOfMemory();
slouken@7393
    69
        }
slouken@7393
    70
        storage->limit = newlimit;
slouken@7393
    71
        for (i = oldlimit; i < newlimit; ++i) {
slouken@7393
    72
            storage->array[i].data = NULL;
slouken@7393
    73
            storage->array[i].destructor = NULL;
slouken@7393
    74
        }
slouken@7393
    75
        if (SDL_SYS_SetTLSData(storage) != 0) {
slouken@7393
    76
            return -1;
slouken@7393
    77
        }
slouken@7393
    78
    }
slouken@7393
    79
slouken@7393
    80
    storage->array[id-1].data = SDL_const_cast(void*, value);
slouken@7393
    81
    storage->array[id-1].destructor = destructor;
slouken@7393
    82
    return 0;
slouken@7393
    83
}
slouken@7393
    84
slouken@7393
    85
static void
slouken@7393
    86
SDL_TLSCleanup()
slouken@7393
    87
{
slouken@7393
    88
    SDL_TLSData *storage;
slouken@7393
    89
slouken@7393
    90
    storage = SDL_SYS_GetTLSData();
slouken@7393
    91
    if (storage) {
icculus@7484
    92
        unsigned int i;
slouken@7393
    93
        for (i = 0; i < storage->limit; ++i) {
slouken@7393
    94
            if (storage->array[i].destructor) {
slouken@7393
    95
                storage->array[i].destructor(storage->array[i].data);
slouken@7393
    96
            }
slouken@7393
    97
        }
slouken@7393
    98
        SDL_SYS_SetTLSData(NULL);
slouken@7393
    99
        SDL_free(storage);
slouken@7393
   100
    }
slouken@7393
   101
}
slouken@7393
   102
slouken@7393
   103
slouken@7393
   104
/* This is a generic implementation of thread-local storage which doesn't
slouken@7393
   105
   require additional OS support.
slouken@7393
   106
slouken@7393
   107
   It is not especially efficient and doesn't clean up thread-local storage
slouken@7393
   108
   as threads exit.  If there is a real OS that doesn't support thread-local
slouken@7393
   109
   storage this implementation should be improved to be production quality.
slouken@7393
   110
*/
slouken@7393
   111
slouken@7393
   112
typedef struct SDL_TLSEntry {
slouken@7393
   113
    SDL_threadID thread;
slouken@7393
   114
    SDL_TLSData *storage;
slouken@7393
   115
    struct SDL_TLSEntry *next;
slouken@7393
   116
} SDL_TLSEntry;
slouken@7393
   117
slouken@7393
   118
static SDL_mutex *SDL_generic_TLS_mutex;
slouken@7393
   119
static SDL_TLSEntry *SDL_generic_TLS;
slouken@7393
   120
slouken@7393
   121
slouken@7393
   122
SDL_TLSData *
slouken@7393
   123
SDL_Generic_GetTLSData()
slouken@7393
   124
{
slouken@7393
   125
    SDL_threadID thread = SDL_ThreadID();
slouken@7393
   126
    SDL_TLSEntry *entry;
slouken@7393
   127
    SDL_TLSData *storage = NULL;
slouken@7393
   128
slouken@7730
   129
#if !SDL_THREADS_DISABLED
slouken@7393
   130
    if (!SDL_generic_TLS_mutex) {
slouken@7393
   131
        static SDL_SpinLock tls_lock;
slouken@7393
   132
        SDL_AtomicLock(&tls_lock);
slouken@7393
   133
        if (!SDL_generic_TLS_mutex) {
slouken@7393
   134
            SDL_mutex *mutex = SDL_CreateMutex();
slouken@7393
   135
            SDL_MemoryBarrierRelease();
slouken@7393
   136
            SDL_generic_TLS_mutex = mutex;
slouken@7393
   137
            if (!SDL_generic_TLS_mutex) {
slouken@7393
   138
                SDL_AtomicUnlock(&tls_lock);
slouken@7393
   139
                return NULL;
slouken@7393
   140
            }
slouken@7393
   141
        }
slouken@7393
   142
        SDL_AtomicUnlock(&tls_lock);
slouken@7393
   143
    }
slouken@7730
   144
#endif /* SDL_THREADS_DISABLED */
slouken@7393
   145
slouken@7393
   146
    SDL_MemoryBarrierAcquire();
slouken@7393
   147
    SDL_LockMutex(SDL_generic_TLS_mutex);
slouken@7393
   148
    for (entry = SDL_generic_TLS; entry; entry = entry->next) {
slouken@7393
   149
        if (entry->thread == thread) {
slouken@7393
   150
            storage = entry->storage;
slouken@7393
   151
            break;
slouken@7393
   152
        }
slouken@7393
   153
    }
slouken@7730
   154
#if !SDL_THREADS_DISABLED
slouken@7393
   155
    SDL_UnlockMutex(SDL_generic_TLS_mutex);
slouken@7730
   156
#endif
slouken@7393
   157
slouken@7393
   158
    return storage;
slouken@7393
   159
}
slouken@7393
   160
slouken@7393
   161
int
slouken@7393
   162
SDL_Generic_SetTLSData(SDL_TLSData *storage)
slouken@7393
   163
{
slouken@7393
   164
    SDL_threadID thread = SDL_ThreadID();
slouken@7393
   165
    SDL_TLSEntry *prev, *entry;
slouken@7393
   166
slouken@7393
   167
    /* SDL_Generic_GetTLSData() is always called first, so we can assume SDL_generic_TLS_mutex */
slouken@7393
   168
    SDL_LockMutex(SDL_generic_TLS_mutex);
slouken@7393
   169
    prev = NULL;
slouken@7393
   170
    for (entry = SDL_generic_TLS; entry; entry = entry->next) {
slouken@7393
   171
        if (entry->thread == thread) {
slouken@7393
   172
            if (storage) {
slouken@7393
   173
                entry->storage = storage;
slouken@7393
   174
            } else {
slouken@7393
   175
                if (prev) {
slouken@7393
   176
                    prev->next = entry->next;
slouken@7393
   177
                } else {
slouken@7393
   178
                    SDL_generic_TLS = entry->next;
slouken@7393
   179
                }
slouken@7393
   180
                SDL_free(entry);
slouken@7393
   181
            }
slouken@7393
   182
            break;
slouken@7393
   183
        }
slouken@7393
   184
        prev = entry;
slouken@7393
   185
    }
slouken@7393
   186
    if (!entry) {
slouken@7393
   187
        entry = (SDL_TLSEntry *)SDL_malloc(sizeof(*entry));
slouken@7393
   188
        if (entry) {
slouken@7393
   189
            entry->thread = thread;
slouken@7393
   190
            entry->storage = storage;
slouken@7393
   191
            entry->next = SDL_generic_TLS;
slouken@7393
   192
            SDL_generic_TLS = entry;
slouken@7393
   193
        }
slouken@7393
   194
    }
slouken@7393
   195
    SDL_UnlockMutex(SDL_generic_TLS_mutex);
slouken@7393
   196
slouken@7393
   197
    if (!entry) {
slouken@7393
   198
        return SDL_OutOfMemory();
slouken@7393
   199
    }
slouken@7393
   200
    return 0;
slouken@7393
   201
}
slouken@7393
   202
slouken@0
   203
/* Routine to get the thread-specific error variable */
slouken@1895
   204
SDL_error *
slouken@1895
   205
SDL_GetErrBuf(void)
slouken@0
   206
{
slouken@7393
   207
    static SDL_SpinLock tls_lock;
slouken@7391
   208
    static SDL_bool tls_being_created;
slouken@7391
   209
    static SDL_TLSID tls_errbuf;
slouken@7391
   210
    static SDL_error SDL_global_errbuf;
slouken@7393
   211
    const SDL_error *ALLOCATION_IN_PROGRESS = (SDL_error *)-1;
slouken@1895
   212
    SDL_error *errbuf;
slouken@0
   213
slouken@7393
   214
    /* tls_being_created is there simply to prevent recursion if SDL_TLSCreate() fails.
slouken@7393
   215
       It also means it's possible for another thread to also use SDL_global_errbuf,
slouken@7393
   216
       but that's very unlikely and hopefully won't cause issues.
slouken@7393
   217
     */
slouken@7391
   218
    if (!tls_errbuf && !tls_being_created) {
slouken@7393
   219
        SDL_AtomicLock(&tls_lock);
slouken@7391
   220
        if (!tls_errbuf) {
slouken@7393
   221
            SDL_TLSID slot;
slouken@7391
   222
            tls_being_created = SDL_TRUE;
slouken@7393
   223
            slot = SDL_TLSCreate();
slouken@7391
   224
            tls_being_created = SDL_FALSE;
slouken@7393
   225
            SDL_MemoryBarrierRelease();
slouken@7393
   226
            tls_errbuf = slot;
slouken@7391
   227
        }
slouken@7393
   228
        SDL_AtomicUnlock(&tls_lock);
slouken@7391
   229
    }
slouken@7391
   230
    if (!tls_errbuf) {
slouken@7391
   231
        return &SDL_global_errbuf;
slouken@7391
   232
    }
slouken@0
   233
slouken@7393
   234
    SDL_MemoryBarrierAcquire();
slouken@7393
   235
    errbuf = (SDL_error *)SDL_TLSGet(tls_errbuf);
slouken@7393
   236
    if (errbuf == ALLOCATION_IN_PROGRESS) {
slouken@7393
   237
        return &SDL_global_errbuf;
slouken@7393
   238
    }
slouken@7391
   239
    if (!errbuf) {
slouken@7393
   240
        /* Mark that we're in the middle of allocating our buffer */
slouken@7393
   241
        SDL_TLSSet(tls_errbuf, ALLOCATION_IN_PROGRESS, NULL);
slouken@7391
   242
        errbuf = (SDL_error *)SDL_malloc(sizeof(*errbuf));
slouken@7391
   243
        if (!errbuf) {
slouken@7393
   244
            SDL_TLSSet(tls_errbuf, NULL, NULL);
slouken@7391
   245
            return &SDL_global_errbuf;
slouken@1895
   246
        }
slouken@7391
   247
        SDL_zerop(errbuf);
slouken@7393
   248
        SDL_TLSSet(tls_errbuf, errbuf, SDL_free);
slouken@1895
   249
    }
slouken@7391
   250
    return errbuf;
slouken@0
   251
}
slouken@0
   252
slouken@0
   253
slouken@0
   254
/* Arguments and callback to setup and run the user thread function */
slouken@1895
   255
typedef struct
slouken@1895
   256
{
slouken@1895
   257
    int (SDLCALL * func) (void *);
slouken@1895
   258
    void *data;
slouken@1895
   259
    SDL_Thread *info;
slouken@1895
   260
    SDL_sem *wait;
slouken@0
   261
} thread_args;
slouken@0
   262
slouken@1895
   263
void
slouken@1895
   264
SDL_RunThread(void *data)
slouken@0
   265
{
icculus@5969
   266
    thread_args *args = (thread_args *) data;
icculus@5969
   267
    int (SDLCALL * userfunc) (void *) = args->func;
icculus@5969
   268
    void *userdata = args->data;
icculus@7978
   269
    SDL_Thread *thread = args->info;
icculus@7978
   270
    int *statusloc = &thread->status;
slouken@0
   271
slouken@7393
   272
    /* Perform any system-dependent setup - this function may not fail */
icculus@7978
   273
    SDL_SYS_SetupThread(thread->name);
slouken@0
   274
slouken@1895
   275
    /* Get the thread id */
icculus@7978
   276
    thread->threadid = SDL_ThreadID();
slouken@0
   277
slouken@1895
   278
    /* Wake up the parent thread */
slouken@1895
   279
    SDL_SemPost(args->wait);
slouken@0
   280
slouken@1895
   281
    /* Run the function */
slouken@1895
   282
    *statusloc = userfunc(userdata);
slouken@7393
   283
slouken@7393
   284
    /* Clean up thread-local storage */
slouken@7393
   285
    SDL_TLSCleanup();
icculus@7978
   286
icculus@7978
   287
    /* Mark us as ready to be joined (or detached) */
icculus@7978
   288
    if (!SDL_AtomicCAS(&thread->state, SDL_THREAD_STATE_ALIVE, SDL_THREAD_STATE_ZOMBIE)) {
icculus@7978
   289
        /* Clean up if something already detached us. */
icculus@7978
   290
        if (SDL_AtomicCAS(&thread->state, SDL_THREAD_STATE_DETACHED, SDL_THREAD_STATE_CLEANED)) {
icculus@7978
   291
            if (thread->name) {
icculus@7978
   292
                SDL_free(thread->name);
icculus@7978
   293
            }
icculus@7978
   294
            SDL_free(thread);
icculus@7978
   295
        }
icculus@7978
   296
    }
slouken@0
   297
}
slouken@0
   298
icculus@8094
   299
#ifdef SDL_CreateThread
icculus@8094
   300
#undef SDL_CreateThread
icculus@8094
   301
#endif
icculus@8094
   302
#if SDL_DYNAMIC_API
icculus@8094
   303
#define SDL_CreateThread SDL_CreateThread_REAL
icculus@8094
   304
#endif
icculus@8094
   305
slouken@1471
   306
#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
slouken@1895
   307
DECLSPEC SDL_Thread *SDLCALL
icculus@5969
   308
SDL_CreateThread(int (SDLCALL * fn) (void *),
icculus@5969
   309
                 const char *name, void *data,
slouken@1895
   310
                 pfnSDL_CurrentBeginThread pfnBeginThread,
slouken@1895
   311
                 pfnSDL_CurrentEndThread pfnEndThread)
icculus@1190
   312
#else
slouken@1895
   313
DECLSPEC SDL_Thread *SDLCALL
icculus@5969
   314
SDL_CreateThread(int (SDLCALL * fn) (void *),
icculus@5969
   315
                 const char *name, void *data)
icculus@1190
   316
#endif
slouken@0
   317
{
slouken@1895
   318
    SDL_Thread *thread;
slouken@1895
   319
    thread_args *args;
slouken@1895
   320
    int ret;
slouken@0
   321
slouken@1895
   322
    /* Allocate memory for the thread info structure */
slouken@1895
   323
    thread = (SDL_Thread *) SDL_malloc(sizeof(*thread));
slouken@1895
   324
    if (thread == NULL) {
slouken@1895
   325
        SDL_OutOfMemory();
slouken@1895
   326
        return (NULL);
slouken@1895
   327
    }
icculus@7978
   328
    SDL_zerop(thread);
slouken@1895
   329
    thread->status = -1;
icculus@7978
   330
    SDL_AtomicSet(&thread->state, SDL_THREAD_STATE_ALIVE);
slouken@0
   331
slouken@1895
   332
    /* Set up the arguments for the thread */
icculus@5969
   333
    if (name != NULL) {
icculus@5969
   334
        thread->name = SDL_strdup(name);
icculus@5969
   335
        if (thread->name == NULL) {
icculus@5969
   336
            SDL_OutOfMemory();
icculus@5969
   337
            SDL_free(thread);
icculus@5969
   338
            return (NULL);
icculus@5969
   339
        }
icculus@5969
   340
    }
icculus@5969
   341
icculus@5969
   342
    /* Set up the arguments for the thread */
slouken@1895
   343
    args = (thread_args *) SDL_malloc(sizeof(*args));
slouken@1895
   344
    if (args == NULL) {
slouken@1895
   345
        SDL_OutOfMemory();
slouken@7606
   346
        if (thread->name) {
slouken@7606
   347
            SDL_free(thread->name);
slouken@7606
   348
        }
slouken@1895
   349
        SDL_free(thread);
slouken@1895
   350
        return (NULL);
slouken@1895
   351
    }
slouken@1895
   352
    args->func = fn;
slouken@1895
   353
    args->data = data;
slouken@1895
   354
    args->info = thread;
slouken@1895
   355
    args->wait = SDL_CreateSemaphore(0);
slouken@1895
   356
    if (args->wait == NULL) {
slouken@7606
   357
        if (thread->name) {
slouken@7606
   358
            SDL_free(thread->name);
slouken@7606
   359
        }
slouken@1895
   360
        SDL_free(thread);
slouken@1895
   361
        SDL_free(args);
slouken@1895
   362
        return (NULL);
slouken@1895
   363
    }
slouken@0
   364
slouken@1895
   365
    /* Create the thread and go! */
slouken@1471
   366
#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
slouken@1895
   367
    ret = SDL_SYS_CreateThread(thread, args, pfnBeginThread, pfnEndThread);
icculus@1190
   368
#else
slouken@1895
   369
    ret = SDL_SYS_CreateThread(thread, args);
icculus@1190
   370
#endif
slouken@1895
   371
    if (ret >= 0) {
slouken@1895
   372
        /* Wait for the thread function to use arguments */
slouken@1895
   373
        SDL_SemWait(args->wait);
slouken@1895
   374
    } else {
slouken@1895
   375
        /* Oops, failed.  Gotta free everything */
slouken@7606
   376
        if (thread->name) {
slouken@7606
   377
            SDL_free(thread->name);
slouken@7606
   378
        }
slouken@1895
   379
        SDL_free(thread);
slouken@1895
   380
        thread = NULL;
slouken@1895
   381
    }
slouken@1895
   382
    SDL_DestroySemaphore(args->wait);
slouken@1895
   383
    SDL_free(args);
slouken@0
   384
slouken@1895
   385
    /* Everything is running now */
slouken@1895
   386
    return (thread);
slouken@0
   387
}
slouken@0
   388
slouken@5506
   389
SDL_threadID
slouken@5506
   390
SDL_GetThreadID(SDL_Thread * thread)
slouken@5506
   391
{
slouken@5506
   392
    SDL_threadID id;
slouken@5506
   393
slouken@5506
   394
    if (thread) {
slouken@5506
   395
        id = thread->threadid;
slouken@5506
   396
    } else {
slouken@5506
   397
        id = SDL_ThreadID();
slouken@5506
   398
    }
slouken@5506
   399
    return id;
slouken@5506
   400
}
slouken@5506
   401
icculus@5969
   402
const char *
icculus@5969
   403
SDL_GetThreadName(SDL_Thread * thread)
icculus@5969
   404
{
slouken@7606
   405
    if (thread) {
slouken@7606
   406
        return thread->name;
slouken@7606
   407
    } else {
slouken@7606
   408
        return NULL;
slouken@7606
   409
    }
icculus@5969
   410
}
icculus@5969
   411
slouken@5506
   412
int
slouken@5509
   413
SDL_SetThreadPriority(SDL_ThreadPriority priority)
slouken@5506
   414
{
slouken@5509
   415
    return SDL_SYS_SetThreadPriority(priority);
slouken@5506
   416
}
slouken@5506
   417
slouken@1895
   418
void
slouken@1895
   419
SDL_WaitThread(SDL_Thread * thread, int *status)
slouken@0
   420
{
slouken@1895
   421
    if (thread) {
slouken@1895
   422
        SDL_SYS_WaitThread(thread);
slouken@1895
   423
        if (status) {
slouken@1895
   424
            *status = thread->status;
slouken@1895
   425
        }
slouken@7606
   426
        if (thread->name) {
slouken@7606
   427
            SDL_free(thread->name);
slouken@7606
   428
        }
slouken@1895
   429
        SDL_free(thread);
slouken@1895
   430
    }
slouken@0
   431
}
slouken@0
   432
icculus@7978
   433
void
icculus@7978
   434
SDL_DetachThread(SDL_Thread * thread)
icculus@7978
   435
{
icculus@7978
   436
    if (!thread) {
icculus@7978
   437
        return;
icculus@7978
   438
    }
icculus@7978
   439
icculus@7978
   440
    /* Grab dibs if the state is alive+joinable. */
icculus@7978
   441
    if (SDL_AtomicCAS(&thread->state, SDL_THREAD_STATE_ALIVE, SDL_THREAD_STATE_DETACHED)) {
icculus@7978
   442
        SDL_SYS_DetachThread(thread);
icculus@7978
   443
    } else {
icculus@7978
   444
        /* all other states are pretty final, see where we landed. */
slouken@8922
   445
        const int thread_state = SDL_AtomicGet(&thread->state);
slouken@8922
   446
        if ((thread_state == SDL_THREAD_STATE_DETACHED) || (thread_state == SDL_THREAD_STATE_CLEANED)) {
icculus@7978
   447
            return;  /* already detached (you shouldn't call this twice!) */
slouken@8922
   448
        } else if (thread_state == SDL_THREAD_STATE_ZOMBIE) {
icculus@7978
   449
            SDL_WaitThread(thread, NULL);  /* already done, clean it up. */
icculus@7978
   450
        } else {
icculus@7978
   451
            SDL_assert(0 && "Unexpected thread state");
icculus@7978
   452
        }
icculus@7978
   453
    }
icculus@7978
   454
}
icculus@7978
   455
slouken@1895
   456
/* vi: set ts=4 sw=4 expandtab: */