src/thread/pthread/SDL_syssem.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 25 Feb 2018 19:51:34 -0800
changeset 11905 29bf3038a246
parent 11811 5d94cb6b24d3
child 12503 806492103856
permissions -rw-r--r--
Fixed bug 4097 - Segmentation fault by SDL_CreateThreadWithStackSize

Dongsun Kim

Normal case
1. [thread 1] SDL_CreateThreadWithStackSize calls SDL_SYS_CreateThread.
2. [thread 1] If successful, it calls SDL_SemWait.
3. [thread 2] SDL_RunThread calls SDL_SYS_SetupThread, SDL_ThreadID, SDL_SemPost.
4. [thread 1] SDL_CreateThreadWithStackSize calls SDL_DestroySemaphore, SDL_free.

Crash case (Segmentation fault)
1. [thread 1] SDL_CreateThreadWithStackSize calls SDL_SYS_CreateThread.
2. [thread 1] If successful, it calls SDL_SemWait.
--> Error return due to SIGNAL(SYSTEM or Real Time) at sem_wait(pthread).
3. [thread 1] SDL_CreateThreadWithStackSize calls SDL_DestroySemaphore, SDL_free.
4. [thread 2] SDL_RunThread calls SDL_SYS_SetupThread, SDL_ThreadID, SDL_SemPost.
--> Segmentation fault at strlen or sem_post.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "../../SDL_internal.h"
    22 
    23 #include <errno.h>
    24 #include <pthread.h>
    25 #include <semaphore.h>
    26 #include <sys/time.h>
    27 #include <time.h>
    28 
    29 #include "SDL_thread.h"
    30 #include "SDL_timer.h"
    31 
    32 /* Wrapper around POSIX 1003.1b semaphores */
    33 
    34 #if defined(__MACOSX__) || defined(__IPHONEOS__)
    35 /* Mac OS X doesn't support sem_getvalue() as of version 10.4 */
    36 #include "../generic/SDL_syssem.c"
    37 #else
    38 
    39 struct SDL_semaphore
    40 {
    41     sem_t sem;
    42 };
    43 
    44 /* Create a semaphore, initialized with value */
    45 SDL_sem *
    46 SDL_CreateSemaphore(Uint32 initial_value)
    47 {
    48     SDL_sem *sem = (SDL_sem *) SDL_malloc(sizeof(SDL_sem));
    49     if (sem) {
    50         if (sem_init(&sem->sem, 0, initial_value) < 0) {
    51             SDL_SetError("sem_init() failed");
    52             SDL_free(sem);
    53             sem = NULL;
    54         }
    55     } else {
    56         SDL_OutOfMemory();
    57     }
    58     return sem;
    59 }
    60 
    61 void
    62 SDL_DestroySemaphore(SDL_sem * sem)
    63 {
    64     if (sem) {
    65         sem_destroy(&sem->sem);
    66         SDL_free(sem);
    67     }
    68 }
    69 
    70 int
    71 SDL_SemTryWait(SDL_sem * sem)
    72 {
    73     int retval;
    74 
    75     if (!sem) {
    76         return SDL_SetError("Passed a NULL semaphore");
    77     }
    78     retval = SDL_MUTEX_TIMEDOUT;
    79     if (sem_trywait(&sem->sem) == 0) {
    80         retval = 0;
    81     }
    82     return retval;
    83 }
    84 
    85 int
    86 SDL_SemWait(SDL_sem * sem)
    87 {
    88     int retval;
    89 
    90     if (!sem) {
    91         return SDL_SetError("Passed a NULL semaphore");
    92     }
    93 
    94     do {
    95         retval = sem_wait(&sem->sem);
    96     } while (retval < 0 && errno == EINTR);
    97 
    98     if (retval < 0) {
    99         retval = SDL_SetError("sem_wait() failed");
   100     }
   101     return retval;
   102 }
   103 
   104 int
   105 SDL_SemWaitTimeout(SDL_sem * sem, Uint32 timeout)
   106 {
   107     int retval;
   108 #ifdef HAVE_SEM_TIMEDWAIT
   109 #ifndef HAVE_CLOCK_GETTIME
   110     struct timeval now;
   111 #endif
   112     struct timespec ts_timeout;
   113 #else
   114     Uint32 end;
   115 #endif
   116 
   117     if (!sem) {
   118         return SDL_SetError("Passed a NULL semaphore");
   119     }
   120 
   121     /* Try the easy cases first */
   122     if (timeout == 0) {
   123         return SDL_SemTryWait(sem);
   124     }
   125     if (timeout == SDL_MUTEX_MAXWAIT) {
   126         return SDL_SemWait(sem);
   127     }
   128 
   129 #ifdef HAVE_SEM_TIMEDWAIT
   130     /* Setup the timeout. sem_timedwait doesn't wait for
   131     * a lapse of time, but until we reach a certain time.
   132     * This time is now plus the timeout.
   133     */
   134 #ifdef HAVE_CLOCK_GETTIME
   135     clock_gettime(CLOCK_REALTIME, &ts_timeout);
   136 
   137     /* Add our timeout to current time */
   138     ts_timeout.tv_nsec += (timeout % 1000) * 1000000;
   139     ts_timeout.tv_sec += timeout / 1000;
   140 #else
   141     gettimeofday(&now, NULL);
   142 
   143     /* Add our timeout to current time */
   144     ts_timeout.tv_sec = now.tv_sec + (timeout / 1000);
   145     ts_timeout.tv_nsec = (now.tv_usec + (timeout % 1000) * 1000) * 1000;
   146 #endif
   147 
   148     /* Wrap the second if needed */
   149     if (ts_timeout.tv_nsec > 1000000000) {
   150         ts_timeout.tv_sec += 1;
   151         ts_timeout.tv_nsec -= 1000000000;
   152     }
   153 
   154     /* Wait. */
   155     do {
   156         retval = sem_timedwait(&sem->sem, &ts_timeout);
   157     } while (retval < 0 && errno == EINTR);
   158 
   159     if (retval < 0) {
   160         if (errno == ETIMEDOUT) {
   161             retval = SDL_MUTEX_TIMEDOUT;
   162         } else {
   163             SDL_SetError("sem_timedwait returned an error: %s", strerror(errno));
   164         }
   165     }
   166 #else
   167     end = SDL_GetTicks() + timeout;
   168     while ((retval = SDL_SemTryWait(sem)) == SDL_MUTEX_TIMEDOUT) {
   169         if (SDL_TICKS_PASSED(SDL_GetTicks(), end)) {
   170             break;
   171         }
   172         SDL_Delay(1);
   173     }
   174 #endif /* HAVE_SEM_TIMEDWAIT */
   175 
   176     return retval;
   177 }
   178 
   179 Uint32
   180 SDL_SemValue(SDL_sem * sem)
   181 {
   182     int ret = 0;
   183     if (sem) {
   184         sem_getvalue(&sem->sem, &ret);
   185         if (ret < 0) {
   186             ret = 0;
   187         }
   188     }
   189     return (Uint32) ret;
   190 }
   191 
   192 int
   193 SDL_SemPost(SDL_sem * sem)
   194 {
   195     int retval;
   196 
   197     if (!sem) {
   198         return SDL_SetError("Passed a NULL semaphore");
   199     }
   200 
   201     retval = sem_post(&sem->sem);
   202     if (retval < 0) {
   203         SDL_SetError("sem_post() failed");
   204     }
   205     return retval;
   206 }
   207 
   208 #endif /* __MACOSX__ */
   209 /* vi: set ts=4 sw=4 expandtab: */