Skip to content

Commit d2b8409

Browse files
Add _PyXI_Preserve().
1 parent 99b7a48 commit d2b8409

File tree

2 files changed

+137
-10
lines changed

2 files changed

+137
-10
lines changed

Include/internal/pycore_crossinterp.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,7 @@ PyAPI_FUNC(_PyXI_session *) _PyXI_NewSession(void);
351351
PyAPI_FUNC(void) _PyXI_FreeSession(_PyXI_session *);
352352

353353
typedef struct {
354+
PyObject *preserved;
354355
PyObject *excinfo;
355356
} _PyXI_session_result;
356357

@@ -363,6 +364,9 @@ PyAPI_FUNC(int) _PyXI_Exit(_PyXI_session *, _PyXI_session_result *);
363364

364365
PyAPI_FUNC(PyObject *) _PyXI_GetMainNamespace(_PyXI_session *);
365366

367+
PyAPI_FUNC(int) _PyXI_Preserve(_PyXI_session *, const char *, PyObject *);
368+
PyAPI_FUNC(PyObject *) _PyXI_GetPreserved(_PyXI_session_result *, const char *);
369+
366370

367371
/*************/
368372
/* other API */

Python/crossinterp.c

Lines changed: 133 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1830,7 +1830,8 @@ _sharednsitem_has_value(_PyXI_namespace_item *item, int64_t *p_interpid)
18301830
}
18311831

18321832
static int
1833-
_sharednsitem_set_value(_PyXI_namespace_item *item, PyObject *value)
1833+
_sharednsitem_set_value(_PyXI_namespace_item *item, PyObject *value,
1834+
xidata_fallback_t fallback)
18341835
{
18351836
assert(_sharednsitem_is_initialized(item));
18361837
assert(item->xidata == NULL);
@@ -1839,8 +1840,7 @@ _sharednsitem_set_value(_PyXI_namespace_item *item, PyObject *value)
18391840
return -1;
18401841
}
18411842
PyThreadState *tstate = PyThreadState_Get();
1842-
// XXX Use _PyObject_GetXIDataWithFallback()?
1843-
if (_PyObject_GetXIDataNoFallback(tstate, value, item->xidata) != 0) {
1843+
if (_PyObject_GetXIData(tstate, value, fallback, item->xidata) < 0) {
18441844
PyMem_RawFree(item->xidata);
18451845
item->xidata = NULL;
18461846
// The caller may want to propagate PyExc_NotShareableError
@@ -1872,7 +1872,8 @@ _sharednsitem_clear(_PyXI_namespace_item *item)
18721872
}
18731873

18741874
static int
1875-
_sharednsitem_copy_from_ns(struct _sharednsitem *item, PyObject *ns)
1875+
_sharednsitem_copy_from_ns(struct _sharednsitem *item, PyObject *ns,
1876+
xidata_fallback_t fallback)
18761877
{
18771878
assert(item->name != NULL);
18781879
assert(item->xidata == NULL);
@@ -1884,7 +1885,7 @@ _sharednsitem_copy_from_ns(struct _sharednsitem *item, PyObject *ns)
18841885
// When applied, this item will be set to the default (or fail).
18851886
return 0;
18861887
}
1887-
if (_sharednsitem_set_value(item, value) < 0) {
1888+
if (_sharednsitem_set_value(item, value, fallback) < 0) {
18881889
return -1;
18891890
}
18901891
return 0;
@@ -2138,14 +2139,15 @@ _create_sharedns(PyObject *names)
21382139
static void _propagate_not_shareable_error(_PyXI_session *);
21392140

21402141
static int
2141-
_fill_sharedns(_PyXI_namespace *ns, PyObject *nsobj, _PyXI_session *session)
2142+
_fill_sharedns(_PyXI_namespace *ns, PyObject *nsobj,
2143+
xidata_fallback_t fallback, _PyXI_session *session)
21422144
{
21432145
// All items are expected to be shareable.
21442146
assert(_sharedns_check_counts(ns));
21452147
assert(ns->numnames == ns->maxitems);
21462148
assert(ns->numvalues == 0);
21472149
for (Py_ssize_t i=0; i < ns->maxitems; i++) {
2148-
if (_sharednsitem_copy_from_ns(&ns->items[i], nsobj) < 0) {
2150+
if (_sharednsitem_copy_from_ns(&ns->items[i], nsobj, fallback) < 0) {
21492151
if (session != NULL) {
21502152
_propagate_not_shareable_error(session);
21512153
}
@@ -2254,10 +2256,14 @@ struct xi_session {
22542256
// beginning of the session as a convenience.
22552257
PyObject *main_ns;
22562258

2259+
// This is a dict of objects that will be available (via sharing)
2260+
// once the session exits. Do not access this directly; use
2261+
// _PyXI_Preserve() and _PyXI_GetPreserved() instead;
2262+
PyObject *_preserved;
2263+
22572264
struct xi_session_error error;
22582265
};
22592266

2260-
22612267
_PyXI_session *
22622268
_PyXI_NewSession(void)
22632269
{
@@ -2350,14 +2356,16 @@ _exit_session(_PyXI_session *session)
23502356
PyThreadState *tstate = session->init_tstate;
23512357
assert(tstate != NULL);
23522358
assert(PyThreadState_Get() == tstate);
2359+
assert(!_PyErr_Occurred(tstate));
23532360

23542361
// Release any of the entered interpreters resources.
23552362
Py_CLEAR(session->main_ns);
2363+
Py_CLEAR(session->_preserved);
23562364

23572365
// Ensure this thread no longer owns __main__.
23582366
if (session->running) {
23592367
_PyInterpreterState_SetNotRunningMain(tstate->interp);
2360-
assert(!PyErr_Occurred());
2368+
assert(!_PyErr_Occurred(tstate));
23612369
session->running = 0;
23622370
}
23632371

@@ -2415,7 +2423,9 @@ _PyXI_Enter(_PyXI_session *session,
24152423
if (sharedns == NULL) {
24162424
return -1;
24172425
}
2418-
if (_fill_sharedns(sharedns, nsupdates, NULL) < 0) {
2426+
// For now we limit it to shareable objects.
2427+
xidata_fallback_t fallback = _PyXIDATA_XIDATA_ONLY;
2428+
if (_fill_sharedns(sharedns, nsupdates, fallback, NULL) < 0) {
24192429
assert(session->error.info == NULL);
24202430
_destroy_sharedns(sharedns);
24212431
return -1;
@@ -2485,12 +2495,27 @@ _PyXI_Enter(_PyXI_session *session,
24852495
return -1;
24862496
}
24872497

2498+
static PyObject * _capture_preserved(_PyXI_session *);
2499+
24882500
int
24892501
_PyXI_Exit(_PyXI_session *session, _PyXI_session_result *result)
24902502
{
24912503
_capture_current_exception(session);
24922504
assert(!PyErr_Occurred());
24932505

2506+
if (result != NULL) {
2507+
result->preserved = _capture_preserved(session);
2508+
if (result->preserved == NULL && PyErr_Occurred()) {
2509+
if (session->error.info != NULL) {
2510+
PyErr_FormatUnraisable(
2511+
"Exception ignored while capturing preserved objects");
2512+
}
2513+
else {
2514+
_capture_current_exception(session);
2515+
}
2516+
}
2517+
}
2518+
24942519
struct xi_session_error err;
24952520
(void)_session_pop_error(session, &err);
24962521
_exit_session(session);
@@ -2634,6 +2659,104 @@ _PyXI_GetMainNamespace(_PyXI_session *session)
26342659
}
26352660

26362661

2662+
static PyObject *
2663+
_capture_preserved(_PyXI_session *session)
2664+
{
2665+
assert(_PyThreadState_GET() == session->init_tstate); // active session
2666+
if (session->init_tstate == session->prev_tstate) {
2667+
// didn't switch
2668+
return Py_XNewRef(session->_preserved);
2669+
}
2670+
2671+
_PyXI_namespace *preserved = NULL;
2672+
if (session->_preserved != NULL) {
2673+
Py_ssize_t len = PyDict_Size(session->_preserved);
2674+
if (len < 0) {
2675+
return NULL;
2676+
}
2677+
if (len > 0) {
2678+
preserved = _create_sharedns(session->_preserved);
2679+
if (preserved == NULL) {
2680+
return NULL;
2681+
}
2682+
if (_fill_sharedns(preserved, session->_preserved,
2683+
_PyXIDATA_FULL_FALLBACK, NULL) < 0)
2684+
{
2685+
assert(session->error.info == NULL);
2686+
_destroy_sharedns(preserved);
2687+
return NULL;
2688+
}
2689+
}
2690+
Py_CLEAR(session->_preserved);
2691+
}
2692+
if (preserved == NULL) {
2693+
return NULL;
2694+
}
2695+
2696+
// We need to switch back to the original interpreter long enough
2697+
// to restore the preservd objects.
2698+
(void)PyThreadState_Swap(session->prev_tstate);
2699+
2700+
PyObject *ns = PyDict_New();
2701+
if (ns == NULL) {
2702+
goto finally;
2703+
}
2704+
if (_apply_sharedns(preserved, ns, NULL) < 0) {
2705+
Py_CLEAR(ns);
2706+
goto finally;
2707+
}
2708+
2709+
finally:
2710+
// Swap back into the session.
2711+
(void)PyThreadState_Swap(session->init_tstate);
2712+
assert(preserved != NULL);
2713+
_destroy_sharedns(preserved);
2714+
return ns;
2715+
}
2716+
2717+
static void
2718+
set_exc_with_cause(PyObject *exctype, const char *msg)
2719+
{
2720+
PyObject *cause = PyErr_GetRaisedException();
2721+
PyErr_SetString(exctype, msg);
2722+
PyObject *exc = PyErr_GetRaisedException();
2723+
PyException_SetCause(exc, cause);
2724+
PyErr_SetRaisedException(exc);
2725+
}
2726+
2727+
int
2728+
_PyXI_Preserve(_PyXI_session *session, const char *name, PyObject *value)
2729+
{
2730+
if (session->init_tstate == NULL) {
2731+
PyErr_SetString(PyExc_RuntimeError, "session not active");
2732+
return -1;
2733+
}
2734+
if (session->_preserved == NULL) {
2735+
session->_preserved = PyDict_New();
2736+
if (session->_preserved == NULL) {
2737+
set_exc_with_cause(PyExc_RuntimeError,
2738+
"failed to initialize preserved objects");
2739+
return -1;
2740+
}
2741+
}
2742+
if (PyDict_SetItemString(session->_preserved, name, value) < 0) {
2743+
set_exc_with_cause(PyExc_RuntimeError, "failed to preserve object");
2744+
return -1;
2745+
}
2746+
return 0;
2747+
}
2748+
2749+
PyObject *
2750+
_PyXI_GetPreserved(_PyXI_session_result *result, const char *name)
2751+
{
2752+
PyObject *value = NULL;
2753+
if (result->preserved != NULL) {
2754+
(void)PyDict_GetItemStringRef(result->preserved, name, &value);
2755+
}
2756+
return value;
2757+
}
2758+
2759+
26372760
/*********************/
26382761
/* runtime lifecycle */
26392762
/*********************/

0 commit comments

Comments
 (0)