Hi, I'm having a problem using embOS to test the thread
safety of one of my modules because if I create threads and start running the
OS within my unit test, I don't have a way to exit the OS and finish execution
of the unit test.
Regarding my current setup, I'm using embOS version 3.86n
with IAR compiler running on a TI MSP430 (MSP430F6779). The IAR version is
7.0.5.3137 for the common components and v6.10.1 for the MSP430-specific
items. Our project is entirely in C and we're using the Unity unit test
framework from throwtheswitch.org. We're using the Unity version using
Test groups and "unity_framework.c" but not Ceedling with Ruby
scripts. I’m running the unit tests on IAR’s simulator for the MSP430.
Below I've included a reduced sample of the code I've
written, which hopefully illustrates the OS setup that I'm using. I want
to test that a single object can be sent requests from two different threads
and if context switching occurs then the common object won't be left in an
invalid state with part of its data written while the rest is unchanged.
So I created two threads that cycle through 1000-10000 iterations of getting
and putting data to the common object, verifying the validity of the data at
each step. Then I want the test threads to complete. From the embOS
documentation I need to use the "OS_TerminateTask" command to
properly terminate each thread task (otherwise the processor jumps to execute
code at 0x0 evidently). However, after both threads have terminated, the
OS has nothing else to do but run the OS_Idle thread. OS_Start() will
never return. I need to somehow return program execution to the point in
the unit test function where OS_Start() was called so that the unit test can
finish executing, print out total statistics, and exit.
Possible solutions discussed and discarded:
* Just run the multi-threaded test at the end of the unit
tests. See its printf() to verify everything ran correctly, and then
manually kill it in the IAR debugger. Yes, we could see that the test ran
successfully, but we can't run it on a Jenkins server.
* Run all unit tests within a main thread in the OS.
This allows control to return to the 'main' thread after the test threads have
terminated. However, the OS will never finish so the unit tests will
never finish.
* Assembly code of some sort that grabs the program counter
position before OS_Start(). In a third thread which is lower priority and
only executes after the test threads are finished, somehow pop the OS_Start()
and OS_StartASM() function calls off of the stack, and then set the program
counter position to just after the OS_Start() command back in the main thread
and begin execution again. Frankly this seems feasible but far fetched
and there has to be a better way especially since multi-threaded unit tests
seems like a common use case.
Questions:
1) How can we terminate OS execution after all threads have
completed so that our unit tests can successfully exit?
2) I see that there’s a “OS_Exit” command in our RTOS.h that
could be used for Windows platform testing, but it’s not linkable with our
pre-compiled version of embOS. Would that be an appropriate OS interface
function to call and how can we get access to it?
3) Oddly, I haven't seen any failures from my test so
far. I'm following TDD and haven't implemented any thread safe protection
mechanisms in the production code yet, so it seems that the OS isn't forcing
any task switching. From my printf() commands I can see that the threads
run sequentially without any switching, even though they may take 1-10seconds
each to run. I've given them equal priority (50) so that neither is a
higher priority and the OS should use Round Robin scheduling while limiting the
execution time of each to the timeslice period. (I'm not sure what the
default timeslice is at the moment.)
4) Is there a thread 'join' command to only terminate a
thread while it isn't active? I haven't seen it supported in RTOS.h, so
I'm assuming we'd have to implement that ourselves.
Display All
safety of one of my modules because if I create threads and start running the
OS within my unit test, I don't have a way to exit the OS and finish execution
of the unit test.
Regarding my current setup, I'm using embOS version 3.86n
with IAR compiler running on a TI MSP430 (MSP430F6779). The IAR version is
7.0.5.3137 for the common components and v6.10.1 for the MSP430-specific
items. Our project is entirely in C and we're using the Unity unit test
framework from throwtheswitch.org. We're using the Unity version using
Test groups and "unity_framework.c" but not Ceedling with Ruby
scripts. I’m running the unit tests on IAR’s simulator for the MSP430.
Below I've included a reduced sample of the code I've
written, which hopefully illustrates the OS setup that I'm using. I want
to test that a single object can be sent requests from two different threads
and if context switching occurs then the common object won't be left in an
invalid state with part of its data written while the rest is unchanged.
So I created two threads that cycle through 1000-10000 iterations of getting
and putting data to the common object, verifying the validity of the data at
each step. Then I want the test threads to complete. From the embOS
documentation I need to use the "OS_TerminateTask" command to
properly terminate each thread task (otherwise the processor jumps to execute
code at 0x0 evidently). However, after both threads have terminated, the
OS has nothing else to do but run the OS_Idle thread. OS_Start() will
never return. I need to somehow return program execution to the point in
the unit test function where OS_Start() was called so that the unit test can
finish executing, print out total statistics, and exit.
Possible solutions discussed and discarded:
* Just run the multi-threaded test at the end of the unit
tests. See its printf() to verify everything ran correctly, and then
manually kill it in the IAR debugger. Yes, we could see that the test ran
successfully, but we can't run it on a Jenkins server.
* Run all unit tests within a main thread in the OS.
This allows control to return to the 'main' thread after the test threads have
terminated. However, the OS will never finish so the unit tests will
never finish.
* Assembly code of some sort that grabs the program counter
position before OS_Start(). In a third thread which is lower priority and
only executes after the test threads are finished, somehow pop the OS_Start()
and OS_StartASM() function calls off of the stack, and then set the program
counter position to just after the OS_Start() command back in the main thread
and begin execution again. Frankly this seems feasible but far fetched
and there has to be a better way especially since multi-threaded unit tests
seems like a common use case.
Questions:
1) How can we terminate OS execution after all threads have
completed so that our unit tests can successfully exit?
2) I see that there’s a “OS_Exit” command in our RTOS.h that
could be used for Windows platform testing, but it’s not linkable with our
pre-compiled version of embOS. Would that be an appropriate OS interface
function to call and how can we get access to it?
3) Oddly, I haven't seen any failures from my test so
far. I'm following TDD and haven't implemented any thread safe protection
mechanisms in the production code yet, so it seems that the OS isn't forcing
any task switching. From my printf() commands I can see that the threads
run sequentially without any switching, even though they may take 1-10seconds
each to run. I've given them equal priority (50) so that neither is a
higher priority and the OS should use Round Robin scheduling while limiting the
execution time of each to the timeslice period. (I'm not sure what the
default timeslice is at the moment.)
4) Is there a thread 'join' command to only terminate a
thread while it isn't active? I haven't seen it supported in RTOS.h, so
I'm assuming we'd have to implement that ourselves.
C Source Code
- #include "unity.h"
- #include "unity_fixture.h"
- #include "RTOS.h"
- #define MAXIMUM_CONCURRENT_THREADS 4u
- static OS_TASK osTasks[MAXIMUM_CONCURRENT_THREADS];
- static TUSIGN8 osTasksAllocated = 0u;
- static void ThreadTask(void);
- static TUSIGN8 numTestThreadsRunning = 0; // indication of how many test threads are running, so that unit test can wait for them to finish.
- static TUSIGN8 numThreadSafetyTestFailures = 0; // no asserts inside spawned threads, but tracked with this flag.
- static TINT16 stack1ForThreadSafetyTest[500], stack2ForThreadSafetyTest[500]; /* Task stacks */
- TEST(TestGroup1, Get_Put_ThreadSafe)
- {
- // Setup the module under test.
- // Setup and run the threads. Use the same test function in both threads (no static variables).
- TEST_ASSERT_EQUAL(0, numTestThreadsRunning);
- numThreadSafetyTestFailures = 0;
- // Initialize the OS
- OS_CreateRSema(&sema);
- OS_IncDI(); // Initially disable interrupts
- OS_InitKern(); // Initialize OS
- OS_InitHW(); // Initialize Hardware for OS
- OS_CreateTask(osTasks[osTasksAllocated++], "ThreadSafety Task1", 50,
- ThreadTask, (void OS_STACKPTR*)stack1ForThreadSafetyTest, (unsigned int)stack1ForThreadSafetyTest CTPARA_TIMESLICE);
- OS_CreateTask(osTasks[osTasksAllocated++], "ThreadSafety Task2", 50,
- ThreadTask, (void OS_STACKPTR*)stack2ForThreadSafetyTest, (unsigned int)stack2ForThreadSafetyTest CTPARA_TIMESLICE);
- OS_Start();
- // This code never runs so far.
- while (numTestThreadsRunning > 0)
- {
- // wait until test threads have finished running before exiting this unit test function.
- printf("Test_TestGroup1_Get_Put_ThreadSafe waiting on test threads to finish. numTestThreadsRunning=%u\n\r",
- numTestThreadsRunning);
- Sleep_OSAL(100u);
- }
- TEST_ASSERT_EQUAL(0, numThreadSafetyTestFailures);
- }
- static void ThreadTask(void)
- {
- numTestThreadsRunning++;
- // ......
- // Execute some tasks which access data of a common shared object to verify that it can handle requests from multiple threads.
- // ......
- // Instead of using TEST_ASSERT_EQUAL(a, b);
- // use if (a != b) { numThreadSafetyTestFailures++; }
- // ......
- numTestThreadsRunning--;
- printf("Task '%s' exiting. numTestThreadsRunning=%u.\n\r", OS_GetTaskName(NULL), numTestThreadsRunning);
- OS_TerminateTask(NULL); // Terminate the thread.
- }