Commit d242261f authored by Jeremy Hylton's avatar Jeremy Hylton

Substantial reformatting of code for readability and style.

A few substantive changes:

Split internals of cc_ass_sub() into two smaller function
cc_add_item() and cc_del_item(), removing several levels of
indentation in the process.

Fix incorrect check for return value from PyDict_SetItem().
parent 612505f5
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
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.44 2002/04/01 23:45:48 jeremy Exp $\n"; "$Id: cPickleCache.c,v 1.45 2002/04/02 06:03:39 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))
...@@ -31,6 +31,9 @@ static char cPickleCache_doc_string[] = ...@@ -31,6 +31,9 @@ static char cPickleCache_doc_string[] =
static PyObject *py__p_oid, *py_reload, *py__p_jar, *py__p_changed; static PyObject *py__p_oid, *py_reload, *py__p_jar, *py__p_changed;
/* define this for extra debugging checks, and lousy performance */ /* define this for extra debugging checks, and lousy performance */
/* Are any of these checks necessary in production code? How do we
decide when to disable it?
*/
#define MUCH_RING_CHECKING 1 #define MUCH_RING_CHECKING 1
/* Do we want 'engine noise'.... abstract debugging output useful for /* Do we want 'engine noise'.... abstract debugging output useful for
...@@ -121,16 +124,16 @@ scan_gc_items(ccobject *self,int target) ...@@ -121,16 +124,16 @@ scan_gc_items(ccobject *self,int target)
#ifdef MUCH_RING_CHECKING #ifdef MUCH_RING_CHECKING
int safety_counter = self->cache_size*10; int safety_counter = self->cache_size*10;
if(safety_counter<10000) safety_counter = 10000; if (safety_counter<10000)
safety_counter = 10000;
#endif #endif
while(1) while (1) {
{ if (check_ring(self, "mid-gc"))
if(check_ring(self,"mid-gc")) return -1; return -1;
#ifdef MUCH_RING_CHECKING #ifdef MUCH_RING_CHECKING
if(!safety_counter--) if (!safety_counter--) {
{
/* This loop has been running for a very long time. /* This loop has been running for a very long time.
It is possible that someone loaded a very large number of objects, It is possible that someone loaded a very large number of objects,
and now wants us to blow them all away. However it may and now wants us to blow them all away. However it may
...@@ -138,37 +141,35 @@ scan_gc_items(ccobject *self,int target) ...@@ -138,37 +141,35 @@ scan_gc_items(ccobject *self,int target)
long then you really have to doubt it will ever terminate. long then you really have to doubt it will ever terminate.
In the MUCH_RING_CHECKING build we prefer to raise an exception In the MUCH_RING_CHECKING build we prefer to raise an exception
here */ here */
PyErr_SetString(PyExc_RuntimeError,"scan_gc_items safety counter exceeded"); PyErr_SetString(PyExc_RuntimeError,
"scan_gc_items safety counter exceeded");
return -1; return -1;
} }
if(!present_in_ring(self,here)) if (!present_in_ring(self, here)) {
{ /* Our current working position is no longer in the ring.
/* Our current working position is no longer in the ring. Thats bad. */ That's bad. */
PyErr_SetString(PyExc_RuntimeError,"working position fell out the ring, in scan_gc_items"); PyErr_SetString(PyExc_RuntimeError,
"working position fell out the ring, in scan_gc_items");
return -1; return -1;
} }
#endif #endif
if(here==&self->ring_home) /* back to the home position. stop looking */
{ if (here == &self->ring_home)
/* back to the home position. stop looking */
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 from
persistent objects, plus our own home node. We can safely persistent objects, plus our own home node. We can safely
assume this is a persistent object now we know it is not the home */ assume this 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) return -1; if (!object)
return -1;
if(self->non_ghost_count<=target) /* we are small enough */
{ if (self->non_ghost_count <= target)
/* we are small enough */
return 0; return 0;
} 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. */
ENGINE_NOISE("G"); ENGINE_NOISE("G");
...@@ -177,49 +178,46 @@ scan_gc_items(ccobject *self,int target) ...@@ -177,49 +178,46 @@ scan_gc_items(ccobject *self,int target)
placeholder.next = here->next; placeholder.next = here->next;
placeholder.prev = here; placeholder.prev = here;
here->next->prev = &placeholder; here->next->prev = &placeholder;
here->next = &placeholder; here->next = &placeholder;
error = PyObject_SetAttr((PyObject *)object,py__p_changed,Py_None); error = PyObject_SetAttr((PyObject *)object, py__p_changed,
Py_None);
/* unlink the placeholder */ /* unlink the placeholder */
placeholder.next->prev=placeholder.prev; placeholder.next->prev = placeholder.prev;
placeholder.prev->next=placeholder.next; placeholder.prev->next = placeholder.next;
here = placeholder.next; here = placeholder.next;
if(error) if(error)
return -1; /* problem */ return -1; /* problem */
} } else {
else
{
ENGINE_NOISE("."); ENGINE_NOISE(".");
here = here->next; here = here->next;
} }
} }
} }
static PyObject * static PyObject *
lockgc(ccobject *self,int target_size) lockgc(ccobject *self, int target_size)
{ {
if(self->ring_lock) if (self->ring_lock) {
{
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
} }
if(check_ring(self,"pre-gc")) return NULL; if (check_ring(self, "pre-gc"))
return NULL;
ENGINE_NOISE("<"); ENGINE_NOISE("<");
self->ring_lock = 1; self->ring_lock = 1;
if(scan_gc_items(self,target_size)) if (scan_gc_items(self, target_size)) {
{
self->ring_lock = 0; self->ring_lock = 0;
return NULL; return NULL;
} }
self->ring_lock = 0; self->ring_lock = 0;
ENGINE_NOISE(">\n"); ENGINE_NOISE(">\n");
if(check_ring(self,"post-gc")) return NULL; if (check_ring(self, "post-gc"))
return NULL;
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
...@@ -228,23 +226,22 @@ lockgc(ccobject *self,int target_size) ...@@ -228,23 +226,22 @@ lockgc(ccobject *self,int target_size)
static PyObject * static PyObject *
cc_incrgc(ccobject *self, PyObject *args) cc_incrgc(ccobject *self, PyObject *args)
{ {
int n=1; int n = 1;
int starting_size = self->non_ghost_count; int starting_size = self->non_ghost_count;
int target_size = self->cache_size; int target_size = self->cache_size;
if(self->cache_drain_resistance>=1) if (self->cache_drain_resistance >= 1) {
{
/* This cache will gradually drain down to a small size. Check /* This cache will gradually drain down to a small size. Check
a (small) number of objects proportional to the current size */ a (small) number of objects proportional to the current size */
int target_size_2 = starting_size - 1 - starting_size/self->cache_drain_resistance; int target_size_2 = (starting_size - 1
if(target_size_2<target_size) - starting_size / self->cache_drain_resistance);
if (target_size_2 < target_size)
target_size = target_size_2; target_size = target_size_2;
} }
UNLESS (PyArg_ParseTuple(args, "|i",&n)) return NULL; if (!PyArg_ParseTuple(args, "|i:incrgc", &n))
return NULL;
return lockgc(self,target_size); return lockgc(self,target_size);
} }
...@@ -252,54 +249,46 @@ cc_incrgc(ccobject *self, PyObject *args) ...@@ -252,54 +249,46 @@ cc_incrgc(ccobject *self, PyObject *args)
static PyObject * static PyObject *
cc_full_sweep(ccobject *self, PyObject *args) cc_full_sweep(ccobject *self, PyObject *args)
{ {
int dt=0; int dt = 0;
UNLESS(PyArg_ParseTuple(args, "|i", &dt)) return NULL; if (!PyArg_ParseTuple(args, "|i:full_sweep", &dt))
return lockgc(self,0); return NULL;
return lockgc(self,0);
} }
static PyObject * static PyObject *
cc_reallyfull_sweep(ccobject *self, PyObject *args) cc_reallyfull_sweep(ccobject *self, PyObject *args)
{ {
int dt=0; int dt = 0;
UNLESS(PyArg_ParseTuple(args, "|i", &dt)) return NULL; if (!PyArg_ParseTuple(args, "|i:reallyfull_sweep", &dt))
return NULL;
return lockgc(self,0); return lockgc(self,0);
} }
static void static void
_invalidate(ccobject *self, PyObject *key) _invalidate(ccobject *self, PyObject *key)
{ {
PyObject *v=object_from_oid(self, key); PyObject *v = object_from_oid(self, key);
if(!v) if (!v)
{ return;
/* shouldnt this be an error? for now Ill follow Jims lead */ if (PyExtensionClass_Check(v)) {
PyErr_Clear(); if (v->ob_refcnt <= 1) {
} self->klass_count--;
else if (PyDict_DelItem(self->data, key) < 0)
{ PyErr_Clear();
if (PyExtensionClass_Check(v)) }
{ else {
if(v->ob_refcnt <= 1) v = PyObject_CallFunction(self->setklassstate, "O", v);
{ if (v)
self->klass_count--; Py_DECREF(v);
if (PyDict_DelItem(self->data, key) < 0) else
PyErr_Clear(); PyErr_Clear();
} }
else } else {
{ if (PyObject_DelAttr(v, py__p_changed) < 0)
v=PyObject_CallFunction(self->setklassstate, PyErr_Clear();
"O", v);
if (v) Py_DECREF(v);
else PyErr_Clear();
}
}
else
{
if(PyObject_DelAttr(v,py__p_changed) < 0)
PyErr_Clear();
}
Py_DECREF(v);
} }
Py_DECREF(v);
} }
static PyObject * static PyObject *
...@@ -379,21 +368,26 @@ cc_klass_items(ccobject *self, PyObject *args) ...@@ -379,21 +368,26 @@ cc_klass_items(ccobject *self, PyObject *args)
PyObject *l,*k,*v; PyObject *l,*k,*v;
int p = 0; int p = 0;
if(!PyArg_ParseTuple(args,"")) return NULL; if (!PyArg_ParseTuple(args, ":klass_items"))
return NULL;
l = PyList_New(0); l = PyList_New(PyDict_Size(self->data));
if(!l) return NULL; if (l == NULL)
return NULL;
while(PyDict_Next(self->data, &p, &k, &v)) while (PyDict_Next(self->data, &p, &k, &v)) {
{ if(PyExtensionClass_Check(v)) {
if(PyExtensionClass_Check(v)) v = Py_BuildValue("OO", k, v);
{ if (v == NULL) {
v=PyObject_CallMethod(l,"append","((OO))",k,v); Py_DECREF(l);
if(!v) return NULL;
{ }
Py_DECREF(l); if (PyList_Append(l, v) < 0) {
return NULL; Py_DECREF(v);
} Py_DECREF(l);
return NULL;
}
Py_DECREF(v);
} }
} }
...@@ -406,35 +400,41 @@ cc_lru_items(ccobject *self, PyObject *args) ...@@ -406,35 +400,41 @@ cc_lru_items(ccobject *self, PyObject *args)
PyObject *l; PyObject *l;
CPersistentRing *here; CPersistentRing *here;
if(!PyArg_ParseTuple(args,"")) return NULL; if (!PyArg_ParseTuple(args, ":lru_items"))
return NULL;
if(self->ring_lock) if (self->ring_lock) {
{ 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;
} }
if(check_ring(self,"pre-cc_items")) return NULL; if (check_ring(self, "pre-cc_items"))
return NULL;
l = PyList_New(0); l = PyList_New(0);
if(!l) return NULL; if (l == NULL)
return NULL;
here = self->ring_home.next; here = self->ring_home.next;
while(here!=&self->ring_home) while (here != &self->ring_home) {
{
cPersistentObject *object = object_from_ring(self,here,"cc_items");
PyObject *v; PyObject *v;
if(!object) cPersistentObject *object = object_from_ring(self, here, "cc_items");
{
if (object == NULL) {
Py_DECREF(l); Py_DECREF(l);
return NULL; return NULL;
} }
v=PyObject_CallMethod(l,"append","((OO))",object->oid,object); v = Py_BuildValue("OO", object->oid, object);
if(!v) if (v == NULL) {
{
Py_DECREF(l); Py_DECREF(l);
return NULL; return NULL;
} }
if (PyList_Append(l, v) < 0) {
Py_DECREF(v);
Py_DECREF(l);
return NULL;
}
Py_DECREF(v); Py_DECREF(v);
here = here->next; here = here->next;
} }
...@@ -442,23 +442,38 @@ cc_lru_items(ccobject *self, PyObject *args) ...@@ -442,23 +442,38 @@ cc_lru_items(ccobject *self, PyObject *args)
return l; return l;
} }
/* XXX What does this function do? */
static PyObject * static PyObject *
cc_oid_unreferenced(ccobject *self, PyObject *args) cc_oid_unreferenced(ccobject *self, PyObject *args)
{ {
PyObject *oid,*v; PyObject *oid, *v;
if(!PyArg_ParseTuple(args,"O",&oid)) return NULL; if (!PyArg_ParseTuple(args, "O:_oid_unreferenced", &oid))
return NULL;
v = PyDict_GetItem(self->data, oid); v = PyDict_GetItem(self->data, oid);
if(!v) return NULL; if (v == NULL) {
PyErr_SetObject(PyExc_KeyError, oid);
/* jeremy debug
fprintf(stderr, "oid_unreferenced: key error\n");
*/
return NULL;
}
if(v->ob_refcnt) /* jeremy debug
{ fprintf(stderr, "oid_unreferenced: %X %d %s\n", v,
PyErr_Format(PyExc_ValueError,"object has reference count of %d, should be zero",v->ob_refcnt); 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; return NULL;
} }
/* 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.
*/
#ifdef Py_TRACE_REFS #ifdef Py_TRACE_REFS
#error "this code path has not been tested - Toby Dickenson" #error "this code path has not been tested - Toby Dickenson"
...@@ -468,9 +483,9 @@ cc_oid_unreferenced(ccobject *self, PyObject *args) ...@@ -468,9 +483,9 @@ cc_oid_unreferenced(ccobject *self, PyObject *args)
Py_INCREF(v); Py_INCREF(v);
#endif #endif
if(v->ob_refcnt!=1) if (v->ob_refcnt != 1) {
{ PyErr_SetString(PyExc_ValueError,
PyErr_SetString(PyExc_ValueError,"refcount is not 1 after resurrection"); "refcount is not 1 after resurrection");
return NULL; return NULL;
} }
...@@ -479,9 +494,9 @@ cc_oid_unreferenced(ccobject *self, PyObject *args) ...@@ -479,9 +494,9 @@ cc_oid_unreferenced(ccobject *self, PyObject *args)
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 NULL;
} }
...@@ -546,7 +561,8 @@ cc_getattr(ccobject *self, char *name) ...@@ -546,7 +561,8 @@ cc_getattr(ccobject *self, char *name)
{ {
PyObject *r; PyObject *r;
if(check_ring(self,"getattr")) return NULL; if (check_ring(self, "getattr"))
return NULL;
if(*name=='c') if(*name=='c')
{ {
...@@ -632,179 +648,196 @@ cc_subscript(ccobject *self, PyObject *key) ...@@ -632,179 +648,196 @@ cc_subscript(ccobject *self, PyObject *key)
} }
static int static int
cc_ass_sub(ccobject *self, PyObject *key, PyObject *v) cc_add_item(ccobject *self, PyObject *key, PyObject *v)
{ {
int result; int result;
if(v) PyExtensionClass *class;
{
if( ( PyExtensionInstance_Check(v) && if (!PyExtensionInstance_Check(v)) {
(((PyExtensionClass*)(v->ob_type))->class_flags & PERSISTENT_TYPE_FLAG) && PyErr_SetString(PyExc_ValueError,
(v->ob_type->tp_basicsize >= sizeof(cPersistentObject)) "Cache values must be persistent objects.");
) return -1;
|| }
PyExtensionClass_Check(v) /* Is this set of tests necessary? */
) class = (PyExtensionClass *)(v->ob_type);
{ if (!((class->class_flags & PERSISTENT_TYPE_FLAG)
PyObject *oid = PyObject_GetAttr(v,py__p_oid); && v->ob_type->tp_basicsize >= sizeof(cPersistentObject))) {
PyObject *object_again; PyErr_SetString(PyExc_ValueError,
if(!oid) "Cache values must be persistent objects.");
{ return -1;
return -1; }
}
if(PyObject_Cmp(key,oid,&result)) /* XXX Why not self->oid? */
{ PyObject *oid = PyObject_GetAttr(v, py__p_oid);
Py_DECREF(oid); PyObject *object_again;
return -1; cPersistentObject *p;
}
Py_DECREF(oid); if (oid == NULL)
if(result) return -1;
{ /* XXX key and oid should both be PyString objects */
PyErr_SetString(PyExc_ValueError,"key must be the same as the object's oid attribute"); if (PyObject_Cmp(key, oid, &result)) {
return -1; Py_DECREF(oid);
} return -1;
object_again = object_from_oid(self, key); }
if(object_again) Py_DECREF(oid);
{ if (result) {
if(object_again!=v) PyErr_SetString(PyExc_ValueError,
{ "key must be the same as the object's oid attribute");
Py_DECREF(object_again); return -1;
PyErr_SetString(PyExc_ValueError,"Can not re-register object under a different oid"); }
return -1; object_again = object_from_oid(self, key);
} if (object_again) {
else /* XXX Do we need to check for this? */
{ if (object_again != v) {
/* re-register under the same oid - no work needed */ Py_DECREF(object_again);
Py_DECREF(object_again); PyErr_SetString(PyExc_ValueError,
return 0; "Can not re-register object under a different oid");
} return -1;
} } else {
if(PyExtensionClass_Check(v)) /* re-register under the same oid - no work needed */
{ Py_DECREF(object_again);
if(PyDict_SetItem(self->data, key, v)) return -1; return 0;
self->klass_count++; }
return 0; }
} if (PyExtensionClass_Check(v)) {
else if (PyDict_SetItem(self->data, key, v))
{ return -1;
if(((cPersistentObject*)v)->cache) { self->klass_count++;
if(((cPersistentObject*)v)->cache != (PerCache *)self) { return 0;
/* This object is already in a different cache. */ } else {
PyErr_SetString(PyExc_ValueError, PerCache *cache = ((cPersistentObject *)v)->cache;
if (cache) {
if (cache != (PerCache *)self)
/* This object is already in a different cache. */
PyErr_SetString(PyExc_ValueError,
"Cache values may only be in one cache."); "Cache values may only be in one cache.");
return -1; return -1;
} }
/* else: /* else:
This object is already one of ours, which This object is already one of ours, which is ok. It
is ok. It would be very strange if someone would be very strange if someone was trying to register
was trying to register the same object under a the same object under a different key.
different key. */
*/ }
}
if (check_ring(self, "pre-setitem"))
if(check_ring(self,"pre-setitem")) return -1; return -1;
if(PyDict_SetItem(self->data, key, v)) return -1; if (PyDict_SetItem(self->data, key, v))
return -1;
Py_INCREF(self);
((cPersistentObject*)v)->cache = (PerCache *)self; Py_INCREF(self);
if(((cPersistentObject*)v)->state>=0) p = (cPersistentObject *)v;
{ p->cache = (PerCache *)self;
/* insert this non-ghost object into the ring just behind the home position */ if (p->state >= 0) {
self->non_ghost_count++; /* insert this non-ghost object into the ring just
((cPersistentObject*)v)->ring.next = &self->ring_home; behind the home position */
((cPersistentObject*)v)->ring.prev = self->ring_home.prev; self->non_ghost_count++;
self->ring_home.prev->next = &((cPersistentObject*)v)->ring; p->ring.next = &self->ring_home;
self->ring_home.prev = &((cPersistentObject*)v)->ring; p->ring.prev = self->ring_home.prev;
} self->ring_home.prev->next = &p->ring;
else self->ring_home.prev = &p->ring;
{ } else {
/* steal a reference from the dictionary; ghosts have a weak reference */ /* steal a reference from the dictionary;
Py_DECREF(v); ghosts have a weak reference */
} Py_DECREF(v);
if(check_ring(self,"post-setitem")) return -1;
return 0;
}
}
else
{
PyErr_SetString(PyExc_ValueError, "Cache values must be persistent objects.");
return -1;
}
} }
if (check_ring(self, "post-setitem"))
return -1;
else else
{ return 0;
/* unlink this item from the ring */ }
if(check_ring(self,"pre-delitem")) return -1;
v = (PyObject *)object_from_oid(self,key);
if(!v) return -1;
if(PyExtensionClass_Check(v)) static int
{ cc_del_item(ccobject *self, PyObject *key)
self->klass_count--; {
} PyObject *v;
else cPersistentObject *p;
{
if(((cPersistentObject*)v)->state>=0) /* unlink this item from the ring */
{ if (check_ring(self, "pre-delitem"))
self->non_ghost_count--; return -1;
((cPersistentObject*)v)->ring.next->prev = ((cPersistentObject*)v)->ring.prev;
((cPersistentObject*)v)->ring.prev->next = ((cPersistentObject*)v)->ring.next; v = (PyObject *)object_from_oid(self, key);
((cPersistentObject*)v)->ring.prev = NULL; if (v == NULL)
((cPersistentObject*)v)->ring.next = NULL; return -1;
}
else if (PyExtensionClass_Check(v)) {
{ self->klass_count--;
/* This is a ghost object, so we havent kept a reference count on it. } else {
For it have stayed alive this long someone else must be keeping a reference p = (cPersistentObject *)v;
to it. Therefore we need to temporarily give it back a reference count if (p->state >= 0) {
before calling DelItem below */ self->non_ghost_count--;
Py_INCREF(v); p->ring.next->prev = p->ring.prev;
} p->ring.prev->next = p->ring.next;
p->ring.prev = NULL;
p->ring.next = NULL;
} else {
/* This is a ghost object, so we havent kept a reference
count on it. For it have stayed alive this long
someone else must be keeping a reference to
it. Therefore we need to temporarily give it back a
reference count before calling DelItem below */
Py_INCREF(v);
}
Py_DECREF((PyObject *)((cPersistentObject*)v)->cache); Py_DECREF((PyObject *)p->cache);
((cPersistentObject*)v)->cache = NULL; p->cache = NULL;
} }
Py_DECREF(v); Py_DECREF(v);
if(PyDict_DelItem(self->data, key)) if (PyDict_DelItem(self->data, key) < 0) {
{ PyErr_SetString(PyExc_RuntimeError,
PyErr_SetString(PyExc_RuntimeError, "unexpectedly couldnt remove key in cc_ass_sub");
"unexpectedly couldnt remove key in cc_ass_sub"); return -1;
return -1; }
}
if(check_ring(self,"post-delitem")) return -1; if (check_ring(self, "post-delitem"))
return -1;
return 0; return 0;
}
} }
static int _check_ring(ccobject *self,const char *context) static int
cc_ass_sub(ccobject *self, PyObject *key, PyObject *v)
{ {
if (v)
return cc_add_item(self, key, v);
else
return cc_del_item(self, key);
}
static int
_check_ring(ccobject *self,const char *context)
{
CPersistentRing *here = &(self->ring_home); CPersistentRing *here = &(self->ring_home);
int expected = 1+self->non_ghost_count; int expected = 1+self->non_ghost_count;
int total = 0; int total = 0;
do do {
{ if (++total > (expected + 10))
if(++total>(expected+10)) return 3; /* ring too big, by a large margin */ return 3; /* ring too big, by a large margin */
if(!here->next) return 4; /* various linking problems */ if (!here->next)
if(!here->prev) return 5; return 4; /* various linking problems */
if(!here->next->prev) return 7; if (!here->prev)
if(!here->prev->next) return 8; return 5;
if(here->prev->next!=here) return 9; if (!here->next->prev)
if(here->next->prev!=here) return 10; return 7;
if(!self->ring_lock) if (!here->prev->next)
return 8;
if (here->prev->next != here)
return 9;
if (here->next->prev != here)
return 10;
if (!self->ring_lock)
{ {
/* if the ring must be locked then it only contains object other than persistent instances */ /* if the ring must be locked then it only contains object other than persistent instances */
if(here!=&self->ring_home) if (here!=&self->ring_home)
{ {
cPersistentObject *object = object_from_ring(self,here,context); cPersistentObject *object = object_from_ring(self,here,context);
if(!object) return 12; if (!object) return 12;
if(object->state==cPersistent_GHOST_STATE) if (object->state==cPersistent_GHOST_STATE)
return 13; return 13;
} }
} }
...@@ -812,27 +845,35 @@ static int _check_ring(ccobject *self,const char *context) ...@@ -812,27 +845,35 @@ static int _check_ring(ccobject *self,const char *context)
} }
while(here!=&self->ring_home); while(here!=&self->ring_home);
if(self->ring_lock) if (self->ring_lock)
{ {
if(total<expected) return 6; /* ring too small; too big is ok when locked */ if (total<expected) return 6; /* ring too small; too big is ok when locked */
} }
else else
{ {
if(total!=expected) return 14; /* ring size wrong, or bad ghost accounting */ if (total!=expected) return 14; /* ring size wrong, or bad ghost accounting */
} }
return 0; return 0;
} }
static int check_ring(ccobject *self,const char *context) /* XXX This function returns false if everything is okay, which is
backwards given its name. All the tests say:
if (check_ring(...))
return error
*/
static int
check_ring(ccobject *self, const char *context)
{ {
#ifdef MUCH_RING_CHECKING #ifdef MUCH_RING_CHECKING
int code=_check_ring(self,context); int code = _check_ring(self,context);
if(code) if (code) {
{ PyErr_Format(PyExc_RuntimeError,
/*printf(stderr,"BROKEN RING (code %d) in %s, size %d\n",code,context,PyDict_Size(self->data));*/ "broken ring (code %d) in %s, size %d",
PyErr_Format(PyExc_RuntimeError,"broken ring (code %d) in %s, size %d",code,context,PyDict_Size(self->data)); code, context, PyDict_Size(self->data));
return code; return code;
} }
#endif #endif
...@@ -845,11 +886,11 @@ present_in_ring(ccobject *self,CPersistentRing *target) ...@@ -845,11 +886,11 @@ present_in_ring(ccobject *self,CPersistentRing *target)
CPersistentRing *here = self->ring_home.next; CPersistentRing *here = self->ring_home.next;
while(1) while(1)
{ {
if(here==target) if (here==target)
{ {
return 1; return 1;
} }
if(here==&self->ring_home) if (here==&self->ring_home)
{ {
/* back to the home position, and we didnt find it */ /* back to the home position, and we didnt find it */
return 0; return 0;
...@@ -897,7 +938,7 @@ newccobject(PyObject *jar, int cache_size, int cache_age) ...@@ -897,7 +938,7 @@ newccobject(PyObject *jar, int cache_size, int cache_age)
UNLESS(self = PyObject_NEW(ccobject, &Cctype)) return NULL; UNLESS(self = PyObject_NEW(ccobject, &Cctype)) return NULL;
self->setklassstate=self->jar=NULL; self->setklassstate=self->jar=NULL;
if((self->data=PyDict_New())) if ((self->data=PyDict_New()))
{ {
self->jar=jar; self->jar=jar;
Py_INCREF(jar); Py_INCREF(jar);
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
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.44 2002/04/01 23:45:48 jeremy Exp $\n"; "$Id: cPickleCache.c,v 1.45 2002/04/02 06:03:39 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))
...@@ -31,6 +31,9 @@ static char cPickleCache_doc_string[] = ...@@ -31,6 +31,9 @@ static char cPickleCache_doc_string[] =
static PyObject *py__p_oid, *py_reload, *py__p_jar, *py__p_changed; static PyObject *py__p_oid, *py_reload, *py__p_jar, *py__p_changed;
/* define this for extra debugging checks, and lousy performance */ /* define this for extra debugging checks, and lousy performance */
/* Are any of these checks necessary in production code? How do we
decide when to disable it?
*/
#define MUCH_RING_CHECKING 1 #define MUCH_RING_CHECKING 1
/* Do we want 'engine noise'.... abstract debugging output useful for /* Do we want 'engine noise'.... abstract debugging output useful for
...@@ -121,16 +124,16 @@ scan_gc_items(ccobject *self,int target) ...@@ -121,16 +124,16 @@ scan_gc_items(ccobject *self,int target)
#ifdef MUCH_RING_CHECKING #ifdef MUCH_RING_CHECKING
int safety_counter = self->cache_size*10; int safety_counter = self->cache_size*10;
if(safety_counter<10000) safety_counter = 10000; if (safety_counter<10000)
safety_counter = 10000;
#endif #endif
while(1) while (1) {
{ if (check_ring(self, "mid-gc"))
if(check_ring(self,"mid-gc")) return -1; return -1;
#ifdef MUCH_RING_CHECKING #ifdef MUCH_RING_CHECKING
if(!safety_counter--) if (!safety_counter--) {
{
/* This loop has been running for a very long time. /* This loop has been running for a very long time.
It is possible that someone loaded a very large number of objects, It is possible that someone loaded a very large number of objects,
and now wants us to blow them all away. However it may and now wants us to blow them all away. However it may
...@@ -138,37 +141,35 @@ scan_gc_items(ccobject *self,int target) ...@@ -138,37 +141,35 @@ scan_gc_items(ccobject *self,int target)
long then you really have to doubt it will ever terminate. long then you really have to doubt it will ever terminate.
In the MUCH_RING_CHECKING build we prefer to raise an exception In the MUCH_RING_CHECKING build we prefer to raise an exception
here */ here */
PyErr_SetString(PyExc_RuntimeError,"scan_gc_items safety counter exceeded"); PyErr_SetString(PyExc_RuntimeError,
"scan_gc_items safety counter exceeded");
return -1; return -1;
} }
if(!present_in_ring(self,here)) if (!present_in_ring(self, here)) {
{ /* Our current working position is no longer in the ring.
/* Our current working position is no longer in the ring. Thats bad. */ That's bad. */
PyErr_SetString(PyExc_RuntimeError,"working position fell out the ring, in scan_gc_items"); PyErr_SetString(PyExc_RuntimeError,
"working position fell out the ring, in scan_gc_items");
return -1; return -1;
} }
#endif #endif
if(here==&self->ring_home) /* back to the home position. stop looking */
{ if (here == &self->ring_home)
/* back to the home position. stop looking */
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 from
persistent objects, plus our own home node. We can safely persistent objects, plus our own home node. We can safely
assume this is a persistent object now we know it is not the home */ assume this 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) return -1; if (!object)
return -1;
if(self->non_ghost_count<=target) /* we are small enough */
{ if (self->non_ghost_count <= target)
/* we are small enough */
return 0; return 0;
} 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. */
ENGINE_NOISE("G"); ENGINE_NOISE("G");
...@@ -177,49 +178,46 @@ scan_gc_items(ccobject *self,int target) ...@@ -177,49 +178,46 @@ scan_gc_items(ccobject *self,int target)
placeholder.next = here->next; placeholder.next = here->next;
placeholder.prev = here; placeholder.prev = here;
here->next->prev = &placeholder; here->next->prev = &placeholder;
here->next = &placeholder; here->next = &placeholder;
error = PyObject_SetAttr((PyObject *)object,py__p_changed,Py_None); error = PyObject_SetAttr((PyObject *)object, py__p_changed,
Py_None);
/* unlink the placeholder */ /* unlink the placeholder */
placeholder.next->prev=placeholder.prev; placeholder.next->prev = placeholder.prev;
placeholder.prev->next=placeholder.next; placeholder.prev->next = placeholder.next;
here = placeholder.next; here = placeholder.next;
if(error) if(error)
return -1; /* problem */ return -1; /* problem */
} } else {
else
{
ENGINE_NOISE("."); ENGINE_NOISE(".");
here = here->next; here = here->next;
} }
} }
} }
static PyObject * static PyObject *
lockgc(ccobject *self,int target_size) lockgc(ccobject *self, int target_size)
{ {
if(self->ring_lock) if (self->ring_lock) {
{
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
} }
if(check_ring(self,"pre-gc")) return NULL; if (check_ring(self, "pre-gc"))
return NULL;
ENGINE_NOISE("<"); ENGINE_NOISE("<");
self->ring_lock = 1; self->ring_lock = 1;
if(scan_gc_items(self,target_size)) if (scan_gc_items(self, target_size)) {
{
self->ring_lock = 0; self->ring_lock = 0;
return NULL; return NULL;
} }
self->ring_lock = 0; self->ring_lock = 0;
ENGINE_NOISE(">\n"); ENGINE_NOISE(">\n");
if(check_ring(self,"post-gc")) return NULL; if (check_ring(self, "post-gc"))
return NULL;
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
...@@ -228,23 +226,22 @@ lockgc(ccobject *self,int target_size) ...@@ -228,23 +226,22 @@ lockgc(ccobject *self,int target_size)
static PyObject * static PyObject *
cc_incrgc(ccobject *self, PyObject *args) cc_incrgc(ccobject *self, PyObject *args)
{ {
int n=1; int n = 1;
int starting_size = self->non_ghost_count; int starting_size = self->non_ghost_count;
int target_size = self->cache_size; int target_size = self->cache_size;
if(self->cache_drain_resistance>=1) if (self->cache_drain_resistance >= 1) {
{
/* This cache will gradually drain down to a small size. Check /* This cache will gradually drain down to a small size. Check
a (small) number of objects proportional to the current size */ a (small) number of objects proportional to the current size */
int target_size_2 = starting_size - 1 - starting_size/self->cache_drain_resistance; int target_size_2 = (starting_size - 1
if(target_size_2<target_size) - starting_size / self->cache_drain_resistance);
if (target_size_2 < target_size)
target_size = target_size_2; target_size = target_size_2;
} }
UNLESS (PyArg_ParseTuple(args, "|i",&n)) return NULL; if (!PyArg_ParseTuple(args, "|i:incrgc", &n))
return NULL;
return lockgc(self,target_size); return lockgc(self,target_size);
} }
...@@ -252,54 +249,46 @@ cc_incrgc(ccobject *self, PyObject *args) ...@@ -252,54 +249,46 @@ cc_incrgc(ccobject *self, PyObject *args)
static PyObject * static PyObject *
cc_full_sweep(ccobject *self, PyObject *args) cc_full_sweep(ccobject *self, PyObject *args)
{ {
int dt=0; int dt = 0;
UNLESS(PyArg_ParseTuple(args, "|i", &dt)) return NULL; if (!PyArg_ParseTuple(args, "|i:full_sweep", &dt))
return lockgc(self,0); return NULL;
return lockgc(self,0);
} }
static PyObject * static PyObject *
cc_reallyfull_sweep(ccobject *self, PyObject *args) cc_reallyfull_sweep(ccobject *self, PyObject *args)
{ {
int dt=0; int dt = 0;
UNLESS(PyArg_ParseTuple(args, "|i", &dt)) return NULL; if (!PyArg_ParseTuple(args, "|i:reallyfull_sweep", &dt))
return NULL;
return lockgc(self,0); return lockgc(self,0);
} }
static void static void
_invalidate(ccobject *self, PyObject *key) _invalidate(ccobject *self, PyObject *key)
{ {
PyObject *v=object_from_oid(self, key); PyObject *v = object_from_oid(self, key);
if(!v) if (!v)
{ return;
/* shouldnt this be an error? for now Ill follow Jims lead */ if (PyExtensionClass_Check(v)) {
PyErr_Clear(); if (v->ob_refcnt <= 1) {
} self->klass_count--;
else if (PyDict_DelItem(self->data, key) < 0)
{ PyErr_Clear();
if (PyExtensionClass_Check(v)) }
{ else {
if(v->ob_refcnt <= 1) v = PyObject_CallFunction(self->setklassstate, "O", v);
{ if (v)
self->klass_count--; Py_DECREF(v);
if (PyDict_DelItem(self->data, key) < 0) else
PyErr_Clear(); PyErr_Clear();
} }
else } else {
{ if (PyObject_DelAttr(v, py__p_changed) < 0)
v=PyObject_CallFunction(self->setklassstate, PyErr_Clear();
"O", v);
if (v) Py_DECREF(v);
else PyErr_Clear();
}
}
else
{
if(PyObject_DelAttr(v,py__p_changed) < 0)
PyErr_Clear();
}
Py_DECREF(v);
} }
Py_DECREF(v);
} }
static PyObject * static PyObject *
...@@ -379,21 +368,26 @@ cc_klass_items(ccobject *self, PyObject *args) ...@@ -379,21 +368,26 @@ cc_klass_items(ccobject *self, PyObject *args)
PyObject *l,*k,*v; PyObject *l,*k,*v;
int p = 0; int p = 0;
if(!PyArg_ParseTuple(args,"")) return NULL; if (!PyArg_ParseTuple(args, ":klass_items"))
return NULL;
l = PyList_New(0); l = PyList_New(PyDict_Size(self->data));
if(!l) return NULL; if (l == NULL)
return NULL;
while(PyDict_Next(self->data, &p, &k, &v)) while (PyDict_Next(self->data, &p, &k, &v)) {
{ if(PyExtensionClass_Check(v)) {
if(PyExtensionClass_Check(v)) v = Py_BuildValue("OO", k, v);
{ if (v == NULL) {
v=PyObject_CallMethod(l,"append","((OO))",k,v); Py_DECREF(l);
if(!v) return NULL;
{ }
Py_DECREF(l); if (PyList_Append(l, v) < 0) {
return NULL; Py_DECREF(v);
} Py_DECREF(l);
return NULL;
}
Py_DECREF(v);
} }
} }
...@@ -406,35 +400,41 @@ cc_lru_items(ccobject *self, PyObject *args) ...@@ -406,35 +400,41 @@ cc_lru_items(ccobject *self, PyObject *args)
PyObject *l; PyObject *l;
CPersistentRing *here; CPersistentRing *here;
if(!PyArg_ParseTuple(args,"")) return NULL; if (!PyArg_ParseTuple(args, ":lru_items"))
return NULL;
if(self->ring_lock) if (self->ring_lock) {
{ 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;
} }
if(check_ring(self,"pre-cc_items")) return NULL; if (check_ring(self, "pre-cc_items"))
return NULL;
l = PyList_New(0); l = PyList_New(0);
if(!l) return NULL; if (l == NULL)
return NULL;
here = self->ring_home.next; here = self->ring_home.next;
while(here!=&self->ring_home) while (here != &self->ring_home) {
{
cPersistentObject *object = object_from_ring(self,here,"cc_items");
PyObject *v; PyObject *v;
if(!object) cPersistentObject *object = object_from_ring(self, here, "cc_items");
{
if (object == NULL) {
Py_DECREF(l); Py_DECREF(l);
return NULL; return NULL;
} }
v=PyObject_CallMethod(l,"append","((OO))",object->oid,object); v = Py_BuildValue("OO", object->oid, object);
if(!v) if (v == NULL) {
{
Py_DECREF(l); Py_DECREF(l);
return NULL; return NULL;
} }
if (PyList_Append(l, v) < 0) {
Py_DECREF(v);
Py_DECREF(l);
return NULL;
}
Py_DECREF(v); Py_DECREF(v);
here = here->next; here = here->next;
} }
...@@ -442,23 +442,38 @@ cc_lru_items(ccobject *self, PyObject *args) ...@@ -442,23 +442,38 @@ cc_lru_items(ccobject *self, PyObject *args)
return l; return l;
} }
/* XXX What does this function do? */
static PyObject * static PyObject *
cc_oid_unreferenced(ccobject *self, PyObject *args) cc_oid_unreferenced(ccobject *self, PyObject *args)
{ {
PyObject *oid,*v; PyObject *oid, *v;
if(!PyArg_ParseTuple(args,"O",&oid)) return NULL; if (!PyArg_ParseTuple(args, "O:_oid_unreferenced", &oid))
return NULL;
v = PyDict_GetItem(self->data, oid); v = PyDict_GetItem(self->data, oid);
if(!v) return NULL; if (v == NULL) {
PyErr_SetObject(PyExc_KeyError, oid);
/* jeremy debug
fprintf(stderr, "oid_unreferenced: key error\n");
*/
return NULL;
}
if(v->ob_refcnt) /* jeremy debug
{ fprintf(stderr, "oid_unreferenced: %X %d %s\n", v,
PyErr_Format(PyExc_ValueError,"object has reference count of %d, should be zero",v->ob_refcnt); 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; return NULL;
} }
/* 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.
*/
#ifdef Py_TRACE_REFS #ifdef Py_TRACE_REFS
#error "this code path has not been tested - Toby Dickenson" #error "this code path has not been tested - Toby Dickenson"
...@@ -468,9 +483,9 @@ cc_oid_unreferenced(ccobject *self, PyObject *args) ...@@ -468,9 +483,9 @@ cc_oid_unreferenced(ccobject *self, PyObject *args)
Py_INCREF(v); Py_INCREF(v);
#endif #endif
if(v->ob_refcnt!=1) if (v->ob_refcnt != 1) {
{ PyErr_SetString(PyExc_ValueError,
PyErr_SetString(PyExc_ValueError,"refcount is not 1 after resurrection"); "refcount is not 1 after resurrection");
return NULL; return NULL;
} }
...@@ -479,9 +494,9 @@ cc_oid_unreferenced(ccobject *self, PyObject *args) ...@@ -479,9 +494,9 @@ cc_oid_unreferenced(ccobject *self, PyObject *args)
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 NULL;
} }
...@@ -546,7 +561,8 @@ cc_getattr(ccobject *self, char *name) ...@@ -546,7 +561,8 @@ cc_getattr(ccobject *self, char *name)
{ {
PyObject *r; PyObject *r;
if(check_ring(self,"getattr")) return NULL; if (check_ring(self, "getattr"))
return NULL;
if(*name=='c') if(*name=='c')
{ {
...@@ -632,179 +648,196 @@ cc_subscript(ccobject *self, PyObject *key) ...@@ -632,179 +648,196 @@ cc_subscript(ccobject *self, PyObject *key)
} }
static int static int
cc_ass_sub(ccobject *self, PyObject *key, PyObject *v) cc_add_item(ccobject *self, PyObject *key, PyObject *v)
{ {
int result; int result;
if(v) PyExtensionClass *class;
{
if( ( PyExtensionInstance_Check(v) && if (!PyExtensionInstance_Check(v)) {
(((PyExtensionClass*)(v->ob_type))->class_flags & PERSISTENT_TYPE_FLAG) && PyErr_SetString(PyExc_ValueError,
(v->ob_type->tp_basicsize >= sizeof(cPersistentObject)) "Cache values must be persistent objects.");
) return -1;
|| }
PyExtensionClass_Check(v) /* Is this set of tests necessary? */
) class = (PyExtensionClass *)(v->ob_type);
{ if (!((class->class_flags & PERSISTENT_TYPE_FLAG)
PyObject *oid = PyObject_GetAttr(v,py__p_oid); && v->ob_type->tp_basicsize >= sizeof(cPersistentObject))) {
PyObject *object_again; PyErr_SetString(PyExc_ValueError,
if(!oid) "Cache values must be persistent objects.");
{ return -1;
return -1; }
}
if(PyObject_Cmp(key,oid,&result)) /* XXX Why not self->oid? */
{ PyObject *oid = PyObject_GetAttr(v, py__p_oid);
Py_DECREF(oid); PyObject *object_again;
return -1; cPersistentObject *p;
}
Py_DECREF(oid); if (oid == NULL)
if(result) return -1;
{ /* XXX key and oid should both be PyString objects */
PyErr_SetString(PyExc_ValueError,"key must be the same as the object's oid attribute"); if (PyObject_Cmp(key, oid, &result)) {
return -1; Py_DECREF(oid);
} return -1;
object_again = object_from_oid(self, key); }
if(object_again) Py_DECREF(oid);
{ if (result) {
if(object_again!=v) PyErr_SetString(PyExc_ValueError,
{ "key must be the same as the object's oid attribute");
Py_DECREF(object_again); return -1;
PyErr_SetString(PyExc_ValueError,"Can not re-register object under a different oid"); }
return -1; object_again = object_from_oid(self, key);
} if (object_again) {
else /* XXX Do we need to check for this? */
{ if (object_again != v) {
/* re-register under the same oid - no work needed */ Py_DECREF(object_again);
Py_DECREF(object_again); PyErr_SetString(PyExc_ValueError,
return 0; "Can not re-register object under a different oid");
} return -1;
} } else {
if(PyExtensionClass_Check(v)) /* re-register under the same oid - no work needed */
{ Py_DECREF(object_again);
if(PyDict_SetItem(self->data, key, v)) return -1; return 0;
self->klass_count++; }
return 0; }
} if (PyExtensionClass_Check(v)) {
else if (PyDict_SetItem(self->data, key, v))
{ return -1;
if(((cPersistentObject*)v)->cache) { self->klass_count++;
if(((cPersistentObject*)v)->cache != (PerCache *)self) { return 0;
/* This object is already in a different cache. */ } else {
PyErr_SetString(PyExc_ValueError, PerCache *cache = ((cPersistentObject *)v)->cache;
if (cache) {
if (cache != (PerCache *)self)
/* This object is already in a different cache. */
PyErr_SetString(PyExc_ValueError,
"Cache values may only be in one cache."); "Cache values may only be in one cache.");
return -1; return -1;
} }
/* else: /* else:
This object is already one of ours, which This object is already one of ours, which is ok. It
is ok. It would be very strange if someone would be very strange if someone was trying to register
was trying to register the same object under a the same object under a different key.
different key. */
*/ }
}
if (check_ring(self, "pre-setitem"))
if(check_ring(self,"pre-setitem")) return -1; return -1;
if(PyDict_SetItem(self->data, key, v)) return -1; if (PyDict_SetItem(self->data, key, v))
return -1;
Py_INCREF(self);
((cPersistentObject*)v)->cache = (PerCache *)self; Py_INCREF(self);
if(((cPersistentObject*)v)->state>=0) p = (cPersistentObject *)v;
{ p->cache = (PerCache *)self;
/* insert this non-ghost object into the ring just behind the home position */ if (p->state >= 0) {
self->non_ghost_count++; /* insert this non-ghost object into the ring just
((cPersistentObject*)v)->ring.next = &self->ring_home; behind the home position */
((cPersistentObject*)v)->ring.prev = self->ring_home.prev; self->non_ghost_count++;
self->ring_home.prev->next = &((cPersistentObject*)v)->ring; p->ring.next = &self->ring_home;
self->ring_home.prev = &((cPersistentObject*)v)->ring; p->ring.prev = self->ring_home.prev;
} self->ring_home.prev->next = &p->ring;
else self->ring_home.prev = &p->ring;
{ } else {
/* steal a reference from the dictionary; ghosts have a weak reference */ /* steal a reference from the dictionary;
Py_DECREF(v); ghosts have a weak reference */
} Py_DECREF(v);
if(check_ring(self,"post-setitem")) return -1;
return 0;
}
}
else
{
PyErr_SetString(PyExc_ValueError, "Cache values must be persistent objects.");
return -1;
}
} }
if (check_ring(self, "post-setitem"))
return -1;
else else
{ return 0;
/* unlink this item from the ring */ }
if(check_ring(self,"pre-delitem")) return -1;
v = (PyObject *)object_from_oid(self,key);
if(!v) return -1;
if(PyExtensionClass_Check(v)) static int
{ cc_del_item(ccobject *self, PyObject *key)
self->klass_count--; {
} PyObject *v;
else cPersistentObject *p;
{
if(((cPersistentObject*)v)->state>=0) /* unlink this item from the ring */
{ if (check_ring(self, "pre-delitem"))
self->non_ghost_count--; return -1;
((cPersistentObject*)v)->ring.next->prev = ((cPersistentObject*)v)->ring.prev;
((cPersistentObject*)v)->ring.prev->next = ((cPersistentObject*)v)->ring.next; v = (PyObject *)object_from_oid(self, key);
((cPersistentObject*)v)->ring.prev = NULL; if (v == NULL)
((cPersistentObject*)v)->ring.next = NULL; return -1;
}
else if (PyExtensionClass_Check(v)) {
{ self->klass_count--;
/* This is a ghost object, so we havent kept a reference count on it. } else {
For it have stayed alive this long someone else must be keeping a reference p = (cPersistentObject *)v;
to it. Therefore we need to temporarily give it back a reference count if (p->state >= 0) {
before calling DelItem below */ self->non_ghost_count--;
Py_INCREF(v); p->ring.next->prev = p->ring.prev;
} p->ring.prev->next = p->ring.next;
p->ring.prev = NULL;
p->ring.next = NULL;
} else {
/* This is a ghost object, so we havent kept a reference
count on it. For it have stayed alive this long
someone else must be keeping a reference to
it. Therefore we need to temporarily give it back a
reference count before calling DelItem below */
Py_INCREF(v);
}
Py_DECREF((PyObject *)((cPersistentObject*)v)->cache); Py_DECREF((PyObject *)p->cache);
((cPersistentObject*)v)->cache = NULL; p->cache = NULL;
} }
Py_DECREF(v); Py_DECREF(v);
if(PyDict_DelItem(self->data, key)) if (PyDict_DelItem(self->data, key) < 0) {
{ PyErr_SetString(PyExc_RuntimeError,
PyErr_SetString(PyExc_RuntimeError, "unexpectedly couldnt remove key in cc_ass_sub");
"unexpectedly couldnt remove key in cc_ass_sub"); return -1;
return -1; }
}
if(check_ring(self,"post-delitem")) return -1; if (check_ring(self, "post-delitem"))
return -1;
return 0; return 0;
}
} }
static int _check_ring(ccobject *self,const char *context) static int
cc_ass_sub(ccobject *self, PyObject *key, PyObject *v)
{ {
if (v)
return cc_add_item(self, key, v);
else
return cc_del_item(self, key);
}
static int
_check_ring(ccobject *self,const char *context)
{
CPersistentRing *here = &(self->ring_home); CPersistentRing *here = &(self->ring_home);
int expected = 1+self->non_ghost_count; int expected = 1+self->non_ghost_count;
int total = 0; int total = 0;
do do {
{ if (++total > (expected + 10))
if(++total>(expected+10)) return 3; /* ring too big, by a large margin */ return 3; /* ring too big, by a large margin */
if(!here->next) return 4; /* various linking problems */ if (!here->next)
if(!here->prev) return 5; return 4; /* various linking problems */
if(!here->next->prev) return 7; if (!here->prev)
if(!here->prev->next) return 8; return 5;
if(here->prev->next!=here) return 9; if (!here->next->prev)
if(here->next->prev!=here) return 10; return 7;
if(!self->ring_lock) if (!here->prev->next)
return 8;
if (here->prev->next != here)
return 9;
if (here->next->prev != here)
return 10;
if (!self->ring_lock)
{ {
/* if the ring must be locked then it only contains object other than persistent instances */ /* if the ring must be locked then it only contains object other than persistent instances */
if(here!=&self->ring_home) if (here!=&self->ring_home)
{ {
cPersistentObject *object = object_from_ring(self,here,context); cPersistentObject *object = object_from_ring(self,here,context);
if(!object) return 12; if (!object) return 12;
if(object->state==cPersistent_GHOST_STATE) if (object->state==cPersistent_GHOST_STATE)
return 13; return 13;
} }
} }
...@@ -812,27 +845,35 @@ static int _check_ring(ccobject *self,const char *context) ...@@ -812,27 +845,35 @@ static int _check_ring(ccobject *self,const char *context)
} }
while(here!=&self->ring_home); while(here!=&self->ring_home);
if(self->ring_lock) if (self->ring_lock)
{ {
if(total<expected) return 6; /* ring too small; too big is ok when locked */ if (total<expected) return 6; /* ring too small; too big is ok when locked */
} }
else else
{ {
if(total!=expected) return 14; /* ring size wrong, or bad ghost accounting */ if (total!=expected) return 14; /* ring size wrong, or bad ghost accounting */
} }
return 0; return 0;
} }
static int check_ring(ccobject *self,const char *context) /* XXX This function returns false if everything is okay, which is
backwards given its name. All the tests say:
if (check_ring(...))
return error
*/
static int
check_ring(ccobject *self, const char *context)
{ {
#ifdef MUCH_RING_CHECKING #ifdef MUCH_RING_CHECKING
int code=_check_ring(self,context); int code = _check_ring(self,context);
if(code) if (code) {
{ PyErr_Format(PyExc_RuntimeError,
/*printf(stderr,"BROKEN RING (code %d) in %s, size %d\n",code,context,PyDict_Size(self->data));*/ "broken ring (code %d) in %s, size %d",
PyErr_Format(PyExc_RuntimeError,"broken ring (code %d) in %s, size %d",code,context,PyDict_Size(self->data)); code, context, PyDict_Size(self->data));
return code; return code;
} }
#endif #endif
...@@ -845,11 +886,11 @@ present_in_ring(ccobject *self,CPersistentRing *target) ...@@ -845,11 +886,11 @@ present_in_ring(ccobject *self,CPersistentRing *target)
CPersistentRing *here = self->ring_home.next; CPersistentRing *here = self->ring_home.next;
while(1) while(1)
{ {
if(here==target) if (here==target)
{ {
return 1; return 1;
} }
if(here==&self->ring_home) if (here==&self->ring_home)
{ {
/* back to the home position, and we didnt find it */ /* back to the home position, and we didnt find it */
return 0; return 0;
...@@ -897,7 +938,7 @@ newccobject(PyObject *jar, int cache_size, int cache_age) ...@@ -897,7 +938,7 @@ newccobject(PyObject *jar, int cache_size, int cache_age)
UNLESS(self = PyObject_NEW(ccobject, &Cctype)) return NULL; UNLESS(self = PyObject_NEW(ccobject, &Cctype)) return NULL;
self->setklassstate=self->jar=NULL; self->setklassstate=self->jar=NULL;
if((self->data=PyDict_New())) if ((self->data=PyDict_New()))
{ {
self->jar=jar; self->jar=jar;
Py_INCREF(jar); Py_INCREF(jar);
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
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.44 2002/04/01 23:45:48 jeremy Exp $\n"; "$Id: cPickleCache.c,v 1.45 2002/04/02 06:03:39 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))
...@@ -31,6 +31,9 @@ static char cPickleCache_doc_string[] = ...@@ -31,6 +31,9 @@ static char cPickleCache_doc_string[] =
static PyObject *py__p_oid, *py_reload, *py__p_jar, *py__p_changed; static PyObject *py__p_oid, *py_reload, *py__p_jar, *py__p_changed;
/* define this for extra debugging checks, and lousy performance */ /* define this for extra debugging checks, and lousy performance */
/* Are any of these checks necessary in production code? How do we
decide when to disable it?
*/
#define MUCH_RING_CHECKING 1 #define MUCH_RING_CHECKING 1
/* Do we want 'engine noise'.... abstract debugging output useful for /* Do we want 'engine noise'.... abstract debugging output useful for
...@@ -121,16 +124,16 @@ scan_gc_items(ccobject *self,int target) ...@@ -121,16 +124,16 @@ scan_gc_items(ccobject *self,int target)
#ifdef MUCH_RING_CHECKING #ifdef MUCH_RING_CHECKING
int safety_counter = self->cache_size*10; int safety_counter = self->cache_size*10;
if(safety_counter<10000) safety_counter = 10000; if (safety_counter<10000)
safety_counter = 10000;
#endif #endif
while(1) while (1) {
{ if (check_ring(self, "mid-gc"))
if(check_ring(self,"mid-gc")) return -1; return -1;
#ifdef MUCH_RING_CHECKING #ifdef MUCH_RING_CHECKING
if(!safety_counter--) if (!safety_counter--) {
{
/* This loop has been running for a very long time. /* This loop has been running for a very long time.
It is possible that someone loaded a very large number of objects, It is possible that someone loaded a very large number of objects,
and now wants us to blow them all away. However it may and now wants us to blow them all away. However it may
...@@ -138,37 +141,35 @@ scan_gc_items(ccobject *self,int target) ...@@ -138,37 +141,35 @@ scan_gc_items(ccobject *self,int target)
long then you really have to doubt it will ever terminate. long then you really have to doubt it will ever terminate.
In the MUCH_RING_CHECKING build we prefer to raise an exception In the MUCH_RING_CHECKING build we prefer to raise an exception
here */ here */
PyErr_SetString(PyExc_RuntimeError,"scan_gc_items safety counter exceeded"); PyErr_SetString(PyExc_RuntimeError,
"scan_gc_items safety counter exceeded");
return -1; return -1;
} }
if(!present_in_ring(self,here)) if (!present_in_ring(self, here)) {
{ /* Our current working position is no longer in the ring.
/* Our current working position is no longer in the ring. Thats bad. */ That's bad. */
PyErr_SetString(PyExc_RuntimeError,"working position fell out the ring, in scan_gc_items"); PyErr_SetString(PyExc_RuntimeError,
"working position fell out the ring, in scan_gc_items");
return -1; return -1;
} }
#endif #endif
if(here==&self->ring_home) /* back to the home position. stop looking */
{ if (here == &self->ring_home)
/* back to the home position. stop looking */
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 from
persistent objects, plus our own home node. We can safely persistent objects, plus our own home node. We can safely
assume this is a persistent object now we know it is not the home */ assume this 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) return -1; if (!object)
return -1;
if(self->non_ghost_count<=target) /* we are small enough */
{ if (self->non_ghost_count <= target)
/* we are small enough */
return 0; return 0;
} 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. */
ENGINE_NOISE("G"); ENGINE_NOISE("G");
...@@ -177,49 +178,46 @@ scan_gc_items(ccobject *self,int target) ...@@ -177,49 +178,46 @@ scan_gc_items(ccobject *self,int target)
placeholder.next = here->next; placeholder.next = here->next;
placeholder.prev = here; placeholder.prev = here;
here->next->prev = &placeholder; here->next->prev = &placeholder;
here->next = &placeholder; here->next = &placeholder;
error = PyObject_SetAttr((PyObject *)object,py__p_changed,Py_None); error = PyObject_SetAttr((PyObject *)object, py__p_changed,
Py_None);
/* unlink the placeholder */ /* unlink the placeholder */
placeholder.next->prev=placeholder.prev; placeholder.next->prev = placeholder.prev;
placeholder.prev->next=placeholder.next; placeholder.prev->next = placeholder.next;
here = placeholder.next; here = placeholder.next;
if(error) if(error)
return -1; /* problem */ return -1; /* problem */
} } else {
else
{
ENGINE_NOISE("."); ENGINE_NOISE(".");
here = here->next; here = here->next;
} }
} }
} }
static PyObject * static PyObject *
lockgc(ccobject *self,int target_size) lockgc(ccobject *self, int target_size)
{ {
if(self->ring_lock) if (self->ring_lock) {
{
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
} }
if(check_ring(self,"pre-gc")) return NULL; if (check_ring(self, "pre-gc"))
return NULL;
ENGINE_NOISE("<"); ENGINE_NOISE("<");
self->ring_lock = 1; self->ring_lock = 1;
if(scan_gc_items(self,target_size)) if (scan_gc_items(self, target_size)) {
{
self->ring_lock = 0; self->ring_lock = 0;
return NULL; return NULL;
} }
self->ring_lock = 0; self->ring_lock = 0;
ENGINE_NOISE(">\n"); ENGINE_NOISE(">\n");
if(check_ring(self,"post-gc")) return NULL; if (check_ring(self, "post-gc"))
return NULL;
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
...@@ -228,23 +226,22 @@ lockgc(ccobject *self,int target_size) ...@@ -228,23 +226,22 @@ lockgc(ccobject *self,int target_size)
static PyObject * static PyObject *
cc_incrgc(ccobject *self, PyObject *args) cc_incrgc(ccobject *self, PyObject *args)
{ {
int n=1; int n = 1;
int starting_size = self->non_ghost_count; int starting_size = self->non_ghost_count;
int target_size = self->cache_size; int target_size = self->cache_size;
if(self->cache_drain_resistance>=1) if (self->cache_drain_resistance >= 1) {
{
/* This cache will gradually drain down to a small size. Check /* This cache will gradually drain down to a small size. Check
a (small) number of objects proportional to the current size */ a (small) number of objects proportional to the current size */
int target_size_2 = starting_size - 1 - starting_size/self->cache_drain_resistance; int target_size_2 = (starting_size - 1
if(target_size_2<target_size) - starting_size / self->cache_drain_resistance);
if (target_size_2 < target_size)
target_size = target_size_2; target_size = target_size_2;
} }
UNLESS (PyArg_ParseTuple(args, "|i",&n)) return NULL; if (!PyArg_ParseTuple(args, "|i:incrgc", &n))
return NULL;
return lockgc(self,target_size); return lockgc(self,target_size);
} }
...@@ -252,54 +249,46 @@ cc_incrgc(ccobject *self, PyObject *args) ...@@ -252,54 +249,46 @@ cc_incrgc(ccobject *self, PyObject *args)
static PyObject * static PyObject *
cc_full_sweep(ccobject *self, PyObject *args) cc_full_sweep(ccobject *self, PyObject *args)
{ {
int dt=0; int dt = 0;
UNLESS(PyArg_ParseTuple(args, "|i", &dt)) return NULL; if (!PyArg_ParseTuple(args, "|i:full_sweep", &dt))
return lockgc(self,0); return NULL;
return lockgc(self,0);
} }
static PyObject * static PyObject *
cc_reallyfull_sweep(ccobject *self, PyObject *args) cc_reallyfull_sweep(ccobject *self, PyObject *args)
{ {
int dt=0; int dt = 0;
UNLESS(PyArg_ParseTuple(args, "|i", &dt)) return NULL; if (!PyArg_ParseTuple(args, "|i:reallyfull_sweep", &dt))
return NULL;
return lockgc(self,0); return lockgc(self,0);
} }
static void static void
_invalidate(ccobject *self, PyObject *key) _invalidate(ccobject *self, PyObject *key)
{ {
PyObject *v=object_from_oid(self, key); PyObject *v = object_from_oid(self, key);
if(!v) if (!v)
{ return;
/* shouldnt this be an error? for now Ill follow Jims lead */ if (PyExtensionClass_Check(v)) {
PyErr_Clear(); if (v->ob_refcnt <= 1) {
} self->klass_count--;
else if (PyDict_DelItem(self->data, key) < 0)
{ PyErr_Clear();
if (PyExtensionClass_Check(v)) }
{ else {
if(v->ob_refcnt <= 1) v = PyObject_CallFunction(self->setklassstate, "O", v);
{ if (v)
self->klass_count--; Py_DECREF(v);
if (PyDict_DelItem(self->data, key) < 0) else
PyErr_Clear(); PyErr_Clear();
} }
else } else {
{ if (PyObject_DelAttr(v, py__p_changed) < 0)
v=PyObject_CallFunction(self->setklassstate, PyErr_Clear();
"O", v);
if (v) Py_DECREF(v);
else PyErr_Clear();
}
}
else
{
if(PyObject_DelAttr(v,py__p_changed) < 0)
PyErr_Clear();
}
Py_DECREF(v);
} }
Py_DECREF(v);
} }
static PyObject * static PyObject *
...@@ -379,21 +368,26 @@ cc_klass_items(ccobject *self, PyObject *args) ...@@ -379,21 +368,26 @@ cc_klass_items(ccobject *self, PyObject *args)
PyObject *l,*k,*v; PyObject *l,*k,*v;
int p = 0; int p = 0;
if(!PyArg_ParseTuple(args,"")) return NULL; if (!PyArg_ParseTuple(args, ":klass_items"))
return NULL;
l = PyList_New(0); l = PyList_New(PyDict_Size(self->data));
if(!l) return NULL; if (l == NULL)
return NULL;
while(PyDict_Next(self->data, &p, &k, &v)) while (PyDict_Next(self->data, &p, &k, &v)) {
{ if(PyExtensionClass_Check(v)) {
if(PyExtensionClass_Check(v)) v = Py_BuildValue("OO", k, v);
{ if (v == NULL) {
v=PyObject_CallMethod(l,"append","((OO))",k,v); Py_DECREF(l);
if(!v) return NULL;
{ }
Py_DECREF(l); if (PyList_Append(l, v) < 0) {
return NULL; Py_DECREF(v);
} Py_DECREF(l);
return NULL;
}
Py_DECREF(v);
} }
} }
...@@ -406,35 +400,41 @@ cc_lru_items(ccobject *self, PyObject *args) ...@@ -406,35 +400,41 @@ cc_lru_items(ccobject *self, PyObject *args)
PyObject *l; PyObject *l;
CPersistentRing *here; CPersistentRing *here;
if(!PyArg_ParseTuple(args,"")) return NULL; if (!PyArg_ParseTuple(args, ":lru_items"))
return NULL;
if(self->ring_lock) if (self->ring_lock) {
{ 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;
} }
if(check_ring(self,"pre-cc_items")) return NULL; if (check_ring(self, "pre-cc_items"))
return NULL;
l = PyList_New(0); l = PyList_New(0);
if(!l) return NULL; if (l == NULL)
return NULL;
here = self->ring_home.next; here = self->ring_home.next;
while(here!=&self->ring_home) while (here != &self->ring_home) {
{
cPersistentObject *object = object_from_ring(self,here,"cc_items");
PyObject *v; PyObject *v;
if(!object) cPersistentObject *object = object_from_ring(self, here, "cc_items");
{
if (object == NULL) {
Py_DECREF(l); Py_DECREF(l);
return NULL; return NULL;
} }
v=PyObject_CallMethod(l,"append","((OO))",object->oid,object); v = Py_BuildValue("OO", object->oid, object);
if(!v) if (v == NULL) {
{
Py_DECREF(l); Py_DECREF(l);
return NULL; return NULL;
} }
if (PyList_Append(l, v) < 0) {
Py_DECREF(v);
Py_DECREF(l);
return NULL;
}
Py_DECREF(v); Py_DECREF(v);
here = here->next; here = here->next;
} }
...@@ -442,23 +442,38 @@ cc_lru_items(ccobject *self, PyObject *args) ...@@ -442,23 +442,38 @@ cc_lru_items(ccobject *self, PyObject *args)
return l; return l;
} }
/* XXX What does this function do? */
static PyObject * static PyObject *
cc_oid_unreferenced(ccobject *self, PyObject *args) cc_oid_unreferenced(ccobject *self, PyObject *args)
{ {
PyObject *oid,*v; PyObject *oid, *v;
if(!PyArg_ParseTuple(args,"O",&oid)) return NULL; if (!PyArg_ParseTuple(args, "O:_oid_unreferenced", &oid))
return NULL;
v = PyDict_GetItem(self->data, oid); v = PyDict_GetItem(self->data, oid);
if(!v) return NULL; if (v == NULL) {
PyErr_SetObject(PyExc_KeyError, oid);
/* jeremy debug
fprintf(stderr, "oid_unreferenced: key error\n");
*/
return NULL;
}
if(v->ob_refcnt) /* jeremy debug
{ fprintf(stderr, "oid_unreferenced: %X %d %s\n", v,
PyErr_Format(PyExc_ValueError,"object has reference count of %d, should be zero",v->ob_refcnt); 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; return NULL;
} }
/* 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.
*/
#ifdef Py_TRACE_REFS #ifdef Py_TRACE_REFS
#error "this code path has not been tested - Toby Dickenson" #error "this code path has not been tested - Toby Dickenson"
...@@ -468,9 +483,9 @@ cc_oid_unreferenced(ccobject *self, PyObject *args) ...@@ -468,9 +483,9 @@ cc_oid_unreferenced(ccobject *self, PyObject *args)
Py_INCREF(v); Py_INCREF(v);
#endif #endif
if(v->ob_refcnt!=1) if (v->ob_refcnt != 1) {
{ PyErr_SetString(PyExc_ValueError,
PyErr_SetString(PyExc_ValueError,"refcount is not 1 after resurrection"); "refcount is not 1 after resurrection");
return NULL; return NULL;
} }
...@@ -479,9 +494,9 @@ cc_oid_unreferenced(ccobject *self, PyObject *args) ...@@ -479,9 +494,9 @@ cc_oid_unreferenced(ccobject *self, PyObject *args)
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 NULL;
} }
...@@ -546,7 +561,8 @@ cc_getattr(ccobject *self, char *name) ...@@ -546,7 +561,8 @@ cc_getattr(ccobject *self, char *name)
{ {
PyObject *r; PyObject *r;
if(check_ring(self,"getattr")) return NULL; if (check_ring(self, "getattr"))
return NULL;
if(*name=='c') if(*name=='c')
{ {
...@@ -632,179 +648,196 @@ cc_subscript(ccobject *self, PyObject *key) ...@@ -632,179 +648,196 @@ cc_subscript(ccobject *self, PyObject *key)
} }
static int static int
cc_ass_sub(ccobject *self, PyObject *key, PyObject *v) cc_add_item(ccobject *self, PyObject *key, PyObject *v)
{ {
int result; int result;
if(v) PyExtensionClass *class;
{
if( ( PyExtensionInstance_Check(v) && if (!PyExtensionInstance_Check(v)) {
(((PyExtensionClass*)(v->ob_type))->class_flags & PERSISTENT_TYPE_FLAG) && PyErr_SetString(PyExc_ValueError,
(v->ob_type->tp_basicsize >= sizeof(cPersistentObject)) "Cache values must be persistent objects.");
) return -1;
|| }
PyExtensionClass_Check(v) /* Is this set of tests necessary? */
) class = (PyExtensionClass *)(v->ob_type);
{ if (!((class->class_flags & PERSISTENT_TYPE_FLAG)
PyObject *oid = PyObject_GetAttr(v,py__p_oid); && v->ob_type->tp_basicsize >= sizeof(cPersistentObject))) {
PyObject *object_again; PyErr_SetString(PyExc_ValueError,
if(!oid) "Cache values must be persistent objects.");
{ return -1;
return -1; }
}
if(PyObject_Cmp(key,oid,&result)) /* XXX Why not self->oid? */
{ PyObject *oid = PyObject_GetAttr(v, py__p_oid);
Py_DECREF(oid); PyObject *object_again;
return -1; cPersistentObject *p;
}
Py_DECREF(oid); if (oid == NULL)
if(result) return -1;
{ /* XXX key and oid should both be PyString objects */
PyErr_SetString(PyExc_ValueError,"key must be the same as the object's oid attribute"); if (PyObject_Cmp(key, oid, &result)) {
return -1; Py_DECREF(oid);
} return -1;
object_again = object_from_oid(self, key); }
if(object_again) Py_DECREF(oid);
{ if (result) {
if(object_again!=v) PyErr_SetString(PyExc_ValueError,
{ "key must be the same as the object's oid attribute");
Py_DECREF(object_again); return -1;
PyErr_SetString(PyExc_ValueError,"Can not re-register object under a different oid"); }
return -1; object_again = object_from_oid(self, key);
} if (object_again) {
else /* XXX Do we need to check for this? */
{ if (object_again != v) {
/* re-register under the same oid - no work needed */ Py_DECREF(object_again);
Py_DECREF(object_again); PyErr_SetString(PyExc_ValueError,
return 0; "Can not re-register object under a different oid");
} return -1;
} } else {
if(PyExtensionClass_Check(v)) /* re-register under the same oid - no work needed */
{ Py_DECREF(object_again);
if(PyDict_SetItem(self->data, key, v)) return -1; return 0;
self->klass_count++; }
return 0; }
} if (PyExtensionClass_Check(v)) {
else if (PyDict_SetItem(self->data, key, v))
{ return -1;
if(((cPersistentObject*)v)->cache) { self->klass_count++;
if(((cPersistentObject*)v)->cache != (PerCache *)self) { return 0;
/* This object is already in a different cache. */ } else {
PyErr_SetString(PyExc_ValueError, PerCache *cache = ((cPersistentObject *)v)->cache;
if (cache) {
if (cache != (PerCache *)self)
/* This object is already in a different cache. */
PyErr_SetString(PyExc_ValueError,
"Cache values may only be in one cache."); "Cache values may only be in one cache.");
return -1; return -1;
} }
/* else: /* else:
This object is already one of ours, which This object is already one of ours, which is ok. It
is ok. It would be very strange if someone would be very strange if someone was trying to register
was trying to register the same object under a the same object under a different key.
different key. */
*/ }
}
if (check_ring(self, "pre-setitem"))
if(check_ring(self,"pre-setitem")) return -1; return -1;
if(PyDict_SetItem(self->data, key, v)) return -1; if (PyDict_SetItem(self->data, key, v))
return -1;
Py_INCREF(self);
((cPersistentObject*)v)->cache = (PerCache *)self; Py_INCREF(self);
if(((cPersistentObject*)v)->state>=0) p = (cPersistentObject *)v;
{ p->cache = (PerCache *)self;
/* insert this non-ghost object into the ring just behind the home position */ if (p->state >= 0) {
self->non_ghost_count++; /* insert this non-ghost object into the ring just
((cPersistentObject*)v)->ring.next = &self->ring_home; behind the home position */
((cPersistentObject*)v)->ring.prev = self->ring_home.prev; self->non_ghost_count++;
self->ring_home.prev->next = &((cPersistentObject*)v)->ring; p->ring.next = &self->ring_home;
self->ring_home.prev = &((cPersistentObject*)v)->ring; p->ring.prev = self->ring_home.prev;
} self->ring_home.prev->next = &p->ring;
else self->ring_home.prev = &p->ring;
{ } else {
/* steal a reference from the dictionary; ghosts have a weak reference */ /* steal a reference from the dictionary;
Py_DECREF(v); ghosts have a weak reference */
} Py_DECREF(v);
if(check_ring(self,"post-setitem")) return -1;
return 0;
}
}
else
{
PyErr_SetString(PyExc_ValueError, "Cache values must be persistent objects.");
return -1;
}
} }
if (check_ring(self, "post-setitem"))
return -1;
else else
{ return 0;
/* unlink this item from the ring */ }
if(check_ring(self,"pre-delitem")) return -1;
v = (PyObject *)object_from_oid(self,key);
if(!v) return -1;
if(PyExtensionClass_Check(v)) static int
{ cc_del_item(ccobject *self, PyObject *key)
self->klass_count--; {
} PyObject *v;
else cPersistentObject *p;
{
if(((cPersistentObject*)v)->state>=0) /* unlink this item from the ring */
{ if (check_ring(self, "pre-delitem"))
self->non_ghost_count--; return -1;
((cPersistentObject*)v)->ring.next->prev = ((cPersistentObject*)v)->ring.prev;
((cPersistentObject*)v)->ring.prev->next = ((cPersistentObject*)v)->ring.next; v = (PyObject *)object_from_oid(self, key);
((cPersistentObject*)v)->ring.prev = NULL; if (v == NULL)
((cPersistentObject*)v)->ring.next = NULL; return -1;
}
else if (PyExtensionClass_Check(v)) {
{ self->klass_count--;
/* This is a ghost object, so we havent kept a reference count on it. } else {
For it have stayed alive this long someone else must be keeping a reference p = (cPersistentObject *)v;
to it. Therefore we need to temporarily give it back a reference count if (p->state >= 0) {
before calling DelItem below */ self->non_ghost_count--;
Py_INCREF(v); p->ring.next->prev = p->ring.prev;
} p->ring.prev->next = p->ring.next;
p->ring.prev = NULL;
p->ring.next = NULL;
} else {
/* This is a ghost object, so we havent kept a reference
count on it. For it have stayed alive this long
someone else must be keeping a reference to
it. Therefore we need to temporarily give it back a
reference count before calling DelItem below */
Py_INCREF(v);
}
Py_DECREF((PyObject *)((cPersistentObject*)v)->cache); Py_DECREF((PyObject *)p->cache);
((cPersistentObject*)v)->cache = NULL; p->cache = NULL;
} }
Py_DECREF(v); Py_DECREF(v);
if(PyDict_DelItem(self->data, key)) if (PyDict_DelItem(self->data, key) < 0) {
{ PyErr_SetString(PyExc_RuntimeError,
PyErr_SetString(PyExc_RuntimeError, "unexpectedly couldnt remove key in cc_ass_sub");
"unexpectedly couldnt remove key in cc_ass_sub"); return -1;
return -1; }
}
if(check_ring(self,"post-delitem")) return -1; if (check_ring(self, "post-delitem"))
return -1;
return 0; return 0;
}
} }
static int _check_ring(ccobject *self,const char *context) static int
cc_ass_sub(ccobject *self, PyObject *key, PyObject *v)
{ {
if (v)
return cc_add_item(self, key, v);
else
return cc_del_item(self, key);
}
static int
_check_ring(ccobject *self,const char *context)
{
CPersistentRing *here = &(self->ring_home); CPersistentRing *here = &(self->ring_home);
int expected = 1+self->non_ghost_count; int expected = 1+self->non_ghost_count;
int total = 0; int total = 0;
do do {
{ if (++total > (expected + 10))
if(++total>(expected+10)) return 3; /* ring too big, by a large margin */ return 3; /* ring too big, by a large margin */
if(!here->next) return 4; /* various linking problems */ if (!here->next)
if(!here->prev) return 5; return 4; /* various linking problems */
if(!here->next->prev) return 7; if (!here->prev)
if(!here->prev->next) return 8; return 5;
if(here->prev->next!=here) return 9; if (!here->next->prev)
if(here->next->prev!=here) return 10; return 7;
if(!self->ring_lock) if (!here->prev->next)
return 8;
if (here->prev->next != here)
return 9;
if (here->next->prev != here)
return 10;
if (!self->ring_lock)
{ {
/* if the ring must be locked then it only contains object other than persistent instances */ /* if the ring must be locked then it only contains object other than persistent instances */
if(here!=&self->ring_home) if (here!=&self->ring_home)
{ {
cPersistentObject *object = object_from_ring(self,here,context); cPersistentObject *object = object_from_ring(self,here,context);
if(!object) return 12; if (!object) return 12;
if(object->state==cPersistent_GHOST_STATE) if (object->state==cPersistent_GHOST_STATE)
return 13; return 13;
} }
} }
...@@ -812,27 +845,35 @@ static int _check_ring(ccobject *self,const char *context) ...@@ -812,27 +845,35 @@ static int _check_ring(ccobject *self,const char *context)
} }
while(here!=&self->ring_home); while(here!=&self->ring_home);
if(self->ring_lock) if (self->ring_lock)
{ {
if(total<expected) return 6; /* ring too small; too big is ok when locked */ if (total<expected) return 6; /* ring too small; too big is ok when locked */
} }
else else
{ {
if(total!=expected) return 14; /* ring size wrong, or bad ghost accounting */ if (total!=expected) return 14; /* ring size wrong, or bad ghost accounting */
} }
return 0; return 0;
} }
static int check_ring(ccobject *self,const char *context) /* XXX This function returns false if everything is okay, which is
backwards given its name. All the tests say:
if (check_ring(...))
return error
*/
static int
check_ring(ccobject *self, const char *context)
{ {
#ifdef MUCH_RING_CHECKING #ifdef MUCH_RING_CHECKING
int code=_check_ring(self,context); int code = _check_ring(self,context);
if(code) if (code) {
{ PyErr_Format(PyExc_RuntimeError,
/*printf(stderr,"BROKEN RING (code %d) in %s, size %d\n",code,context,PyDict_Size(self->data));*/ "broken ring (code %d) in %s, size %d",
PyErr_Format(PyExc_RuntimeError,"broken ring (code %d) in %s, size %d",code,context,PyDict_Size(self->data)); code, context, PyDict_Size(self->data));
return code; return code;
} }
#endif #endif
...@@ -845,11 +886,11 @@ present_in_ring(ccobject *self,CPersistentRing *target) ...@@ -845,11 +886,11 @@ present_in_ring(ccobject *self,CPersistentRing *target)
CPersistentRing *here = self->ring_home.next; CPersistentRing *here = self->ring_home.next;
while(1) while(1)
{ {
if(here==target) if (here==target)
{ {
return 1; return 1;
} }
if(here==&self->ring_home) if (here==&self->ring_home)
{ {
/* back to the home position, and we didnt find it */ /* back to the home position, and we didnt find it */
return 0; return 0;
...@@ -897,7 +938,7 @@ newccobject(PyObject *jar, int cache_size, int cache_age) ...@@ -897,7 +938,7 @@ newccobject(PyObject *jar, int cache_size, int cache_age)
UNLESS(self = PyObject_NEW(ccobject, &Cctype)) return NULL; UNLESS(self = PyObject_NEW(ccobject, &Cctype)) return NULL;
self->setklassstate=self->jar=NULL; self->setklassstate=self->jar=NULL;
if((self->data=PyDict_New())) if ((self->data=PyDict_New()))
{ {
self->jar=jar; self->jar=jar;
Py_INCREF(jar); Py_INCREF(jar);
......
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