13.7.3. Macro “relauncherCJitFunctionThreadTest.C

13.7.3.1. Objective

The goal of this macro is to show how to handle thread-safe compiled function (or code) that would contain TDataServer objects (this is not particularly recommended, but has been requested to us). This example is only written in C++ as the CJit interface only works for this, but the idea is the same if one has a code and use the python interface. This will be further discussed below. The function is pointless, a pure illustrative toy and the results is not important as long as one sees that in one case, the macro runs smoothly while on the other hand, it crashes.

13.7.3.2. Macro

void multiply(double *x, double *y)
{
    // New dataserver reading all points in flowrate 
    URANIE::DataServer::TDataServer test("test","notindir");
    test.keepFinalTuple(false); // Remove the tuple from ROOT internal list
    test.fileDataRead("flowrateUniformDesign.dat",false);
     
    // Dummy functions with a loop to slow down the function
    double max=-1000000;
    for(int i=0; i<test.getNPatterns(); i++)
    {
        double val = test.getValue("ystar",i);
        max = ((val>=max) ? val : max); 
    }
    y[0] = max * x[0];

}

void relauncherCJitFunctionThreadTest(const int seed=0)
{
   
    /* This macro can be used in two modes:
    */
    bool threaded=true;

    // If thread-safe, call this method that will take out the dataserver from ROOT internal list
    if(threaded)
        ROOT::EnableThreadSafety();  // part of the solution

    // input and output attributes
    URANIE::DataServer::TUniformDistribution x("multiplier",1,10);
    URANIE::DataServer::TAttribute    MultMean("MultMean");

    // Interface to the compiled function above
    URANIE::Relauncher::TCJitEval eval(multiply);
    eval.addInput(&x);
    eval.addOutput(&MultMean);

    // Threaded runner
    URANIE::Relauncher::TThreadedRun run(&eval,4);
    run.startSlave();

    if( run.onMaster() )
    {
        // Global dataserver
        URANIE::DataServer::TDataServer tds("pouet","pouet_notindir");
        tds.addAttribute(&x);
    
        // Doe for the multiplier
        URANIE::Sampler::TSampling sam(&tds,"lhs",24);
        sam.generateSample();

        // Run the code
        URANIE::Relauncher::TLauncher2 launch(&tds, &run);
        launch.solverLoop();
        tds.getTuple()->SetScanField(-1);
        tds.scan();

        run.stopSlave();
 
    }
}

The very first part of the macro is a function that would be applied to all points of our design-of-experiments. The general context is simple: let’s assume one wants to find the maximum value of a variable in a given dataset, and let’s assume that this maximum should have to be scaled by some factor. We create the multiply function to do so, as done here:

void multiply(double *x, double *y)
{
    // New dataserver reading all points in flowrate 
    URANIE::DataServer::TDataServer test("test","notindir");
    test.keepFinalTuple(false); // Remove the tuple from ROOT internal list
    test.fileDataRead("flowrateUniformDesign.dat",false);
     
    // Dummy functions with a loop to slow down the function
    double max=-1000000;
    for(int i=0; i<test.getNPatterns(); i++)
    {
        double val = test.getValue("ystar",i);
        max = ((val>=max) ? val : max); 
    }
    y[0] = max * x[0];

}

In this function the dataset is always the same (flowrateUniformDesign.dat) and the multiplier is the only input attribute. The maximum of the dataset is found by creating a TDataServer object, by calling the fileDataRead method to read the dataset and by looping over the events (this is not at all the best way to do it, but it is a toy model to show what problems can arise when a TDataServer object is created in an evaluator use in multi-thread approach). Few important points in this function to prevent from race condition (not thread-safe behaviour):

  • the TDataServer object is created statistically so that at the end of the function it is automatically destroyed;

  • the line below is used to tell the TDataServer object not to write his data tree in the ROOT internal list and not to dump it in the archive file when the object is destroyed

        test.keepFinalTuple(false); // Remove the tuple from ROOT internal list
    

  • the line below is used to tell the fileDataRead method not to create the archive ROOT file once the dataset is read (thanks to the optional boolean set to false here)

        test.fileDataRead("flowrateUniformDesign.dat",false);
    

The main function starts then with a block that allows to test the main point here: the method develop by ROOT to prevent the internal list to store object which would lead to race conditions. This block is commented to explain how to run the use-case macro discuss here. The important part is, if one wants to run the macro properly, to call

        ROOT::EnableThreadSafety();  // part of the solution

Once this is done, then the macro can be briefly described in the few key steps

  1. create the input (multiplier) and output (MultMean) attribute;

  2. create the interface to the multiply function;

  3. create the interface for the runner;

  4. create the TDataServer object that would contain the multiplier and the results. This object is created within the onMaster() part because otherwise there would have been as many dataserver object as there are threads.

  5. create a sampler and a design-of-experiments to read 24 times the given dataset;

  6. run the computations

At the end, once the macro is launched by using the command below, the

root -b -q relauncherCJitFunctionThreadTest.C

13.7.3.3. Console

--- Uranie v4.11/0 --- Developed with ROOT (6.36.06)
                      Copyright (C) 2013-2026 CEA/DES 
                      Contact: support-uranie@cea.fr 
                      Date: Thu Feb 12, 2026

************************************************
*    Row   * pouet__n_ * multiplie * MultMean. *
************************************************
*        0 *         1 * 8.1562084 * 1343.9800 *
*        1 *         3 * 6.5650956 * 1081.7964 *
*        2 *         0 * 4.1412811 * 682.40030 *
*        3 *         5 * 4.7243584 * 778.47978 *
*        4 *         2 * 9.4179988 * 1551.8978 *
*        5 *         4 * 7.0461633 * 1161.0667 *
*        6 *         7 * 7.4193739 * 1222.5644 *
*        7 *         6 * 2.5976785 * 428.04546 *
*        8 *         8 * 5.3607876 * 883.35059 *
*        9 *         9 * 5.6692661 * 934.18168 *
*       10 *        11 * 3.5864637 * 590.97748 *
*       11 *        10 * 1.0476136 * 172.62578 *
*       12 *        12 * 1.5680924 * 258.39027 *
*       13 *        13 * 4.8760831 * 803.48098 *
*       14 *        15 * 9.0195417 * 1486.2400 *
*       15 *        14 * 6.0024502 * 989.08374 *
*       16 *        16 * 9.9852106 * 1645.3630 *
*       17 *        18 * 2.4617970 * 405.65491 *
*       18 *        19 * 6.7059225 * 1105.0019 *
*       19 *        20 * 3.9170611 * 645.45333 *
*       20 *        17 * 3.1396825 * 517.35689 *
*       21 *        21 * 7.9536348 * 1310.5999 *
*       22 *        23 * 2.1125191 * 348.10091 *
*       23 *        22 * 8.6398795 * 1423.6793 *
************************************************