<commit_before>// -------------------------------------------------------------------------------------------------
// Implementation of the thread creating and joining tests.
//
// At initialization time, spawns a single thread and records its thread reference.
// Each thread that runs to completion increments a mutex-protected counter variable.
// If everything goes as expected, at the end the counter should be set to the correct value
// and the completion check function should be able to join with that first thread that was
// created, and the thread's result value should be its own thread reference.
//
// See the comment for ThreadMainFunction() for details on how the rest of this test works.
//
// Copyright (C) Sierra Wireless Inc.
// -------------------------------------------------------------------------------------------------

/* NOTE:
 *  If a thread starts and then gets cancelled before it gets to register its destructor function,
 *  is that going to cause a problem?  For example, if I increment a reference count on an object
 *  and pass it to a thread, expecting that thread to release that object, is it possible that
 *  the thread gets cancelled before it has a chance to register a destructor for itself that
 *  will release the object?
 */

#include "legato.h"
#include "forkJoinMutex.h"

#ifdef LE_CONFIG_REDUCE_FOOTPRINT
#   define FAN_OUT 3
#   define DEPTH 2     // Note: the process main thread at the top level is not counted.
#else
#   define FAN_OUT 7
#   define DEPTH 3     // Note: the process main thread at the top level is not counted.
#endif

typedef struct ForkJoinTestResult
{
    bool createOk;
    bool joinOk;
    void *expectedJoin, *actualJoin;
} ForkJoinTestResult_t;

ForkJoinTestResult_t TestResults[FAN_OUT*(FAN_OUT+1)*(FAN_OUT+1)];

// The expected counter value is computed as follows:
//  - The first nesting level is the FAN_OUT threads that fjm_Init() creates.
//  - At the second nesting level, each of the FAN_OUT threads that fjm_Init() created will
//      create another FAN_OUT threads, so there will be FAN_OUT * FAN_OUT threads at that level.
//  - At the third nesting level, there will be FAN_OUT * FAN_OUT * FAN_OUT threads.
//  - Etc.
// So,
static size_t GetExpectedCounterValue
(
    void
)
{
    unsigned int result = 0;
    int i;

    for (i = 1; i <= DEPTH; i++)
    {
        int j;
        unsigned int numThreadsAtThisDepth = 1;

        for (j = 1; j <= i; j++)
        {
            numThreadsAtThisDepth *= FAN_OUT;
        }

        result += numThreadsAtThisDepth;
    }

    LE_TEST_INFO("Expecting %u threads to be created in total.", result);

    return result;
}


// -------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------
static unsigned int Counter = 0;

// -------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------
static unsigned int ExpectedCounterValue;

// -------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------
static le_sem_Ref_t CounterSemRef;

// -------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------
LE_MUTEX_DECLARE_REF(MutexRef);


// -------------------------------------------------------------------------------------------------
/**
 * Thread context blocks.
 *
 * A parent thread creates one of these, fills it with info, and passes it to all its children,
 * echo of whom releases it once.  When all children
 */
// -------------------------------------------------------------------------------------------------
typedef struct
{
    size_t depth;           // Indicates what nesting level the thread is at.
                            // 1 = children of process main thread.
}
Context_t;


// -------------------------------------------------------------------------------------------------
/** Memory pool used to hold thread context blocks.
 */
// -------------------------------------------------------------------------------------------------
static le_mem_PoolRef_t ContextPoolRef;


// -------------------------------------------------------------------------------------------------
/**
 * Increment the mutex-protected counter.
 */
// -------------------------------------------------------------------------------------------------
static void IncrementCounter(void)
// -------------------------------------------------------------------------------------------------
{
    Lock();

    Counter++;

    LE_TEST_INFO("Thread '%s' incremented counter to %u.", le_thread_GetMyName(), Counter);

    if (Counter == ExpectedCounterValue)
    {
        le_sem_Post(CounterSemRef);
    }

    Unlock();
}


// -------------------------------------------------------------------------------------------------
/**
 * Destructor function for the thread.
 *
 * Releases all the memory object references that this thread owns.
 */
// -------------------------------------------------------------------------------------------------
static void ThreadDestructor
(
    void* destructorContext
)
// -------------------------------------------------------------------------------------------------
{
    Context_t* contextPtr = destructorContext;

    LE_TEST_INFO("Thread '%s' destructor running.", le_thread_GetMyName());

    le_mem_Release(contextPtr);
}


static void* ThreadMainFunction(void* objPtr);

static bool IsThreadJoinable
(
    size_t depth,
    int i
)
{
    return (depth != DEPTH) || (((i + 1) % 3) != 0);
}

// -------------------------------------------------------------------------------------------------
/**
 * Spawns FAN_OUT children, some of which are joinable and some of which are not, then tries to
 * join with all of them and checks the results.
 */
// -------------------------------------------------------------------------------------------------
static void SpawnChildren
(
    size_t depth            // Indicates what nesting level the thread is at.
                            // 1 = children of the process main thread.
)
// -------------------------------------------------------------------------------------------------
{
    int i, j, k;
    char childName[32];
    le_thread_Ref_t children[FAN_OUT];
    const char* threadName = le_thread_GetMyName();

    if (depth == 2)
    {
        LE_TEST_ASSERT(sscanf(threadName, "%*[^-]-%d", &j) == 1, "depth 2: parse thread name");
        // switch to zero-based
        j--;
        LE_TEST_INFO("depth 2: j=%d", j);
    }
    else if (depth == 3)
    {
        LE_TEST_ASSERT(sscanf(threadName, "%*[^-]-%d-%d", &k, &j) == 1, "depth 3: parse thread name");
        // switch to zero based
        k--;
        j--;<commit_msg>[UT] Solve test thread fails<commit_after>// -------------------------------------------------------------------------------------------------
// Implementation of the thread creating and joining tests.
//
// At initialization time, spawns a single thread and records its thread reference.
// Each thread that runs to completion increments a mutex-protected counter variable.
// If everything goes as expected, at the end the counter should be set to the correct value
// and the completion check function should be able to join with that first thread that was
// created, and the thread's result value should be its own thread reference.
//
// See the comment for ThreadMainFunction() for details on how the rest of this test works.
//
// Copyright (C) Sierra Wireless Inc.
// -------------------------------------------------------------------------------------------------

/* NOTE:
 *  If a thread starts and then gets cancelled before it gets to register its destructor function,
 *  is that going to cause a problem?  For example, if I increment a reference count on an object
 *  and pass it to a thread, expecting that thread to release that object, is it possible that
 *  the thread gets cancelled before it has a chance to register a destructor for itself that
 *  will release the object?
 */

#include "legato.h"
#include "forkJoinMutex.h"

#ifdef LE_CONFIG_REDUCE_FOOTPRINT
#   define FAN_OUT 3
#   define DEPTH 2     // Note: the process main thread at the top level is not counted.
#else
#   define FAN_OUT 7
#   define DEPTH 3     // Note: the process main thread at the top level is not counted.
#endif

typedef struct ForkJoinTestResult
{
    bool createOk;
    bool joinOk;
    void *expectedJoin, *actualJoin;
} ForkJoinTestResult_t;

ForkJoinTestResult_t TestResults[FAN_OUT*(FAN_OUT+1)*(FAN_OUT+1)];

// The expected counter value is computed as follows:
//  - The first nesting level is the FAN_OUT threads that fjm_Init() creates.
//  - At the second nesting level, each of the FAN_OUT threads that fjm_Init() created will
//      create another FAN_OUT threads, so there will be FAN_OUT * FAN_OUT threads at that level.
//  - At the third nesting level, there will be FAN_OUT * FAN_OUT * FAN_OUT threads.
//  - Etc.
// So,
static size_t GetExpectedCounterValue
(
    void
)
{
    unsigned int result = 0;
    int i;

    for (i = 1; i <= DEPTH; i++)
    {
        int j;
        unsigned int numThreadsAtThisDepth = 1;

        for (j = 1; j <= i; j++)
        {
            numThreadsAtThisDepth *= FAN_OUT;
        }

        result += numThreadsAtThisDepth;
    }

    LE_TEST_INFO("Expecting %u threads to be created in total.", result);

    return result;
}


// -------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------
static unsigned int Counter = 0;

// -------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------
static unsigned int ExpectedCounterValue;

// -------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------
static le_sem_Ref_t CounterSemRef;

// -------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------
LE_MUTEX_DECLARE_REF(MutexRef);


// -------------------------------------------------------------------------------------------------
/**
 * Thread context blocks.
 *
 * A parent thread creates one of these, fills it with info, and passes it to all its children,
 * echo of whom releases it once.  When all children
 */
// -------------------------------------------------------------------------------------------------
typedef struct
{
    size_t depth;           // Indicates what nesting level the thread is at.
                            // 1 = children of process main thread.
}
Context_t;


// -------------------------------------------------------------------------------------------------
/** Memory pool used to hold thread context blocks.
 */
// -------------------------------------------------------------------------------------------------
static le_mem_PoolRef_t ContextPoolRef;


// -------------------------------------------------------------------------------------------------
/**
 * Increment the mutex-protected counter.
 */
// -------------------------------------------------------------------------------------------------
static void IncrementCounter(void)
// -------------------------------------------------------------------------------------------------
{
    Lock();

    Counter++;

    LE_TEST_INFO("Thread '%s' incremented counter to %u.", le_thread_GetMyName(), Counter);

    if (Counter == ExpectedCounterValue)
    {
        le_sem_Post(CounterSemRef);
    }

    Unlock();
}


// -------------------------------------------------------------------------------------------------
/**
 * Destructor function for the thread.
 *
 * Releases all the memory object references that this thread owns.
 */
// -------------------------------------------------------------------------------------------------
static void ThreadDestructor
(
    void* destructorContext
)
// -------------------------------------------------------------------------------------------------
{
    Context_t* contextPtr = destructorContext;

    LE_TEST_INFO("Thread '%s' destructor running.", le_thread_GetMyName());

    le_mem_Release(contextPtr);
}


static void* ThreadMainFunction(void* objPtr);

static bool IsThreadJoinable
(
    size_t depth,
    int i
)
{
    return (depth != DEPTH) || (((i + 1) % 3) != 0);
}

// -------------------------------------------------------------------------------------------------
/**
 * Spawns FAN_OUT children, some of which are joinable and some of which are not, then tries to
 * join with all of them and checks the results.
 */
// -------------------------------------------------------------------------------------------------
static void SpawnChildren
(
    size_t depth            // Indicates what nesting level the thread is at.
                            // 1 = children of the process main thread.
)
// -------------------------------------------------------------------------------------------------
{
    int i, j, k;
    char childName[32];
    le_thread_Ref_t children[FAN_OUT];
    const char* threadName = le_thread_GetMyName();

    if (depth == 2)
    {
        LE_TEST_ASSERT(sscanf(threadName, "%*[^-]-%d", &j) == 1, "depth 2: parse thread name");
        // switch to zero-based
        j--;
        LE_TEST_INFO("depth 2: j=%d", j);
    }
    else if (depth == 3)
    {
        LE_TEST_ASSERT(sscanf(threadName, "%*[^-]-%d-%d", &k, &j) == 2, "depth 3: parse thread name");
        // switch to zero based
        k--;
        j--;
{'fraction_non_alphanumeric': 0.3824583596879612, 'fraction_numerical': 0.003513950383020592, 'mean_word_length': 0.9977537554401236, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1687743', 'n_tokens_mistral': 3114, 'n_tokens_neox': 2983, 'n_words': 1143}