How to exit embOS while running multi-threaded unit tests?

This site uses cookies. By continuing to browse this site, you are agreeing to our Cookie Policy.

  • How to exit embOS while running multi-threaded unit tests?

    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.

    C Source Code

    1. #include "unity.h"
    2. #include "unity_fixture.h"
    3. #include "RTOS.h"
    4. #define MAXIMUM_CONCURRENT_THREADS 4u
    5. static OS_TASK osTasks[MAXIMUM_CONCURRENT_THREADS];
    6. static TUSIGN8 osTasksAllocated = 0u;
    7. static void ThreadTask(void);
    8. static TUSIGN8 numTestThreadsRunning = 0; // indication of how many test threads are running, so that unit test can wait for them to finish.
    9. static TUSIGN8 numThreadSafetyTestFailures = 0; // no asserts inside spawned threads, but tracked with this flag.
    10. static TINT16 stack1ForThreadSafetyTest[500], stack2ForThreadSafetyTest[500]; /* Task stacks */
    11. TEST(TestGroup1, Get_Put_ThreadSafe)
    12. {
    13. // Setup the module under test.
    14. // Setup and run the threads. Use the same test function in both threads (no static variables).
    15. TEST_ASSERT_EQUAL(0, numTestThreadsRunning);
    16. numThreadSafetyTestFailures = 0;
    17. // Initialize the OS
    18. OS_CreateRSema(&sema);
    19. OS_IncDI(); // Initially disable interrupts
    20. OS_InitKern(); // Initialize OS
    21. OS_InitHW(); // Initialize Hardware for OS
    22. OS_CreateTask(osTasks[osTasksAllocated++], "ThreadSafety Task1", 50,
    23. ThreadTask, (void OS_STACKPTR*)stack1ForThreadSafetyTest, (unsigned int)stack1ForThreadSafetyTest CTPARA_TIMESLICE);
    24. OS_CreateTask(osTasks[osTasksAllocated++], "ThreadSafety Task2", 50,
    25. ThreadTask, (void OS_STACKPTR*)stack2ForThreadSafetyTest, (unsigned int)stack2ForThreadSafetyTest CTPARA_TIMESLICE);
    26. OS_Start();
    27. // This code never runs so far.
    28. while (numTestThreadsRunning > 0)
    29. {
    30. // wait until test threads have finished running before exiting this unit test function.
    31. printf("Test_TestGroup1_Get_Put_ThreadSafe waiting on test threads to finish. numTestThreadsRunning=%u\n\r",
    32. numTestThreadsRunning);
    33. Sleep_OSAL(100u);
    34. }
    35. TEST_ASSERT_EQUAL(0, numThreadSafetyTestFailures);
    36. }
    37. static void ThreadTask(void)
    38. {
    39. numTestThreadsRunning++;
    40. // ......
    41. // Execute some tasks which access data of a common shared object to verify that it can handle requests from multiple threads.
    42. // ......
    43. // Instead of using TEST_ASSERT_EQUAL(a, b);
    44. // use if (a != b) { numThreadSafetyTestFailures++; }
    45. // ......
    46. numTestThreadsRunning--;
    47. printf("Task '%s' exiting. numTestThreadsRunning=%u.\n\r", OS_GetTaskName(NULL), numTestThreadsRunning);
    48. OS_TerminateTask(NULL); // Terminate the thread.
    49. }
    Display All