Skip to content

Commit 236cc25

Browse files
committed
Change PYBIND11_MODULE to multi-phase init
Required for sub-interpreter support.
1 parent 50d3ccd commit 236cc25

File tree

3 files changed

+153
-11
lines changed

3 files changed

+153
-11
lines changed

include/pybind11/detail/common.h

+20-11
Original file line numberDiff line numberDiff line change
@@ -372,11 +372,9 @@
372372
#define PYBIND11_CATCH_INIT_EXCEPTIONS \
373373
catch (pybind11::error_already_set & e) { \
374374
pybind11::raise_from(e, PyExc_ImportError, "initialization failed"); \
375-
return nullptr; \
376375
} \
377376
catch (const std::exception &e) { \
378377
::pybind11::set_error(PyExc_ImportError, e.what()); \
379-
return nullptr; \
380378
}
381379

382380
/** \rst
@@ -404,6 +402,7 @@
404402
return pybind11_init(); \
405403
} \
406404
PYBIND11_CATCH_INIT_EXCEPTIONS \
405+
return nullptr; \
407406
} \
408407
PyObject *pybind11_init()
409408

@@ -447,23 +446,33 @@
447446
PYBIND11_WARNING_PUSH
448447
PYBIND11_WARNING_DISABLE_CLANG("-Wgnu-zero-variadic-macro-arguments")
449448
#define PYBIND11_MODULE(name, variable, ...) \
450-
static ::pybind11::module_::module_def PYBIND11_CONCAT(pybind11_module_def_, name) \
451-
PYBIND11_MAYBE_UNUSED; \
452-
PYBIND11_MAYBE_UNUSED \
449+
static ::pybind11::module_::module_def PYBIND11_CONCAT(pybind11_module_def_, name); \
450+
static PyModuleDef_Slot PYBIND11_CONCAT(pybind11_module_slots_, name)[5]; \
451+
static int PYBIND11_CONCAT(pybind11_exec_, name)(PyObject *); \
453452
static void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &); \
454453
PYBIND11_PLUGIN_IMPL(name) { \
455454
PYBIND11_CHECK_PYTHON_VERSION \
456455
PYBIND11_ENSURE_INTERNALS_READY \
457-
auto m = ::pybind11::module_::create_extension_module( \
458-
PYBIND11_TOSTRING(name), \
459-
nullptr, \
460-
&PYBIND11_CONCAT(pybind11_module_def_, name), \
461-
##__VA_ARGS__); \
456+
PYBIND11_CONCAT(pybind11_module_slots_, name)[0].slot = Py_mod_exec; \
457+
PYBIND11_CONCAT(pybind11_module_slots_, name) \
458+
[0].value = reinterpret_cast<void *>(&PYBIND11_CONCAT(pybind11_exec_, name)); \
459+
PYBIND11_CONCAT(pybind11_module_slots_, name)[1] = {0, nullptr}; \
460+
auto m \
461+
= ::pybind11::module_::init_module_def(PYBIND11_TOSTRING(name), \
462+
nullptr, \
463+
&PYBIND11_CONCAT(pybind11_module_def_, name), \
464+
PYBIND11_CONCAT(pybind11_module_slots_, name), \
465+
##__VA_ARGS__); \
466+
return m.ptr(); \
467+
} \
468+
int PYBIND11_CONCAT(pybind11_exec_, name)(PyObject * pm) { \
462469
try { \
470+
auto m = pybind11::reinterpret_borrow<::pybind11::module_>(pm); \
463471
PYBIND11_CONCAT(pybind11_init_, name)(m); \
464-
return m.ptr(); \
472+
return 0; \
465473
} \
466474
PYBIND11_CATCH_INIT_EXCEPTIONS \
475+
return -1; \
467476
} \
468477
void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ & (variable))
469478
PYBIND11_WARNING_POP

include/pybind11/embed.h

+1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
return m.ptr(); \
5050
} \
5151
PYBIND11_CATCH_INIT_EXCEPTIONS \
52+
return nullptr; \
5253
} \
5354
PYBIND11_EMBEDDED_MODULE_IMPL(name) \
5455
::pybind11::detail::embedded_module PYBIND11_CONCAT(pybind11_module_, name)( \

include/pybind11/pybind11.h

+132
Original file line numberDiff line numberDiff line change
@@ -1242,6 +1242,71 @@ class mod_gil_not_used {
12421242
bool flag_;
12431243
};
12441244

1245+
// Use to activate Py_MOD_PER_INTERPRETER_GIL_SUPPORTED
1246+
class mod_per_interpreter_gil {
1247+
public:
1248+
explicit mod_per_interpreter_gil(bool flag = true) : flag_(flag) {}
1249+
bool flag() const { return flag_; }
1250+
1251+
private:
1252+
bool flag_;
1253+
};
1254+
1255+
// Use to activate Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED
1256+
class mod_multi_interpreter_one_gil {
1257+
public:
1258+
explicit mod_multi_interpreter_one_gil(bool flag = true) : flag_(flag) {}
1259+
bool flag() const { return flag_; }
1260+
1261+
private:
1262+
bool flag_;
1263+
};
1264+
1265+
PYBIND11_NAMESPACE_BEGIN(detail)
1266+
1267+
inline bool gil_not_used_option() { return false; }
1268+
template <typename F, typename... O>
1269+
bool gil_not_used_option(F &&, O &&...o);
1270+
template <typename... O>
1271+
inline bool gil_not_used_option(mod_gil_not_used f, O &&...o) {
1272+
return f.flag() || gil_not_used_option(o...);
1273+
}
1274+
template <typename F, typename... O>
1275+
inline bool gil_not_used_option(F &&, O &&...o) {
1276+
return false || gil_not_used_option(o...);
1277+
}
1278+
1279+
#ifdef Py_mod_multiple_interpreters
1280+
inline void *multi_interp_option() { return Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED; }
1281+
# ifdef PYBIND11_SUBINTERPRETER_SUPPORT
1282+
template <typename F, typename... O>
1283+
void *multi_interp_option(F &&, O &&...o);
1284+
template <typename... O>
1285+
void *multi_interp_option(mod_multi_interpreter_one_gil f, O &&...o);
1286+
template <typename... O>
1287+
inline void *multi_interp_option(mod_per_interpreter_gil f, O &&...o) {
1288+
if (f.flag()) {
1289+
return Py_MOD_PER_INTERPRETER_GIL_SUPPORTED;
1290+
}
1291+
return multi_interp_option(o...);
1292+
}
1293+
template <typename... O>
1294+
inline void *multi_interp_option(mod_multi_interpreter_one_gil f, O &&...o) {
1295+
void *others = multi_interp_option(o...);
1296+
if (!f.flag() || others == Py_MOD_PER_INTERPRETER_GIL_SUPPORTED) {
1297+
return others;
1298+
}
1299+
return Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED;
1300+
}
1301+
# endif
1302+
template <typename F, typename... O>
1303+
inline void *multi_interp_option(F &&, O &&...o) {
1304+
return multi_interp_option(o...);
1305+
}
1306+
#endif
1307+
1308+
PYBIND11_NAMESPACE_END(detail)
1309+
12451310
/// Wrapper for Python extension modules
12461311
class module_ : public object {
12471312
public:
@@ -1376,6 +1441,73 @@ class module_ : public object {
13761441
// For Python 2, reinterpret_borrow was correct.
13771442
return reinterpret_borrow<module_>(m);
13781443
}
1444+
1445+
/** \rst
1446+
Initialized a module def for use with multi-phase module initialization.
1447+
1448+
``def`` should point to a statically allocated module_def.
1449+
``slots`` must point to an initialized array of slots with space for at
1450+
least two additional slots to be populated based on the options.
1451+
\endrst */
1452+
template <typename... Options>
1453+
static object init_module_def(const char *name,
1454+
const char *doc,
1455+
module_def *def,
1456+
PyModuleDef_Slot *slots,
1457+
Options &&...options) {
1458+
int i = 0;
1459+
while (slots[i].slot != 0) {
1460+
++i;
1461+
}
1462+
1463+
bool nogil PYBIND11_MAYBE_UNUSED = detail::gil_not_used_option(options...);
1464+
void *interp PYBIND11_MAYBE_UNUSED = detail::multi_interp_option(options...);
1465+
1466+
#ifdef Py_mod_multiple_interpreters
1467+
if (nogil && interp == Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED) {
1468+
// if you support free threading and multi-interpreters,
1469+
// then you definitely also support per-interpreter GIL
1470+
// even if you don't know it.
1471+
interp = Py_MOD_PER_INTERPRETER_GIL_SUPPORTED;
1472+
}
1473+
slots[i].slot = Py_mod_multiple_interpreters;
1474+
slots[i].value = interp;
1475+
++i;
1476+
#endif
1477+
#ifdef Py_mod_gil
1478+
slots[i].slot = Py_mod_gil;
1479+
slots[i].value = Py_MOD_GIL_USED;
1480+
if (nogil) {
1481+
# ifdef Py_GIL_DISABLED
1482+
slots[i].value = Py_MOD_GIL_NOT_USED;
1483+
# endif
1484+
}
1485+
++i;
1486+
#endif
1487+
slots[i].slot = 0;
1488+
slots[i].value = nullptr;
1489+
1490+
// module_def is PyModuleDef
1491+
// Placement new (not an allocation).
1492+
def = new (def)
1493+
PyModuleDef{/* m_base */ PyModuleDef_HEAD_INIT,
1494+
/* m_name */ name,
1495+
/* m_doc */ options::show_user_defined_docstrings() ? doc : nullptr,
1496+
/* m_size */ 0,
1497+
/* m_methods */ nullptr,
1498+
/* m_slots */ slots,
1499+
/* m_traverse */ nullptr,
1500+
/* m_clear */ nullptr,
1501+
/* m_free */ nullptr};
1502+
auto *m = PyModuleDef_Init(def);
1503+
if (m == nullptr) {
1504+
if (PyErr_Occurred()) {
1505+
throw error_already_set();
1506+
}
1507+
pybind11_fail("Internal error in module_::init_module_def()");
1508+
}
1509+
return reinterpret_borrow<object>(m);
1510+
}
13791511
};
13801512

13811513
PYBIND11_NAMESPACE_BEGIN(detail)

0 commit comments

Comments
 (0)