github.com/cellofellow/gopkg@v0.0.0-20140722061823-eec0544a62ad/image/webp/libwebp/src/utils/thread.c (about)

     1  // Copyright 2011 Google Inc. All Rights Reserved.
     2  //
     3  // Use of this source code is governed by a BSD-style license
     4  // that can be found in the COPYING file in the root of the source
     5  // tree. An additional intellectual property rights grant can be found
     6  // in the file PATENTS. All contributing project authors may
     7  // be found in the AUTHORS file in the root of the source tree.
     8  // -----------------------------------------------------------------------------
     9  //
    10  // Multi-threaded worker
    11  //
    12  // Author: Skal (pascal.massimino@gmail.com)
    13  
    14  #include <assert.h>
    15  #include <string.h>   // for memset()
    16  #include "./thread.h"
    17  
    18  #ifdef WEBP_USE_THREAD
    19  
    20  #if defined(_WIN32)
    21  
    22  //------------------------------------------------------------------------------
    23  // simplistic pthread emulation layer
    24  
    25  #include <process.h>
    26  
    27  // _beginthreadex requires __stdcall
    28  #define THREADFN unsigned int __stdcall
    29  #define THREAD_RETURN(val) (unsigned int)((DWORD_PTR)val)
    30  
    31  static int pthread_create(pthread_t* const thread, const void* attr,
    32                            unsigned int (__stdcall *start)(void*), void* arg) {
    33    (void)attr;
    34    *thread = (pthread_t)_beginthreadex(NULL,   /* void *security */
    35                                        0,      /* unsigned stack_size */
    36                                        start,
    37                                        arg,
    38                                        0,      /* unsigned initflag */
    39                                        NULL);  /* unsigned *thrdaddr */
    40    if (*thread == NULL) return 1;
    41    SetThreadPriority(*thread, THREAD_PRIORITY_ABOVE_NORMAL);
    42    return 0;
    43  }
    44  
    45  static int pthread_join(pthread_t thread, void** value_ptr) {
    46    (void)value_ptr;
    47    return (WaitForSingleObject(thread, INFINITE) != WAIT_OBJECT_0 ||
    48            CloseHandle(thread) == 0);
    49  }
    50  
    51  // Mutex
    52  static int pthread_mutex_init(pthread_mutex_t* const mutex, void* mutexattr) {
    53    (void)mutexattr;
    54    InitializeCriticalSection(mutex);
    55    return 0;
    56  }
    57  
    58  static int pthread_mutex_lock(pthread_mutex_t* const mutex) {
    59    EnterCriticalSection(mutex);
    60    return 0;
    61  }
    62  
    63  static int pthread_mutex_unlock(pthread_mutex_t* const mutex) {
    64    LeaveCriticalSection(mutex);
    65    return 0;
    66  }
    67  
    68  static int pthread_mutex_destroy(pthread_mutex_t* const mutex) {
    69    DeleteCriticalSection(mutex);
    70    return 0;
    71  }
    72  
    73  // Condition
    74  static int pthread_cond_destroy(pthread_cond_t* const condition) {
    75    int ok = 1;
    76    ok &= (CloseHandle(condition->waiting_sem_) != 0);
    77    ok &= (CloseHandle(condition->received_sem_) != 0);
    78    ok &= (CloseHandle(condition->signal_event_) != 0);
    79    return !ok;
    80  }
    81  
    82  static int pthread_cond_init(pthread_cond_t* const condition, void* cond_attr) {
    83    (void)cond_attr;
    84    condition->waiting_sem_ = CreateSemaphore(NULL, 0, 1, NULL);
    85    condition->received_sem_ = CreateSemaphore(NULL, 0, 1, NULL);
    86    condition->signal_event_ = CreateEvent(NULL, FALSE, FALSE, NULL);
    87    if (condition->waiting_sem_ == NULL ||
    88        condition->received_sem_ == NULL ||
    89        condition->signal_event_ == NULL) {
    90      pthread_cond_destroy(condition);
    91      return 1;
    92    }
    93    return 0;
    94  }
    95  
    96  static int pthread_cond_signal(pthread_cond_t* const condition) {
    97    int ok = 1;
    98    if (WaitForSingleObject(condition->waiting_sem_, 0) == WAIT_OBJECT_0) {
    99      // a thread is waiting in pthread_cond_wait: allow it to be notified
   100      ok = SetEvent(condition->signal_event_);
   101      // wait until the event is consumed so the signaler cannot consume
   102      // the event via its own pthread_cond_wait.
   103      ok &= (WaitForSingleObject(condition->received_sem_, INFINITE) !=
   104             WAIT_OBJECT_0);
   105    }
   106    return !ok;
   107  }
   108  
   109  static int pthread_cond_wait(pthread_cond_t* const condition,
   110                               pthread_mutex_t* const mutex) {
   111    int ok;
   112    // note that there is a consumer available so the signal isn't dropped in
   113    // pthread_cond_signal
   114    if (!ReleaseSemaphore(condition->waiting_sem_, 1, NULL))
   115      return 1;
   116    // now unlock the mutex so pthread_cond_signal may be issued
   117    pthread_mutex_unlock(mutex);
   118    ok = (WaitForSingleObject(condition->signal_event_, INFINITE) ==
   119          WAIT_OBJECT_0);
   120    ok &= ReleaseSemaphore(condition->received_sem_, 1, NULL);
   121    pthread_mutex_lock(mutex);
   122    return !ok;
   123  }
   124  
   125  #else  // !_WIN32
   126  # define THREADFN void*
   127  # define THREAD_RETURN(val) val
   128  #endif  // _WIN32
   129  
   130  //------------------------------------------------------------------------------
   131  
   132  static THREADFN ThreadLoop(void* ptr) {
   133    WebPWorker* const worker = (WebPWorker*)ptr;
   134    int done = 0;
   135    while (!done) {
   136      pthread_mutex_lock(&worker->mutex_);
   137      while (worker->status_ == OK) {   // wait in idling mode
   138        pthread_cond_wait(&worker->condition_, &worker->mutex_);
   139      }
   140      if (worker->status_ == WORK) {
   141        WebPWorkerExecute(worker);
   142        worker->status_ = OK;
   143      } else if (worker->status_ == NOT_OK) {   // finish the worker
   144        done = 1;
   145      }
   146      // signal to the main thread that we're done (for Sync())
   147      pthread_cond_signal(&worker->condition_);
   148      pthread_mutex_unlock(&worker->mutex_);
   149    }
   150    return THREAD_RETURN(NULL);    // Thread is finished
   151  }
   152  
   153  // main thread state control
   154  static void ChangeState(WebPWorker* const worker,
   155                          WebPWorkerStatus new_status) {
   156    // no-op when attempting to change state on a thread that didn't come up
   157    if (worker->status_ < OK) return;
   158  
   159    pthread_mutex_lock(&worker->mutex_);
   160    // wait for the worker to finish
   161    while (worker->status_ != OK) {
   162      pthread_cond_wait(&worker->condition_, &worker->mutex_);
   163    }
   164    // assign new status and release the working thread if needed
   165    if (new_status != OK) {
   166      worker->status_ = new_status;
   167      pthread_cond_signal(&worker->condition_);
   168    }
   169    pthread_mutex_unlock(&worker->mutex_);
   170  }
   171  
   172  #endif  // WEBP_USE_THREAD
   173  
   174  //------------------------------------------------------------------------------
   175  
   176  void WebPWorkerInit(WebPWorker* const worker) {
   177    memset(worker, 0, sizeof(*worker));
   178    worker->status_ = NOT_OK;
   179  }
   180  
   181  int WebPWorkerSync(WebPWorker* const worker) {
   182  #ifdef WEBP_USE_THREAD
   183    ChangeState(worker, OK);
   184  #endif
   185    assert(worker->status_ <= OK);
   186    return !worker->had_error;
   187  }
   188  
   189  int WebPWorkerReset(WebPWorker* const worker) {
   190    int ok = 1;
   191    worker->had_error = 0;
   192    if (worker->status_ < OK) {
   193  #ifdef WEBP_USE_THREAD
   194      if (pthread_mutex_init(&worker->mutex_, NULL) ||
   195          pthread_cond_init(&worker->condition_, NULL)) {
   196        return 0;
   197      }
   198      pthread_mutex_lock(&worker->mutex_);
   199      ok = !pthread_create(&worker->thread_, NULL, ThreadLoop, worker);
   200      if (ok) worker->status_ = OK;
   201      pthread_mutex_unlock(&worker->mutex_);
   202  #else
   203      worker->status_ = OK;
   204  #endif
   205    } else if (worker->status_ > OK) {
   206      ok = WebPWorkerSync(worker);
   207    }
   208    assert(!ok || (worker->status_ == OK));
   209    return ok;
   210  }
   211  
   212  void WebPWorkerExecute(WebPWorker* const worker) {
   213    if (worker->hook != NULL) {
   214      worker->had_error |= !worker->hook(worker->data1, worker->data2);
   215    }
   216  }
   217  
   218  void WebPWorkerLaunch(WebPWorker* const worker) {
   219  #ifdef WEBP_USE_THREAD
   220    ChangeState(worker, WORK);
   221  #else
   222    WebPWorkerExecute(worker);
   223  #endif
   224  }
   225  
   226  void WebPWorkerEnd(WebPWorker* const worker) {
   227    if (worker->status_ >= OK) {
   228  #ifdef WEBP_USE_THREAD
   229      ChangeState(worker, NOT_OK);
   230      pthread_join(worker->thread_, NULL);
   231      pthread_mutex_destroy(&worker->mutex_);
   232      pthread_cond_destroy(&worker->condition_);
   233  #else
   234      worker->status_ = NOT_OK;
   235  #endif
   236    }
   237    assert(worker->status_ == NOT_OK);
   238  }
   239  
   240  //------------------------------------------------------------------------------
   241