text: /* mbed Microcontroller Library
   * Copyright (c) 2006-2012 ARM Limited
   *
   * 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 "rtos/Thread.h"
  
  #include "mbed.h"
  #include "rtos/rtos_idle.h"
  
  static void (*terminate_hook)(osThreadId_t id) = 0;
  extern "C" void thread_terminate_hook(osThreadId_t id)
  {
      if (terminate_hook != (void (*)(osThreadId_t))NULL) {
          terminate_hook(id);
      }
  }
  
  namespace rtos {
  
  void Thread::constructor(osPriority priority,
          uint32_t stack_size, unsigned char *stack_mem, const char *name) {
      _tid = 0;
      _dynamic_stack = (stack_mem == NULL);
      _finished = false;
      memset(&_obj_mem, 0, sizeof(_obj_mem));
      memset(&_attr, 0, sizeof(_attr));
      _attr.priority = priority;
      _attr.stack_size = stack_size;
      _attr.name = name ? name : "application_unnamed_thread";
      _attr.stack_mem = (uint32_t*)stack_mem;
  }
  
  void Thread::constructor(Callback<void()> task,
          osPriority priority, uint32_t stack_size, unsigned char *stack_mem, const char *name) {
      constructor(priority, stack_size, stack_mem, name);
  
      switch (start(task)) {
          case osErrorResource:
              error("OS ran out of threads!\n");
              break;
          case osErrorParameter:
              error("Thread already running!\n");
              break;
          case osErrorNoMemory:
              error("Error allocating the stack memory\n");
          default:
              break;
      }
  }
  
  osStatus Thread::start(Callback<void()> task) {
      _mutex.lock();
  
      if ((_tid != 0) || _finished) {
          _mutex.unlock();
          return osErrorParameter;
      }
  
      if (_attr.stack_mem == NULL) {
          _attr.stack_mem = new uint32_t[_attr.stack_size/sizeof(uint32_t)];
          MBED_ASSERT(_attr.stack_mem != NULL);
      }
  
      //Fill the stack with a magic word for maximum usage checking
      for (uint32_t i = 0; i < (_attr.stack_size / sizeof(uint32_t)); i++) {
          ((uint32_t *)_attr.stack_mem)[i] = 0xE25A2EA5;
      }
  
      memset(&_obj_mem, 0, sizeof(_obj_mem));
      _attr.cb_size = sizeof(_obj_mem);
      _attr.cb_mem = &_obj_mem;
      _task = task;
      _tid = osThreadNew(Thread::_thunk, this, &_attr);
      if (_tid == NULL) {
          if (_dynamic_stack) {
              delete[] (uint32_t *)(_attr.stack_mem);
              _attr.stack_mem = (uint32_t*)NULL;
          }
          _mutex.unlock();
          _join_sem.release();
          return osErrorResource;
      }
  
      _mutex.unlock();
      return osOK;
  }
  
  osStatus Thread::terminate() {
      osStatus_t ret = osOK;
      _mutex.lock();
  
      // Set the Thread's tid to NULL and
      // release the semaphore before terminating
      // since this thread could be terminating itself
      osThreadId_t local_id = _tid;
      _join_sem.release();
      _tid = (osThreadId_t)NULL;
      if (!_finished) {
          _finished = true;
          ret = osThreadTerminate(local_id);
      }
      _mutex.unlock();
      return ret;
  }
  
  osStatus Thread::join() {
      int32_t ret = _join_sem.wait();
      if (ret < 0) {
          return osError;
      }
  
      // The semaphore has been released so this thread is being
      // terminated or has been terminated. Once the mutex has
      // been locked it is ensured that the thread is deleted.
      _mutex.lock();
      MBED_ASSERT(NULL == _tid);
      _mutex.unlock();
  
      // Release sem so any other threads joining this thread wake up
      _join_sem.release();
      return osOK;
  }
  
  osStatus Thread::set_priority(osPriority priority) {
      osStatus_t ret;
      _mutex.lock();
  
      ret = osThreadSetPriority(_tid, priority);
  
      _mutex.unlock();
      return ret;
  }
  
  osPriority Thread::get_priority() {
      osPriority_t ret;
      _mutex.lock();
  
      ret = osThreadGetPriority(_tid);
  
      _mutex.unlock();
      return ret;
  }
  
  int32_t Thread::signal_set(int32_t flags) {
      return osThreadFlagsSet(_tid, flags);
  }
  
  Thread::State Thread::get_state() {
      uint8_t state = osThreadTerminated;
  
      _mutex.lock();
  
      if (_tid != NULL) {
          state = _obj_mem.state;
      }
  
      _mutex.unlock();
  
      State user_state;
  
      switch(state) {
          case osThreadInactive:
              user_state = Inactive;
              break;
          case osThreadReady:
              user_state = Ready;
              break;
          case osThreadRunning:
              user_state = Running;
              break;
          case osRtxThreadWaitingDelay:
              user_state = WaitingDelay;
              break;
          case osRtxThreadWaitingJoin:
              user_state = WaitingJoin;
              break;
          case osRtxThreadWaitingThreadFlags:
              user_state = WaitingThreadFlag;
              break;
          case osRtxThreadWaitingEventFlags:
              user_state = WaitingEventFlag;
              break;
          case osRtxThreadWaitingMutex:
              user_state = WaitingMutex;
              break;
          case osRtxThreadWaitingSemaphore:
              user_state = WaitingSemaphore;
              break;
          case osRtxThreadWaitingMemoryPool:
              user_state = WaitingMemoryPool;
              break;
          case osRtxThreadWaitingMessageGet:
              user_state = WaitingMessageGet;
              break;
          case osRtxThreadWaitingMessagePut:
              user_state = WaitingMessagePut;
              break;
          case osThreadTerminated:
          default:
              user_state = Deleted;
              break;
      }
  
      return user_state;
  }
  
  uint32_t Thread::stack_size() {
      uint32_t size = 0;
      _mutex.lock();
  
      if (_tid != NULL) {
          os_thread_t *thread = (os_thread_t *)_tid;
          size = thread->stack_size;
      }
  
      _mutex.unlock();
      return size;
  }
  
  uint32_t Thread::free_stack() {
      uint32_t size = 0;
      _mutex.lock();
  
      if (_tid != NULL) {
          os_thread_t *thread = (os_thread_t *)_tid;
          size = (uint32_t)thread->sp - (uint32_t)thread->stack_mem;
      }
  
      _mutex.unlock();
      return size;
  }
  
  uint32_t Thread::used_stack() {
      uint32_t size = 0;
      _mutex.lock();
  
      if (_tid != NULL) {
          os_thread_t *thread = (os_thread_t *)_tid;
          size = ((uint32_t)thread->stack_mem + thread->stack_size) - thread->sp;
      }
  
      _mutex.unlock();
      return size;
  }
  
  uint32_t Thread::max_stack() {
      uint32_t size = 0;
      _mutex.lock();
  
      if (_tid != NULL) {
          os_thread_t *thread = (os_thread_t *)_tid;
          uint32_t high_mark = 0;
          while (((uint32_t *)(thread->stack_mem))[high_mark] == 0xE25A2EA5)
              high_mark++;
          size = thread->stack_size - (high_mark * sizeof(uint32_t));
      }
  
      _mutex.unlock();
      return size;
  }
  
  const char *Thread::get_name() {
      return _attr.name;
  }
  
  int32_t Thread::signal_clr(int32_t flags) {
      return osThreadFlagsClear(flags);
  }
  
  osEvent Thread::signal_wait(int32_t signals, uint32_t millisec) {
      uint32_t res;
      osEvent evt;
      uint32_t options = osFlagsWaitAll;
      if (signals == 0) {
          options = osFlagsWaitAny;
          signals = 0x7FFFFFFF;
      }
      res = osThreadFlagsWait(signals, options, millisec);
      if (res & osFlagsError) {
          switch (res) {
              case osFlagsErrorISR:
                  evt.status = osErrorISR;
                  break;
              case osFlagsErrorResource:
                  evt.status = osOK;
                  break;
              case osFlagsErrorTimeout:
                  evt.status = (osStatus)osEventTimeout;
                  break;
              case osFlagsErrorParameter:
              default:
                  evt.status = (osStatus)osErrorValue;
                  break;
          }
      } else {
          evt.status = (osStatus)osEventSignal;
          evt.value.signals = res;
      }
  
      return evt;
  }
  
  osStatus Thread::wait(uint32_t millisec) {
      return osDelay(millisec);
  }
  
  osStatus Thread::yield() {
      return osThreadYield();
  }
  
  osThreadId Thread::gettid() {
      return osThreadGetId();
  }
  
  void Thread::attach_idle_hook(void (*fptr)(void)) {
      rtos_attach_idle_hook(fptr);
  }
  
  void Thread::attach_terminate_hook(void (*fptr)(osThreadId_t id)) {
      terminate_hook = fptr;
  }
  
  Thread::~Thread() {
      // terminate is thread safe
      terminate();
      if (_dynamic_stack) {
          delete[] (uint32_t*)(_attr.stack_mem);
          _attr.stack_mem = (uint32_t*)NULL;
      }
  }
  
  void Thread::_thunk(void * thread_ptr)
  {
      Thread *t = (Thread*)thread_ptr;
      t->_task();
      t->_mutex.lock();
      t->_tid = (osThreadId)NULL;
      t->_finished = true;
      t->_join_sem.release();
      // rtos will release the mutex automatically
  }
  
  }
  