Back to home page

EIC code displayed by LXR

 
 

    


Warning, file /jana2/src/python/plugins/janapy/janapy_plugin.cc was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 //
0002 // Copyright 2020, Jefferson Science Associates, LLC.
0003 // Subject to the terms in the LICENSE file found in the top-level directory.
0004 
0005 
0006 #include <thread>
0007 #include <mutex>
0008 #include <chrono>
0009 using namespace std;
0010 
0011 
0012 #include <JANA/JApplication.h>
0013 #include <JANA/Utils/JCpuInfo.h>
0014 #include <JANA/Services/JParameterManager.h>
0015 
0016 #include <janapy.h>
0017 #include <pybind11/embed.h>
0018 
0019 extern std::mutex pymutex;    // declared in JEventProcessorPY.h
0020 
0021 void JANA_EmbeddedPythonModuleInit(JApplication *sApp);
0022 
0023 extern "C"{
0024 __attribute__((visibility("default")))  // counteract the -fvisibility=hidden compiler option
0025 void InitPlugin(JApplication *app){
0026     InitJANAPlugin(app);
0027 
0028     // Launch thread to set up python interface and execute user script.
0029     // This is done in a thread in case the script needs to continue
0030     // running throughout the life of the process.
0031     std::thread thr(JANA_EmbeddedPythonModuleInit, app);
0032     thr.detach();
0033 
0034     // Wait for python interface to set up and user script to execute
0035     // until it indicates it is ready for JANA system to continue.
0036     // This allows more control from python by stalling the initialization
0037     // so it has a chance to modify things a bit before full JANA
0038     // initialization completes and data processing starts.
0039     while( !PY_INITIALIZED ) std::this_thread::sleep_for (std::chrono::milliseconds(100));
0040 }
0041 
0042 void FinalizePlugin(JApplication */*app*/){
0043     // Finalize the python interpreter
0044     if( PY_INITIALIZED ) {
0045         // Wait upt to 2 seconds for interpreter to not be in use before finalizing it.
0046         for(int i=0; i<20; i++) {
0047             if (pymutex.try_lock()) {
0048                 PY_INITIALIZED = false;
0049                 // TODO: Fix this
0050                 //  There is an issue with seg faults when dlclose is called on the janapy plugin
0051                 //  if we call finalize here. I tried to ensure no other python methods were being
0052                 //  called, but couldn't find how to plug things up completely. Thus, to avoid
0053                 //  crashes when ending (especially when ctl-C is hit) we do not finalize the
0054                 //  python interpreter.
0055                 py::finalize_interpreter();
0056                 break;
0057             }
0058             std::this_thread::sleep_for(std::chrono::milliseconds(100));
0059         }
0060     }
0061 }
0062 } // "C"
0063 
0064 //.....................................................
0065 
0066 
0067 //================================================================================
0068 
0069 //-------------------------------------
0070 // JANA_EmbeddedPythonModuleInit
0071 //-------------------------------------
0072 void JANA_EmbeddedPythonModuleInit(JApplication *sApp)
0073 {
0074     /// This will initialize the jana Python system and import the
0075     /// JANA module (defined by the routines in this file). This gets called
0076     /// when the janapy plugin gets initialized. It will then automatically
0077     /// look for a file whose name is given by either the JANA_PYTHON_FILE
0078     /// config. parameter or environment variable in that order. If neither
0079     /// exists, it looks for a file name "jana.py" in the current working
0080     /// directory and if found, executes that. If you do not wish to execute
0081     /// a python file, then you can set the JANA_PYTHON_FILE value to an empty
0082     /// string (or better yet, just not attach the janapy plugin!).
0083     ///
0084     /// IMPORTANT: The janapy InitPlugin routine will block (resulting in the whole
0085     /// JANA system pausing) until either this routine finishes or the python script
0086     /// it invokes calls "jana.Start()" or "jana.Run()". This is to give the python script
0087     /// a chance to modify running conditions prior to event processing starting.
0088     /// If the python script intends to run throughout the life of the process,
0089     /// then it MUST call jana.Start() at some point. If the script is small and only
0090     /// runs for a short time, then you don't need to call it since it will be
0091     /// called automatically when the script ends.
0092 
0093     // Initialize the python interpreter. It will be finalized (shutdown)
0094     // when the plugin is detached in the FinalizePlugin routine above.
0095     py::initialize_interpreter();
0096 
0097     // Use existing JApplication.
0098     pyjapp = sApp;
0099 
0100     // Get name of python file to execute
0101     string fname = "jana.py";
0102     try{
0103         fname = pyjapp->GetParameterValue<string>("JANA_PYTHON_FILE");
0104     }catch(...){
0105         auto JANA_PYTHON_FILE = getenv("JANA_PYTHON_FILE");
0106         if( JANA_PYTHON_FILE ) fname = JANA_PYTHON_FILE;
0107     }
0108 
0109     // Fill in the script name in sys.argv. This is needed for tkinter which
0110     // is hardcoded to look at sys.argv[0].
0111     std::vector<wchar_t*> argv;
0112     auto wfname = std::wstring(fname.begin(), fname.end());
0113     argv.push_back( (wchar_t*)wfname.c_str() );
0114     PySys_SetArgv(argv.size(), argv.data());
0115 
0116     // Execute python script.
0117     // n.b. The script may choose to run for the lifetime of the program!
0118     try {
0119         std::cout << "[INFO] Executing python script: " << fname << std::endl;
0120         py::eval_file(fname);
0121         std::cout << "[INFO] Finished executing python script " << std::endl;
0122     }catch(std::runtime_error &e){
0123         std::cerr << std::endl;
0124         std::cerr << string(60, '-') << std::endl;
0125         std::cerr << "ERROR processing python file: \"" << fname << "\"" << std::endl;
0126         std::cerr << e.what() << std::endl;
0127         std::cerr << std::endl;
0128         if( string(e.what()).find("could not be opened!") != string::npos) {
0129             std::cerr << "Please make sure one of the following is true:" << std::endl;
0130             std::cerr << "  - The file \"" << fname << "\" exists" << std::endl;
0131             std::cerr << "  - The JANA_PYTHON_FILE config. parameter points to a file that exists" << std::endl;
0132             std::cerr << "  - The janapy plugin is not in the configuration for the job" << std::endl;
0133         }
0134         std::cerr << string(60, '-') << std::endl;
0135         std::cerr << std::endl;
0136         pyjapp->Quit();
0137     }
0138 
0139     PY_INITIALIZED = true;
0140 }
0141 
0142 
0143 //================================================================================
0144 // Module definition
0145 // The arguments of this structure tell Python what to call the extension,
0146 // what it's methods are and where to look for it's method definitions.
0147 // The routines themselves are all defined in src/python/common/janapy.h
0148 // and src/python/common/janapy.cc.
0149 PYBIND11_EMBEDDED_MODULE(jana, m) {
0150 
0151     m.doc() = "JANA2 Embedded Python Interface";
0152 
0153     // (see src/python/common/janapy.h)
0154     JANA_MODULE_DEF
0155 }