text: /******************************************************************************
   * Copyright (C) 2014-2020 Zhifeng Gong <gozfree@163.com>
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in all
   * copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
   * SOFTWARE.
   ******************************************************************************/
  #include "libthread.h"
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #if defined (__linux__) || defined (__CYGWIN__)
  #include <unistd.h>
  #include <sched.h>
  #include <pthread.h>
  #endif
  #include <errno.h>
  
  /******************************************************************************
   * spin lock APIs
   *****************************************************************************/
  #if ( __i386__ || __i386 || __amd64__ || __amd64 )
  #define cpu_pause() __asm__ ("pause")
  #else
  #define cpu_pause()
  #endif
  
  #if defined (__linux__) || defined (__CYGWIN__)
  #define atomic_cmp_set(lock, old, set) \
      __sync_bool_compare_and_swap(lock, old, set)
  #endif
  
  int spin_lock(spin_lock_t *lock)
  {
  #if defined (__linux__) || defined (__CYGWIN__)
      int spin = 2048;
      int value = 1;
      int i, n;
      long g_ncpu = sysconf(_SC_NPROCESSORS_ONLN);
      for ( ;; ) {
          if (*lock == 0 && atomic_cmp_set(lock, 0, value)) {
              return 0;
          }
          if (g_ncpu > 1) {
              for (n = 1; n < spin; n <<= 1) {
                  for (i = 0; i < n; i++) {
                      cpu_pause();
                  }
                  if (*lock == 0 && atomic_cmp_set(lock, 0, value)) {
                      return 0;
                  }
              }
          }
          sched_yield();
      }
  #endif
      return 0;
  }
  
  int spin_unlock(spin_lock_t *lock)
  {
      *(lock) = 0;
      return 0;
  }
  
  int spin_trylock(spin_lock_t *lock)
  {
  #if defined (__linux__) || defined (__CYGWIN__)
      return (*(lock) == 0 && atomic_cmp_set(lock, 0, 1));
  #else
      return 0;
  #endif
  }
  
  /******************************************************************************
   * mutex lock APIs
   *****************************************************************************/
  int mutex_lock_init(mutex_lock_t *lock)
  {
  #if defined (__linux__) || defined (__CYGWIN__)
      pthread_mutexattr_t  attr;
  
      if (0 != pthread_mutexattr_init(&attr)) {
          printf("pthread_mutexattr_init failed\n");
          return -1;
      }
      if (0 != pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK)) {
          printf("pthread_mutexattr_settype (PTHREAD_MUTEX_ERRORCHECK) failed\n");
          return -1;
      }
  #endif
      pthread_mutex_init(lock, NULL);
  
  #if defined (__linux__) || defined (__CYGWIN__)
      if (0 != pthread_mutexattr_destroy(&attr)) {
          printf("pthread_mutexattr_destroy failed\n");
      }
  #endif
      return 0;
  }
  
  void mutex_lock_deinit(mutex_lock_t *ptr)
  {
      int ret;
      pthread_mutex_t *lock = (pthread_mutex_t *)ptr;
      if (!ptr) {
          return;
      }
      ret = pthread_mutex_destroy(lock);
      if (ret != 0) {
          switch (ret) {
          case EBUSY:
              printf("the mutex is currently locked.\n");
              break;
          default:
              printf("pthread_mutex_trylock error:%s.\n", strerror(ret));
              break;
          }
      }
  }
  
  int mutex_trylock(mutex_lock_t *ptr)
  {
      int ret = 0;
  #if defined (__linux__) || defined (__CYGWIN__)
      pthread_mutex_t *lock = (pthread_mutex_t *)ptr;
      if (!ptr) {
          return -1;
      }
      ret = pthread_mutex_trylock(lock);
      if (ret != 0) {
          switch (ret) {
          case EBUSY:
              printf("the mutex could not be acquired"
                     " because it was currently locked.\n");
              break;
          case EINVAL:
              printf("the mutex has not been properly initialized.\n");
              break;
          default:
              printf("pthread_mutex_trylock error:%s.\n", strerror(ret));
              break;
          }
      }
  #endif
      return ret;
  }
  
  int mutex_lock(mutex_lock_t *ptr)
  {
      int ret;
      pthread_mutex_t *lock = (pthread_mutex_t *)ptr;
      if (!ptr) {
          return -1;
      }
      ret = pthread_mutex_lock(lock);
      if (ret != 0) {
          switch (ret) {
          case EDEADLK:
              printf("the mutex is already locked by the calling thread"
                     " (``error checking'' mutexes only).\n");
              break;
          case EINVAL:
              printf("the mutex has not been properly initialized.\n");
              break;
          default:
              printf("pthread_mutex_trylock error:%s.\n", strerror(ret));
              break;
          }
      }
      return ret;
  }
  
  int mutex_unlock(mutex_lock_t *ptr)
  {
      int ret;
      pthread_mutex_t *lock = (pthread_mutex_t *)ptr;
      if (!ptr) {
          return -1;
      }
      ret = pthread_mutex_unlock(lock);
      if (ret != 0) {
          switch (ret) {
          case EPERM:
              printf("the calling thread does not own the mutex"
                     " (``error checking'' mutexes only).\n");
              break;
          case EINVAL:
              printf("the mutex has not been properly initialized.\n");
              break;
          default:
              printf("pthread_mutex_trylock error:%s.\n", strerror(ret));
              break;
          }
      }
      return ret;
  }
  
  int mutex_cond_init(mutex_cond_t *cond)
  {
      if (!cond) {
          printf("malloc pthread_cond_t failed:%d\n", errno);
          return -1;
      }
      //never return an error code
      pthread_cond_init(cond, NULL);
      return 0;
  }
  
  void mutex_cond_deinit(mutex_cond_t *ptr)
  {
      int ret;
      pthread_cond_t *cond = (pthread_cond_t *)ptr;
      if (!ptr) {
          return;
      }
      ret = pthread_cond_destroy(cond);
      if (ret != 0) {
          switch (ret) {
          case EBUSY:
              printf("some threads are currently waiting on cond.\n");
              break;
          default:
              printf("pthread_cond_destroy error:%s.\n", strerror(ret));
              break;
          }
      }
  }
  
  int mutex_cond_wait(mutex_lock_t *mutexp, mutex_cond_t *condp, int64_t ms)
  {
      int ret = 0;
  #if defined (__linux__) || defined (__CYGWIN__)
      int retry = 3;
      struct timespec ts;
      pthread_mutex_t *mutex = (pthread_mutex_t *)mutexp;
      pthread_cond_t *cond = (pthread_cond_t *)condp;
      if (!condp || !mutexp) {
          return -1;
      }
      if (ms <= 0) {
          //never return an error code
          pthread_cond_wait(cond, mutex);
      } else {
          clock_gettime(CLOCK_REALTIME, &ts);
          uint64_t ns = ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec;
          ns += ms * 1000 * 1000;
          ts.tv_sec = ns / (1000 * 1000 * 1000);
          ts.tv_nsec = ns % 1000 * 1000 * 1000;
  wait:
          ret = pthread_cond_timedwait(cond, mutex, &ts);
          if (ret != 0) {
              switch (ret) {
              case ETIMEDOUT:
                  printf("the condition variable was not signaled "
                         "until the timeout specified by abstime.\n");
                  break;
              case EINTR:
                  printf("pthread_cond_timedwait was interrupted by a signal.\n");
                  if (--retry != 0) {
                      goto wait;
                  }
                  break;
              default:
                  printf("pthread_cond_timedwait error:%s.\n", strerror(ret));
                  break;
              }
          }
      }
  #endif
      return ret;
  }
  
  void mutex_cond_signal(mutex_cond_t *ptr)
  {
      pthread_cond_t *cond = (pthread_cond_t *)ptr;
      if (!ptr) {
          return;
      }
      //never return an error code
      pthread_cond_signal(cond);
  }
  
  void mutex_cond_signal_all(mutex_cond_t *ptr)
  {
      pthread_cond_t *cond = (pthread_cond_t *)ptr;
      if (!ptr) {
          return;
      }
      //never return an error code
      pthread_cond_broadcast(cond);
  }
  
  /******************************************************************************
   * read-write lock APIs
   *****************************************************************************/
  int rwlock_init(rw_lock_t *lock)
  {
      int ret;
      if (!lock) {
          printf("malloc pthread_rwlock_t failed:%d\n", errno);
          return -1;
      }
      ret = pthread_rwlock_init(lock, NULL);
      if (ret != 0) {
          switch (ret) {
          case EAGAIN:
              printf("The system lacked the necessary resources (other than"
                     " memory) to initialize another read-write lock.\n");
              break;
          case ENOMEM:
              printf("Insufficient memory exists to initialize the "
                     "read-write lock.\n");
              break;
          case EPERM:
              printf("The caller does not have the privilege to perform "
                     "the operation.\n");
              break;
          default:
              printf("pthread_rwlock_init failed:%d\n", ret);
              break;
          }
          lock = NULL;
      }
  
      return 0;
  }
  
  void rwlock_deinit(rw_lock_t *ptr)
  {
      pthread_rwlock_t *lock = (pthread_rwlock_t *)ptr;
      if (!ptr) {
          return;
      }
      if (0 != pthread_rwlock_destroy(lock)) {
          printf("pthread_rwlock_destroy failed!\n");
      }
  }
  
  int rwlock_rdlock(rw_lock_t *ptr)
  {
      int ret;
      pthread_rwlock_t *lock = (pthread_rwlock_t *)ptr;
      if (!ptr) {
          return -1;
      }
      ret = pthread_rwlock_rdlock(lock);
      if (ret != 0) {
          switch (ret) {
          case EBUSY:
              printf("The read-write lock could not be acquired for reading "
                     "because a writer holds the lock or a writer with the "
                     "appropriate priority was blocked on it.\n");
              break;
          case EAGAIN:
              printf("The read lock could not be acquired because the maximum "
                     "number of read locks for rwlock has been exceeded.\n");
              break;
          case EDEADLK:
              printf("A deadlock condition was detected or the current thread "
                     "already owns the read-write lock for writing.\n");
              break;
          default:
              printf("pthread_rwlock_rdlock failed!\n");
              break;
          }
      }
      return ret;
  }
  
  int rwlock_tryrdlock(rw_lock_t *ptr)
  {
      int ret = 0;
  #if defined (__linux__) || defined (__CYGWIN__)
      pthread_rwlock_t *lock = (pthread_rwlock_t *)ptr;
      if (!ptr) {
          return -1;
      }
      ret = pthread_rwlock_tryrdlock(lock);
      if (ret != 0) {
          switch (ret) {
          case EBUSY:
              printf("The read-write lock could not be acquired for reading "
                     "because a writer holds the lock or a writer with the "
                     "appropriate priority was blocked on it.\n");
              break;
          case EAGAIN:
              printf("The read lock could not be acquired because the maximum "
                     "number of read locks for rwlock has been exceeded.\n");
              break;
          case EDEADLK:
              printf("A deadlock condition was detected or the current thread "
                     "already owns the read-write lock for writing.\n");
              break;
          default:
              printf("pthread_rwlock_tryrdlock failed!\n");
              break;
          }
      }
  #endif
      return ret;
  }
  
  int rwlock_wrlock(rw_lock_t *ptr)
  {
      int ret = 0;
  #if defined (__linux__) || defined (__CYGWIN__)
      pthread_rwlock_t *lock = (pthread_rwlock_t *)ptr;
      if (!ptr) {
          return -1;
      }
      ret = pthread_rwlock_wrlock(lock);
      if (ret != 0) {
          switch (ret) {
          case EDEADLK:
              printf("A deadlock condition was detected or the current thread "
                    "already owns the read-write lock for writing or reading.\n");
              break;
          default:
              printf("pthread_rwlock_wrlock failed!\n");
              break;
          }
      }
  #endif
      return ret;
  }
  
  int rwlock_trywrlock(rw_lock_t *ptr)
  {
      int ret = 0;
  #if defined (__linux__) || defined (__CYGWIN__)
      pthread_rwlock_t *lock = (pthread_rwlock_t *)ptr;
      if (!ptr) {
          return -1;
      }
      ret = pthread_rwlock_trywrlock(lock);
      if (ret != 0) {
          switch (ret) {
          case EBUSY:
              printf("The read-write lock could not be acquired for writing "
                     "because it was already locked for reading or writing.\n");
              break;
  
          default:
              printf("pthread_rwlock_trywrlock failed!\n");
              break;
          }
      }
  #endif
      return ret;
  }
  
  int rwlock_unlock(rw_lock_t *ptr)
  {
      int ret;
      pthread_rwlock_t *lock = (pthread_rwlock_t *)ptr;
      if (!ptr) {
          return -1;
      }
      ret = pthread_rwlock_unlock(lock);
      if (ret != 0) {
          switch (ret) {
          default:
              printf("pthread_rwlock_unlock failed!\n");
              break;
          }
      }
      return ret;
  }
  
  /******************************************************************************
   * sem lock APIs
   *****************************************************************************/
  int sem_lock_init(sem_lock_t *lock)
  {
  #ifndef FREERTOS
      int pshared = 0;//0: threads, 1: processes
      if (0 != sem_init(lock, pshared, 0)) {
          printf("sem_init failed %d:%s\n", errno, strerror(errno));
          return -1;
      }
  #endif
      return 0;
  }
  
  void sem_lock_deinit(sem_lock_t *ptr)
  {
  #ifndef FREERTOS
      sem_t *lock = (sem_t *)ptr;
      if (!ptr) {
          return;
      }
      if (0 != sem_destroy(lock)) {
          printf("sem_destroy %d:%s\n", errno , strerror(errno));
      }
  #endif
  }
  
  int sem_lock_wait(sem_lock_t *ptr, int64_t ms)
  {
      int ret = 0;
  #if defined (__linux__) || defined (__CYGWIN__)
      struct timespec ts;
      sem_t *lock = (sem_t *)ptr;
      if (!ptr) {
          return -1;
      }
      if (ms < 0) {
          ret = sem_wait(lock);
          if (ret != 0) {
              switch (errno) {
              case EINTR:
                  printf("The call was interrupted by a signal handler.\n");
                  break;
              case EINVAL:
                  printf("sem is not a valid semaphore.\n");
                  break;
              }
          }
      } else {
          clock_gettime(CLOCK_REALTIME, &ts);
          uint64_t ns = ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec;
          ns += ms * 1000 * 1000;
          ts.tv_sec = ns / (1000 * 1000 * 1000);
          ts.tv_nsec = ns % 1000 * 1000 * 1000;
          ret = sem_timedwait(lock, &ts);
          if (ret != 0) {
              switch (errno) {
              case EINVAL:
                  printf("The value of abs_timeout.tv_nsecs is less than 0, "
                         "or greater than or equal to 1000 million.\n");
                  break;
              case ETIMEDOUT:
                  printf("The call timed out before the semaphore could be locked.\n");
                  break;
              }
          }
      }
  #endif
      return ret;
  }
  
  int sem_lock_trywait(sem_lock_t *ptr)
  {
      int ret = 0;
  #if defined (__linux__) || defined (__CYGWIN__)
      sem_t *lock = (sem_t *)ptr;
      if (!ptr) {
          return -1;
      }
      ret = sem_trywait(lock);
      if (ret != 0) {
          switch (errno) {
          case EAGAIN:
              printf("The operation could not be performed without blocking\n");
              break;
          }
      }
  #endif
      return ret;
  }
  
  int sem_lock_signal(sem_lock_t *ptr)
  {
      int ret = 0;
  #ifndef FREERTOS
      sem_t *lock = (sem_t *)ptr;
      if (!ptr) {
          return -1;
      }
      ret = sem_post(lock);
      if (ret != 0) {
          switch (errno) {
          case EINVAL:
              printf("sem is not a valid semaphore.\n");
              break;
          case EOVERFLOW:
              printf("The maximum allowable value for a semaphore would be exceeded.\n");
              break;
          }
      }
  #endif
      return ret;
  }
  