implement multi-phase init - #1024
Conversation
There was a problem hiding this comment.
since we're changing how the subinterpreter support is flagged, i wanted some tests to show it's still working
d65661e to
16fe68d
Compare
| return 0; | ||
| } | ||
|
|
||
| static struct PyModuleDef msgspecmodule = { |
There was a problem hiding this comment.
This was simply moved down to make the flow easier, as it references _core_exec
74741fb to
06be1a7
Compare
Siyet
left a comment
There was a problem hiding this comment.
Leaving a note for the history / backlink: this obsoletes the PyState_AddModule workaround jcrist added in #561, where the body said "in the long run we should move to using multiphase module init which should avoid this problem entirely." Good to have the thread connected.
please do ping if/when that happens 🙏🏼 |
|
@provinzkraut Happy to push these small fixups to your branch myself if you're short on time, just give me a thumbs up. Keen to get this in so the subinterp work in #1026 can start moving. |
|
@Siyet feel free to push. Otherwise I could get it done some time later this week |
4a5d5a1 to
d396f44
Compare
| state is populated in _core_exec and cleared and freed by m_clear and m_free | ||
| respectively. | ||
| */ | ||
| static MsgspecState *_core_state = NULL; |
There was a problem hiding this comment.
I would advokate against this pattern :)
It would be required to refactor it anyway for future FT / SI builds.
Maybe we can instead use something like
static MsgspecState *
_get_current_module_state(void)
{
PyObject *mod = _get_current_module();
if (mod == NULL) {
mod = PyImport_ImportModule("msgspec._core");
if (mod == NULL) {
return NULL;
}
}
MsgspecState *state = get_module_state(mod);
Py_DECREF(mod);
return state;
}?
There was a problem hiding this comment.
Or PyType_GetModuleByDef / PyType_GetModule :)
There was a problem hiding this comment.
I've tried using PyType_GetModule at first, but there were some issues. Can't remember what it was though, it's been a while. Let me re-evaluate that again
There was a problem hiding this comment.
PyType_GetModuleByDef is only available on 3.11+. I'll see if PyType_GetModule can be made to work
There was a problem hiding this comment.
Best would probably to just pipe the mod through to every caller that needs it. A bit bigger API surface, but less mucking around with global state and such. Currently checking if that's viable
There was a problem hiding this comment.
@sobolevn So I played around with it a bit. I tried to reduce the calls to the global state by piping it through wherever possible, which does work. However, it reveals quite a lot of issues, specifically in places where that piping through is currently just not possible where we have static types, which means if we don't want to store process-global state anywhere, things get really complicated (or at least I haven't been able to find a good solution).
What we could do is reverse the implementation order here:
- Make the move to heap types
- Remove global state
- Multi-phase init
There was a problem hiding this comment.
The solution I could find to address everything within this PR would be to use change msgspec_get_global_state to use PyInterpreterState_GetDict, but its docs say
This is not a replacement for PyModule_GetState(), which extensions should use to store interpreter-specific state information.
Soo.. I think unless we're fine with having this global state around until we migrate to heap types, I don't see a way how we can get this PR merged. Which is fine, I don't mind doing it the other way around
There was a problem hiding this comment.
Oh, we don't have heap types yet 🤦♂️
You are right, this is the required first step for new APIs :)
There was a problem hiding this comment.
You are right, this is the required first step for new APIs :)
Alright, let's try working towards this then. For that, we really ought to have some continuous benchmarking set up though. I'll take a look at https://codspeed.io
There was a problem hiding this comment.
I'm not sure if you can see but I accepted the installation request. It looks like they recommend the OIDC method of authenticating in GitHub Actions and here is the badge:
[](https://app.codspeed.io/msgspec/msgspec)
| .m_methods = msgspec_methods, | ||
| .m_traverse = msgspec_traverse, | ||
| .m_clear = msgspec_clear, | ||
| .m_free =(freefunc)msgspec_free, |
There was a problem hiding this comment.
| .m_free =(freefunc)msgspec_free, | |
| .m_free = (freefunc)msgspec_free, |
style nit
| #ifdef Py_GIL_DISABLED | ||
| PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED); | ||
| #if !PY313_PLUS | ||
| PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED); |
There was a problem hiding this comment.
This is not needed, we set {Py_mod_gil, Py_MOD_GIL_NOT_USED}, module slot
| if (st->astimezone == NULL) return -1; | ||
|
|
||
| /* uuid module imports */ | ||
| temp_module = PyImport_ImportModule("uuid"); |
There was a problem hiding this comment.
There are missing Py_DECREF(temp_module) calls in many places.
There was a problem hiding this comment.
Yeah, I think this was discussed in a previous PR. I think we should clean those up. Should be a separate PR though IMO
|
Reviewed and tested this (built on 3.12, full unit suite green, the
Built and suite-verified on 3.12. |
|
Pushed the small review nits we discussed:
Left out of scope as agreed: the missing |
|
Currently blocked until we have moved to heap types. See #1024 (comment) I'll revert this to a draft |
Closes #563.
Implement multi-phase init.
Relatively straightforward, the only slight hiccough is
msgspec_get_global_state, which relied onPyState_FindModule/PyState_AddModule, which in turn don't work (well) with multi-phase init. I've replaced them with a globalstatic MsgspecState, set during module init; This is not safe for sub-interpreters, but handling it properly would be a large refactor that I think is best kept for separate PRs.Road to subinterpreter support
This is the first step for msgspec to support subinterpreters, but it's still a long way to go. I'll open a separate tracking issue for that once this gets merged, but it'll likely involve some larger refactors, especially around moving things to heap types where absolutely necessary, and ensuring this does not impact performance negatively.