Goto sanos source index

//
// pthread.c
//
// POSIX threads library
//
// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 
// 1. Redistributions of source code must retain the above copyright 
//    notice, this list of conditions and the following disclaimer.  
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.  
// 3. Neither the name of the project nor the names of its contributors
//    may be used to endorse or promote products derived from this software
//    without specific prior written permission. 
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
// SUCH DAMAGE.
// 

#include <os.h>
#include <pthread.h>
#include <sched.h> 
#include <atomic.h>
#include <string.h>

//
// Helper functions
//

long __abstime2timeout(const struct timespec *abstime) {
  struct timeval curtime;
  long timeout;

  if (abstime == NULL) return INFINITE;
  if (gettimeofday(&curtime, NULL) < 0) return -1;
  timeout = ((long) (abstime->tv_sec - curtime.tv_sec) * 1000L +
             (long)((abstime->tv_nsec / 1000) - curtime.tv_usec) / 1000L);
  if (timeout < 0) timeout = 0L;
  return timeout;
}

//
// Threads
//

int pthread_attr_init(pthread_attr_t *attr) {
  if (!attr) return EINVAL;
  attr->stackaddr = NULL;
  attr->stacksize = 0;
  attr->detachstate = PTHREAD_CREATE_JOINABLE;
  attr->param.sched_priority = PRIORITY_NORMAL;
  attr->inheritsched = PTHREAD_EXPLICIT_SCHED;
  attr->contentionscope = PTHREAD_SCOPE_SYSTEM;
  return 0;
}

int pthread_attr_destroy(pthread_attr_t *attr) {
  return 0;
}

int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate) {
  if (!attr || !detachstate) return EINVAL;
  *detachstate = attr->detachstate;
  return 0;
}

int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate) {
  if (!attr) return EINVAL;
  if (detachstate != PTHREAD_CREATE_JOINABLE && detachstate != PTHREAD_CREATE_DETACHED) return EINVAL;
  attr->detachstate = detachstate;
  return 0;
}

int pthread_attr_getstackaddr(const pthread_attr_t *attr, void **stackaddr) {
  if (!attr || !stackaddr) return EINVAL;
  *stackaddr = attr->stackaddr;
  return 0;
}

int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr) {
  if (!attr) return EINVAL;
  attr->stackaddr = stackaddr;
  return 0;
}

int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize) {
  if (!attr || !stacksize) return EINVAL;
  *stacksize = attr->stacksize;
  return 0;
}

int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize) {
  if (!attr) return EINVAL;
  attr->stacksize = stacksize;
  return 0;
}

int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param) {
  if (!attr || !param) return EINVAL;
  memcpy(param, &attr->param, sizeof(struct sched_param));
  return 0;
}

int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param) {
  if (!attr || !param) return EINVAL;
  if (param->sched_priority < PRIORITY_IDLE || param->sched_priority > PRIORITY_TIME_CRITICAL) return EINVAL; 
  memcpy(&attr->param, param, sizeof(struct sched_param));
  return 0;
}

int pthread_attr_getschedpolicy(pthread_attr_t *attr, int *policy) {
  if (!attr || !policy) return EINVAL;
  *policy = SCHED_OTHER;
  return 0;
}

int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy) {
  if (!attr) return EINVAL;
  if (policy != SCHED_OTHER) return ENOSYS;
  return 0;
}

int pthread_attr_getinheritsched(pthread_attr_t *attr, int *inheritsched) {
  if (!attr || !inheritsched) return EINVAL;
  *inheritsched = attr->inheritsched;
  return 0;
}

int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched) {
  if (!attr) return EINVAL;
  if (inheritsched != PTHREAD_INHERIT_SCHED && inheritsched != PTHREAD_EXPLICIT_SCHED) return EINVAL;
  attr->inheritsched = inheritsched;
  return 0;
}

int pthread_attr_getscope(const pthread_attr_t *attr, int *contentionscope) {
  if (!attr || !contentionscope) return EINVAL;
  *contentionscope = attr->contentionscope;
  return 0;
}

int pthread_attr_setscope(pthread_attr_t *attr, int contentionscope) {
  if (!attr) return EINVAL;
  if (contentionscope != PTHREAD_INHERIT_SCHED && contentionscope != PTHREAD_EXPLICIT_SCHED) return EINVAL;
  attr->contentionscope = contentionscope;
  return 0;
}

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start)(void *), void *arg) {
  unsigned int stacksize = 0;
  int priority = PRIORITY_NORMAL;
  int detach = 0;
  struct tib *tib;
  handle_t hthread;

  if (!thread) return EINVAL;

  if (attr) {
    stacksize = attr->stacksize;
    if (attr->detachstate == PTHREAD_CREATE_DETACHED) detach = 1;
    priority = attr->param.sched_priority;
  }

  hthread = beginthread((void (__stdcall *)(void *)) start, stacksize, arg, CREATE_POSIX | CREATE_SUSPENDED, NULL, &tib);
  if (hthread < 0) return errno;

  if (priority != PRIORITY_NORMAL) setprio(hthread, priority);
  if (detach) {
    *thread = tib->hndl;
    resume(hthread);
    close(hthread);
  } else {
    *thread = hthread;
    resume(hthread);
  }

  return 0;
}

int pthread_detach(pthread_t thread) {
  if (close(thread) < 0) return errno;
  return 0;
}

int pthread_equal(pthread_t t1, pthread_t t2) {
  if (t1 == t2) return 1;
  if (getthreadblock(t1) == getthreadblock(t2)) return 1;
  return 0;
}

void pthread_exit(void *value_ptr) {
  endthread((int) value_ptr);
}

int pthread_join(pthread_t thread, void **value_ptr) {
  int rc;

  if (pthread_equal(thread, pthread_self())) return EDEADLK;
  rc = waitone(thread, INFINITE);
  if (rc < 0) return errno;
  if (*value_ptr) *value_ptr = (void *) rc;
  return 0;
}

pthread_t pthread_self(void) {
  return self();
}

int pthread_cancel(pthread_t thread) {
  return 0;
}

int pthread_setcancelstate(int state, int *oldstate) {
  return ENOSYS;
}

int pthread_setcanceltype(int type, int *oldtype) {
  return ENOSYS;
}

void pthread_testcancel(void) {
}

//
// Scheduling functions
//

int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param *param) {
  if (policy < SCHED_MIN || policy > SCHED_MAX) return EINVAL;
  if (policy != SCHED_OTHER) return ENOSYS;
  if (!param) return EINVAL;
  if (param->sched_priority < PRIORITY_IDLE || param->sched_priority > PRIORITY_TIME_CRITICAL) return EINVAL; 
  if (setprio(thread, param->sched_priority) < 0) return errno;
  return 0;
}

int pthread_getschedparam(pthread_t thread, int *policy, struct sched_param *param) {
  int prio;
  if (!policy || !param) return EINVAL;
  prio = getprio(thread);
  if (prio < 0) return errno;
  *policy = SCHED_OTHER;
  param->sched_priority = prio;
  return 0;
}

int pthread_setconcurrency(int level) {
  return 0;
}

int pthread_getconcurrency(void) {
  return 0;
}

//
// Once
//

int pthread_once(pthread_once_t *once_control, void (*init_routine)(void)) {
  if (!once_control || !init_routine) return EINVAL;

  if (!once_control->done) {
    if (atomic_increment(&once_control->started) == 0) {
      // First thread to increment the started variable
      (*init_routine)();
      once_control->done = 1;
    } else {
      // Block until other thread finishes executing the once routine
      while (!once_control->done) msleep(0);
    }
  }

  return 0;
}

//
// Thread specific data
//

int pthread_key_create(pthread_key_t *key, void (*destructor)(void *)) {
  // TODO: implement support for tls destructors
  *key = tlsalloc();
  if (*key == INVALID_TLS_INDEX) return EAGAIN;
  return 0;
}

int pthread_key_delete(pthread_key_t key) {
  tlsfree(key);
  return 0;
}

int pthread_setspecific(pthread_key_t key, const void *value) {
  tlsset(key, (void *) value);
  return 0;
}

void *pthread_getspecific(pthread_key_t key) {
  return tlsget(key);
}