Added SDL_DetachThread() API.
authorRyan C. Gordon
Thu, 14 Nov 2013 00:52:39 -0500
changeset 797870ac84e49797
parent 7977 5272ae8fccd7
child 7979 93d31af030bc
Added SDL_DetachThread() API.
include/SDL_thread.h
src/thread/SDL_systhread.h
src/thread/SDL_thread.c
src/thread/SDL_thread_c.h
src/thread/generic/SDL_systhread.c
src/thread/psp/SDL_systhread.c
src/thread/pthread/SDL_systhread.c
src/thread/windows/SDL_systhread.c
     1.1 --- a/include/SDL_thread.h	Wed Nov 13 22:35:26 2013 -0500
     1.2 +++ b/include/SDL_thread.h	Thu Nov 14 00:52:39 2013 -0500
     1.3 @@ -165,14 +165,54 @@
     1.4  extern DECLSPEC int SDLCALL SDL_SetThreadPriority(SDL_ThreadPriority priority);
     1.5  
     1.6  /**
     1.7 - *  Wait for a thread to finish.
     1.8 + *  Wait for a thread to finish. Threads that haven't been detached will
     1.9 + *  remain (as a "zombie") until this function cleans them up. Not doing so
    1.10 + *  is a resource leak.
    1.11 + *
    1.12 + *  Once a thread has been cleaned up through this function, the SDL_Thread
    1.13 + *  that references it becomes invalid and should not be referenced again.
    1.14 + *  As such, only one thread may call SDL_WaitThread() on another.
    1.15   *
    1.16   *  The return code for the thread function is placed in the area
    1.17   *  pointed to by \c status, if \c status is not NULL.
    1.18 + *
    1.19 + *  You may not wait on a thread that has been used in a call to
    1.20 + *  SDL_DetachThread(). Use either that function or this one, but not
    1.21 + *  both, or behavior is undefined.
    1.22 + *
    1.23 + *  It is safe to pass NULL to this function; it is a no-op.
    1.24   */
    1.25  extern DECLSPEC void SDLCALL SDL_WaitThread(SDL_Thread * thread, int *status);
    1.26  
    1.27  /**
    1.28 + *  A thread may be "detached" to signify that it should not remain until
    1.29 + *  another thread has called SDL_WaitThread() on it. Detaching a thread
    1.30 + *  is useful for long-running threads that nothing needs to synchronize
    1.31 + *  with or further manage. When a detached thread is done, it simply
    1.32 + *  goes away.
    1.33 + *
    1.34 + *  There is no way to recover the return code of a detached thread. If you
    1.35 + *  need this, don't detach the thread and instead use SDL_WaitThread().
    1.36 + *
    1.37 + *  Once a thread is detached, you should usually assume the SDL_Thread isn't
    1.38 + *  safe to reference again, as it will become invalid immediately upon
    1.39 + *  the detached thread's exit, instead of remaining until someone has called
    1.40 + *  SDL_WaitThread() to finally clean it up. As such, don't detach the same
    1.41 + *  thread more than once.
    1.42 + *
    1.43 + *  If a thread has already exited when passed to SDL_DetachThread(), it will
    1.44 + *  stop waiting for a call to SDL_WaitThread() and clean up immediately.
    1.45 + *  It is not safe to detach a thread that might be used with SDL_WaitThread().
    1.46 + *
    1.47 + *  You may not call SDL_WaitThread() on a thread that has been detached.
    1.48 + *  Use either that function or this one, but not both, or behavior is
    1.49 + *  undefined.
    1.50 + *
    1.51 + *  It is safe to pass NULL to this function; it is a no-op.
    1.52 + */
    1.53 +extern DECLSPEC void SDLCALL SDL_DetachThread(SDL_Thread * thread);
    1.54 +
    1.55 +/**
    1.56   *  \brief Create an identifier that is globally visible to all threads but refers to data that is thread-specific.
    1.57   *
    1.58   *  \return The newly created thread local storage identifier, or 0 on error
     2.1 --- a/src/thread/SDL_systhread.h	Wed Nov 13 22:35:26 2013 -0500
     2.2 +++ b/src/thread/SDL_systhread.h	Thu Nov 14 00:52:39 2013 -0500
     2.3 @@ -51,6 +51,9 @@
     2.4   */
     2.5  extern void SDL_SYS_WaitThread(SDL_Thread * thread);
     2.6  
     2.7 +/* Mark thread as cleaned up as soon as it exits, without joining. */
     2.8 +extern void SDL_SYS_DetachThread(SDL_Thread * thread);
     2.9 +
    2.10  /* Get the thread local storage for this thread */
    2.11  extern SDL_TLSData *SDL_SYS_GetTLSData();
    2.12  
     3.1 --- a/src/thread/SDL_thread.c	Wed Nov 13 22:35:26 2013 -0500
     3.2 +++ b/src/thread/SDL_thread.c	Thu Nov 14 00:52:39 2013 -0500
     3.3 @@ -22,6 +22,7 @@
     3.4  
     3.5  /* System independent thread management routines for SDL */
     3.6  
     3.7 +#include "SDL_assert.h"
     3.8  #include "SDL_thread.h"
     3.9  #include "SDL_thread_c.h"
    3.10  #include "SDL_systhread.h"
    3.11 @@ -265,13 +266,14 @@
    3.12      thread_args *args = (thread_args *) data;
    3.13      int (SDLCALL * userfunc) (void *) = args->func;
    3.14      void *userdata = args->data;
    3.15 -    int *statusloc = &args->info->status;
    3.16 +    SDL_Thread *thread = args->info;
    3.17 +    int *statusloc = &thread->status;
    3.18  
    3.19      /* Perform any system-dependent setup - this function may not fail */
    3.20 -    SDL_SYS_SetupThread(args->info->name);
    3.21 +    SDL_SYS_SetupThread(thread->name);
    3.22  
    3.23      /* Get the thread id */
    3.24 -    args->info->threadid = SDL_ThreadID();
    3.25 +    thread->threadid = SDL_ThreadID();
    3.26  
    3.27      /* Wake up the parent thread */
    3.28      SDL_SemPost(args->wait);
    3.29 @@ -281,6 +283,17 @@
    3.30  
    3.31      /* Clean up thread-local storage */
    3.32      SDL_TLSCleanup();
    3.33 +
    3.34 +    /* Mark us as ready to be joined (or detached) */
    3.35 +    if (!SDL_AtomicCAS(&thread->state, SDL_THREAD_STATE_ALIVE, SDL_THREAD_STATE_ZOMBIE)) {
    3.36 +        /* Clean up if something already detached us. */
    3.37 +        if (SDL_AtomicCAS(&thread->state, SDL_THREAD_STATE_DETACHED, SDL_THREAD_STATE_CLEANED)) {
    3.38 +            if (thread->name) {
    3.39 +                SDL_free(thread->name);
    3.40 +            }
    3.41 +            SDL_free(thread);
    3.42 +        }
    3.43 +    }
    3.44  }
    3.45  
    3.46  #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
    3.47 @@ -306,8 +319,9 @@
    3.48          SDL_OutOfMemory();
    3.49          return (NULL);
    3.50      }
    3.51 -    SDL_memset(thread, 0, (sizeof *thread));
    3.52 +    SDL_zerop(thread);
    3.53      thread->status = -1;
    3.54 +    SDL_AtomicSet(&thread->state, SDL_THREAD_STATE_ALIVE);
    3.55  
    3.56      /* Set up the arguments for the thread */
    3.57      if (name != NULL) {
    3.58 @@ -410,4 +424,27 @@
    3.59      }
    3.60  }
    3.61  
    3.62 +void
    3.63 +SDL_DetachThread(SDL_Thread * thread)
    3.64 +{
    3.65 +    if (!thread) {
    3.66 +        return;
    3.67 +    }
    3.68 +
    3.69 +    /* Grab dibs if the state is alive+joinable. */
    3.70 +    if (SDL_AtomicCAS(&thread->state, SDL_THREAD_STATE_ALIVE, SDL_THREAD_STATE_DETACHED)) {
    3.71 +        SDL_SYS_DetachThread(thread);
    3.72 +    } else {
    3.73 +        /* all other states are pretty final, see where we landed. */
    3.74 +        const int state = SDL_AtomicGet(&thread->state);
    3.75 +        if ((state == SDL_THREAD_STATE_DETACHED) || (state == SDL_THREAD_STATE_CLEANED)) {
    3.76 +            return;  /* already detached (you shouldn't call this twice!) */
    3.77 +        } else if (state == SDL_THREAD_STATE_ZOMBIE) {
    3.78 +            SDL_WaitThread(thread, NULL);  /* already done, clean it up. */
    3.79 +        } else {
    3.80 +            SDL_assert(0 && "Unexpected thread state");
    3.81 +        }
    3.82 +    }
    3.83 +}
    3.84 +
    3.85  /* vi: set ts=4 sw=4 expandtab: */
     4.1 --- a/src/thread/SDL_thread_c.h	Wed Nov 13 22:35:26 2013 -0500
     4.2 +++ b/src/thread/SDL_thread_c.h	Thu Nov 14 00:52:39 2013 -0500
     4.3 @@ -40,12 +40,21 @@
     4.4  #endif
     4.5  #include "../SDL_error_c.h"
     4.6  
     4.7 +typedef enum SDL_ThreadState
     4.8 +{
     4.9 +    SDL_THREAD_STATE_ALIVE,
    4.10 +    SDL_THREAD_STATE_DETACHED,
    4.11 +    SDL_THREAD_STATE_ZOMBIE,
    4.12 +    SDL_THREAD_STATE_CLEANED,
    4.13 +} SDL_ThreadState;
    4.14 +
    4.15  /* This is the system-independent thread info structure */
    4.16  struct SDL_Thread
    4.17  {
    4.18      SDL_threadID threadid;
    4.19      SYS_ThreadHandle handle;
    4.20      int status;
    4.21 +    SDL_atomic_t state;  /* SDL_THREAD_STATE_* */
    4.22      SDL_error errbuf;
    4.23      char *name;
    4.24      void *data;
     5.1 --- a/src/thread/generic/SDL_systhread.c	Wed Nov 13 22:35:26 2013 -0500
     5.2 +++ b/src/thread/generic/SDL_systhread.c	Thu Nov 14 00:52:39 2013 -0500
     5.3 @@ -62,4 +62,10 @@
     5.4      return;
     5.5  }
     5.6  
     5.7 +void
     5.8 +SDL_SYS_DetachThread(SDL_Thread * thread)
     5.9 +{
    5.10 +    return;
    5.11 +}
    5.12 +
    5.13  /* vi: set ts=4 sw=4 expandtab: */
     6.1 --- a/src/thread/psp/SDL_systhread.c	Wed Nov 13 22:35:26 2013 -0500
     6.2 +++ b/src/thread/psp/SDL_systhread.c	Thu Nov 14 00:52:39 2013 -0500
     6.3 @@ -77,6 +77,12 @@
     6.4      sceKernelDeleteThread(thread->handle);
     6.5  }
     6.6  
     6.7 +void SDL_SYS_DetachThread(SDL_Thread *thread)
     6.8 +{
     6.9 +    /* !!! FIXME: is this correct? */
    6.10 +    sceKernelDeleteThread(thread->handle);
    6.11 +}
    6.12 +
    6.13  void SDL_SYS_KillThread(SDL_Thread *thread)
    6.14  {
    6.15      sceKernelTerminateDeleteThread(thread->handle);
     7.1 --- a/src/thread/pthread/SDL_systhread.c	Wed Nov 13 22:35:26 2013 -0500
     7.2 +++ b/src/thread/pthread/SDL_systhread.c	Thu Nov 14 00:52:39 2013 -0500
     7.3 @@ -213,4 +213,10 @@
     7.4      pthread_join(thread->handle, 0);
     7.5  }
     7.6  
     7.7 +void
     7.8 +SDL_SYS_DetachThread(SDL_Thread * thread)
     7.9 +{
    7.10 +    pthread_detach(thread->handle);
    7.11 +}
    7.12 +
    7.13  /* vi: set ts=4 sw=4 expandtab: */
     8.1 --- a/src/thread/windows/SDL_systhread.c	Wed Nov 13 22:35:26 2013 -0500
     8.2 +++ b/src/thread/windows/SDL_systhread.c	Thu Nov 14 00:52:39 2013 -0500
     8.3 @@ -234,6 +234,12 @@
     8.4      CloseHandle(thread->handle);
     8.5  }
     8.6  
     8.7 +void
     8.8 +SDL_SYS_DetachThread(SDL_Thread * thread)
     8.9 +{
    8.10 +    CloseHandle(thread->handle);
    8.11 +}
    8.12 +
    8.13  #endif /* SDL_THREAD_WINDOWS */
    8.14  
    8.15  /* vi: set ts=4 sw=4 expandtab: */