/** * \b atomSemPut * * Perform a put operation on a semaphore. * * This increments the current count value for the semaphore and returns. * * If the count value was previously zero and there are threads blocking on the * semaphore, the call will wake up the highest priority thread suspended. Only * one thread is woken per call to atomSemPut(). If multiple threads of the * same priority are suspended, they are woken in order of suspension (FIFO). * * This function can be called from interrupt context. * * @param[in] sem Pointer to semaphore object * * @retval ATOM_OK Success * @retval ATOM_ERR_OVF The semaphore count would have overflowed (>255) * @retval ATOM_ERR_PARAM Bad parameter * @retval ATOM_ERR_QUEUE Problem putting a woken thread on the ready queue * @retval ATOM_ERR_TIMER Problem cancelling a timeout for a woken thread */ uint8_t atomSemPut (ATOM_SEM * sem) { uint8_t status; CRITICAL_STORE; ATOM_TCB *tcb_ptr; /* Check parameters */ if (sem == NULL) { /* Bad semaphore pointer */ status = ATOM_ERR_PARAM; } else { /* Protect access to the semaphore object and OS queues */ CRITICAL_START (); /* If any threads are blocking on the semaphore, wake up one */ if (sem->suspQ) { /** * Threads are woken up in priority order, with a FIFO system * used on same priority threads. We always take the head, * ordering is taken care of by an ordered list enqueue. */ tcb_ptr = tcbDequeueHead (&sem->suspQ); if (tcbEnqueuePriority (&tcbReadyQ, tcb_ptr) != ATOM_OK) { /* Exit critical region */ CRITICAL_END (); /* There was a problem putting the thread on the ready queue */ status = ATOM_ERR_QUEUE; } else { /* Set OK status to be returned to the waiting thread */ tcb_ptr->suspend_wake_status = ATOM_OK; /* If there's a timeout on this suspension, cancel it */ if ((tcb_ptr->suspend_timo_cb != NULL) && (atomTimerCancel (tcb_ptr->suspend_timo_cb) != ATOM_OK)) { /* There was a problem cancelling a timeout on this semaphore */ status = ATOM_ERR_TIMER; } else { /* Flag as no timeout registered */ tcb_ptr->suspend_timo_cb = NULL; /* Successful */ status = ATOM_OK; } /* Exit critical region */ CRITICAL_END (); /** * The scheduler may now make a policy decision to thread * switch if we are currently in thread context. If we are * in interrupt context it will be handled by atomIntExit(). */ if (atomCurrentContext()) atomSched (FALSE); } } /* If no threads waiting, just increment the count and return */ else { /* Check for count overflow */ if (sem->count == 255) { /* Don't increment, just return error status */ status = ATOM_ERR_OVF; } else { /* Increment the count and return success */ sem->count++; status = ATOM_OK; } /* Exit critical region */ CRITICAL_END (); } } return (status); }
/** * \b test_start * * Start timer test. * * This test exercises the atomTimerCancel() API, particularly its * behaviour when there are several timers registered. Four timers * are registered, two of which are cancelled, and the test confirms * that only the two which are not cancelled are called back. * * @retval Number of failures */ uint32_t test_start (void) { int failures; int i; /* Default to zero failures */ failures = 0; /* Clear down the ran flag for all four timers */ for (i = 0; i < 4; i++) { callback_ran_flag[i] = FALSE; } /* * Fill out four timer request structures. Callbacks are * requested starting in one second, with the others * at 1 tick intervals thereafter. */ for (i = 0; i < 4; i++) { /* * testCallback() is passed a pointer to the flag it * should set to notify that it has run. */ timer_cb[i].cb_ticks = SYSTEM_TICKS_PER_SEC + i; timer_cb[i].cb_func = testCallback; timer_cb[i].cb_data = &callback_ran_flag[i]; } /* Register all four timers */ for (i = 0; i < 4; i++) { if (atomTimerRegister (&timer_cb[i]) != ATOM_OK) { ATOMLOG (_STR("TimerReg\n")); failures++; } } /* Check timers were successfully created */ if (failures == 0) { /* Cancel two of the callbacks */ if (atomTimerCancel (&timer_cb[1]) != ATOM_OK) { ATOMLOG (_STR("Cancel1\n")); failures++; } if (atomTimerCancel (&timer_cb[2]) != ATOM_OK) { ATOMLOG (_STR("Cancel2\n")); failures++; } /* Wait two seconds for callbacks to complete */ if (atomTimerDelay(2 * SYSTEM_TICKS_PER_SEC) != ATOM_OK) { ATOMLOG (_STR("Wait\n")); failures++; } else { /* * We should now find that timer callbacks 0 and 3 * have run, but 1 and 2 did not (due to cancellation). */ if ((callback_ran_flag[0] != TRUE) || (callback_ran_flag[3] != TRUE) || (callback_ran_flag[1] != FALSE) || (callback_ran_flag[2] != FALSE)) { ATOMLOG (_STR("Cancellations\n")); failures++; } } } /* Quit */ return failures; }
/** * \b atomSemDelete * * Deletes a semaphore object. * * Any threads currently suspended on the semaphore will be woken up with * return status ATOM_ERR_DELETED. If called at thread context then the * scheduler will be called during this function which may schedule in one * of the woken threads depending on relative priorities. * * This function can be called from interrupt context, but loops internally * waking up all threads blocking on the semaphore, so the potential * execution cycles cannot be determined in advance. * * @param[in] sem Pointer to semaphore object * * @retval ATOM_OK Success * @retval ATOM_ERR_QUEUE Problem putting a woken thread on the ready queue * @retval ATOM_ERR_TIMER Problem cancelling a timeout on a woken thread */ uint8_t atomSemDelete (ATOM_SEM *sem) { uint8_t status; CRITICAL_STORE; ATOM_TCB *tcb_ptr; uint8_t woken_threads = FALSE; /* Parameter check */ if (sem == NULL) { /* Bad semaphore pointer */ status = ATOM_ERR_PARAM; } else { /* Default to success status unless errors occur during wakeup */ status = ATOM_OK; /* Wake up all suspended tasks */ while (1) { /* Enter critical region */ CRITICAL_START (); /* Check if any threads are suspended */ tcb_ptr = tcbDequeueHead (&sem->suspQ); /* A thread is suspended on the semaphore */ if (tcb_ptr) { /* Return error status to the waiting thread */ tcb_ptr->suspend_wake_status = ATOM_ERR_DELETED; /* Put the thread on the ready queue */ if (tcbEnqueuePriority (&tcbReadyQ, tcb_ptr) != ATOM_OK) { /* Exit critical region */ CRITICAL_END (); /* Quit the loop, returning error */ status = ATOM_ERR_QUEUE; break; } /* If there's a timeout on this suspension, cancel it */ if (tcb_ptr->suspend_timo_cb) { /* Cancel the callback */ if (atomTimerCancel (tcb_ptr->suspend_timo_cb) != ATOM_OK) { /* Exit critical region */ CRITICAL_END (); /* Quit the loop, returning error */ status = ATOM_ERR_TIMER; break; } /* Flag as no timeout registered */ tcb_ptr->suspend_timo_cb = NULL; } /* Exit critical region */ CRITICAL_END (); /* Request a reschedule */ woken_threads = TRUE; } /* No more suspended threads */ else { /* Exit critical region and quit the loop */ CRITICAL_END (); break; } } /* Call scheduler if any threads were woken up */ if (woken_threads == TRUE) { /** * Only call the scheduler if we are in thread context, otherwise * it will be called on exiting the ISR by atomIntExit(). */ if (atomCurrentContext()) atomSched (FALSE); } } return (status); }
/** * \b atomMutexPut * * Give back the lock on a mutex. * * This checks that the mutex is owned by the calling thread, and decrements * the recursive lock count. Once the lock count reaches zero, the lock is * considered relinquished and no longer owned by this thread. * * If the lock is relinquished and there are threads blocking on the mutex, the * call will wake up the highest priority thread suspended. Only one thread is * woken per call to atomMutexPut(). If multiple threads of the same priority * are suspended, they are woken in order of suspension (FIFO). * * This function can only be called from thread context. A mutex has the * concept of an owner thread, so it is never valid to make a mutex call * from interrupt context when there is no thread to associate with. * * @param[in] mutex Pointer to mutex object * * @retval ATOM_OK Success * @retval ATOM_ERR_PARAM Bad parameter * @retval ATOM_ERR_QUEUE Problem putting a woken thread on the ready queue * @retval ATOM_ERR_TIMER Problem cancelling a timeout for a woken thread * @retval ATOM_ERR_OWNERSHIP Attempt to unlock mutex not owned by this thread */ uint8_t atomMutexPut (ATOM_MUTEX * mutex){ uint8_t status; CRITICAL_STORE; ATOM_TCB *tcb_ptr, *curr_tcb_ptr; /* Check parameters */ if (mutex == NULL) { /* Bad mutex pointer */ status = ATOM_ERR_PARAM; } else { /* Get the current TCB */ curr_tcb_ptr = atomCurrentContext(); /* Protect access to the mutex object and OS queues */ CRITICAL_START (); /* Check if the calling thread owns this mutex */ if (mutex->owner != curr_tcb_ptr) { /* Exit critical region */ CRITICAL_END (); /* Attempt to unlock by non-owning thread */ status = ATOM_ERR_OWNERSHIP; } else { /* Lock is owned by this thread, decrement the recursive lock count */ mutex->count--; /* Once recursive lock count reaches zero, we relinquish ownership */ if (mutex->count == 0) { /* Relinquish ownership */ mutex->owner = NULL; /* If any threads are blocking on this mutex, wake them now */ if (mutex->suspQ) { /** * Threads are woken up in priority order, with a FIFO system * used on same priority threads. We always take the head, * ordering is taken care of by an ordered list enqueue. */ tcb_ptr = tcbDequeueHead (&mutex->suspQ); if (tcbEnqueuePriority (&tcbReadyQ, tcb_ptr) != ATOM_OK) { /* Exit critical region */ CRITICAL_END (); /* There was a problem putting the thread on the ready queue */ status = ATOM_ERR_QUEUE; } else { /* Set OK status to be returned to the waiting thread */ tcb_ptr->suspend_wake_status = ATOM_OK; /* Set this thread as the new owner of the mutex */ mutex->owner = tcb_ptr; /* If there's a timeout on this suspension, cancel it */ if ((tcb_ptr->suspend_timo_cb != NULL) && (atomTimerCancel (tcb_ptr->suspend_timo_cb) != ATOM_OK)) { /* There was a problem cancelling a timeout on this mutex */ status = ATOM_ERR_TIMER; } else { /* Flag as no timeout registered */ tcb_ptr->suspend_timo_cb = NULL; /* Successful */ status = ATOM_OK; } /* Exit critical region */ CRITICAL_END (); /** * The scheduler may now make a policy decision to * thread switch. We already know we are in thread * context so can call the scheduler from here. */ atomSched (FALSE); } } else { /** * Relinquished ownership and no threads waiting. * Nothing to do. */ /* Exit critical region */ CRITICAL_END (); /* Successful */ status = ATOM_OK; } } else { /** * Decremented lock but still retain ownership due to * recursion. Nothing to do. */ /* Exit critical region */ CRITICAL_END (); /* Successful */ status = ATOM_OK; } } } return (status); }