MeshLib C++ Docs
Loading...
Searching...
No Matches
MREmbeddedPython.h
Go to the documentation of this file.
1#pragma once
2
3#include "exports.h"
4
5#include <atomic>
6#include <condition_variable>
7#include <filesystem>
8#include <functional>
9#include <mutex>
10#include <string>
11#include <thread>
12#include <vector>
13
14namespace MR
15{
16
17class MRPYTHON_CLASS EmbeddedPython
18{
19public:
20 struct Config
21 {
22 bool siteImport{ true };
23 std::string home;
24 std::vector<std::string> argv;
25 };
26 MRPYTHON_API static Config pythonConfig; // Set this once before running anything.
27
28 MRPYTHON_API static bool isAvailable();
29
30 // If you have used `runScript()` at least once, you should call this before terminating the program.
31 // Otherwise you can get a crash in Python cleanup. I have only observed this with our patched Pybind.
32 MRPYTHON_API static void shutdown();
33
34 // Returns true if the interpreter is busy running something.
35 // If you try to run something else, your thread will block until it's done.
36 [[nodiscard]] static bool nowRunning() { return instance_().state_.load() != State::idle; }
37
38 // Returns false on failure.
39 // If `onDoneAsync` is set, doesn't wait for the script to finish.
40 // Will call `onDoneAsync` asynchronously when done (from the Python interpreter thread).
41 MRPYTHON_API static bool runString( std::string pythonString, std::function<void( bool success )> onDoneAsync = nullptr );
42
43 MRPYTHON_API static bool runScript( const std::filesystem::path& path );
44
45 MRPYTHON_API static bool isPythonScript( const std::filesystem::path& path );
46private:
48 EmbeddedPython( const EmbeddedPython& ) = delete;
49 EmbeddedPython& operator=( const EmbeddedPython& ) = delete;
51
52 bool init_();
53 void ensureInterpreterThreadIsRunning_();
54
55 MRPYTHON_API static EmbeddedPython& instance_();
56 bool available_ = false;
57 bool shutdownCalled_ = false;
58
59 enum class State
60 {
61 idle, // Waiting for a script to run.
62 running, // Interpreter is running.
63 finishing, // Interpreter is done, waiting for the submitter thread to read the result.
64 };
65
66 std::atomic<State> state_ = State::idle; // Making this atomic allows `nowRunning()` to read this without locking the mutex.
67 std::string queuedSource_;
68 bool lastRunSuccessful_ = false;
69 std::function<void( bool success )> onDoneAsync_;
70 std::mutex cvMutex_;
71 std::condition_variable cv_; // It's could (?) be more efficient to have more CVs here, but one is simpler.
72
73 // We maintain ONE persistent thread that runs all python scripts, and persists while the program runs.
74 // This seems to be the safest option, I had issues otherwise. We need everything Python-related to happen in the same thread,
75 // and we also need to not finalize-and-recreate the interpreter while the program runs because that breaks our generated bindings
76 // (which may or may not be possible to fix in the bindings, but it's easier not to, and the manual even advises that
77 // some modules can break if you recreate the interpeter: https://docs.python.org/3/c-api/init.html#c.Py_FinalizeEx).
78
79 std::thread interpreterThread_;
80
81 std::atomic_bool stopInterpreterThread_ = false;
82};
83
84} //namespace MR
Definition MREmbeddedPython.h:18
static MRPYTHON_API void shutdown()
static MRPYTHON_API bool isAvailable()
static MRPYTHON_API bool runScript(const std::filesystem::path &path)
static MRPYTHON_API bool isPythonScript(const std::filesystem::path &path)
static bool nowRunning()
Definition MREmbeddedPython.h:36
static MRPYTHON_API bool runString(std::string pythonString, std::function< void(bool success)> onDoneAsync=nullptr)
static MRPYTHON_API Config pythonConfig
Definition MREmbeddedPython.h:26
Definition MREmbeddedPython.h:21
std::string home
Definition MREmbeddedPython.h:23
std::vector< std::string > argv
Definition MREmbeddedPython.h:24