OrcaSlicer latest
OrcaSlicer
Loading...
Searching...
No Matches
Slic3r::ScriptAsyncRuntime::Impl Struct Reference
Collaboration diagram for Slic3r::ScriptAsyncRuntime::Impl:

Classes

struct  Run

Public Member Functions

void run_invocation (const std::shared_ptr< Run > &run)

Public Attributes

std::mutex mutex
std::condition_variable cv
std::vector< std::shared_ptr< Run > > active
bool stopping = false

Detailed Description


Class Documentation

◆ Slic3r::ScriptAsyncRuntime::Impl::Run

struct Slic3r::ScriptAsyncRuntime::Impl::Run
Collaboration diagram for Slic3r::ScriptAsyncRuntime::Impl::Run:
Class Members
atomic< bool > cancel_requested {false}
shared_ptr< PluginCapabilityInterface > capability
string capability_name
mutex handle_mutex
atomic< bool > has_loop {false}
object loop
function< void(const ScriptCompletion &)> on_done
string plugin_key
object task

Member Function Documentation

◆ run_invocation()

void Slic3r::ScriptAsyncRuntime::Impl::run_invocation ( const std::shared_ptr< Run > & run)
2150{
2151 ScriptCompletion completion;
2152
2153 {
2154 PythonRuntimeLease lease = PythonInterpreter::instance().acquire_runtime_lease();
2155 if (!lease) {
2156 completion.error = _u8L("Python interpreter is shutting down.");
2157 } else {
2158 pybind11::gil_scoped_acquire gil;
2159
2160 if (run->cancel_requested.load(std::memory_order_acquire)) {
2161 // Cancelled before it ran: expected termination, never an error.
2162 completion.cancelled = true;
2163 } else {
2164 // Hold the capability reference, the audit scope and the GIL for the whole
2165 // invocation, so unload/shutdown cannot tear the module out from under it.
2166 PluginCapabilityInterface::RefCounter ref_counter(*run->capability);
2167
2168 std::optional<ScopedPluginAuditContext> audit_scope;
2169 if (!run->capability->audit_plugin_key().empty())
2170 audit_scope.emplace(run->capability->audit_plugin_key(), run->capability->name());
2171
2172 // Detection and execution both happen here, on the worker: a synchronous
2173 // `def execute` runs to completion off the UI thread, exactly like an `async def`.
2174 ScriptExecuteInvocation invocation = invoke_script_execute(run->capability);
2175 if (!invocation.error.empty()) {
2176 completion.error = invocation.error;
2177 } else if (!invocation.is_async) {
2178 completion.result = invocation.result;
2179 } else if (run->cancel_requested.load(std::memory_order_acquire)) {
2180 completion.cancelled = true;
2181 } else {
2182 try {
2183 pybind11::module_ asyncio = pybind11::module_::import("asyncio");
2184 pybind11::object loop = asyncio.attr("new_event_loop")();
2185 asyncio.attr("set_event_loop")(loop);
2186
2187 tl_script_loop = loop.ptr();
2188 Py_INCREF(tl_script_loop);
2189
2190 pybind11::object task = loop.attr("create_task")(std::move(invocation.awaitable));
2191 {
2192 std::lock_guard<std::mutex> lock(run->handle_mutex);
2193 run->loop = loop;
2194 run->task = task;
2195 }
2196 run->has_loop.store(true, std::memory_order_release);
2197 // A cancellation that arrived before the loop came up must still land.
2198 if (run->cancel_requested.load(std::memory_order_acquire))
2199 loop.attr("call_soon_threadsafe")(task.attr("cancel"));
2200
2201 try {
2202 pybind11::object returned = loop.attr("run_until_complete")(task);
2203 completion.result = returned.cast<ExecutionResult>();
2204 } catch (pybind11::error_already_set& err) {
2205 // Only our own cancel_plugin() cancellation is expected termination; a
2206 // CancelledError the plugin raised on its own stays a real error.
2207 if (err.matches(asyncio.attr("CancelledError")) &&
2208 run->cancel_requested.load(std::memory_order_acquire)) {
2209 completion.cancelled = true;
2210 } else {
2212 completion.error = err.what();
2213 }
2214 }
2215
2216 run->has_loop.store(false, std::memory_order_release);
2217 {
2218 std::lock_guard<std::mutex> lock(run->handle_mutex);
2219 run->task = pybind11::object();
2220 run->loop = pybind11::object();
2221 }
2222 Py_CLEAR(tl_script_loop);
2223 loop.attr("close")();
2224 asyncio.attr("set_event_loop")(pybind11::none());
2225 } catch (pybind11::error_already_set& err) {
2227 completion.error = err.what();
2228 } catch (const std::exception& ex) {
2229 completion.error = ex.what();
2230 }
2231
2232 // Covers an exception thrown after the loop came up but before the clear above.
2233 if (run->has_loop.load(std::memory_order_acquire)) {
2234 run->has_loop.store(false, std::memory_order_release);
2235 std::lock_guard<std::mutex> lock(run->handle_mutex);
2236 run->task = pybind11::object();
2237 run->loop = pybind11::object();
2238 }
2239
2240 if (tl_script_loop != nullptr)
2241 Py_CLEAR(tl_script_loop);
2242 }
2243 }
2244
2245 // Drop the capability reference while the GIL is held: releasing a pybind holder from
2246 // a non-Python thread would DECREF the capability without the GIL. Runs on every
2247 // lease-valid path, including an invocation cancelled before it ran.
2248 run->capability.reset();
2249 }
2250 }
2251
2252 // A cancellation requested by cancel_plugin() (unload/shutdown) is expected termination and is
2253 // authoritative: whatever the body did — finished, swallowed CancelledError, or failed — nothing
2254 // is surfaced as an error or as a normal success.
2255 if (run->cancel_requested.load(std::memory_order_acquire)) {
2256 completion.cancelled = true;
2257 completion.error.clear();
2258 }
2259
2260 auto on_done = std::move(run->on_done);
2261 auto deliver = [on_done, completion]() {
2262 if (on_done)
2263 on_done(completion);
2264 };
2265
2266 if (wxTheApp == nullptr || wxIsMainThread())
2267 deliver();
2268 else
2269 GUI::wxGetApp().CallAfter(deliver);
2270
2271 // Leave `active` last, so a drain cannot observe the run as finished before its completion was
2272 // queued/delivered and its capability reference released.
2273 {
2274 std::lock_guard<std::mutex> lock(mutex);
2275 active.erase(std::remove(active.begin(), active.end(), run), active.end());
2276 }
2277 cv.notify_all();
2278}
PythonRuntimeLease acquire_runtime_lease()
Definition PythonInterpreter.hpp:162
static PythonInterpreter & instance()
Definition PythonInterpreter.cpp:434
AppActionRunResult run(const std::string &key, const std::string &param={})
Definition NativeCommands.cpp:668
PyObject * tl_script_loop
Definition PluginManager.cpp:2116
ScriptExecuteInvocation invoke_script_execute(const std::shared_ptr< PluginCapabilityInterface > &capability)
Definition PyPluginTrampoline.hpp:101
void log_python_exception_keep(pybind11::error_already_set &err)
Definition PythonInterpreter.cpp:96
@ lock
Definition GLGizmoEmboss.cpp:209
#define _u8L(s)
Definition I18N.hpp:11
std::vector< std::shared_ptr< Run > > active
Definition PluginManager.cpp:2142
std::condition_variable cv
Definition PluginManager.cpp:2141
std::mutex mutex
Definition PluginManager.cpp:2140

References _u8L, Slic3r::PythonInterpreter::acquire_runtime_lease(), active, Slic3r::ScriptExecuteInvocation::awaitable, Slic3r::ScriptCompletion::cancelled, Slic3r::ScriptCompletion::error, Slic3r::ScriptExecuteInvocation::error, Slic3r::PythonInterpreter::instance(), Slic3r::invoke_script_execute(), Slic3r::ScriptExecuteInvocation::is_async, Slic3r::log_python_exception_keep(), mutex, Slic3r::ScriptCompletion::result, and Slic3r::ScriptExecuteInvocation::result.

Here is the call graph for this function:

Member Data Documentation

◆ active

std::vector<std::shared_ptr<Run> > Slic3r::ScriptAsyncRuntime::Impl::active

Referenced by run_invocation().

◆ cv

std::condition_variable Slic3r::ScriptAsyncRuntime::Impl::cv

◆ mutex

std::mutex Slic3r::ScriptAsyncRuntime::Impl::mutex

Referenced by run_invocation().

◆ stopping

bool Slic3r::ScriptAsyncRuntime::Impl::stopping = false

The documentation for this struct was generated from the following file: