Skip to content

Commit

Permalink
add event_loop doc
Browse files Browse the repository at this point in the history
  • Loading branch information
philoinovsky committed Aug 9, 2021
1 parent b3a0bc5 commit cd45a7a
Showing 1 changed file with 84 additions and 0 deletions.
84 changes: 84 additions & 0 deletions doc/reference/eventloop.qbk
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
[chapter Event Loop
[quickbook 1.7]
]

[section boost/python/eventloop.hpp]

[section Introduction]
Provide a Boost.Asio-based implementation for the Python [@https://docs.python.org/3/library/asyncio-eventloop.html `EventLoop`] type. Every callback is scheduled in strand.
[endsect]

[section Function `set_default_event_loop`]
``
void set_default_event_loop(const boost::asio::io_context::strand& strand);
``
[variablelist
[[Effect][construct an `event_loop` object using provided [@https://www.boost.org/doc/libs/1_76_0/doc/html/boost_asio/overview/core/strands.html `strand`] object. Setup a new [@https://docs.python.org/3/library/asyncio-policy.html event loop policy], when user call `get_event_loop` using that policy, it returns the Boost Asio `event_loop` object]]
[[Throws][nothing]]
]
[endsect]

[section Function `PyInit_boost_event_loop`]
``
extern "C"
{
PyObject* PyInit_boost_event_loop();
}
``
[variablelist
[[Effect][user must call `PyImport_AppendInittab("boost_event_loop", &PyInit_boost_event_loop);` before [@https://docs.python.org/3/c-api/init.html#c.Py_Initialize `Py_Initialize`]]]
[[Throws][nothing]]
]
[endsect]

[section Example]
``
// example.cpp
io_context ctx;
io_context::strand st{ctx};

PyImport_AppendInittab("boost_event_loop", &PyInit_boost_event_loop);
Py_Initialize();
set_default_event_loop(st);

object py_module = import("example.py");
py_module.attr("hello_world")();
st.context().run();

// example.py
import asyncio
def hello_world():
print("hello world")

def call_event_loop():
loop = asyncio.get_event_loop_policy().get_event_loop()
loop.call_soon(hello_world)
``
[endsect]

[section Event Loop and Multiple Python Sub-interpreters]
It's allowed to have multiple Python sub-interpreter instances in a same program. Each interpreter will act as a guest VM, and C++ host will schedule all the asynchronous events committed by the Python VM.
[endsect]

[section Create, Swap, and Destroy Sub-interpreter]
One way to create an interpreter is
``
PyThreadState* ts = PyThreadState_Swap(Py_NewInterpreter());
``
This will create an interpreter and its first thread.[br]
Call [@https://docs.python.org/3/c-api/init.html#c.PyThreadState_Swap `PyThreadState_Swap`] to swap between different interpreter.[br]
To destroy an interpreter, simply call
``
// this will destroy the interpreter and switch to other_ts
Py_EndInterpreter(ts);
PyThreadState_Swap(other_ts);
``
The Python interpreter must outlive the [@https://www.boost.org/doc/libs/1_76_0/doc/html/boost_asio/reference/io_service.html `asio::io_context`] objects it owns. It's not safe to destroy the interpreter midways.[br]
[@https://docs.python.org/3/c-api/structures.html#c.PyObject `PyObject`] [@https://docs.python.org/3/c-api/refcounting.html `reference count`] won't protect its interpreter from being destroyed.
[endsect]

[section [@https://docs.python.org/3/c-api/init.html#thread-state-and-the-global-interpreter-lock `GIL`]]
GIL is shared by all sub-interpreters in the same program, which means after setting up the Python IO object and call the async functions, C++ host should release the GIL of current interpreter.
[endsect]

[endsect]

0 comments on commit cd45a7a

Please sign in to comment.