unpickler.memo = {-1: None}
unpickler.memo = {1: None}
+ def test_concurrent_pickler_dump(self):
+ f = io.BytesIO()
+ pickler = self.pickler_class(f)
+ class X:
+ def __reduce__(slf):
+ self.assertRaises(RuntimeError, pickler.dump, 42)
+ return list, ()
+ pickler.dump(X()) # should not crash
+ self.assertEqual(pickle.loads(f.getvalue()), [])
+
+ def test_concurrent_pickler_dump_and_init(self):
+ f = io.BytesIO()
+ pickler = self.pickler_class(f)
+ class X:
+ def __reduce__(slf):
+ self.assertRaises(RuntimeError, pickler.__init__, f)
+ return list, ()
+ pickler.dump([X()]) # should not fail
+ self.assertEqual(pickle.loads(f.getvalue()), [[]])
+
+ def test_concurrent_unpickler_load(self):
+ global reducer
+ def reducer():
+ self.assertRaises(RuntimeError, unpickler.load)
+ return 42
+ f = io.BytesIO(b'(c%b\nreducer\n(tRl.' % (__name__.encode(),))
+ unpickler = self.unpickler_class(f)
+ unpickled = unpickler.load() # should not fail
+ self.assertEqual(unpickled, [42])
+
+ def test_concurrent_unpickler_load_and_init(self):
+ global reducer
+ def reducer():
+ self.assertRaises(RuntimeError, unpickler.__init__, f)
+ return 42
+ f = io.BytesIO(b'(c%b\nreducer\n(tRl.' % (__name__.encode(),))
+ unpickler = self.unpickler_class(f)
+ unpickled = unpickler.load() # should not crash
+ self.assertEqual(unpickled, [42])
+
class CDispatchTableTests(AbstractDispatchTableTests, unittest.TestCase):
pickler_class = pickle.Pickler
def get_dispatch_table(self):
check_sizeof = support.check_sizeof
def test_pickler(self):
- basesize = support.calcobjsize('7P2n3i2n3i2P')
+ basesize = support.calcobjsize('7P2n3i2n4i2P')
p = _pickle.Pickler(io.BytesIO())
self.assertEqual(object.__sizeof__(p), basesize)
MT_size = struct.calcsize('3nP0n')
0) # Write buffer is cleared after every dump().
def test_unpickler(self):
- basesize = support.calcobjsize('2P2n3P 2P2n2i5P 2P3n8P2n2i')
+ basesize = support.calcobjsize('2P2n3P 2P2n2i5P 2P3n8P2n3i')
unpickler = _pickle.Unpickler
P = struct.calcsize('P') # Size of memo table entry.
n = struct.calcsize('n') # Size of mark table entry.
int fast_nesting;
int fix_imports; /* Indicate whether Pickler should fix
the name of globals for Python 2.x. */
+ int running; /* True when a method of Pickler is executing. */
PyObject *fast_memo;
PyObject *buffer_callback; /* Callback for out-of-band buffers, or NULL */
} PicklerObject;
int proto; /* Protocol of the pickle loaded. */
int fix_imports; /* Indicate whether Unpickler should fix
the name of globals pickled by Python 2.x. */
+ int running; /* True when a method of Unpickler is executing. */
+
} UnpicklerObject;
typedef struct {
#define PicklerMemoProxyObject_CAST(op) ((PicklerMemoProxyObject *)(op))
#define UnpicklerMemoProxyObject_CAST(op) ((UnpicklerMemoProxyObject *)(op))
+#define BEGIN_USING_PICKLER(SELF, RET) do { \
+ if ((SELF)->running) { \
+ PyErr_SetString(PyExc_RuntimeError, \
+ "Pickler object is already used"); \
+ return (RET); \
+ } \
+ (SELF)->running = 1; \
+ } while (0)
+
+#define END_USING_PICKLER(SELF) do { \
+ (SELF)->running = 0; \
+ } while (0)
+
+#define BEGIN_USING_UNPICKLER(SELF, RET) do { \
+ if ((SELF)->running) { \
+ PyErr_SetString(PyExc_RuntimeError, \
+ "Unpickler object is already used"); \
+ return (RET); \
+ } \
+ (SELF)->running = 1; \
+ } while (0)
+
+#define END_USING_UNPICKLER(SELF) do { \
+ (SELF)->running = 0; \
+ } while (0)
+
/* Forward declarations */
static int save(PickleState *state, PicklerObject *, PyObject *, int);
static int save_reduce(PickleState *, PicklerObject *, PyObject *, PyObject *);
self->fast = 0;
self->fast_nesting = 0;
self->fix_imports = 0;
+ self->running = 0;
self->fast_memo = NULL;
self->buffer_callback = NULL;
self->marks_size = 0;
self->proto = 0;
self->fix_imports = 0;
+ self->running = 0;
PyObject_GC_Track(self);
return self;
Py_TYPE(self)->tp_name);
return NULL;
}
+ BEGIN_USING_PICKLER(self, NULL);
- if (_Pickler_ClearBuffer(self) < 0)
- return NULL;
-
- if (dump(st, self, obj) < 0)
- return NULL;
-
- if (_Pickler_FlushToFile(self) < 0)
- return NULL;
-
+ if (_Pickler_ClearBuffer(self) < 0) {
+ goto error;
+ }
+ if (dump(st, self, obj) < 0) {
+ goto error;
+ }
+ if (_Pickler_FlushToFile(self) < 0) {
+ goto error;
+ }
+ END_USING_PICKLER(self);
Py_RETURN_NONE;
+
+error:
+ END_USING_PICKLER(self);
+ return NULL;
}
/*[clinic input]
PyObject *buffer_callback)
/*[clinic end generated code: output=0abedc50590d259b input=cddc50f66b770002]*/
{
+ BEGIN_USING_PICKLER(self, -1);
/* In case of multiple __init__() calls, clear previous content. */
if (self->write != NULL)
(void)Pickler_clear((PyObject *)self);
- if (_Pickler_SetProtocol(self, protocol, fix_imports) < 0)
- return -1;
-
- if (_Pickler_SetOutputStream(self, file) < 0)
- return -1;
-
- if (_Pickler_SetBufferCallback(self, buffer_callback) < 0)
- return -1;
-
+ if (_Pickler_SetProtocol(self, protocol, fix_imports) < 0) {
+ goto error;
+ }
+ if (_Pickler_SetOutputStream(self, file) < 0) {
+ goto error;
+ }
+ if (_Pickler_SetBufferCallback(self, buffer_callback) < 0) {
+ goto error;
+ }
/* memo and output_buffer may have already been created in _Pickler_New */
if (self->memo == NULL) {
self->memo = PyMemoTable_New();
- if (self->memo == NULL)
- return -1;
+ if (self->memo == NULL) {
+ goto error;
+ }
}
self->output_len = 0;
if (self->output_buffer == NULL) {
self->max_output_len = WRITE_BUF_SIZE;
self->output_buffer = PyBytes_FromStringAndSize(NULL,
self->max_output_len);
- if (self->output_buffer == NULL)
- return -1;
+ if (self->output_buffer == NULL) {
+ goto error;
+ }
}
self->fast = 0;
self->fast_nesting = 0;
self->fast_memo = NULL;
- if (self->dispatch_table != NULL) {
- return 0;
- }
- if (PyObject_GetOptionalAttr((PyObject *)self, &_Py_ID(dispatch_table),
- &self->dispatch_table) < 0) {
- return -1;
+ if (self->dispatch_table == NULL) {
+ if (PyObject_GetOptionalAttr((PyObject *)self, &_Py_ID(dispatch_table),
+ &self->dispatch_table) < 0) {
+ goto error;
+ }
}
+ END_USING_PICKLER(self);
return 0;
+
+error:
+ END_USING_PICKLER(self);
+ return -1;
}
_pickle_Unpickler_load_impl(UnpicklerObject *self, PyTypeObject *cls)
/*[clinic end generated code: output=cc88168f608e3007 input=f5d2f87e61d5f07f]*/
{
- UnpicklerObject *unpickler = (UnpicklerObject*)self;
-
PickleState *st = _Pickle_GetStateByClass(cls);
/* Check whether the Unpickler was initialized correctly. This prevents
segfaulting if a subclass overridden __init__ with a function that does
not call Unpickler.__init__(). Here, we simply ensure that self->read
is not NULL. */
- if (unpickler->read == NULL) {
+ if (self->read == NULL) {
PyErr_Format(st->UnpicklingError,
"Unpickler.__init__() was not called by %s.__init__()",
- Py_TYPE(unpickler)->tp_name);
+ Py_TYPE(self)->tp_name);
return NULL;
}
-
- return load(st, unpickler);
+ BEGIN_USING_UNPICKLER(self, NULL);
+ PyObject *res = load(st, self);
+ END_USING_UNPICKLER(self);
+ return res;
}
/* The name of find_class() is misleading. In newer pickle protocols, this
const char *errors, PyObject *buffers)
/*[clinic end generated code: output=09f0192649ea3f85 input=ca4c1faea9553121]*/
{
+ BEGIN_USING_UNPICKLER(self, -1);
/* In case of multiple __init__() calls, clear previous content. */
if (self->read != NULL)
(void)Unpickler_clear((PyObject *)self);
- if (_Unpickler_SetInputStream(self, file) < 0)
- return -1;
-
- if (_Unpickler_SetInputEncoding(self, encoding, errors) < 0)
- return -1;
-
- if (_Unpickler_SetBuffers(self, buffers) < 0)
- return -1;
-
+ if (_Unpickler_SetInputStream(self, file) < 0) {
+ goto error;
+ }
+ if (_Unpickler_SetInputEncoding(self, encoding, errors) < 0) {
+ goto error;
+ }
+ if (_Unpickler_SetBuffers(self, buffers) < 0) {
+ goto error;
+ }
self->fix_imports = fix_imports;
PyTypeObject *tp = Py_TYPE(self);
PickleState *state = _Pickle_FindStateByType(tp);
self->stack = (Pdata *)Pdata_New(state);
- if (self->stack == NULL)
- return -1;
-
+ if (self->stack == NULL) {
+ goto error;
+ }
self->memo_size = 32;
self->memo = _Unpickler_NewMemo(self->memo_size);
- if (self->memo == NULL)
- return -1;
-
+ if (self->memo == NULL) {
+ goto error;
+ }
self->proto = 0;
+ END_USING_UNPICKLER(self);
return 0;
+
+error:
+ END_USING_UNPICKLER(self);
+ return -1;
}