/*****************************************************************************
Copyright (c) 2001, 2002 Zope Foundation and Contributors.
All Rights Reserved.
This software is subject to the provisions of the Zope Public License,
Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
FOR A PARTICULAR PURPOSE
****************************************************************************/
static char cPersistence_doc_string[] =
"Defines Persistent mixin class for persistent objects.\n"
"\n"
"$Id$\n";
#define PY_SSIZE_T_CLEAN
#include "cPersistence.h"
#include "structmember.h"
struct ccobject_head_struct
{
CACHE_HEAD
};
/*
The compiler on Windows used for Python 2.7 doesn't include
stdint.h.
*/
#if !defined(PY3K) && defined(_WIN32)
typedef unsigned long long uint64_t;
# define PRIx64 "llx"
#else
# include <stdint.h>
# include <inttypes.h>
#endif
/* These objects are initialized when the module is loaded */
static PyObject *py_simple_new;
/* Strings initialized by init_strings() below. */
static PyObject *py_keys, *py_setstate, *py___dict__, *py_timeTime;
static PyObject *py__p_changed, *py__p_deactivate;
static PyObject *py___getattr__, *py___setattr__, *py___delattr__;
static PyObject *py___slotnames__, *copy_reg_slotnames, *__newobj__;
static PyObject *py___getnewargs__, *py___getstate__;
static PyObject *py_unsaved, *py_ghost, *py_saved, *py_changed, *py_sticky;
static int
init_strings(void)
{
#define INIT_STRING(S) \
if (!(py_ ## S = INTERN(#S))) \
return -1;
INIT_STRING(keys);
INIT_STRING(setstate);
INIT_STRING(timeTime);
INIT_STRING(__dict__);
INIT_STRING(_p_changed);
INIT_STRING(_p_deactivate);
INIT_STRING(__getattr__);
INIT_STRING(__setattr__);
INIT_STRING(__delattr__);
INIT_STRING(__slotnames__);
INIT_STRING(__getnewargs__);
INIT_STRING(__getstate__);
INIT_STRING(unsaved);
INIT_STRING(ghost);
INIT_STRING(saved);
INIT_STRING(changed);
INIT_STRING(sticky);
#undef INIT_STRING
return 0;
}
#ifdef Py_DEBUG
static void
fatal_1350(cPersistentObject *self, const char *caller, const char *detail)
{
char buf[1000];
PyOS_snprintf(buf, sizeof(buf),
"cPersistence.c %s(): object at %p with type %.200s\n"
"%s.\n"
"The only known cause is multiple threads trying to ghost and\n"
"unghost the object simultaneously.\n"
"That's not legal, but ZODB can't stop it.\n"
"See Collector #1350.\n",
caller, self, Py_TYPE(self)->tp_name, detail);
Py_FatalError(buf);
}
#endif
static void ghostify(cPersistentObject*);
static PyObject * pickle_slotnames(PyTypeObject *cls);
static PyObject * convert_name(PyObject *name);
/* Load the state of the object, unghostifying it. Upon success, return 1.
* If an error occurred, re-ghostify the object and return -1.
*/
static int
unghostify(cPersistentObject *self)
{
if (self->state < 0 && self->jar)
{
PyObject *r;
/* Is it ever possible to not have a cache? */
if (self->cache)
{
/* Create a node in the ring for this unghostified object. */
self->cache->non_ghost_count++;
self->cache->total_estimated_size +=
_estimated_size_in_bytes(self->estimated_size);
ring_add(&self->cache->ring_home, &self->ring);
Py_INCREF(self);
}
/* set state to CHANGED while setstate() call is in progress
to prevent a recursive call to _PyPersist_Load().
*/
self->state = cPersistent_CHANGED_STATE;
/* Call the object's __setstate__() */
r = PyObject_CallMethod(self->jar, "setstate", "O", (PyObject *)self);
if (r == NULL)
{
ghostify(self);
return -1;
}
self->state = cPersistent_UPTODATE_STATE;
Py_DECREF(r);
if (self->cache && self->ring.r_next == NULL)
{
#ifdef Py_DEBUG
fatal_1350(self, "unghostify",
"is not in the cache despite that we just "
"unghostified it");
#else
PyErr_Format(PyExc_SystemError, "object at %p with type "
"%.200s not in the cache despite that we just "
"unghostified it", self, Py_TYPE(self)->tp_name);
return -1;
#endif
}
}
return 1;
}
/****************************************************************************/
static PyTypeObject Pertype;
static void
accessed(cPersistentObject *self)
{
/* Do nothing unless the object is in a cache and not a ghost. */
if (self->cache && self->state >= 0 && self->ring.r_next)
ring_move_to_head(&self->cache->ring_home, &self->ring);
}
static void
ghostify(cPersistentObject *self)
{
PyObject **dictptr, *slotnames;
PyObject *errtype, *errvalue, *errtb;
/* are we already a ghost? */
if (self->state == cPersistent_GHOST_STATE)
return;
/* Is it ever possible to not have a cache? */
if (self->cache == NULL)
{
self->state = cPersistent_GHOST_STATE;
return;
}
if (self->ring.r_next == NULL)
{
/* There's no way to raise an error in this routine. */
#ifdef Py_DEBUG
fatal_1350(self, "ghostify", "claims to be in a cache but isn't");
#else
return;
#endif
}
/* If we're ghostifying an object, we better have some non-ghosts. */
assert(self->cache->non_ghost_count > 0);
self->cache->non_ghost_count--;
self->cache->total_estimated_size -=
_estimated_size_in_bytes(self->estimated_size);
ring_del(&self->ring);
self->state = cPersistent_GHOST_STATE;
/* clear __dict__ */
dictptr = _PyObject_GetDictPtr((PyObject *)self);
if (dictptr && *dictptr)
{
Py_DECREF(*dictptr);
*dictptr = NULL;
}
/* clear all slots besides _p_*
* ( for backward-compatibility reason we do this only if class does not
* override __new__ ) */
if (Py_TYPE(self)->tp_new == Pertype.tp_new)
{
/* later we might clear an AttributeError but
* if we have a pending exception that still needs to be
* raised so that we don't generate a SystemError.
*/
PyErr_Fetch(&errtype, &errvalue, &errtb);
slotnames = pickle_slotnames(Py_TYPE(self));
if (slotnames && slotnames != Py_None)
{
int i;
for (i = 0; i < PyList_GET_SIZE(slotnames); i++)
{
PyObject *name;
char *cname;
int is_special;
name = PyList_GET_ITEM(slotnames, i);
#ifdef PY3K
if (PyUnicode_Check(name))
{
PyObject *converted = convert_name(name);
cname = PyBytes_AS_STRING(converted);
#else
if (PyBytes_Check(name))
{
cname = PyBytes_AS_STRING(name);
#endif
is_special = !strncmp(cname, "_p_", 3);
#ifdef PY3K
Py_DECREF(converted);
#endif
if (is_special) /* skip persistent */
{
continue;
}
}
/* NOTE: this skips our delattr hook */
if (PyObject_GenericSetAttr((PyObject *)self, name, NULL) < 0)
/* delattr of non-set slot will raise AttributeError - we
* simply ignore. */
PyErr_Clear();
}
}
Py_XDECREF(slotnames);
PyErr_Restore(errtype, errvalue, errtb);
}
/* We remove the reference to the just ghosted object that the ring
* holds. Note that the dictionary of oids->objects has an uncounted
* reference, so if the ring's reference was the only one, this frees
* the ghost object. Note further that the object's dealloc knows to
* inform the dictionary that it is going away.
*/
Py_DECREF(self);
}
static int
changed(cPersistentObject *self)
{
if ((self->state == cPersistent_UPTODATE_STATE ||
self->state == cPersistent_STICKY_STATE)
&& self->jar)
{
PyObject *meth, *arg, *result;
static PyObject *s_register;
if (s_register == NULL)
s_register = INTERN("register");
meth = PyObject_GetAttr((PyObject *)self->jar, s_register);
if (meth == NULL)
return -1;
arg = PyTuple_New(1);
if (arg == NULL)
{
Py_DECREF(meth);
return -1;
}
Py_INCREF(self);
PyTuple_SET_ITEM(arg, 0, (PyObject *)self);
result = PyObject_CallObject(meth, arg);
Py_DECREF(arg);
Py_DECREF(meth);
if (result == NULL)
return -1;
Py_DECREF(result);
self->state = cPersistent_CHANGED_STATE;
}
return 0;
}
static int
readCurrent(cPersistentObject *self)
{
if ((self->state == cPersistent_UPTODATE_STATE ||
self->state == cPersistent_STICKY_STATE)
&& self->jar && self->oid)
{
static PyObject *s_readCurrent=NULL;
PyObject *r;
if (s_readCurrent == NULL)
s_readCurrent = INTERN("readCurrent");
r = PyObject_CallMethodObjArgs(self->jar, s_readCurrent, self, NULL);
if (r == NULL)
return -1;
Py_DECREF(r);
}
return 0;
}
static PyObject *
Per__p_deactivate(cPersistentObject *self)
{
if (self->state == cPersistent_UPTODATE_STATE && self->jar)
{
PyObject **dictptr = _PyObject_GetDictPtr((PyObject *)self);
if (dictptr && *dictptr)
{
Py_DECREF(*dictptr);
*dictptr = NULL;
}
/* Note that we need to set to ghost state unless we are
called directly. Methods that override this need to
do the same! */
ghostify(self);
if (PyErr_Occurred())
return NULL;
Loading ...