diff --git a/TESTS/mbedmicro-rtos-mbed/threads/main.cpp b/TESTS/mbedmicro-rtos-mbed/threads/main.cpp index c6d52084e62..b6ad51985ba 100644 --- a/TESTS/mbedmicro-rtos-mbed/threads/main.cpp +++ b/TESTS/mbedmicro-rtos-mbed/threads/main.cpp @@ -59,17 +59,76 @@ void self_terminate(Thread *self) { } // Tests that spawn tasks in different configurations + +/** Template for tests: single thread, with yield, with wait, with child, with murder + + Testing single thread + Given single thread is started + when the thread increments the counter + then the final value of the counter is equal to 1 + + Testing single thread with yield + Given single thread is started + when the thread yields and then increments the counter + then the final value of the counter is equal to 1 + + Testing single thread with wait + Given single thread is started + when the thread waits for 100ms and then increments the counter + then the final value of the counter is equal to 1 + + Testing single thread with child + Given single thread is started + when the thread spawns another thread that increments the counter + then the final value of the counter is equal to 1 + + Testing serial threads with murder + Given single thread is started + when the parent thread is holding a lock + and the parent thread spawns a child thread that waits for the lock before incrementing the counter + and the parent terminates the child before releasing the lock + and the parent increments the counter + then the final value of the counter is equal to 1 +*/ template void test_single_thread() { - const char tname[] = "Single Thread"; counter_t counter(0); - Thread thread(osPriorityNormal, THREAD_STACK_SIZE, NULL, tname); + Thread thread(osPriorityNormal, THREAD_STACK_SIZE); thread.start(callback(F, &counter)); thread.join(); TEST_ASSERT_EQUAL(counter, 1); - TEST_ASSERT_EQUAL(strcmp(tname, thread.get_name()), 0); } +/** Template for tests: parallel threads, with yield, with wait, with child, with murder + + Testing parallel threads + Given multiple threads are started in parallel + when each of the threads increments the counter + then the final value of the counter is equal to number of threads + + Testing parallel threads with yield + Given multiple threads are started in parallel + when each of the threads yields and then increments the counter + then the final value of the counter is equal to number of threads + + Testing parallel threads with wait + Given multiple threads are started in parallel + when each of the threads waits for 100ms and then increments the counter + then the final value of the counter is equal to number of threads + + Testing parallel threads with child + Given multiple threads are started in parallel + when each of the threads spawns another thread that increments the counter + then the final value of the counter is equal to number of parallel threads + + Testing parallel threads with murder + Given multiple threads are started in parallel + when the parent thread is holding a lock + and the parent thread spawns a child thread that waits for the lock before incrementing the counter + and the parent terminates the child before releasing the lock + and the parent increments the counter + then the final value of the counter is equal to number of parallel threads +*/ template void test_parallel_threads() { counter_t counter(0); @@ -88,6 +147,36 @@ void test_parallel_threads() { TEST_ASSERT_EQUAL(counter, N); } +/** Template for tests: serial threads, with yield, with wait, with child, with murder + + Testing serial threads + Given multiple threads are started serially + when each of the threads increments the counter + then the final value of the counter is equal to number of threads + + Testing serial threads with yield + Given multiple threads are started serially + when each of the threads yields and then increments the counter + then the final value of the counter is equal to number of threads + + Testing serial threads with wait + Given multiple threads are started serially + when each of the threads waits for 100ms and then increments the counter + then the final value of the counter is equal to number of threads + + Testing serial threads with child + Given multiple threads are started serially + when each of the threads spawns another thread that increments the counter + then the final value of the counter is equal to number of serial threads + + Testing serial threads with murder + Given multiple threads are started serially + when the parent thread is holding a lock + and the parent thread spawns a child thread that waits for the lock before incrementing the counter + and the parent terminates the child before releasing the lock + and the parent increments the counter + then the final value of the counter is equal to number of serial threads +*/ template void test_serial_threads() { counter_t counter(0); @@ -101,6 +190,12 @@ void test_serial_threads() { TEST_ASSERT_EQUAL(counter, N); } +/** Testing thread self terminate + + Given the thread is running + when the thread calls @a terminate on its self + then the thread terminates execution cleanly + */ void test_self_terminate() { Thread *thread = new Thread(osPriorityNormal, THREAD_STACK_SIZE); thread->start(callback(self_terminate, thread)); @@ -108,34 +203,497 @@ void test_self_terminate() { delete thread; } +void signal_wait() +{ + osEvent evt = Thread::signal_wait(0x1); + TEST_ASSERT_EQUAL(osEventSignal, evt.status); + TEST_ASSERT_EQUAL(0x1, evt.value.signals); +} + +void signal_wait_tout() +{ + osEvent evt = Thread::signal_wait(0x2, 50); + TEST_ASSERT_EQUAL(osEventTimeout, evt.status); +} + +void signal_wait_multibit() +{ + osEvent evt = Thread::signal_wait(0x1 | 0x2, 50); + TEST_ASSERT_EQUAL(osEventSignal, evt.status); + TEST_ASSERT_EQUAL(0x3, evt.value.signals); +} + +void signal_wait_multibit_tout() +{ + osEvent evt = Thread::signal_wait(0x1 | 0x2, 50); + TEST_ASSERT_EQUAL(osEventTimeout, evt.status); +} + +/** + Testing thread signal: wait + Given two threads (A & B) are started + when thread A executes @a signal_wait(0x1) + and thread B execute @a signal_set(0x1) + then thread A exits the wait and continues execution + + Testing thread signal: timeout + Given two threads (A & B) are started + when thread A executes @a signal_wait(0x1 | 0x2, 50) with a timeout of 50ms + and thread B execute @a signal_set(0x2) + then thread A keeps waiting for correct signal until it timeouts + + Testing thread signal: multi-bit + Given two threads (A & B) are started + when thread A executes @a signal_wait(0x1 | 0x2) + and thread B execute @a signal_set(0x1 | 0x2) + then thread A exits the wait and continues execution + + Testing thread signal: multi-bit timeout + Given two threads (A & B) are started + when thread A executes @a signal_wait(0x1, 50) with a timeout of 50ms + and thread B execute @a signal_set(0x2) + then thread A keeps waiting for correct signal until it timeouts +*/ +template +void test_thread_signal() +{ + Thread t_wait; + + t_wait.start(callback(F)); + + Thread::yield(); + + Thread::State state = t_wait.get_state(); + TEST_ASSERT_EQUAL(Thread::WaitingThreadFlag, state); + + int32_t res = t_wait.signal_set(S); + + t_wait.join(); +} + +void signal_clr() +{ + Thread::yield(); + + int32_t sig = Thread::signal_clr(0x1); + TEST_ASSERT_EQUAL(0x1, sig); + + /* Signal cleared we should get timeout */ + osEvent evt = Thread::signal_wait(0x1, 0); + TEST_ASSERT_EQUAL(osOK, evt.status); +} + +/** Testing thread signals: signal clear + + Given two threads (A & B) are started + when thread A executes @a signal_set(0x1) + and thread B execute @a signal_clr(0x1) + and thread B execute @a signal_wait(0x1, 0) + then thread B @a signal_wait status should be osOK indicating a timeout + */ +void test_thread_signal_clr() +{ + Thread t_wait; + + t_wait.start(callback(signal_clr)); + + int32_t res = t_wait.signal_set(0x1); + TEST_ASSERT_EQUAL(0x1, res); + + t_wait.join(); +} + +void thread_wait_signal() { + Thread::signal_wait(0x1); +} + +void stack_info() { + Thread::signal_wait(0x1); + + thread_wait_signal(); + + Thread::signal_wait(0x1); +} + +/** Testing thread stack info + + Given the thread is running + when a function is called from the thread context + then the stack usage goes up + and the reported stack size is as requested in the constructor + and the sum of free and used stack sizes is equal to the total stack size + when the function returns + then the stack usage goes down + and the reported stack size is as requested in the constructor + and the sum of free and used stack sizes is equal to the total stack size + */ +void test_thread_stack_info() { + Thread t(osPriorityNormal, THREAD_STACK_SIZE); + t.start(callback(stack_info)); + + Thread::yield(); + + TEST_ASSERT_EQUAL(THREAD_STACK_SIZE, t.stack_size()); + TEST_ASSERT_EQUAL(THREAD_STACK_SIZE, t.free_stack() + t.used_stack()); + uint32_t last_stack = t.used_stack(); + + t.signal_set(0x1); + Thread::yield(); + + TEST_ASSERT_EQUAL(THREAD_STACK_SIZE, t.free_stack() + t.used_stack()); + TEST_ASSERT(last_stack <= t.used_stack()); + last_stack = t.used_stack(); + + t.signal_set(0x1); + Thread::yield(); + + TEST_ASSERT_EQUAL(THREAD_STACK_SIZE, t.free_stack() + t.used_stack()); + TEST_ASSERT(last_stack >= t.used_stack()); + + t.signal_set(0x1); + + t.join(); +} + +/** Testing thread wait aka delay + + Given the thread is running + when the @a wait function is called + then the thread sleeps for given amount of time + */ +void test_thread_wait() { + uint32_t start = us_ticker_read(); + + Thread::wait(150); + + TEST_ASSERT_UINT32_WITHIN(50000, 150000, us_ticker_read() - start); +} + +/** Testing thread name + + Given a thread is started with a specified name + when the name is queried using @a get_name + then the returned name is as set +*/ +void test_thread_name() { + const char tname[] = "Amazing thread"; + Thread t(osPriorityNormal, THREAD_STACK_SIZE, NULL, tname); + t.start(callback(thread_wait_signal)); + TEST_ASSERT_EQUAL(strcmp(tname, t.get_name()), 0); + t.signal_set(0x1); + t.join(); +} + +void test_deleted_thread() +{ +} + +/** Testing thread states: deleted + + Given the thread is not started + then its state, as reported by @a get_state, is @a Deleted + when the thread is started and finishes executing + then its state, as reported by @a get_state, is @a Deleted + */ +void test_deleted() +{ + Thread t; + + TEST_ASSERT_EQUAL(Thread::Deleted, t.get_state()); + + t.start(callback(test_deleted_thread)); + + t.join(); + TEST_ASSERT_EQUAL(Thread::Deleted, t.get_state()); +} + +void test_delay_thread() +{ + Thread::wait(50); +} + +/** Testing thread states: wait delay + + Given the thread is running + when thread calls @a wait + then its state, as reported by @a get_state, is @a WaitingDelay + */ +void test_delay() +{ + Thread t; + + t.start(callback(test_delay_thread)); + + Thread::yield(); + + TEST_ASSERT_EQUAL(Thread::WaitingDelay, t.get_state()); + + t.join(); + TEST_ASSERT_EQUAL(Thread::Deleted, t.get_state()); +} + +void test_signal_thread() +{ + Thread::signal_wait(0x1); +} + +/** Testing thread states: wait signal + + Given the thread is running + when thread waits for a signal + then its state, as reported by @a get_state, is @a WaitingSignal + */ +void test_signal() +{ + Thread t; + + t.start(callback(test_signal_thread)); + + Thread::yield(); + + TEST_ASSERT_EQUAL(Thread::WaitingThreadFlag, t.get_state()); + + t.signal_set(0x1); +} + +void test_evt_flag_thread(osEventFlagsId_t evtflg) +{ + osEventFlagsWait(evtflg, 0x1, osFlagsWaitAny, osWaitForever); +} + +/** Testing thread states: wait evt flag + + Given the thread is running + when thread waits for an event flag + then its state, as reported by @a get_state, is @a WaitingEventFlag + */ +void test_evt_flag() +{ + Thread t; + mbed_rtos_storage_event_flags_t evtflg_mem; + osEventFlagsAttr_t evtflg_attr; + osEventFlagsId_t evtflg; + + evtflg_attr.cb_mem = &evtflg_mem; + evtflg_attr.cb_size = sizeof(evtflg_mem); + evtflg = osEventFlagsNew(&evtflg_attr); + TEST_ASSERT_NOT_EQUAL(NULL, evtflg); + + t.start(callback(test_evt_flag_thread, evtflg)); + + Thread::yield(); + + TEST_ASSERT_EQUAL(Thread::WaitingEventFlag, t.get_state()); + + osEventFlagsSet(evtflg, 0x1); +} + +void test_mutex_thread(Mutex *mutex) +{ + mutex->lock(); +} + +/** Testing thread states: wait mutex + + Given the thread is running + when thread waits for a mutex + then its state, as reported by @a get_state, is @a WaitingMutex + */ +void test_mutex() +{ + Thread t; + Mutex mutex; + + mutex.lock(); + + t.start(callback(test_mutex_thread, &mutex)); + + Thread::yield(); + + TEST_ASSERT_EQUAL(Thread::WaitingMutex, t.get_state()); + + mutex.unlock(); +} + +void test_semaphore_thread(Semaphore *sem) +{ + sem->wait(); +} + +/** Testing thread states: wait semaphore + + Given the thread is running + when thread waits for a semaphore + then its state, as reported by @a get_state, is @a WaitingSemaphore + */ +void test_semaphore() +{ + Thread t; + Semaphore sem; + + t.start(callback(test_semaphore_thread, &sem)); + + Thread::yield(); + + TEST_ASSERT_EQUAL(Thread::WaitingSemaphore, t.get_state()); + + sem.release(); +} + +void test_msg_get_thread(Queue *queue) +{ + queue->get(); +} + +/** Testing thread states: wait message get + + Given the thread is running + when thread tries to get a message from an empty queue + then its state, as reported by @a get_state, is @a WaitingMessageGet + */ +void test_msg_get() +{ + Thread t; + Queue queue; + + t.start(callback(test_msg_get_thread, &queue)); + + Thread::yield(); + + TEST_ASSERT_EQUAL(Thread::WaitingMessageGet, t.get_state()); + + queue.put((int32_t *)0xE1EE7); +} + +void test_msg_put_thread(Queue *queue) +{ + queue->put((int32_t *)0xDEADBEEF, osWaitForever); + +} + +/** Testing thread states: wait message put + + Given the thread is running + when thread tries to put a message into a full queue + then its state, as reported by @a get_state, is @a WaitingMessagePut + */ +void test_msg_put() +{ + Thread t; + Queue queue; + + queue.put((int32_t *)0xE1EE7); + + t.start(callback(test_msg_put_thread, &queue)); + + Thread::yield(); + + TEST_ASSERT_EQUAL(Thread::WaitingMessagePut, t.get_state()); + queue.get(); +} + +/** Utility function that places some date on the stack */ +void use_some_stack () { + volatile uint32_t stack_filler[10] = {0xDEADBEEF}; +} + +/** Testing thread with external stack memory + + Given external buffer is supplied as stack to a thread + when the thread executes + then the supplies buffer is used as a stack + */ +void test_thread_ext_stack() { + char stack[512]; + Thread t(osPriorityNormal, sizeof(stack), (unsigned char*)stack); + + memset(&stack, 0, sizeof(stack)); + t.start(callback(use_some_stack)); + t.join(); + + /* If buffer was used as a stack it was cleared with pattern and some data were placed in it */ + for(unsigned i = 0; i < sizeof(stack); i++) { + if (stack[i] != 0) + return; + } + + TEST_FAIL_MESSAGE("External stack was not used."); +} + +/** Testing thread priority operations + + Given thread running with osPriorityNormal + when new priority is set using @a set_priority + then priority is changed and can be retrieved using @a get_priority + */ +void test_thread_prio() { + Thread t(osPriorityNormal); + t.start(callback(thread_wait_signal)); + + TEST_ASSERT_EQUAL(osPriorityNormal, t.get_priority()); + + t.set_priority(osPriorityHigh); + + TEST_ASSERT_EQUAL(osPriorityHigh, t.get_priority()); + + t.signal_set(0x1); + t.join(); +} + utest::v1::status_t test_setup(const size_t number_of_cases) { - GREENTEA_SETUP(40, "default_auto"); + GREENTEA_SETUP(15, "default_auto"); return verbose_test_setup_handler(number_of_cases); } -// Test cases -Case cases[] = { - Case("Testing single thread", test_single_thread), - Case("Testing parallel threads", test_parallel_threads<3, increment>), - Case("Testing serial threads", test_serial_threads<10, increment>), +#define DEFAULT_HANDLERS NULL,NULL,greentea_case_setup_handler,greentea_case_teardown_handler,greentea_case_failure_abort_handler + +// Test cases. It's spelled out rather than constructed with macro because +// macros don't play nicely with the templates (extra comma). +static const case_t cases[] = { + {"Testing single thread", test_single_thread, DEFAULT_HANDLERS}, + {"Testing parallel threads", test_parallel_threads<3, increment> , DEFAULT_HANDLERS}, + {"Testing serial threads", test_serial_threads<10, increment> , DEFAULT_HANDLERS}, + + {"Testing single thread with yield", test_single_thread, DEFAULT_HANDLERS}, + {"Testing parallel threads with yield", test_parallel_threads<3, increment_with_yield>, DEFAULT_HANDLERS}, + {"Testing serial threads with yield", test_serial_threads<10, increment_with_yield>, DEFAULT_HANDLERS}, + + {"Testing single thread with wait", test_single_thread, DEFAULT_HANDLERS}, + {"Testing parallel threads with wait", test_parallel_threads<3, increment_with_wait>, DEFAULT_HANDLERS}, + {"Testing serial threads with wait", test_serial_threads<10, increment_with_wait>, DEFAULT_HANDLERS}, + + {"Testing single thread with child", test_single_thread, DEFAULT_HANDLERS}, + {"Testing parallel threads with child", test_parallel_threads<3, increment_with_child>, DEFAULT_HANDLERS}, + {"Testing serial threads with child", test_serial_threads<10, increment_with_child>, DEFAULT_HANDLERS}, + + {"Testing single thread with murder", test_single_thread, DEFAULT_HANDLERS}, + {"Testing parallel threads with murder", test_parallel_threads<3, increment_with_murder>, DEFAULT_HANDLERS}, + {"Testing serial threads with murder", test_serial_threads<10, increment_with_murder>, DEFAULT_HANDLERS}, + + {"Testing thread self terminate", test_self_terminate, DEFAULT_HANDLERS}, - Case("Testing single thread with yield", test_single_thread), - Case("Testing parallel threads with yield", test_parallel_threads<3, increment_with_yield>), - Case("Testing serial threads with yield", test_serial_threads<10, increment_with_yield>), + {"Testing thread signals: wait", test_thread_signal<0x1, signal_wait>, DEFAULT_HANDLERS}, + {"Testing thread signals: timeout", test_thread_signal<0x1, signal_wait_tout>, DEFAULT_HANDLERS}, + {"Testing thread signals: multi-bit", test_thread_signal<0x3, signal_wait_multibit>, DEFAULT_HANDLERS}, + {"Testing thread signals: multi-bit timeout", test_thread_signal<0x1, signal_wait_multibit_tout>, DEFAULT_HANDLERS}, + {"Testing thread signals: signal clear", test_thread_signal_clr, DEFAULT_HANDLERS}, - Case("Testing single thread with wait", test_single_thread), - Case("Testing parallel threads with wait", test_parallel_threads<3, increment_with_wait>), - Case("Testing serial threads with wait", test_serial_threads<10, increment_with_wait>), - Case("Testing single thread with child", test_single_thread), - Case("Testing parallel threads with child", test_parallel_threads<3, increment_with_child>), - Case("Testing serial threads with child", test_serial_threads<10, increment_with_child>), + {"Testing thread stack info", test_thread_stack_info, DEFAULT_HANDLERS}, + {"Testing thread wait", test_thread_wait, DEFAULT_HANDLERS}, + {"Testing thread name", test_thread_name, DEFAULT_HANDLERS}, - Case("Testing single thread with murder", test_single_thread), - Case("Testing parallel threads with murder", test_parallel_threads<3, increment_with_murder>), - Case("Testing serial threads with murder", test_serial_threads<10, increment_with_murder>), + {"Testing thread states: deleted", test_deleted, DEFAULT_HANDLERS}, + {"Testing thread states: wait delay", test_delay, DEFAULT_HANDLERS}, + {"Testing thread states: wait signal", test_signal, DEFAULT_HANDLERS}, + {"Testing thread states: wait event flag", test_evt_flag, DEFAULT_HANDLERS}, + {"Testing thread states: wait mutex", test_mutex, DEFAULT_HANDLERS}, + {"Testing thread states: wait semaphore", test_semaphore, DEFAULT_HANDLERS}, + {"Testing thread states: wait message get", test_msg_get, DEFAULT_HANDLERS}, + {"Testing thread states: wait message put", test_msg_put, DEFAULT_HANDLERS}, - Case("Testing thread self terminate", test_self_terminate), + {"Testing thread with external stack memory", test_thread_ext_stack, DEFAULT_HANDLERS}, + {"Testing thread priority ops", test_thread_prio, DEFAULT_HANDLERS} }; Specification specification(test_setup, cases); diff --git a/rtos/Thread.cpp b/rtos/Thread.cpp index cd3364861c8..2db5eccb54d 100644 --- a/rtos/Thread.cpp +++ b/rtos/Thread.cpp @@ -162,10 +162,6 @@ int32_t Thread::signal_set(int32_t flags) { return osThreadFlagsSet(_tid, flags); } -int32_t Thread::signal_clr(int32_t flags) { - return osThreadFlagsClear(flags); -} - Thread::State Thread::get_state() { uint8_t state = osThreadTerminated; @@ -244,7 +240,7 @@ uint32_t Thread::free_stack() { if (_tid != NULL) { os_thread_t *thread = (os_thread_t *)_tid; - size = (uint32_t)thread->stack_mem - thread->sp; + size = (uint32_t)thread->sp - (uint32_t)thread->stack_mem; } _mutex.unlock(); @@ -284,6 +280,10 @@ 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; @@ -309,9 +309,10 @@ osEvent Thread::signal_wait(int32_t signals, uint32_t millisec) { evt.status = (osStatus)osErrorValue; break; } + } else { + evt.status = (osStatus)osEventSignal; + evt.value.signals = res; } - evt.status = (osStatus)osEventSignal; - evt.value.signals = res; return evt; } diff --git a/rtos/Thread.h b/rtos/Thread.h index f692ff10834..3d921bdb2e1 100644 --- a/rtos/Thread.h +++ b/rtos/Thread.h @@ -245,25 +245,19 @@ class Thread : private mbed::NonCopyable { */ osPriority get_priority(); - /** Set the specified Signal Flags of an active thread. + /** Set the specified Thread Flags for the thread. @param signals specifies the signal flags of the thread that should be set. @return previous signal flags of the specified thread or osFlagsError in case of incorrect parameters. */ int32_t signal_set(int32_t signals); - /** Clears the specified Signal Flags of an active thread. - @param signals specifies the signal flags of the thread that should be cleared. - @return resultant signal flags of the specified thread or osFlagsError in case of incorrect parameters. - */ - int32_t signal_clr(int32_t signals); - /** State of the Thread */ enum State { - Inactive, /**< Not created */ + Inactive, /**< NOT USED */ Ready, /**< Ready to run */ Running, /**< Running */ WaitingDelay, /**< Waiting for a delay to occur */ - WaitingJoin, /**< Waiting for thread to join */ + WaitingJoin, /**< Waiting for thread to join. Only happens when using RTX directly. */ WaitingThreadFlag, /**< Waiting for a thread flag to be set */ WaitingEventFlag, /**< Waiting for a event flag to be set */ WaitingMutex, /**< Waiting for a mutex event to occur */ @@ -271,13 +265,13 @@ class Thread : private mbed::NonCopyable { WaitingMemoryPool, /**< Waiting for a memory pool */ WaitingMessageGet, /**< Waiting for message to arrive */ WaitingMessagePut, /**< Waiting for message to be send */ - WaitingInterval, /**< Waiting for an interval to occur */ - WaitingOr, /**< Waiting for one event in a set to occur */ - WaitingAnd, /**< Waiting for multiple events in a set to occur */ - WaitingMailbox, /**< Waiting for a mailbox event to occur */ + WaitingInterval, /**< NOT USED */ + WaitingOr, /**< NOT USED */ + WaitingAnd, /**< NOT USED */ + WaitingMailbox, /**< NOT USED (Mail is implemented as MemoryPool and Queue) */ /* Not in sync with RTX below here */ - Deleted, /**< The task has been deleted */ + Deleted, /**< The task has been deleted or not started */ }; /** State of this Thread @@ -310,10 +304,16 @@ class Thread : private mbed::NonCopyable { */ const char *get_name(); - /** Wait for one or more Signal Flags to become signaled for the current RUNNING thread. + /** Clears the specified Thread Flags of the currently running thread. + @param signals specifies the signal flags of the thread that should be cleared. + @return resultant signal flags of the specified thread or osFlagsError in case of incorrect parameters. + */ + static int32_t signal_clr(int32_t signals); + + /** Wait for one or more Thread Flags to become signaled for the current RUNNING thread. @param signals wait until all specified signal flags are set or 0 for any single signal flag. @param millisec timeout value or 0 in case of no time-out. (default: osWaitForever). - @return event flag information or error code. + @return event flag information or error code. @note if @a millisec is set to 0 and flag is no set the event carries osOK value. @note not callable from interrupt */ static osEvent signal_wait(int32_t signals, uint32_t millisec=osWaitForever);