Commit 75ee5c7a authored by Jeremy Hylton's avatar Jeremy Hylton

Move cc_oid_unreferenced() into the C API and out of Python.

    The rationale is that this is an internal detail of the cache
    implementation called by Per_dealloc().  It's nice to make it
    fast, and it's good to prevent it from being called with anything
    other than an about-to-be-freed object.

    As a result of it being in the C API, omit tests of refcounts.  We
    would have been in Per_dealloc() otherwise.

    Initialize the percachdel slot of cPersistenceAPIstruct to NULL in
    cPersistence.  In cPickleCache, import the CObject and fill in the
    struct.

Also, reformat many of the comments so that all the text starts to the
right of the /*.

Fiddle the text of the placeholder comment.  I think I understand it
now :-).

Add an XXX comment about the "old" cache API e.g. fullsweep and
reallyfullsweep.

Fix cc_get().  object_from_oid() doesn't ever set an exception.

Remove a Py_FatalError() check from the end of a module init
function.  We don't do that anymore.
parent 51e177d7
...@@ -14,11 +14,10 @@ ...@@ -14,11 +14,10 @@
static char cPersistence_doc_string[] = static char cPersistence_doc_string[] =
"Defines Persistent mixin class for persistent objects.\n" "Defines Persistent mixin class for persistent objects.\n"
"\n" "\n"
"$Id: cPersistence.c,v 1.57 2002/04/02 22:46:48 jeremy Exp $\n"; "$Id: cPersistence.c,v 1.58 2002/04/05 01:12:48 jeremy Exp $\n";
#include "cPersistence.h" #include "cPersistence.h"
/* XXX What is this structure used for? */
struct ccobject_head_struct { struct ccobject_head_struct {
CACHE_HEAD CACHE_HEAD
}; };
...@@ -202,15 +201,11 @@ deallocated(cPersistentObject *self) ...@@ -202,15 +201,11 @@ deallocated(cPersistentObject *self)
if (self->state >= 0) if (self->state >= 0)
ghostify(self); ghostify(self);
if (self->cache) { if (self->cache) {
PyObject *v; /* XXX This function shouldn't be able to fail? If not, maybe
it shouldn't set an exception either.
/* XXX should just add this to the C API struct */ */
v = PyObject_CallMethod((PyObject *)self->cache, if (cPersistenceCAPI->percachedel(self->cache, self->oid) < 0)
"_oid_unreferenced", "O", self->oid); PyErr_Clear(); /* I don't think this should ever happen */
if (v == NULL)
PyErr_Clear(); /* I dont think this should ever happen */
else
Py_DECREF(v);
} }
Py_XDECREF(self->jar); Py_XDECREF(self->jar);
Py_XDECREF(self->oid); Py_XDECREF(self->oid);
...@@ -256,19 +251,22 @@ changed(cPersistentObject *self) ...@@ -256,19 +251,22 @@ changed(cPersistentObject *self)
static PyObject * static PyObject *
Per___changed__(cPersistentObject *self, PyObject *args) Per___changed__(cPersistentObject *self, PyObject *args)
{ {
PyObject *v=0; PyObject *v = NULL;
if (args && ! PyArg_ParseTuple(args, "|O",&v)) return NULL; if (args && !PyArg_ParseTuple(args, "|O:__changed__", &v))
if (! v) return PyObject_GetAttr(OBJECT(self), py__p_changed); return NULL;
if (!v)
return PyObject_GetAttr(OBJECT(self), py__p_changed);
if (PyObject_IsTrue(v)) if (PyObject_IsTrue(v)) {
{ if (changed(self) < 0)
if (changed(self) < 0) return NULL; return NULL;
} }
else if (self->state >= 0) self->state=cPersistent_UPTODATE_STATE; else if (self->state >= 0)
self->state = cPersistent_UPTODATE_STATE;
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
} }
static PyObject * static PyObject *
...@@ -295,7 +293,7 @@ Per__p_deactivate(cPersistentObject *self, PyObject *args) ...@@ -295,7 +293,7 @@ Per__p_deactivate(cPersistentObject *self, PyObject *args)
} }
/* need to delay releasing the last reference on instance attributes /* need to delay releasing the last reference on instance attributes
until after we have finished accounting for losing our state */ until after we have finished accounting for losing our state */
if (dict2) if (dict2)
{ {
PyDict_Clear(dict2); PyDict_Clear(dict2);
...@@ -807,6 +805,7 @@ truecPersistenceCAPI = { ...@@ -807,6 +805,7 @@ truecPersistenceCAPI = {
deallocated, deallocated,
(intfunctionwithpythonarg)Per_setstate, (intfunctionwithpythonarg)Per_setstate,
(pergetattr)Per_getattr, (pergetattr)Per_getattr,
NULL
}; };
void void
...@@ -826,7 +825,8 @@ initcPersistence(void) ...@@ -826,7 +825,8 @@ initcPersistence(void)
Py_DECREF(m); Py_DECREF(m);
Py_DECREF(s); Py_DECREF(s);
if (init_strings() < 0) return; if (init_strings() < 0)
return;
m = Py_InitModule4("cPersistence", cP_methods, cPersistence_doc_string, m = Py_InitModule4("cPersistence", cP_methods, cPersistence_doc_string,
(PyObject*)NULL, PYTHON_API_VERSION); (PyObject*)NULL, PYTHON_API_VERSION);
...@@ -837,12 +837,8 @@ initcPersistence(void) ...@@ -837,12 +837,8 @@ initcPersistence(void)
PyExtensionClass_Export(d, "Persistent", Pertype); PyExtensionClass_Export(d, "Persistent", Pertype);
PyExtensionClass_Export(d, "Overridable", Overridable); PyExtensionClass_Export(d, "Overridable", Overridable);
cPersistenceCAPI=&truecPersistenceCAPI; cPersistenceCAPI = &truecPersistenceCAPI;
s = PyCObject_FromVoidPtr(cPersistenceCAPI,NULL); s = PyCObject_FromVoidPtr(cPersistenceCAPI, NULL);
PyDict_SetItemString(d, "CAPI", s); PyDict_SetItemString(d, "CAPI", s);
Py_XDECREF(s); Py_XDECREF(s);
/* Check for errors */
if (PyErr_Occurred())
Py_FatalError("can't initialize module cPersistence");
} }
...@@ -18,6 +18,12 @@ ...@@ -18,6 +18,12 @@
#include "ExtensionClass.h" #include "ExtensionClass.h"
#include <time.h> #include <time.h>
typedef struct CPersistentRing_struct
{
struct CPersistentRing_struct *prev;
struct CPersistentRing_struct *next;
} CPersistentRing;
#define CACHE_HEAD \ #define CACHE_HEAD \
PyObject_HEAD \ PyObject_HEAD \
CPersistentRing ring_home; \ CPersistentRing ring_home; \
...@@ -42,30 +48,26 @@ typedef struct ccobject_head_struct PerCache; ...@@ -42,30 +48,26 @@ typedef struct ccobject_head_struct PerCache;
#define cPersistent_CHANGED_STATE 1 #define cPersistent_CHANGED_STATE 1
#define cPersistent_STICKY_STATE 2 #define cPersistent_STICKY_STATE 2
typedef struct CPersistentRing_struct
{
struct CPersistentRing_struct *prev;
struct CPersistentRing_struct *next;
} CPersistentRing;
typedef struct { typedef struct {
cPersistent_HEAD cPersistent_HEAD
} cPersistentObject; } cPersistentObject;
typedef int (*persetattr)(PyObject *, PyObject*, PyObject *, setattrofunc); typedef int (*persetattr)(PyObject *, PyObject*, PyObject *, setattrofunc);
typedef PyObject *(*pergetattr)(PyObject *, PyObject*, char *, getattrofunc); typedef PyObject *(*pergetattr)(PyObject *, PyObject*, char *, getattrofunc);
typedef int (*percachedelfunc)(PerCache *, PyObject *);
typedef struct { typedef struct {
PyMethodChain *methods; PyMethodChain *methods;
getattrofunc getattro; getattrofunc getattro;
setattrofunc setattro; setattrofunc setattro;
int (*changed)(cPersistentObject*); int (*changed)(cPersistentObject*);
void (*accessed)(cPersistentObject*); void (*accessed)(cPersistentObject*);
void (*ghostify)(cPersistentObject*); void (*ghostify)(cPersistentObject*);
void (*deallocated)(cPersistentObject*); void (*deallocated)(cPersistentObject*);
int (*setstate)(PyObject*); int (*setstate)(PyObject*);
pergetattr pergetattro; pergetattr pergetattro;
persetattr persetattro; persetattr persetattro;
percachedelfunc percachedel;
} cPersistenceCAPIstruct; } cPersistenceCAPIstruct;
#ifndef DONT_USE_CPERSISTENCECAPI #ifndef DONT_USE_CPERSISTENCECAPI
......
...@@ -88,7 +88,7 @@ process must skip such objects, rather than deactivating them. ...@@ -88,7 +88,7 @@ process must skip such objects, rather than deactivating them.
static char cPickleCache_doc_string[] = static char cPickleCache_doc_string[] =
"Defines the PickleCache used by ZODB Connection objects.\n" "Defines the PickleCache used by ZODB Connection objects.\n"
"\n" "\n"
"$Id: cPickleCache.c,v 1.54 2002/04/04 22:02:32 bwarsaw Exp $\n"; "$Id: cPickleCache.c,v 1.55 2002/04/05 01:12:48 jeremy Exp $\n";
#define ASSIGN(V,E) {PyObject *__e; __e=(E); Py_XDECREF(V); (V)=__e;} #define ASSIGN(V,E) {PyObject *__e; __e=(E); Py_XDECREF(V); (V)=__e;}
#define UNLESS(E) if(!(E)) #define UNLESS(E) if(!(E))
...@@ -118,9 +118,9 @@ static PyObject *py__p_oid, *py_reload, *py__p_jar, *py__p_changed; ...@@ -118,9 +118,9 @@ static PyObject *py__p_oid, *py_reload, *py__p_jar, *py__p_changed;
#define ENGINE_NOISE(A) ((void)A) #define ENGINE_NOISE(A) ((void)A)
#endif #endif
/* This object is the pickle cache. The CACHE_HEAD macro guarantees that /* This object is the pickle cache. The CACHE_HEAD macro guarantees
layout of this struct is the same as the start of ccobject_head in that layout of this struct is the same as the start of
cPersistence.c */ ccobject_head in cPersistence.c */
typedef struct { typedef struct {
CACHE_HEAD CACHE_HEAD
int klass_count; /* count of persistent classes */ int klass_count; /* count of persistent classes */
...@@ -156,15 +156,15 @@ static int cc_ass_sub(ccobject *self, PyObject *key, PyObject *v); ...@@ -156,15 +156,15 @@ static int cc_ass_sub(ccobject *self, PyObject *key, PyObject *v);
/* ---------------------------------------------------------------- */ /* ---------------------------------------------------------------- */
static PyObject *object_from_oid(ccobject *self, PyObject *key)
/* somewhat of a replacement for PyDict_GetItem(self->data.... /* somewhat of a replacement for PyDict_GetItem(self->data....
however this returns a *new* reference */ however this returns a *new* reference */
static PyObject *
object_from_oid(ccobject *self, PyObject *key)
{ {
PyObject *v = PyDict_GetItem(self->data, key); PyObject *v = PyDict_GetItem(self->data, key);
if(!v) return NULL; if (!v)
return NULL;
Py_INCREF(v); Py_INCREF(v);
return v; return v;
} }
...@@ -263,10 +263,11 @@ scan_gc_items(ccobject *self,int target) ...@@ -263,10 +263,11 @@ scan_gc_items(ccobject *self,int target)
if (here == &self->ring_home) if (here == &self->ring_home)
return 0; return 0;
/* At this point we know that the ring only contains nodes from /* At this point we know that the ring only contains nodes
persistent objects, plus our own home node. We know this because from persistent objects, plus our own home node. We know
the ring lock is held. We can safely assume the current ring this because the ring lock is held. We can safely assume
node is a persistent object now we know it is not the home */ the current ring node is a persistent object now we know it
is not the home */
object = object_from_ring(self, here, "scan_gc_items"); object = object_from_ring(self, here, "scan_gc_items");
if (!object) if (!object)
return -1; return -1;
...@@ -277,12 +278,15 @@ scan_gc_items(ccobject *self,int target) ...@@ -277,12 +278,15 @@ scan_gc_items(ccobject *self,int target)
else if (object->state == cPersistent_UPTODATE_STATE) { else if (object->state == cPersistent_UPTODATE_STATE) {
/* deactivate it. This is the main memory saver. */ /* deactivate it. This is the main memory saver. */
/* Add a placeholder; a dummy node in the ring. We need to /* Add a placeholder; a dummy node in the ring. We need
do this to mark our position in the ring. All the other nodes to do this to mark our position in the ring. It is
come from persistent objects, and they are all liable possible that the PyObject_SetAttr() call below will
to be deallocated before "obj._p_changed = None" returns invoke an __setattr__() hook in Python. If it does,
to this function. This operation is only safe when the another thread might run; if that thread accesses a
ring lock is held (and it is) */ persistent object and moves it to the head of the ring,
it might cause the gc scan to start working from the
head of the list.
*/
placeholder.next = here->next; placeholder.next = here->next;
placeholder.prev = here; placeholder.prev = here;
...@@ -315,7 +319,7 @@ scan_gc_items(ccobject *self,int target) ...@@ -315,7 +319,7 @@ scan_gc_items(ccobject *self,int target)
static PyObject * static PyObject *
lockgc(ccobject *self, int target_size) lockgc(ccobject *self, int target_size)
{ {
/* We think this is thread-safe because of the GIL, and there's nothing /* This is thread-safe because of the GIL, and there's nothing
* in between checking the ring_lock and acquiring it that calls back * in between checking the ring_lock and acquiring it that calls back
* into Python. * into Python.
*/ */
...@@ -361,16 +365,23 @@ cc_incrgc(ccobject *self, PyObject *args) ...@@ -361,16 +365,23 @@ cc_incrgc(ccobject *self, PyObject *args)
if (!PyArg_ParseTuple(args, "|i:incrgc", &n)) if (!PyArg_ParseTuple(args, "|i:incrgc", &n))
return NULL; return NULL;
return lockgc(self,target_size); return lockgc(self, target_size);
} }
/* XXX Does it make sense for full_sweep() and reallyfull_sweep() to
empty the cache completely? I agree that it would if dt is 0, but
don't think it should for other times. Perhaps it should just call
incrgc() if dt > 2; the new cache may be efficient enough that
incrgc() would suffice.
*/
static PyObject * static PyObject *
cc_full_sweep(ccobject *self, PyObject *args) cc_full_sweep(ccobject *self, PyObject *args)
{ {
int dt = 0; int dt = 0;
if (!PyArg_ParseTuple(args, "|i:full_sweep", &dt)) if (!PyArg_ParseTuple(args, "|i:full_sweep", &dt))
return NULL; return NULL;
return lockgc(self,0); return lockgc(self, 0);
} }
static PyObject * static PyObject *
...@@ -379,7 +390,7 @@ cc_reallyfull_sweep(ccobject *self, PyObject *args) ...@@ -379,7 +390,7 @@ cc_reallyfull_sweep(ccobject *self, PyObject *args)
int dt = 0; int dt = 0;
if (!PyArg_ParseTuple(args, "|i:reallyfull_sweep", &dt)) if (!PyArg_ParseTuple(args, "|i:reallyfull_sweep", &dt))
return NULL; return NULL;
return lockgc(self,0); return lockgc(self, 0);
} }
static void static void
...@@ -460,22 +471,19 @@ cc_get(ccobject *self, PyObject *args) ...@@ -460,22 +471,19 @@ cc_get(ccobject *self, PyObject *args)
{ {
PyObject *r, *key, *d=0; PyObject *r, *key, *d=0;
UNLESS (PyArg_ParseTuple(args,"O|O", &key, &d)) return NULL; if (!PyArg_ParseTuple(args, "O|O:get", &key, &d))
return NULL;
UNLESS (r=(PyObject *)object_from_oid(self, key)) r = (PyObject *)object_from_oid(self, key);
{ if (!r) {
if (d) if (d) {
{ r = d;
PyErr_Clear();
r=d;
Py_INCREF(r); Py_INCREF(r);
} } else {
else
{
PyErr_SetObject(PyExc_KeyError, key); PyErr_SetObject(PyExc_KeyError, key);
return NULL; return NULL;
} }
} }
return r; return r;
} }
...@@ -522,8 +530,9 @@ cc_lru_items(ccobject *self, PyObject *args) ...@@ -522,8 +530,9 @@ cc_lru_items(ccobject *self, PyObject *args)
return NULL; return NULL;
if (self->ring_lock) { if (self->ring_lock) {
/* When the ring lock is held, we have no way of know which ring nodes /* When the ring lock is held, we have no way of know which
belong to persistent objects, and which a placeholders. */ ring nodes belong to persistent objects, and which a
placeholders. */
PyErr_SetString(PyExc_ValueError, PyErr_SetString(PyExc_ValueError,
".lru_items() is unavailable during garbage collection"); ".lru_items() is unavailable during garbage collection");
return NULL; return NULL;
...@@ -562,40 +571,25 @@ cc_lru_items(ccobject *self, PyObject *args) ...@@ -562,40 +571,25 @@ cc_lru_items(ccobject *self, PyObject *args)
return l; return l;
} }
static PyObject * static int
cc_oid_unreferenced(ccobject *self, PyObject *args) cc_oid_unreferenced(ccobject *self, PyObject *oid)
{ {
/* This is called by the persistent object deallocation /* This is called by the persistent object deallocation function
function when the reference count on a persistent when the reference count on a persistent object reaches
object reaches zero. We need to fix up our dictionary; zero. We need to fix up our dictionary; its reference is now
its reference is now dangling because we stole its dangling because we stole its reference count. Be careful to
reference count. Be careful to not release the global not release the global interpreter lock until this is
interpreter lock until this is complete. */ complete. */
PyObject *oid, *v; PyObject *v;
if (!PyArg_ParseTuple(args, "O:_oid_unreferenced", &oid))
return NULL;
v = PyDict_GetItem(self->data, oid); v = PyDict_GetItem(self->data, oid);
if (v == NULL) { if (v == NULL) {
PyErr_SetObject(PyExc_KeyError, oid); PyErr_SetObject(PyExc_KeyError, oid);
/* jeremy debug return -1;
fprintf(stderr, "oid_unreferenced: key error\n");
*/
return NULL;
}
/* jeremy debug
fprintf(stderr, "oid_unreferenced: %X %d %s\n", v,
v->ob_refcnt, v->ob_type->tp_name);
*/
if (v->ob_refcnt) {
PyErr_Format(PyExc_ValueError,
"object has reference count of %d, should be zero", v->ob_refcnt);
return NULL;
} }
assert(v->ob_refcnt == 0);
/* Need to be very hairy here because a dictionary is about /* Need to be very hairy here because a dictionary is about
to decref an already deleted object. to decref an already deleted object.
*/ */
...@@ -609,40 +603,33 @@ cc_oid_unreferenced(ccobject *self, PyObject *args) ...@@ -609,40 +603,33 @@ cc_oid_unreferenced(ccobject *self, PyObject *args)
#else #else
Py_INCREF(v); Py_INCREF(v);
#endif #endif
/* Incremement the refcount again, because delitem is going to
if (v->ob_refcnt != 1) { DECREF it. If it's refcount reached zero again, we'd call back to
PyErr_SetString(PyExc_ValueError, the dealloc function that called us.
"refcount is not 1 after resurrection"); */
return NULL;
}
/* return the stolen reference */
Py_INCREF(v); Py_INCREF(v);
/* XXX what if this fails? */
PyDict_DelItem(self->data, oid); PyDict_DelItem(self->data, oid);
if (v->ob_refcnt != 1) { if (v->ob_refcnt != 1) {
PyErr_SetString(PyExc_ValueError, PyErr_SetString(PyExc_ValueError,
"refcount is not 1 after removal from dict"); "refcount is not 1 after removal from dict");
return NULL; return -1;
} }
/* undo the temporary resurrection */ /* undo the temporary resurrection */
#ifdef Py_TRACE_REFS #ifdef Py_TRACE_REFS
_Py_ForgetReference(v); _Py_ForgetReference(v);
#else #else
v->ob_refcnt=0; v->ob_refcnt = 0;
#endif #endif
Py_INCREF(Py_None); return 0;
return Py_None;
} }
static struct PyMethodDef cc_methods[] = { static struct PyMethodDef cc_methods[] = {
{"_oid_unreferenced", (PyCFunction)cc_oid_unreferenced, METH_VARARGS,
NULL
},
{"lru_items", (PyCFunction)cc_lru_items, METH_VARARGS, {"lru_items", (PyCFunction)cc_lru_items, METH_VARARGS,
"List (oid, object) pairs from the lru list, as 2-tuples.\n" "List (oid, object) pairs from the lru list, as 2-tuples.\n"
}, },
...@@ -1103,10 +1090,17 @@ void ...@@ -1103,10 +1090,17 @@ void
initcPickleCache(void) initcPickleCache(void)
{ {
PyObject *m, *d; PyObject *m, *d;
cPersistenceCAPIstruct *capi;
Cctype.ob_type = &PyType_Type;
Cctype.ob_type=&PyType_Type; if (!ExtensionClassImported)
return;
UNLESS(ExtensionClassImported) return; capi = (cPersistenceCAPIstruct *)PyCObject_Import("cPersistence", "CAPI");
if (!capi)
return;
capi->percachedel = (percachedelfunc)cc_oid_unreferenced;
m = Py_InitModule4("cPickleCache", cCM_methods, cPickleCache_doc_string, m = Py_InitModule4("cPickleCache", cCM_methods, cPickleCache_doc_string,
(PyObject*)NULL, PYTHON_API_VERSION); (PyObject*)NULL, PYTHON_API_VERSION);
...@@ -1118,11 +1112,11 @@ initcPickleCache(void) ...@@ -1118,11 +1112,11 @@ initcPickleCache(void)
d = PyModule_GetDict(m); d = PyModule_GetDict(m);
PyDict_SetItemString(d,"cache_variant",PyString_FromString("stiff/c")); PyDict_SetItemString(d, "cache_variant", PyString_FromString("stiff/c"));
#ifdef MUCH_RING_CHECKING #ifdef MUCH_RING_CHECKING
PyDict_SetItemString(d,"MUCH_RING_CHECKING",PyInt_FromLong(1)); PyDict_SetItemString(d, "MUCH_RING_CHECKING", PyInt_FromLong(1));
#else #else
PyDict_SetItemString(d,"MUCH_RING_CHECKING",PyInt_FromLong(0)); PyDict_SetItemString(d, "MUCH_RING_CHECKING", PyInt_FromLong(0));
#endif #endif
} }
...@@ -14,11 +14,10 @@ ...@@ -14,11 +14,10 @@
static char cPersistence_doc_string[] = static char cPersistence_doc_string[] =
"Defines Persistent mixin class for persistent objects.\n" "Defines Persistent mixin class for persistent objects.\n"
"\n" "\n"
"$Id: cPersistence.c,v 1.57 2002/04/02 22:46:48 jeremy Exp $\n"; "$Id: cPersistence.c,v 1.58 2002/04/05 01:12:48 jeremy Exp $\n";
#include "cPersistence.h" #include "cPersistence.h"
/* XXX What is this structure used for? */
struct ccobject_head_struct { struct ccobject_head_struct {
CACHE_HEAD CACHE_HEAD
}; };
...@@ -202,15 +201,11 @@ deallocated(cPersistentObject *self) ...@@ -202,15 +201,11 @@ deallocated(cPersistentObject *self)
if (self->state >= 0) if (self->state >= 0)
ghostify(self); ghostify(self);
if (self->cache) { if (self->cache) {
PyObject *v; /* XXX This function shouldn't be able to fail? If not, maybe
it shouldn't set an exception either.
/* XXX should just add this to the C API struct */ */
v = PyObject_CallMethod((PyObject *)self->cache, if (cPersistenceCAPI->percachedel(self->cache, self->oid) < 0)
"_oid_unreferenced", "O", self->oid); PyErr_Clear(); /* I don't think this should ever happen */
if (v == NULL)
PyErr_Clear(); /* I dont think this should ever happen */
else
Py_DECREF(v);
} }
Py_XDECREF(self->jar); Py_XDECREF(self->jar);
Py_XDECREF(self->oid); Py_XDECREF(self->oid);
...@@ -256,19 +251,22 @@ changed(cPersistentObject *self) ...@@ -256,19 +251,22 @@ changed(cPersistentObject *self)
static PyObject * static PyObject *
Per___changed__(cPersistentObject *self, PyObject *args) Per___changed__(cPersistentObject *self, PyObject *args)
{ {
PyObject *v=0; PyObject *v = NULL;
if (args && ! PyArg_ParseTuple(args, "|O",&v)) return NULL; if (args && !PyArg_ParseTuple(args, "|O:__changed__", &v))
if (! v) return PyObject_GetAttr(OBJECT(self), py__p_changed); return NULL;
if (!v)
return PyObject_GetAttr(OBJECT(self), py__p_changed);
if (PyObject_IsTrue(v)) if (PyObject_IsTrue(v)) {
{ if (changed(self) < 0)
if (changed(self) < 0) return NULL; return NULL;
} }
else if (self->state >= 0) self->state=cPersistent_UPTODATE_STATE; else if (self->state >= 0)
self->state = cPersistent_UPTODATE_STATE;
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
} }
static PyObject * static PyObject *
...@@ -295,7 +293,7 @@ Per__p_deactivate(cPersistentObject *self, PyObject *args) ...@@ -295,7 +293,7 @@ Per__p_deactivate(cPersistentObject *self, PyObject *args)
} }
/* need to delay releasing the last reference on instance attributes /* need to delay releasing the last reference on instance attributes
until after we have finished accounting for losing our state */ until after we have finished accounting for losing our state */
if (dict2) if (dict2)
{ {
PyDict_Clear(dict2); PyDict_Clear(dict2);
...@@ -807,6 +805,7 @@ truecPersistenceCAPI = { ...@@ -807,6 +805,7 @@ truecPersistenceCAPI = {
deallocated, deallocated,
(intfunctionwithpythonarg)Per_setstate, (intfunctionwithpythonarg)Per_setstate,
(pergetattr)Per_getattr, (pergetattr)Per_getattr,
NULL
}; };
void void
...@@ -826,7 +825,8 @@ initcPersistence(void) ...@@ -826,7 +825,8 @@ initcPersistence(void)
Py_DECREF(m); Py_DECREF(m);
Py_DECREF(s); Py_DECREF(s);
if (init_strings() < 0) return; if (init_strings() < 0)
return;
m = Py_InitModule4("cPersistence", cP_methods, cPersistence_doc_string, m = Py_InitModule4("cPersistence", cP_methods, cPersistence_doc_string,
(PyObject*)NULL, PYTHON_API_VERSION); (PyObject*)NULL, PYTHON_API_VERSION);
...@@ -837,12 +837,8 @@ initcPersistence(void) ...@@ -837,12 +837,8 @@ initcPersistence(void)
PyExtensionClass_Export(d, "Persistent", Pertype); PyExtensionClass_Export(d, "Persistent", Pertype);
PyExtensionClass_Export(d, "Overridable", Overridable); PyExtensionClass_Export(d, "Overridable", Overridable);
cPersistenceCAPI=&truecPersistenceCAPI; cPersistenceCAPI = &truecPersistenceCAPI;
s = PyCObject_FromVoidPtr(cPersistenceCAPI,NULL); s = PyCObject_FromVoidPtr(cPersistenceCAPI, NULL);
PyDict_SetItemString(d, "CAPI", s); PyDict_SetItemString(d, "CAPI", s);
Py_XDECREF(s); Py_XDECREF(s);
/* Check for errors */
if (PyErr_Occurred())
Py_FatalError("can't initialize module cPersistence");
} }
...@@ -18,6 +18,12 @@ ...@@ -18,6 +18,12 @@
#include "ExtensionClass.h" #include "ExtensionClass.h"
#include <time.h> #include <time.h>
typedef struct CPersistentRing_struct
{
struct CPersistentRing_struct *prev;
struct CPersistentRing_struct *next;
} CPersistentRing;
#define CACHE_HEAD \ #define CACHE_HEAD \
PyObject_HEAD \ PyObject_HEAD \
CPersistentRing ring_home; \ CPersistentRing ring_home; \
...@@ -42,30 +48,26 @@ typedef struct ccobject_head_struct PerCache; ...@@ -42,30 +48,26 @@ typedef struct ccobject_head_struct PerCache;
#define cPersistent_CHANGED_STATE 1 #define cPersistent_CHANGED_STATE 1
#define cPersistent_STICKY_STATE 2 #define cPersistent_STICKY_STATE 2
typedef struct CPersistentRing_struct
{
struct CPersistentRing_struct *prev;
struct CPersistentRing_struct *next;
} CPersistentRing;
typedef struct { typedef struct {
cPersistent_HEAD cPersistent_HEAD
} cPersistentObject; } cPersistentObject;
typedef int (*persetattr)(PyObject *, PyObject*, PyObject *, setattrofunc); typedef int (*persetattr)(PyObject *, PyObject*, PyObject *, setattrofunc);
typedef PyObject *(*pergetattr)(PyObject *, PyObject*, char *, getattrofunc); typedef PyObject *(*pergetattr)(PyObject *, PyObject*, char *, getattrofunc);
typedef int (*percachedelfunc)(PerCache *, PyObject *);
typedef struct { typedef struct {
PyMethodChain *methods; PyMethodChain *methods;
getattrofunc getattro; getattrofunc getattro;
setattrofunc setattro; setattrofunc setattro;
int (*changed)(cPersistentObject*); int (*changed)(cPersistentObject*);
void (*accessed)(cPersistentObject*); void (*accessed)(cPersistentObject*);
void (*ghostify)(cPersistentObject*); void (*ghostify)(cPersistentObject*);
void (*deallocated)(cPersistentObject*); void (*deallocated)(cPersistentObject*);
int (*setstate)(PyObject*); int (*setstate)(PyObject*);
pergetattr pergetattro; pergetattr pergetattro;
persetattr persetattro; persetattr persetattro;
percachedelfunc percachedel;
} cPersistenceCAPIstruct; } cPersistenceCAPIstruct;
#ifndef DONT_USE_CPERSISTENCECAPI #ifndef DONT_USE_CPERSISTENCECAPI
......
...@@ -88,7 +88,7 @@ process must skip such objects, rather than deactivating them. ...@@ -88,7 +88,7 @@ process must skip such objects, rather than deactivating them.
static char cPickleCache_doc_string[] = static char cPickleCache_doc_string[] =
"Defines the PickleCache used by ZODB Connection objects.\n" "Defines the PickleCache used by ZODB Connection objects.\n"
"\n" "\n"
"$Id: cPickleCache.c,v 1.54 2002/04/04 22:02:32 bwarsaw Exp $\n"; "$Id: cPickleCache.c,v 1.55 2002/04/05 01:12:48 jeremy Exp $\n";
#define ASSIGN(V,E) {PyObject *__e; __e=(E); Py_XDECREF(V); (V)=__e;} #define ASSIGN(V,E) {PyObject *__e; __e=(E); Py_XDECREF(V); (V)=__e;}
#define UNLESS(E) if(!(E)) #define UNLESS(E) if(!(E))
...@@ -118,9 +118,9 @@ static PyObject *py__p_oid, *py_reload, *py__p_jar, *py__p_changed; ...@@ -118,9 +118,9 @@ static PyObject *py__p_oid, *py_reload, *py__p_jar, *py__p_changed;
#define ENGINE_NOISE(A) ((void)A) #define ENGINE_NOISE(A) ((void)A)
#endif #endif
/* This object is the pickle cache. The CACHE_HEAD macro guarantees that /* This object is the pickle cache. The CACHE_HEAD macro guarantees
layout of this struct is the same as the start of ccobject_head in that layout of this struct is the same as the start of
cPersistence.c */ ccobject_head in cPersistence.c */
typedef struct { typedef struct {
CACHE_HEAD CACHE_HEAD
int klass_count; /* count of persistent classes */ int klass_count; /* count of persistent classes */
...@@ -156,15 +156,15 @@ static int cc_ass_sub(ccobject *self, PyObject *key, PyObject *v); ...@@ -156,15 +156,15 @@ static int cc_ass_sub(ccobject *self, PyObject *key, PyObject *v);
/* ---------------------------------------------------------------- */ /* ---------------------------------------------------------------- */
static PyObject *object_from_oid(ccobject *self, PyObject *key)
/* somewhat of a replacement for PyDict_GetItem(self->data.... /* somewhat of a replacement for PyDict_GetItem(self->data....
however this returns a *new* reference */ however this returns a *new* reference */
static PyObject *
object_from_oid(ccobject *self, PyObject *key)
{ {
PyObject *v = PyDict_GetItem(self->data, key); PyObject *v = PyDict_GetItem(self->data, key);
if(!v) return NULL; if (!v)
return NULL;
Py_INCREF(v); Py_INCREF(v);
return v; return v;
} }
...@@ -263,10 +263,11 @@ scan_gc_items(ccobject *self,int target) ...@@ -263,10 +263,11 @@ scan_gc_items(ccobject *self,int target)
if (here == &self->ring_home) if (here == &self->ring_home)
return 0; return 0;
/* At this point we know that the ring only contains nodes from /* At this point we know that the ring only contains nodes
persistent objects, plus our own home node. We know this because from persistent objects, plus our own home node. We know
the ring lock is held. We can safely assume the current ring this because the ring lock is held. We can safely assume
node is a persistent object now we know it is not the home */ the current ring node is a persistent object now we know it
is not the home */
object = object_from_ring(self, here, "scan_gc_items"); object = object_from_ring(self, here, "scan_gc_items");
if (!object) if (!object)
return -1; return -1;
...@@ -277,12 +278,15 @@ scan_gc_items(ccobject *self,int target) ...@@ -277,12 +278,15 @@ scan_gc_items(ccobject *self,int target)
else if (object->state == cPersistent_UPTODATE_STATE) { else if (object->state == cPersistent_UPTODATE_STATE) {
/* deactivate it. This is the main memory saver. */ /* deactivate it. This is the main memory saver. */
/* Add a placeholder; a dummy node in the ring. We need to /* Add a placeholder; a dummy node in the ring. We need
do this to mark our position in the ring. All the other nodes to do this to mark our position in the ring. It is
come from persistent objects, and they are all liable possible that the PyObject_SetAttr() call below will
to be deallocated before "obj._p_changed = None" returns invoke an __setattr__() hook in Python. If it does,
to this function. This operation is only safe when the another thread might run; if that thread accesses a
ring lock is held (and it is) */ persistent object and moves it to the head of the ring,
it might cause the gc scan to start working from the
head of the list.
*/
placeholder.next = here->next; placeholder.next = here->next;
placeholder.prev = here; placeholder.prev = here;
...@@ -315,7 +319,7 @@ scan_gc_items(ccobject *self,int target) ...@@ -315,7 +319,7 @@ scan_gc_items(ccobject *self,int target)
static PyObject * static PyObject *
lockgc(ccobject *self, int target_size) lockgc(ccobject *self, int target_size)
{ {
/* We think this is thread-safe because of the GIL, and there's nothing /* This is thread-safe because of the GIL, and there's nothing
* in between checking the ring_lock and acquiring it that calls back * in between checking the ring_lock and acquiring it that calls back
* into Python. * into Python.
*/ */
...@@ -361,16 +365,23 @@ cc_incrgc(ccobject *self, PyObject *args) ...@@ -361,16 +365,23 @@ cc_incrgc(ccobject *self, PyObject *args)
if (!PyArg_ParseTuple(args, "|i:incrgc", &n)) if (!PyArg_ParseTuple(args, "|i:incrgc", &n))
return NULL; return NULL;
return lockgc(self,target_size); return lockgc(self, target_size);
} }
/* XXX Does it make sense for full_sweep() and reallyfull_sweep() to
empty the cache completely? I agree that it would if dt is 0, but
don't think it should for other times. Perhaps it should just call
incrgc() if dt > 2; the new cache may be efficient enough that
incrgc() would suffice.
*/
static PyObject * static PyObject *
cc_full_sweep(ccobject *self, PyObject *args) cc_full_sweep(ccobject *self, PyObject *args)
{ {
int dt = 0; int dt = 0;
if (!PyArg_ParseTuple(args, "|i:full_sweep", &dt)) if (!PyArg_ParseTuple(args, "|i:full_sweep", &dt))
return NULL; return NULL;
return lockgc(self,0); return lockgc(self, 0);
} }
static PyObject * static PyObject *
...@@ -379,7 +390,7 @@ cc_reallyfull_sweep(ccobject *self, PyObject *args) ...@@ -379,7 +390,7 @@ cc_reallyfull_sweep(ccobject *self, PyObject *args)
int dt = 0; int dt = 0;
if (!PyArg_ParseTuple(args, "|i:reallyfull_sweep", &dt)) if (!PyArg_ParseTuple(args, "|i:reallyfull_sweep", &dt))
return NULL; return NULL;
return lockgc(self,0); return lockgc(self, 0);
} }
static void static void
...@@ -460,22 +471,19 @@ cc_get(ccobject *self, PyObject *args) ...@@ -460,22 +471,19 @@ cc_get(ccobject *self, PyObject *args)
{ {
PyObject *r, *key, *d=0; PyObject *r, *key, *d=0;
UNLESS (PyArg_ParseTuple(args,"O|O", &key, &d)) return NULL; if (!PyArg_ParseTuple(args, "O|O:get", &key, &d))
return NULL;
UNLESS (r=(PyObject *)object_from_oid(self, key)) r = (PyObject *)object_from_oid(self, key);
{ if (!r) {
if (d) if (d) {
{ r = d;
PyErr_Clear();
r=d;
Py_INCREF(r); Py_INCREF(r);
} } else {
else
{
PyErr_SetObject(PyExc_KeyError, key); PyErr_SetObject(PyExc_KeyError, key);
return NULL; return NULL;
} }
} }
return r; return r;
} }
...@@ -522,8 +530,9 @@ cc_lru_items(ccobject *self, PyObject *args) ...@@ -522,8 +530,9 @@ cc_lru_items(ccobject *self, PyObject *args)
return NULL; return NULL;
if (self->ring_lock) { if (self->ring_lock) {
/* When the ring lock is held, we have no way of know which ring nodes /* When the ring lock is held, we have no way of know which
belong to persistent objects, and which a placeholders. */ ring nodes belong to persistent objects, and which a
placeholders. */
PyErr_SetString(PyExc_ValueError, PyErr_SetString(PyExc_ValueError,
".lru_items() is unavailable during garbage collection"); ".lru_items() is unavailable during garbage collection");
return NULL; return NULL;
...@@ -562,40 +571,25 @@ cc_lru_items(ccobject *self, PyObject *args) ...@@ -562,40 +571,25 @@ cc_lru_items(ccobject *self, PyObject *args)
return l; return l;
} }
static PyObject * static int
cc_oid_unreferenced(ccobject *self, PyObject *args) cc_oid_unreferenced(ccobject *self, PyObject *oid)
{ {
/* This is called by the persistent object deallocation /* This is called by the persistent object deallocation function
function when the reference count on a persistent when the reference count on a persistent object reaches
object reaches zero. We need to fix up our dictionary; zero. We need to fix up our dictionary; its reference is now
its reference is now dangling because we stole its dangling because we stole its reference count. Be careful to
reference count. Be careful to not release the global not release the global interpreter lock until this is
interpreter lock until this is complete. */ complete. */
PyObject *oid, *v; PyObject *v;
if (!PyArg_ParseTuple(args, "O:_oid_unreferenced", &oid))
return NULL;
v = PyDict_GetItem(self->data, oid); v = PyDict_GetItem(self->data, oid);
if (v == NULL) { if (v == NULL) {
PyErr_SetObject(PyExc_KeyError, oid); PyErr_SetObject(PyExc_KeyError, oid);
/* jeremy debug return -1;
fprintf(stderr, "oid_unreferenced: key error\n");
*/
return NULL;
}
/* jeremy debug
fprintf(stderr, "oid_unreferenced: %X %d %s\n", v,
v->ob_refcnt, v->ob_type->tp_name);
*/
if (v->ob_refcnt) {
PyErr_Format(PyExc_ValueError,
"object has reference count of %d, should be zero", v->ob_refcnt);
return NULL;
} }
assert(v->ob_refcnt == 0);
/* Need to be very hairy here because a dictionary is about /* Need to be very hairy here because a dictionary is about
to decref an already deleted object. to decref an already deleted object.
*/ */
...@@ -609,40 +603,33 @@ cc_oid_unreferenced(ccobject *self, PyObject *args) ...@@ -609,40 +603,33 @@ cc_oid_unreferenced(ccobject *self, PyObject *args)
#else #else
Py_INCREF(v); Py_INCREF(v);
#endif #endif
/* Incremement the refcount again, because delitem is going to
if (v->ob_refcnt != 1) { DECREF it. If it's refcount reached zero again, we'd call back to
PyErr_SetString(PyExc_ValueError, the dealloc function that called us.
"refcount is not 1 after resurrection"); */
return NULL;
}
/* return the stolen reference */
Py_INCREF(v); Py_INCREF(v);
/* XXX what if this fails? */
PyDict_DelItem(self->data, oid); PyDict_DelItem(self->data, oid);
if (v->ob_refcnt != 1) { if (v->ob_refcnt != 1) {
PyErr_SetString(PyExc_ValueError, PyErr_SetString(PyExc_ValueError,
"refcount is not 1 after removal from dict"); "refcount is not 1 after removal from dict");
return NULL; return -1;
} }
/* undo the temporary resurrection */ /* undo the temporary resurrection */
#ifdef Py_TRACE_REFS #ifdef Py_TRACE_REFS
_Py_ForgetReference(v); _Py_ForgetReference(v);
#else #else
v->ob_refcnt=0; v->ob_refcnt = 0;
#endif #endif
Py_INCREF(Py_None); return 0;
return Py_None;
} }
static struct PyMethodDef cc_methods[] = { static struct PyMethodDef cc_methods[] = {
{"_oid_unreferenced", (PyCFunction)cc_oid_unreferenced, METH_VARARGS,
NULL
},
{"lru_items", (PyCFunction)cc_lru_items, METH_VARARGS, {"lru_items", (PyCFunction)cc_lru_items, METH_VARARGS,
"List (oid, object) pairs from the lru list, as 2-tuples.\n" "List (oid, object) pairs from the lru list, as 2-tuples.\n"
}, },
...@@ -1103,10 +1090,17 @@ void ...@@ -1103,10 +1090,17 @@ void
initcPickleCache(void) initcPickleCache(void)
{ {
PyObject *m, *d; PyObject *m, *d;
cPersistenceCAPIstruct *capi;
Cctype.ob_type = &PyType_Type;
Cctype.ob_type=&PyType_Type; if (!ExtensionClassImported)
return;
UNLESS(ExtensionClassImported) return; capi = (cPersistenceCAPIstruct *)PyCObject_Import("cPersistence", "CAPI");
if (!capi)
return;
capi->percachedel = (percachedelfunc)cc_oid_unreferenced;
m = Py_InitModule4("cPickleCache", cCM_methods, cPickleCache_doc_string, m = Py_InitModule4("cPickleCache", cCM_methods, cPickleCache_doc_string,
(PyObject*)NULL, PYTHON_API_VERSION); (PyObject*)NULL, PYTHON_API_VERSION);
...@@ -1118,11 +1112,11 @@ initcPickleCache(void) ...@@ -1118,11 +1112,11 @@ initcPickleCache(void)
d = PyModule_GetDict(m); d = PyModule_GetDict(m);
PyDict_SetItemString(d,"cache_variant",PyString_FromString("stiff/c")); PyDict_SetItemString(d, "cache_variant", PyString_FromString("stiff/c"));
#ifdef MUCH_RING_CHECKING #ifdef MUCH_RING_CHECKING
PyDict_SetItemString(d,"MUCH_RING_CHECKING",PyInt_FromLong(1)); PyDict_SetItemString(d, "MUCH_RING_CHECKING", PyInt_FromLong(1));
#else #else
PyDict_SetItemString(d,"MUCH_RING_CHECKING",PyInt_FromLong(0)); PyDict_SetItemString(d, "MUCH_RING_CHECKING", PyInt_FromLong(0));
#endif #endif
} }
...@@ -14,11 +14,10 @@ ...@@ -14,11 +14,10 @@
static char cPersistence_doc_string[] = static char cPersistence_doc_string[] =
"Defines Persistent mixin class for persistent objects.\n" "Defines Persistent mixin class for persistent objects.\n"
"\n" "\n"
"$Id: cPersistence.c,v 1.57 2002/04/02 22:46:48 jeremy Exp $\n"; "$Id: cPersistence.c,v 1.58 2002/04/05 01:12:48 jeremy Exp $\n";
#include "cPersistence.h" #include "cPersistence.h"
/* XXX What is this structure used for? */
struct ccobject_head_struct { struct ccobject_head_struct {
CACHE_HEAD CACHE_HEAD
}; };
...@@ -202,15 +201,11 @@ deallocated(cPersistentObject *self) ...@@ -202,15 +201,11 @@ deallocated(cPersistentObject *self)
if (self->state >= 0) if (self->state >= 0)
ghostify(self); ghostify(self);
if (self->cache) { if (self->cache) {
PyObject *v; /* XXX This function shouldn't be able to fail? If not, maybe
it shouldn't set an exception either.
/* XXX should just add this to the C API struct */ */
v = PyObject_CallMethod((PyObject *)self->cache, if (cPersistenceCAPI->percachedel(self->cache, self->oid) < 0)
"_oid_unreferenced", "O", self->oid); PyErr_Clear(); /* I don't think this should ever happen */
if (v == NULL)
PyErr_Clear(); /* I dont think this should ever happen */
else
Py_DECREF(v);
} }
Py_XDECREF(self->jar); Py_XDECREF(self->jar);
Py_XDECREF(self->oid); Py_XDECREF(self->oid);
...@@ -256,19 +251,22 @@ changed(cPersistentObject *self) ...@@ -256,19 +251,22 @@ changed(cPersistentObject *self)
static PyObject * static PyObject *
Per___changed__(cPersistentObject *self, PyObject *args) Per___changed__(cPersistentObject *self, PyObject *args)
{ {
PyObject *v=0; PyObject *v = NULL;
if (args && ! PyArg_ParseTuple(args, "|O",&v)) return NULL; if (args && !PyArg_ParseTuple(args, "|O:__changed__", &v))
if (! v) return PyObject_GetAttr(OBJECT(self), py__p_changed); return NULL;
if (!v)
return PyObject_GetAttr(OBJECT(self), py__p_changed);
if (PyObject_IsTrue(v)) if (PyObject_IsTrue(v)) {
{ if (changed(self) < 0)
if (changed(self) < 0) return NULL; return NULL;
} }
else if (self->state >= 0) self->state=cPersistent_UPTODATE_STATE; else if (self->state >= 0)
self->state = cPersistent_UPTODATE_STATE;
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
} }
static PyObject * static PyObject *
...@@ -295,7 +293,7 @@ Per__p_deactivate(cPersistentObject *self, PyObject *args) ...@@ -295,7 +293,7 @@ Per__p_deactivate(cPersistentObject *self, PyObject *args)
} }
/* need to delay releasing the last reference on instance attributes /* need to delay releasing the last reference on instance attributes
until after we have finished accounting for losing our state */ until after we have finished accounting for losing our state */
if (dict2) if (dict2)
{ {
PyDict_Clear(dict2); PyDict_Clear(dict2);
...@@ -807,6 +805,7 @@ truecPersistenceCAPI = { ...@@ -807,6 +805,7 @@ truecPersistenceCAPI = {
deallocated, deallocated,
(intfunctionwithpythonarg)Per_setstate, (intfunctionwithpythonarg)Per_setstate,
(pergetattr)Per_getattr, (pergetattr)Per_getattr,
NULL
}; };
void void
...@@ -826,7 +825,8 @@ initcPersistence(void) ...@@ -826,7 +825,8 @@ initcPersistence(void)
Py_DECREF(m); Py_DECREF(m);
Py_DECREF(s); Py_DECREF(s);
if (init_strings() < 0) return; if (init_strings() < 0)
return;
m = Py_InitModule4("cPersistence", cP_methods, cPersistence_doc_string, m = Py_InitModule4("cPersistence", cP_methods, cPersistence_doc_string,
(PyObject*)NULL, PYTHON_API_VERSION); (PyObject*)NULL, PYTHON_API_VERSION);
...@@ -837,12 +837,8 @@ initcPersistence(void) ...@@ -837,12 +837,8 @@ initcPersistence(void)
PyExtensionClass_Export(d, "Persistent", Pertype); PyExtensionClass_Export(d, "Persistent", Pertype);
PyExtensionClass_Export(d, "Overridable", Overridable); PyExtensionClass_Export(d, "Overridable", Overridable);
cPersistenceCAPI=&truecPersistenceCAPI; cPersistenceCAPI = &truecPersistenceCAPI;
s = PyCObject_FromVoidPtr(cPersistenceCAPI,NULL); s = PyCObject_FromVoidPtr(cPersistenceCAPI, NULL);
PyDict_SetItemString(d, "CAPI", s); PyDict_SetItemString(d, "CAPI", s);
Py_XDECREF(s); Py_XDECREF(s);
/* Check for errors */
if (PyErr_Occurred())
Py_FatalError("can't initialize module cPersistence");
} }
...@@ -18,6 +18,12 @@ ...@@ -18,6 +18,12 @@
#include "ExtensionClass.h" #include "ExtensionClass.h"
#include <time.h> #include <time.h>
typedef struct CPersistentRing_struct
{
struct CPersistentRing_struct *prev;
struct CPersistentRing_struct *next;
} CPersistentRing;
#define CACHE_HEAD \ #define CACHE_HEAD \
PyObject_HEAD \ PyObject_HEAD \
CPersistentRing ring_home; \ CPersistentRing ring_home; \
...@@ -42,30 +48,26 @@ typedef struct ccobject_head_struct PerCache; ...@@ -42,30 +48,26 @@ typedef struct ccobject_head_struct PerCache;
#define cPersistent_CHANGED_STATE 1 #define cPersistent_CHANGED_STATE 1
#define cPersistent_STICKY_STATE 2 #define cPersistent_STICKY_STATE 2
typedef struct CPersistentRing_struct
{
struct CPersistentRing_struct *prev;
struct CPersistentRing_struct *next;
} CPersistentRing;
typedef struct { typedef struct {
cPersistent_HEAD cPersistent_HEAD
} cPersistentObject; } cPersistentObject;
typedef int (*persetattr)(PyObject *, PyObject*, PyObject *, setattrofunc); typedef int (*persetattr)(PyObject *, PyObject*, PyObject *, setattrofunc);
typedef PyObject *(*pergetattr)(PyObject *, PyObject*, char *, getattrofunc); typedef PyObject *(*pergetattr)(PyObject *, PyObject*, char *, getattrofunc);
typedef int (*percachedelfunc)(PerCache *, PyObject *);
typedef struct { typedef struct {
PyMethodChain *methods; PyMethodChain *methods;
getattrofunc getattro; getattrofunc getattro;
setattrofunc setattro; setattrofunc setattro;
int (*changed)(cPersistentObject*); int (*changed)(cPersistentObject*);
void (*accessed)(cPersistentObject*); void (*accessed)(cPersistentObject*);
void (*ghostify)(cPersistentObject*); void (*ghostify)(cPersistentObject*);
void (*deallocated)(cPersistentObject*); void (*deallocated)(cPersistentObject*);
int (*setstate)(PyObject*); int (*setstate)(PyObject*);
pergetattr pergetattro; pergetattr pergetattro;
persetattr persetattro; persetattr persetattro;
percachedelfunc percachedel;
} cPersistenceCAPIstruct; } cPersistenceCAPIstruct;
#ifndef DONT_USE_CPERSISTENCECAPI #ifndef DONT_USE_CPERSISTENCECAPI
......
...@@ -88,7 +88,7 @@ process must skip such objects, rather than deactivating them. ...@@ -88,7 +88,7 @@ process must skip such objects, rather than deactivating them.
static char cPickleCache_doc_string[] = static char cPickleCache_doc_string[] =
"Defines the PickleCache used by ZODB Connection objects.\n" "Defines the PickleCache used by ZODB Connection objects.\n"
"\n" "\n"
"$Id: cPickleCache.c,v 1.54 2002/04/04 22:02:32 bwarsaw Exp $\n"; "$Id: cPickleCache.c,v 1.55 2002/04/05 01:12:48 jeremy Exp $\n";
#define ASSIGN(V,E) {PyObject *__e; __e=(E); Py_XDECREF(V); (V)=__e;} #define ASSIGN(V,E) {PyObject *__e; __e=(E); Py_XDECREF(V); (V)=__e;}
#define UNLESS(E) if(!(E)) #define UNLESS(E) if(!(E))
...@@ -118,9 +118,9 @@ static PyObject *py__p_oid, *py_reload, *py__p_jar, *py__p_changed; ...@@ -118,9 +118,9 @@ static PyObject *py__p_oid, *py_reload, *py__p_jar, *py__p_changed;
#define ENGINE_NOISE(A) ((void)A) #define ENGINE_NOISE(A) ((void)A)
#endif #endif
/* This object is the pickle cache. The CACHE_HEAD macro guarantees that /* This object is the pickle cache. The CACHE_HEAD macro guarantees
layout of this struct is the same as the start of ccobject_head in that layout of this struct is the same as the start of
cPersistence.c */ ccobject_head in cPersistence.c */
typedef struct { typedef struct {
CACHE_HEAD CACHE_HEAD
int klass_count; /* count of persistent classes */ int klass_count; /* count of persistent classes */
...@@ -156,15 +156,15 @@ static int cc_ass_sub(ccobject *self, PyObject *key, PyObject *v); ...@@ -156,15 +156,15 @@ static int cc_ass_sub(ccobject *self, PyObject *key, PyObject *v);
/* ---------------------------------------------------------------- */ /* ---------------------------------------------------------------- */
static PyObject *object_from_oid(ccobject *self, PyObject *key)
/* somewhat of a replacement for PyDict_GetItem(self->data.... /* somewhat of a replacement for PyDict_GetItem(self->data....
however this returns a *new* reference */ however this returns a *new* reference */
static PyObject *
object_from_oid(ccobject *self, PyObject *key)
{ {
PyObject *v = PyDict_GetItem(self->data, key); PyObject *v = PyDict_GetItem(self->data, key);
if(!v) return NULL; if (!v)
return NULL;
Py_INCREF(v); Py_INCREF(v);
return v; return v;
} }
...@@ -263,10 +263,11 @@ scan_gc_items(ccobject *self,int target) ...@@ -263,10 +263,11 @@ scan_gc_items(ccobject *self,int target)
if (here == &self->ring_home) if (here == &self->ring_home)
return 0; return 0;
/* At this point we know that the ring only contains nodes from /* At this point we know that the ring only contains nodes
persistent objects, plus our own home node. We know this because from persistent objects, plus our own home node. We know
the ring lock is held. We can safely assume the current ring this because the ring lock is held. We can safely assume
node is a persistent object now we know it is not the home */ the current ring node is a persistent object now we know it
is not the home */
object = object_from_ring(self, here, "scan_gc_items"); object = object_from_ring(self, here, "scan_gc_items");
if (!object) if (!object)
return -1; return -1;
...@@ -277,12 +278,15 @@ scan_gc_items(ccobject *self,int target) ...@@ -277,12 +278,15 @@ scan_gc_items(ccobject *self,int target)
else if (object->state == cPersistent_UPTODATE_STATE) { else if (object->state == cPersistent_UPTODATE_STATE) {
/* deactivate it. This is the main memory saver. */ /* deactivate it. This is the main memory saver. */
/* Add a placeholder; a dummy node in the ring. We need to /* Add a placeholder; a dummy node in the ring. We need
do this to mark our position in the ring. All the other nodes to do this to mark our position in the ring. It is
come from persistent objects, and they are all liable possible that the PyObject_SetAttr() call below will
to be deallocated before "obj._p_changed = None" returns invoke an __setattr__() hook in Python. If it does,
to this function. This operation is only safe when the another thread might run; if that thread accesses a
ring lock is held (and it is) */ persistent object and moves it to the head of the ring,
it might cause the gc scan to start working from the
head of the list.
*/
placeholder.next = here->next; placeholder.next = here->next;
placeholder.prev = here; placeholder.prev = here;
...@@ -315,7 +319,7 @@ scan_gc_items(ccobject *self,int target) ...@@ -315,7 +319,7 @@ scan_gc_items(ccobject *self,int target)
static PyObject * static PyObject *
lockgc(ccobject *self, int target_size) lockgc(ccobject *self, int target_size)
{ {
/* We think this is thread-safe because of the GIL, and there's nothing /* This is thread-safe because of the GIL, and there's nothing
* in between checking the ring_lock and acquiring it that calls back * in between checking the ring_lock and acquiring it that calls back
* into Python. * into Python.
*/ */
...@@ -361,16 +365,23 @@ cc_incrgc(ccobject *self, PyObject *args) ...@@ -361,16 +365,23 @@ cc_incrgc(ccobject *self, PyObject *args)
if (!PyArg_ParseTuple(args, "|i:incrgc", &n)) if (!PyArg_ParseTuple(args, "|i:incrgc", &n))
return NULL; return NULL;
return lockgc(self,target_size); return lockgc(self, target_size);
} }
/* XXX Does it make sense for full_sweep() and reallyfull_sweep() to
empty the cache completely? I agree that it would if dt is 0, but
don't think it should for other times. Perhaps it should just call
incrgc() if dt > 2; the new cache may be efficient enough that
incrgc() would suffice.
*/
static PyObject * static PyObject *
cc_full_sweep(ccobject *self, PyObject *args) cc_full_sweep(ccobject *self, PyObject *args)
{ {
int dt = 0; int dt = 0;
if (!PyArg_ParseTuple(args, "|i:full_sweep", &dt)) if (!PyArg_ParseTuple(args, "|i:full_sweep", &dt))
return NULL; return NULL;
return lockgc(self,0); return lockgc(self, 0);
} }
static PyObject * static PyObject *
...@@ -379,7 +390,7 @@ cc_reallyfull_sweep(ccobject *self, PyObject *args) ...@@ -379,7 +390,7 @@ cc_reallyfull_sweep(ccobject *self, PyObject *args)
int dt = 0; int dt = 0;
if (!PyArg_ParseTuple(args, "|i:reallyfull_sweep", &dt)) if (!PyArg_ParseTuple(args, "|i:reallyfull_sweep", &dt))
return NULL; return NULL;
return lockgc(self,0); return lockgc(self, 0);
} }
static void static void
...@@ -460,22 +471,19 @@ cc_get(ccobject *self, PyObject *args) ...@@ -460,22 +471,19 @@ cc_get(ccobject *self, PyObject *args)
{ {
PyObject *r, *key, *d=0; PyObject *r, *key, *d=0;
UNLESS (PyArg_ParseTuple(args,"O|O", &key, &d)) return NULL; if (!PyArg_ParseTuple(args, "O|O:get", &key, &d))
return NULL;
UNLESS (r=(PyObject *)object_from_oid(self, key)) r = (PyObject *)object_from_oid(self, key);
{ if (!r) {
if (d) if (d) {
{ r = d;
PyErr_Clear();
r=d;
Py_INCREF(r); Py_INCREF(r);
} } else {
else
{
PyErr_SetObject(PyExc_KeyError, key); PyErr_SetObject(PyExc_KeyError, key);
return NULL; return NULL;
} }
} }
return r; return r;
} }
...@@ -522,8 +530,9 @@ cc_lru_items(ccobject *self, PyObject *args) ...@@ -522,8 +530,9 @@ cc_lru_items(ccobject *self, PyObject *args)
return NULL; return NULL;
if (self->ring_lock) { if (self->ring_lock) {
/* When the ring lock is held, we have no way of know which ring nodes /* When the ring lock is held, we have no way of know which
belong to persistent objects, and which a placeholders. */ ring nodes belong to persistent objects, and which a
placeholders. */
PyErr_SetString(PyExc_ValueError, PyErr_SetString(PyExc_ValueError,
".lru_items() is unavailable during garbage collection"); ".lru_items() is unavailable during garbage collection");
return NULL; return NULL;
...@@ -562,40 +571,25 @@ cc_lru_items(ccobject *self, PyObject *args) ...@@ -562,40 +571,25 @@ cc_lru_items(ccobject *self, PyObject *args)
return l; return l;
} }
static PyObject * static int
cc_oid_unreferenced(ccobject *self, PyObject *args) cc_oid_unreferenced(ccobject *self, PyObject *oid)
{ {
/* This is called by the persistent object deallocation /* This is called by the persistent object deallocation function
function when the reference count on a persistent when the reference count on a persistent object reaches
object reaches zero. We need to fix up our dictionary; zero. We need to fix up our dictionary; its reference is now
its reference is now dangling because we stole its dangling because we stole its reference count. Be careful to
reference count. Be careful to not release the global not release the global interpreter lock until this is
interpreter lock until this is complete. */ complete. */
PyObject *oid, *v; PyObject *v;
if (!PyArg_ParseTuple(args, "O:_oid_unreferenced", &oid))
return NULL;
v = PyDict_GetItem(self->data, oid); v = PyDict_GetItem(self->data, oid);
if (v == NULL) { if (v == NULL) {
PyErr_SetObject(PyExc_KeyError, oid); PyErr_SetObject(PyExc_KeyError, oid);
/* jeremy debug return -1;
fprintf(stderr, "oid_unreferenced: key error\n");
*/
return NULL;
}
/* jeremy debug
fprintf(stderr, "oid_unreferenced: %X %d %s\n", v,
v->ob_refcnt, v->ob_type->tp_name);
*/
if (v->ob_refcnt) {
PyErr_Format(PyExc_ValueError,
"object has reference count of %d, should be zero", v->ob_refcnt);
return NULL;
} }
assert(v->ob_refcnt == 0);
/* Need to be very hairy here because a dictionary is about /* Need to be very hairy here because a dictionary is about
to decref an already deleted object. to decref an already deleted object.
*/ */
...@@ -609,40 +603,33 @@ cc_oid_unreferenced(ccobject *self, PyObject *args) ...@@ -609,40 +603,33 @@ cc_oid_unreferenced(ccobject *self, PyObject *args)
#else #else
Py_INCREF(v); Py_INCREF(v);
#endif #endif
/* Incremement the refcount again, because delitem is going to
if (v->ob_refcnt != 1) { DECREF it. If it's refcount reached zero again, we'd call back to
PyErr_SetString(PyExc_ValueError, the dealloc function that called us.
"refcount is not 1 after resurrection"); */
return NULL;
}
/* return the stolen reference */
Py_INCREF(v); Py_INCREF(v);
/* XXX what if this fails? */
PyDict_DelItem(self->data, oid); PyDict_DelItem(self->data, oid);
if (v->ob_refcnt != 1) { if (v->ob_refcnt != 1) {
PyErr_SetString(PyExc_ValueError, PyErr_SetString(PyExc_ValueError,
"refcount is not 1 after removal from dict"); "refcount is not 1 after removal from dict");
return NULL; return -1;
} }
/* undo the temporary resurrection */ /* undo the temporary resurrection */
#ifdef Py_TRACE_REFS #ifdef Py_TRACE_REFS
_Py_ForgetReference(v); _Py_ForgetReference(v);
#else #else
v->ob_refcnt=0; v->ob_refcnt = 0;
#endif #endif
Py_INCREF(Py_None); return 0;
return Py_None;
} }
static struct PyMethodDef cc_methods[] = { static struct PyMethodDef cc_methods[] = {
{"_oid_unreferenced", (PyCFunction)cc_oid_unreferenced, METH_VARARGS,
NULL
},
{"lru_items", (PyCFunction)cc_lru_items, METH_VARARGS, {"lru_items", (PyCFunction)cc_lru_items, METH_VARARGS,
"List (oid, object) pairs from the lru list, as 2-tuples.\n" "List (oid, object) pairs from the lru list, as 2-tuples.\n"
}, },
...@@ -1103,10 +1090,17 @@ void ...@@ -1103,10 +1090,17 @@ void
initcPickleCache(void) initcPickleCache(void)
{ {
PyObject *m, *d; PyObject *m, *d;
cPersistenceCAPIstruct *capi;
Cctype.ob_type = &PyType_Type;
Cctype.ob_type=&PyType_Type; if (!ExtensionClassImported)
return;
UNLESS(ExtensionClassImported) return; capi = (cPersistenceCAPIstruct *)PyCObject_Import("cPersistence", "CAPI");
if (!capi)
return;
capi->percachedel = (percachedelfunc)cc_oid_unreferenced;
m = Py_InitModule4("cPickleCache", cCM_methods, cPickleCache_doc_string, m = Py_InitModule4("cPickleCache", cCM_methods, cPickleCache_doc_string,
(PyObject*)NULL, PYTHON_API_VERSION); (PyObject*)NULL, PYTHON_API_VERSION);
...@@ -1118,11 +1112,11 @@ initcPickleCache(void) ...@@ -1118,11 +1112,11 @@ initcPickleCache(void)
d = PyModule_GetDict(m); d = PyModule_GetDict(m);
PyDict_SetItemString(d,"cache_variant",PyString_FromString("stiff/c")); PyDict_SetItemString(d, "cache_variant", PyString_FromString("stiff/c"));
#ifdef MUCH_RING_CHECKING #ifdef MUCH_RING_CHECKING
PyDict_SetItemString(d,"MUCH_RING_CHECKING",PyInt_FromLong(1)); PyDict_SetItemString(d, "MUCH_RING_CHECKING", PyInt_FromLong(1));
#else #else
PyDict_SetItemString(d,"MUCH_RING_CHECKING",PyInt_FromLong(0)); PyDict_SetItemString(d, "MUCH_RING_CHECKING", PyInt_FromLong(0));
#endif #endif
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment