Commit e6c1e00d authored by Jim Fulton's avatar Jim Fulton

alpha1

parent 618a2277
...@@ -48,12 +48,12 @@ ...@@ -48,12 +48,12 @@
__doc__='''Python implementation of persistent base types __doc__='''Python implementation of persistent base types
$Id: PersistentMapping.py,v 1.2 1998/10/23 21:40:59 jim Exp $''' $Id: PersistentMapping.py,v 1.3 1998/11/11 02:00:56 jim Exp $'''
__version__='$Revision: 1.2 $'[11:-2] __version__='$Revision: 1.3 $'[11:-2]
import Persistence import Persistence
class PersistentMapping(Persistence.Persistent): class PM(Persistence.Persistent):
"""A persistent wrapper for mapping objects. """A persistent wrapper for mapping objects.
This class allows wrapping of mapping objects so that This class allows wrapping of mapping objects so that
...@@ -65,22 +65,36 @@ class PersistentMapping(Persistence.Persistent): ...@@ -65,22 +65,36 @@ class PersistentMapping(Persistence.Persistent):
if container is None: container={} if container is None: container={}
self._container=container self._container=container
def __delitem__(self, key):
del self._container[key]
try: del self._v_keys
except: pass
self.__changed__(1)
def __getitem__(self, key): def __getitem__(self, key):
return self._container[key] return self._container[key]
def __len__(self): return len(self._container)
def __setitem__(self, key, v): def __setitem__(self, key, v):
self._container[key]=v self._container[key]=v
try: del self._v_keys try: del self._v_keys
except: pass except: pass
self.__changed__(1) self.__changed__(1)
def __delitem__(self, key): def clear(self):
del self._container[key] self._container.clear()
try: del self._v_keys self._p_changed=1
except: pass if hasattr(self,'_v_keys'): del self._v_keys
self.__changed__(1)
def __len__(self): return len(self._container) def copy(self): return self.__class__(self._container.copy())
def get(self, key, default): return self._container.get(key, default)
def has_key(self,key): return self._container.has_key(key)
def items(self):
return map(lambda k, d=self: (k,d[k]), self.keys())
def keys(self): def keys(self):
try: return self._v_keys try: return self._v_keys
...@@ -91,14 +105,12 @@ class PersistentMapping(Persistence.Persistent): ...@@ -91,14 +105,12 @@ class PersistentMapping(Persistence.Persistent):
keys.sort() keys.sort()
return keys return keys
def clear(self): def update(self, b):
self._container={} a=self._container
if hasattr(self,'_v_keys'): del self._v_keys for k, v in b.items(): a[k] = v
self._p_changed=1
def values(self): def values(self):
return map(lambda k, d=self: d[k], self.keys()) return map(lambda k, d=self: d[k], self.keys())
def items(self): PersistentMapping=PM
return map(lambda k, d=self: (k,d[k]), self.keys())
def has_key(self,key): return self._container.has_key(key)
/*********************************************************************** /***********************************************************************
$Id: cPersistence.c,v 1.24 1998/07/27 13:03:21 jim Exp $ $Id: cPersistence.c,v 1.25 1998/11/11 02:00:56 jim Exp $
C Persistence Module C Persistence Module
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
*****************************************************************************/ *****************************************************************************/
static char *what_string = "$Id: cPersistence.c,v 1.24 1998/07/27 13:03:21 jim Exp $"; static char *what_string = "$Id: cPersistence.c,v 1.25 1998/11/11 02:00:56 jim Exp $";
#include <time.h> #include <time.h>
#include "cPersistence.h" #include "cPersistence.h"
...@@ -21,8 +21,7 @@ static char *what_string = "$Id: cPersistence.c,v 1.24 1998/07/27 13:03:21 jim E ...@@ -21,8 +21,7 @@ static char *what_string = "$Id: cPersistence.c,v 1.24 1998/07/27 13:03:21 jim E
#define UNLESS(E) if(!(E)) #define UNLESS(E) if(!(E))
#define UNLESS_ASSIGN(V,E) ASSIGN(V,E) UNLESS(V) #define UNLESS_ASSIGN(V,E) ASSIGN(V,E) UNLESS(V)
static PyObject *py_store, *py_oops, *py_keys, *py_setstate, *py___changed__, static PyObject *py_keys, *py_setstate, *py___dict__;
*py___dict__, *py_mtime, *py_onearg, *py___getinitargs__, *py___init__;
#ifdef DEBUG_LOG #ifdef DEBUG_LOG
static PyObject *debug_log=0; static PyObject *debug_log=0;
...@@ -36,8 +35,9 @@ call_debug(char *event, cPersistentObject *self) ...@@ -36,8 +35,9 @@ call_debug(char *event, cPersistentObject *self)
/* /*
printf("%s %p\n",event,self->ob_type->tp_name); printf("%s %p\n",event,self->ob_type->tp_name);
*/ */
r=PyObject_CallFunction(debug_log,"s(sii)",event, r=PyObject_CallFunction(debug_log,"s(ss#i)",event,
self->ob_type->tp_name, self->oid, self->state); self->ob_type->tp_name, self->oid, 8,
self->state);
Py_XDECREF(r); Py_XDECREF(r);
} }
#endif #endif
...@@ -46,16 +46,9 @@ static void ...@@ -46,16 +46,9 @@ static void
init_strings() init_strings()
{ {
#define INIT_STRING(S) py_ ## S = PyString_FromString(#S) #define INIT_STRING(S) py_ ## S = PyString_FromString(#S)
INIT_STRING(store);
INIT_STRING(oops);
INIT_STRING(keys); INIT_STRING(keys);
INIT_STRING(setstate); INIT_STRING(setstate);
INIT_STRING(mtime);
INIT_STRING(__changed__);
INIT_STRING(__init__);
INIT_STRING(__getinitargs__);
INIT_STRING(__dict__); INIT_STRING(__dict__);
py_onearg=Py_BuildValue("(i)",1);
#undef INIT_STRING #undef INIT_STRING
} }
...@@ -113,6 +106,22 @@ callmethod3(PyObject *self, PyObject *name, ...@@ -113,6 +106,22 @@ callmethod3(PyObject *self, PyObject *name,
return self; return self;
} }
#define UPDATE_STATE_IF_NECESSARY(self, ER) \
if(self->state < 0 && self->jar) \
{ \
PyObject *r; \
\
self->state=cPersistent_STICKY_STATE; \
UNLESS(r=callmethod1(self->jar,py_setstate,(PyObject*)self)) \
{ \
self->state=cPersistent_GHOST_STATE; \
return ER; \
} \
self->state=cPersistent_UPTODATE_STATE; \
Py_DECREF(r); \
}
static PyObject * static PyObject *
#ifdef HAVE_STDARG_PROTOTYPES #ifdef HAVE_STDARG_PROTOTYPES
/* VARARGS 2 */ /* VARARGS 2 */
...@@ -148,254 +157,89 @@ PyString_BuildFormat(va_alist) va_dcl ...@@ -148,254 +157,89 @@ PyString_BuildFormat(va_alist) va_dcl
/****************************************************************************/ /****************************************************************************/
static void
PATime_dealloc(PATimeobject *self)
{
/*printf("D");*/
Py_DECREF(self->object);
PyMem_DEL(self);}
static PyObject *
PATime_repr(PATimeobject *self)
{
return PyString_BuildFormat("<access time: %d>","i",self->object->atime);
}
static PyTypeObject
PATimeType = {
PyObject_HEAD_INIT(NULL) 0,
"PersistentATime", /*tp_name*/
sizeof(PATimeobject), /*tp_basicsize*/
0, /*tp_itemsize*/
/* methods */
(destructor)PATime_dealloc, /*tp_dealloc*/
0L,0L,0L,0L,
(reprfunc)PATime_repr, /*tp_repr*/
0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,
"Values for holding access times for persistent objects"
};
/****************************************************************************/
/* Declarations for objects of type Persistent */
#define GHOST_STATE -1
#define UPTODATE_STATE 0
#define CHANGED_STATE 1
staticforward PyExtensionClass Pertype; staticforward PyExtensionClass Pertype;
staticforward PyExtensionClass TPertype; staticforward PyExtensionClass TPertype;
static char Per___changed____doc__[] = static int
"__changed__([flag]) -- Flag or determine whether an object has changed\n" changed(cPersistentObject *self)
" \n"
"If a value is specified, then it should indicate whether an\n"
"object has or has not been changed. If no value is specified,\n"
"then the return value will indicate whether the object has\n"
"changed.\n"
;
static PyObject *changed_args=(PyObject*)Per___changed____doc__;
static PyObject *
T___changed__(cPersistentObject *self, PyObject *args)
{ {
static PyObject *builtins=0, *get_transaction=0, *py_register=0; static PyObject *builtins=0, *get_transaction=0, *py_register=0;
PyObject *o, *T; PyObject *T;
if(PyArg_Parse(args, "O", &o)) if ((self->state == cPersistent_UPTODATE_STATE ||
self->state == cPersistent_STICKY_STATE)
&& self->jar)
{ {
int t; UNLESS (get_transaction)
t=PyObject_IsTrue(o);
if(t && self->state != cPersistent_CHANGED_STATE && self->jar)
{ {
UNLESS(get_transaction) UNLESS (py_register=PyString_FromString("register")) return -1;
{ UNLESS (T=PyImport_ImportModule("__main__")) return -1;
UNLESS(builtins) ASSIGN(T,PyObject_GetAttrString(T,"__builtins__"));
{ UNLESS (T) return -1;
UNLESS(T=PyImport_ImportModule("__main__")) return NULL; builtins=T;
ASSIGN(T,PyObject_GetAttrString(T,"__builtins__")); UNLESS (get_transaction=PyObject_GetAttrString(builtins,
UNLESS(T) return NULL; "get_transaction"))
UNLESS(py_register=PyString_FromString("register")) goto err; PyErr_Clear();
builtins=T; }
} if (get_transaction)
UNLESS(get_transaction=PyObject_GetAttrString(builtins, {
"get_transaction")) UNLESS (T=PyObject_CallObject(get_transaction,NULL)) return -1;
PyErr_Clear(); ASSIGN(T,PyObject_GetAttr(T,py_register));
} UNLESS (T) return -1;
if(get_transaction) ASSIGN(T, PyObject_CallFunction(T,"O",self));
{ if (T) Py_DECREF(T);
UNLESS(T=PyObject_CallObject(get_transaction,NULL)) return NULL; else return -1;
UNLESS_ASSIGN(T,PyObject_GetAttr(T,py_register)) return NULL;
UNLESS(o=PyTuple_New(1)) goto err;
Py_INCREF(self);
PyTuple_SET_ITEM(o,0,(PyObject*)self);
ASSIGN(o,PyObject_CallObject(T,o));
Py_DECREF(T);
UNLESS(o) return NULL;
Py_DECREF(o);
}
} }
if(self->state != cPersistent_GHOST_STATE) self->state=t;
Py_INCREF(Py_None); self->state=cPersistent_CHANGED_STATE;
return Py_None;
} }
else
{
PyErr_Clear();
UNLESS(PyArg_Parse(args, "")) return NULL;
return PyInt_FromLong(self->state==cPersistent_CHANGED_STATE);
}
err:
Py_DECREF(T);
return NULL;
}
static char Per___save____doc__[] =
"__save__() -- Update the object in a persistent database."
;
static PyObject *
Per___save__(self, args)
cPersistentObject *self;
PyObject *args;
{
if(self->oid && self->jar && self->state == CHANGED_STATE)
return callmethod1(self->jar,py_store,(PyObject*)self);
Py_INCREF(Py_None);
return Py_None;
}
static char Per___inform_commit____doc__[] = return 0;
"__inform_commit__(transaction,start_time) -- Commit object changes"
;
static PyObject *
Per___inform_commit__(self, args)
cPersistentObject *self;
PyObject *args;
{
PyObject *T=0, *t=0;
UNLESS(PyArg_ParseTuple(args, "OO", &T, &t)) return NULL;
if(self->oid && self->jar && self->state == CHANGED_STATE)
return callmethod2(self->jar,py_store,(PyObject*)self,T);
Py_INCREF(Py_None);
return Py_None;
} }
static char Per___inform_abort____doc__[] =
"__inform_abort__(transaction,start_time) -- Abort object changes"
;
static PyObject * static PyObject *
Per___inform_abort__(self, args) Per___changed__(cPersistentObject *self, PyObject *args)
cPersistentObject *self;
PyObject *args;
{ {
PyObject *T, *t; PyObject *v=0;
if (args && ! PyArg_ParseTuple(args, "|O",&v)) return NULL;
UNLESS(PyArg_ParseTuple(args, "OO", &T, &t)) return NULL; if (v && ! PyObject_IsTrue(v))
if(self->oid && self->jar && self->state != GHOST_STATE)
{ {
args=callmethod3(self->jar,py_oops,(PyObject*)self,t,T); PyErr_SetString(PyExc_TypeError,
if(args) "Only true arguments are allowed.");
Py_DECREF(args); return NULL;
else
PyErr_Clear();
} }
if (changed(self) < 0) return NULL;
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
} }
static char Per__p___init____doc__[] =
"_p___init__(oid,jar) -- Initialize persistence management data"
;
static PyObject * static PyObject *
Per__p___init__(self, args) Per__p_deactivate(cPersistentObject *self, PyObject *args)
cPersistentObject *self;
PyObject *args;
{ {
int oid; PyObject *init=0, *copy, *dict;
PyObject *jar;
UNLESS(PyArg_Parse(args, "(iO)", &oid, &jar)) return NULL;
#ifdef DEBUG_LOG #ifdef DEBUG_LOG
if(idebug_log < 0) call_debug("init",self); if (idebug_log < 0) call_debug("reinit",self);
#endif #endif
Py_INCREF(jar);
self->oid=oid;
ASSIGN(self->jar, jar);
self->state=GHOST_STATE;
Py_INCREF(Py_None);
return Py_None;
}
static PyObject * if (args && ! PyArg_ParseTuple(args,"")) return NULL;
Per__p___reinit__(cPersistentObject *self, PyObject *args)
{
PyObject *init=0, *copy, *dict;
#ifdef DEBUG_LOG if (self->state==cPersistent_UPTODATE_STATE && self->jar &&
if(idebug_log < 0) call_debug("reinit",self); HasInstDict(self) && (dict=INSTANCE_DICT(self)))
#endif
if(PyArg_Parse(args,""))
{ {
if(self->state==cPersistent_UPTODATE_STATE) PyDict_Clear(dict);
{ self->state=cPersistent_GHOST_STATE;
if(HasInstDict(self) && (dict=INSTANCE_DICT(self)))
{
PyDict_Clear(dict);
self->state=cPersistent_GHOST_STATE;
}
}
} }
else
{
PyErr_Clear();
UNLESS(PyArg_Parse(args, "O", &copy)) return NULL;
if(HasInstDict(self) && self->state==cPersistent_UPTODATE_STATE)
{
UNLESS(args=PyObject_GetAttr(copy,py___dict__)) return NULL;
ASSIGN(INSTANCE_DICT(self),args);
self->state=GHOST_STATE;
}
}
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
err:
Py_XDECREF(init);
return NULL;
} }
static int static int
Per_setstate(self) Per_setstate(cPersistentObject *self)
cPersistentObject *self;
{ {
self->atime=(time_t)1; /* Mark this object as sticky */ UPDATE_STATE_IF_NECESSARY(self, -1);
if(self->state==GHOST_STATE && self->jar) self->state=cPersistent_STICKY_STATE;
{
PyObject *r;
self->state=UPTODATE_STATE;
UNLESS(r=callmethod1(self->jar,py_setstate,(PyObject*)self))
{
self->state=GHOST_STATE;
self->atime=time(NULL); /* Unmark as sticky */
return -1;
}
Py_DECREF(r);
}
return 0; return 0;
} }
...@@ -406,25 +250,13 @@ Per__getstate__(self,args) ...@@ -406,25 +250,13 @@ Per__getstate__(self,args)
{ {
PyObject *__dict__, *d=0; PyObject *__dict__, *d=0;
UNLESS(PyArg_Parse(args, "")) return NULL; UNLESS(PyArg_ParseTuple(args, "")) return NULL;
#ifdef DEBUG_LOG #ifdef DEBUG_LOG
if(idebug_log < 0) call_debug("get",self); if(idebug_log < 0) call_debug("get",self);
#endif #endif
/* Update state, if necessary */ UPDATE_STATE_IF_NECESSARY(self, NULL);
if(self->state==GHOST_STATE && self->jar)
{
PyObject *r;
self->state=UPTODATE_STATE;
UNLESS(r=callmethod1(self->jar,py_setstate,(PyObject*)self))
{
self->state=GHOST_STATE;
return NULL;
}
Py_DECREF(r);
}
if(HasInstDict(self) && (__dict__=INSTANCE_DICT(self))) if(HasInstDict(self) && (__dict__=INSTANCE_DICT(self)))
{ {
...@@ -465,16 +297,13 @@ Per__setstate__(self,args) ...@@ -465,16 +297,13 @@ Per__setstate__(self,args)
PyObject *__dict__, *v, *keys=0, *key=0, *e=0; PyObject *__dict__, *v, *keys=0, *key=0, *e=0;
int l, i; int l, i;
/*printf("%s(%d) ", self->ob_type->tp_name,self->oid);*/
if(HasInstDict(self)) if(HasInstDict(self))
{ {
UNLESS(PyArg_Parse(args, "O", &v)) return NULL; UNLESS(PyArg_ParseTuple(args, "O", &v)) return NULL;
#ifdef DEBUG_LOG #ifdef DEBUG_LOG
if(idebug_log < 0) call_debug("set",self); if(idebug_log < 0) call_debug("set",self);
#endif #endif
self->state=UPTODATE_STATE;
if(v!=Py_None) if(v!=Py_None)
{ {
__dict__=INSTANCE_DICT(self); __dict__=INSTANCE_DICT(self);
...@@ -483,10 +312,7 @@ Per__setstate__(self,args) ...@@ -483,10 +312,7 @@ Per__setstate__(self,args)
{ {
for(i=0; PyDict_Next(v,&i,&key,&e);) for(i=0; PyDict_Next(v,&i,&key,&e);)
if(PyObject_SetItem(__dict__,key,e) < 0) if(PyObject_SetItem(__dict__,key,e) < 0)
{ return NULL;
self->state=GHOST_STATE;
return NULL;
}
} }
else else
{ {
...@@ -509,7 +335,6 @@ Per__setstate__(self,args) ...@@ -509,7 +335,6 @@ Per__setstate__(self,args)
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
err: err:
self->state=GHOST_STATE;
Py_XDECREF(key); Py_XDECREF(key);
Py_XDECREF(e); Py_XDECREF(e);
Py_XDECREF(keys); Py_XDECREF(keys);
...@@ -518,22 +343,13 @@ err: ...@@ -518,22 +343,13 @@ err:
static struct PyMethodDef Per_methods[] = { static struct PyMethodDef Per_methods[] = {
{"__changed__", (PyCFunction)T___changed__, 0, {"__changed__", (PyCFunction)Per___changed__, METH_VARARGS,
Per___changed____doc__}, "DEPRECATED: use self._p_changed=1"},
{"__save__", (PyCFunction)Per___save__, 1, {"_p_deactivate", (PyCFunction)Per__p_deactivate, METH_VARARGS,
Per___save____doc__}, "_p_deactivate(oid) -- Deactivate the object"},
{"__inform_commit__", (PyCFunction)Per___inform_commit__, 1, {"__getstate__", (PyCFunction)Per__getstate__, METH_VARARGS,
Per___inform_commit____doc__},
{"__inform_abort__", (PyCFunction)Per___inform_abort__, 1,
Per___inform_abort____doc__},
{"_p___init__", (PyCFunction)Per__p___init__, 0,
Per__p___init____doc__},
{"_p_deactivate", (PyCFunction)Per__p___reinit__, 0,
"_p_deactivate(oid[,copy]) -- Deactivate the object"},
{"_p___reinit__", (PyCFunction)Per__p___reinit__, 0,""},
{"__getstate__", (PyCFunction)Per__getstate__, 0,
"__getstate__() -- Return the state of the object" }, "__getstate__() -- Return the state of the object" },
{"__setstate__", (PyCFunction)Per__setstate__, 0, {"__setstate__", (PyCFunction)Per__setstate__, METH_VARARGS,
"__setstate__(v) -- Restore the saved state of the object from v" }, "__setstate__(v) -- Restore the saved state of the object from v" },
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
...@@ -549,28 +365,9 @@ Per_dealloc(self) ...@@ -549,28 +365,9 @@ Per_dealloc(self)
if(idebug_log < 0) call_debug("del",self); if(idebug_log < 0) call_debug("del",self);
#endif #endif
Py_XDECREF(self->jar); Py_XDECREF(self->jar);
/*Py_XDECREF(self->atime);*/
PyMem_DEL(self); PyMem_DEL(self);
} }
static void
Per_set_atime(cPersistentObject *self)
{
if(self->atime == (time_t)1) return;
self->atime = time(NULL);
}
static PyObject *
Per_atime(cPersistentObject *self)
{
PATimeobject *r;
UNLESS(r=PyObject_NEW(PATimeobject,&PATimeType)) return NULL;
Py_INCREF(self);
r->object=self;
return (PyObject*)r;
}
static PyObject * static PyObject *
Per_getattr(cPersistentObject *self, PyObject *oname, char *name, Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
PyObject *(*getattrf)(PyObject *, PyObject*)) PyObject *(*getattrf)(PyObject *, PyObject*))
...@@ -584,17 +381,7 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name, ...@@ -584,17 +381,7 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
{ {
case 'o': case 'o':
if(*n++=='i' && *n++=='d' && ! *n) if(*n++=='i' && *n++=='d' && ! *n)
{ return PyString_FromStringAndSize(self->oid, 8);
if(self->oid)
{
return PyInt_FromLong(self->oid);
}
else
{
Py_INCREF(Py_None);
return Py_None;
}
}
break; break;
case 'j': case 'j':
if(*n++=='a' && *n++=='r' && ! *n) if(*n++=='a' && *n++=='r' && ! *n)
...@@ -614,34 +401,15 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name, ...@@ -614,34 +401,15 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
case 'c': case 'c':
if(strcmp(n,"hanged")==0) if(strcmp(n,"hanged")==0)
{ {
if(self->state == GHOST_STATE) if(self->state < 0)
{ {
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
} }
return PyInt_FromLong(self->state == CHANGED_STATE); return PyInt_FromLong(self->state ==
} cPersistent_CHANGED_STATE);
break;
case 'a':
if(strcmp(n,"time")==0)
{
if(self->state != UPTODATE_STATE) Per_set_atime(self);
return Per_atime(self);
} }
break; break;
case 'm':
if(strcmp(n,"time")==0)
{
if(self->jar)
return callmethod1(self->jar,py_mtime,(PyObject*)self);
Py_INCREF(Py_None);
return Py_None;
}
break;
case 's':
if(strcmp(n,"tate")==0)
return PyInt_FromLong(self->state);
break;
} }
return getattrf((PyObject *)self, oname); return getattrf((PyObject *)self, oname);
...@@ -650,21 +418,9 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name, ...@@ -650,21 +418,9 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
(strcmp(name,"dict__")==0 || strcmp(name,"class__")==0 (strcmp(name,"dict__")==0 || strcmp(name,"class__")==0
|| strcmp(name, "of__")==0))) || strcmp(name, "of__")==0)))
{ {
/* Update state, if necessary */ UPDATE_STATE_IF_NECESSARY(self, NULL);
if(self->state==GHOST_STATE && self->jar)
{
PyObject *r;
self->state=UPTODATE_STATE;
UNLESS(r=callmethod1(self->jar,py_setstate,(PyObject*)self))
{
self->state=GHOST_STATE;
return NULL;
}
Py_DECREF(r);
}
Per_set_atime(self); self->atime=((long)(time(NULL)/3))%65536;
} }
return getattrf((PyObject *)self, oname); return getattrf((PyObject *)self, oname);
...@@ -679,15 +435,12 @@ Per_getattro(cPersistentObject *self, PyObject *name) ...@@ -679,15 +435,12 @@ Per_getattro(cPersistentObject *self, PyObject *name)
return Per_getattr(self,name,s, PyExtensionClassCAPI->getattro); return Per_getattr(self,name,s, PyExtensionClassCAPI->getattro);
} }
static int static int
changed(PyObject *self) bad_delattr()
{ {
PyObject *c; PyErr_SetString(PyExc_AttributeError,
"delete undeletable attribute");
UNLESS(c=PyObject_GetAttr(self,py___changed__)) return -1; return -1;
UNLESS_ASSIGN(c,PyObject_CallObject(c,py_onearg)) return -1;
Py_DECREF(c);
return 0;
} }
static int static int
...@@ -703,8 +456,15 @@ _setattro(cPersistentObject *self, PyObject *oname, PyObject *v, ...@@ -703,8 +456,15 @@ _setattro(cPersistentObject *self, PyObject *oname, PyObject *v,
{ {
if(name[3]=='o' && name[4]=='i' && name[5]=='d' && ! name[6]) if(name[3]=='o' && name[4]=='i' && name[5]=='d' && ! name[6])
{ {
if(v && PyInt_Check(v)) self->oid=PyInt_AsLong(v); if (! v) return bad_delattr();
else self->oid=0; if (PyString_Check(v) && PyString_GET_SIZE(v)==8)
memcpy(self->oid, PyString_AS_STRING(v), 8);
else
{
PyErr_SetString(PyExc_AttributeError,
"_p_oid must be an 8-character string");
return -1;
}
return 0; return 0;
} }
if(name[3]=='j' && name[4]=='a' && name[5]=='r' && ! name[6]) if(name[3]=='j' && name[4]=='a' && name[5]=='r' && ! name[6])
...@@ -715,40 +475,29 @@ _setattro(cPersistentObject *self, PyObject *oname, PyObject *v, ...@@ -715,40 +475,29 @@ _setattro(cPersistentObject *self, PyObject *oname, PyObject *v,
} }
if(strcmp(name+3,"changed")==0) if(strcmp(name+3,"changed")==0)
{ {
if(v==Py_None) self->state=GHOST_STATE; if (! v) return bad_delattr();
else self->state= (v && PyObject_IsTrue(v)); if (v==Py_None)
return 0; {
} if (Per__p_deactivate(self, NULL)) Py_DECREF(Py_None);
if(strcmp(name+3,"atime")==0) return 0;
{ }
self->atime=(time_t)1; if (PyObject_IsTrue(v)) return changed(self);
if (self->state >= 0) self->state=cPersistent_UPTODATE_STATE;
return 0; return 0;
} }
} }
else else
{ {
PyObject *r; PyObject *r;
/* Update state, if necessary */ UPDATE_STATE_IF_NECESSARY(self, -1);
if(self->state==GHOST_STATE && self->jar)
{
self->state=UPTODATE_STATE;
UNLESS(r=callmethod1(self->jar,py_setstate,(PyObject*)self))
{
self->state=GHOST_STATE;
return -1;
}
Py_DECREF(r);
}
/* Record access times */ /* Record access times */
Per_set_atime(self); self->atime=((long)(time(NULL)/3))%65536;
if(! (*name=='_' && name[1]=='v' && name[2]=='_') if(! (*name=='_' && name[1]=='v' && name[2]=='_')
&& self->state != CHANGED_STATE && self->jar) && self->state != cPersistent_CHANGED_STATE && self->jar)
if(changed((PyObject*)self) < 0) return -1; if(changed(self) < 0) return -1;
} }
return setattrf((PyObject*)self,oname,v); return setattrf((PyObject*)self,oname,v);
...@@ -760,28 +509,6 @@ Per_setattro(cPersistentObject *self, PyObject *oname, PyObject *v) ...@@ -760,28 +509,6 @@ Per_setattro(cPersistentObject *self, PyObject *oname, PyObject *v)
return _setattro(self,oname, v, PyExtensionClassCAPI->setattro); return _setattro(self,oname, v, PyExtensionClassCAPI->setattro);
} }
static char Pertype__doc__[] =
"Persistent object support mix-in class\n"
"\n"
"When a persistent object is loaded from a database, the object's\n"
"data is not immediately loaded. Loading of the objects data is\n"
"defered until an attempt is made to access an attribute of the\n"
"object. \n"
"\n"
"The object also tries to keep track of whether it has changed. It\n"
"is easy for this to be done incorrectly. For this reason, methods\n"
"of subclasses that change state other than by setting attributes\n"
"should: 'self.__changed__(1)' to flag instances as changed.\n"
"\n"
"Data are not saved automatically. To save an object's state, call\n"
"the object's '__save__' method.\n"
"\n"
"You must not override the object's '__getattr__' and '__setattr__'\n"
"methods. If you override the objects '__getstate__' method, then\n"
"you must be careful not to include any attributes with names\n"
"starting with '_p_' in the state.\n"
;
static PyExtensionClass Pertype = { static PyExtensionClass Pertype = {
PyObject_HEAD_INIT(NULL) PyObject_HEAD_INIT(NULL)
0, /*ob_size*/ 0, /*ob_size*/
...@@ -804,8 +531,7 @@ static PyExtensionClass Pertype = { ...@@ -804,8 +531,7 @@ static PyExtensionClass Pertype = {
(getattrofunc)Per_getattro, /*tp_getattr with object key*/ (getattrofunc)Per_getattro, /*tp_getattr with object key*/
(setattrofunc)Per_setattro, /*tp_setattr with object key*/ (setattrofunc)Per_setattro, /*tp_setattr with object key*/
/* Space for future expansion */ /* Space for future expansion */
0L,0L, 0L,0L,"",
Pertype__doc__, /* Documentation string */
METHOD_CHAIN(Per_methods), METHOD_CHAIN(Per_methods),
EXTENSIONCLASS_BASICNEW_FLAG, EXTENSIONCLASS_BASICNEW_FLAG,
}; };
...@@ -830,7 +556,7 @@ set_debug_log(PyObject *ignored, PyObject *args) ...@@ -830,7 +556,7 @@ set_debug_log(PyObject *ignored, PyObject *args)
static struct PyMethodDef cP_methods[] = { static struct PyMethodDef cP_methods[] = {
#ifdef DEBUG_LOG #ifdef DEBUG_LOG
{"set_debug_log", (PyCFunction)set_debug_log, 0, {"set_debug_log", (PyCFunction)set_debug_log, METH_VARARGS,
"set_debug_log(callable) -- Provide a function to log events\n" "set_debug_log(callable) -- Provide a function to log events\n"
"\n" "\n"
"The function will be called with an event name and a persistent object.\n" "The function will be called with an event name and a persistent object.\n"
...@@ -859,9 +585,7 @@ void ...@@ -859,9 +585,7 @@ void
initcPersistence() initcPersistence()
{ {
PyObject *m, *d; PyObject *m, *d;
char *rev="$Revision: 1.24 $"; char *rev="$Revision: 1.25 $";
PATimeType.ob_type=&PyType_Type;
m = Py_InitModule4("cPersistence", cP_methods, m = Py_InitModule4("cPersistence", cP_methods,
"", "",
...@@ -873,110 +597,11 @@ initcPersistence() ...@@ -873,110 +597,11 @@ initcPersistence()
PyDict_SetItemString(d,"__version__", PyDict_SetItemString(d,"__version__",
PyString_FromStringAndSize(rev+11,strlen(rev+11)-2)); PyString_FromStringAndSize(rev+11,strlen(rev+11)-2));
PyExtensionClass_Export(d,"Persistent",Pertype); PyExtensionClass_Export(d,"Persistent",Pertype);
PyDict_SetItemString(d,"atimeType",(PyObject*)&PATimeType);
cPersistenceCAPI=&truecPersistenceCAPI; cPersistenceCAPI=&truecPersistenceCAPI;
PyDict_SetItemString(d, "CAPI", PyDict_SetItemString(d, "CAPI",
PyCObject_FromVoidPtr(cPersistenceCAPI,NULL)); PyCObject_FromVoidPtr(cPersistenceCAPI,NULL));
#include "dcprotect.h"
if (PyErr_Occurred()) if (PyErr_Occurred())
Py_FatalError("can't initialize module cDocumentTemplate"); Py_FatalError("can't initialize module cDocumentTemplate");
} }
/****************************************************************************
$Log: cPersistence.c,v $
Revision 1.24 1998/07/27 13:03:21 jim
Added __of__ to list of attributes that don't activate object on access.
Revision 1.23 1998/01/09 22:19:28 jim
Fixed bug in deactivation logic. The stupid thing was calling a
constructor when deactivating.
Revision 1.22 1997/12/15 15:28:09 jim
Some cleanup. Removed unused old routine.
Renamed _p___reinit__ to _p_deactivate.
Updated _p_changed attribute protocol. This will allow us to get rid
of _p_state and maybe someday __changed__().
Revision 1.21 1997/12/11 16:03:30 jim
Set EXTENSIONCLASS_BASICNEW_FLAG to support __basicnew__ protocol.
Revision 1.20 1997/11/13 19:46:24 jim
Fixed minor error handling bug in reinit.
Revision 1.19 1997/09/18 19:53:46 jim
Added attribute, _p_state.
Revision 1.18 1997/07/18 14:14:02 jim
Fixed bug in handling delete of certain special attributes.
Revision 1.17 1997/07/16 20:18:32 jim
*** empty log message ***
Revision 1.16 1997/06/30 15:26:35 jim
Changed so getting an object's __class__ does not cause it's
activation.
Revision 1.15 1997/06/06 19:04:40 jim
Modified so that C API setstate makes object temporarily
undeactivatable.
Revision 1.14 1997/05/01 20:33:58 jim
I made (and restored) some optimizations. The effect is probably
minor, but who knows.
Revision 1.13 1997/04/27 09:18:01 jim
Added to the CAPI to support subtypes (like Record) that want to
extend attr functions.
Revision 1.12 1997/04/24 12:48:48 jim
Fixed bug in reinit
Revision 1.11 1997/04/22 02:46:50 jim
Took out debugging info.
Revision 1.10 1997/04/22 02:40:03 jim
Changed object header layout and added sticky feature.
Revision 1.9 1997/04/03 17:34:14 jim
Changed to pass transaction to jar store method during commit.
Revision 1.8 1997/03/28 20:24:52 jim
Added login to really minimice cache size and to
make cache attributes changeable.
Revision 1.7 1997/03/25 20:43:21 jim
Changed to make all persistent objects transactional.
Revision 1.6 1997/03/20 20:58:25 jim
Fixed bug in reinit.
Revision 1.5 1997/03/14 22:59:34 jim
Changed the way Per_setstate was exported to get rid of compilation
error.
Revision 1.4 1997/03/14 22:51:40 jim
Added exported C interface, so that other C classes could subclass
from it.
Added _p_mtime attribute, which returns the persistent modification
time.
Revision 1.3 1997/03/11 20:53:07 jim
Added access-time tracking and special type for efficient access time
management.
Revision 1.2 1997/02/21 20:49:09 jim
Added logic to treat attributes starting with _v_ as volatile.
Changes in these attributes to not make the object thing it's been
saved and these attributes are not saved by the default __getstate__
method.
Revision 1.1 1997/02/14 20:24:55 jim
*** empty log message ***
****************************************************************************/
/* /*
$Id: cPersistence.h,v 1.9 1997/12/15 15:55:16 jim Exp $ $Id: cPersistence.h,v 1.10 1998/11/11 02:00:56 jim Exp $
Definitions to facilitate making cPersistent subclasses in C. Definitions to facilitate making cPersistent subclasses in C.
Copyright
Copyright 1996 Digital Creations, L.C., 910 Princess Anne
Street, Suite 300, Fredericksburg, Virginia 22401 U.S.A. All
rights reserved. Copyright in this software is owned by DCLC,
unless otherwise indicated. Permission to use, copy and
distribute this software is hereby granted, provided that the
above copyright notice appear in all copies and that both that
copyright notice and this permission notice appear. Note that
any product, process or technology described in this software
may be the subject of other Intellectual Property rights
reserved by Digital Creations, L.C. and are not licensed
hereunder.
Trademarks
Digital Creations & DCLC, are trademarks of Digital Creations, L.C..
All other trademarks are owned by their respective companies.
No Warranty
The software is provided "as is" without warranty of any kind,
either express or implied, including, but not limited to, the
implied warranties of merchantability, fitness for a particular
purpose, or non-infringement. This software could include
technical inaccuracies or typographical errors. Changes are
periodically made to the software; these changes will be
incorporated in new editions of the software. DCLC may make
improvements and/or changes in this software at any time
without notice.
Limitation Of Liability
In no event will DCLC be liable for direct, indirect, special,
incidental, economic, cover, or consequential damages arising
out of the use of or inability to use this software even if
advised of the possibility of such damages. Some states do not
allow the exclusion or limitation of implied warranties or
limitation of liability for incidental or consequential
damages, so the above limitation or exclusion may not apply to
you.
If you have questions regarding this software,
contact:
Digital Creations L.C.
info@digicool.com
(540) 371-6909
$Log: cPersistence.h,v $
Revision 1.9 1997/12/15 15:55:16 jim
Changed persistent object header layout. This will require recompile
of all C Persistent objects.
Revision 1.8 1997/12/10 22:19:24 jim
Added PER_USE macro.
Revision 1.7 1997/07/18 14:15:39 jim
Added PER_DEL so that subclasses can handle deallocation correctly.
Revision 1.6 1997/06/06 19:13:32 jim
Changed/fixed convenience macros.
Revision 1.5 1997/05/19 17:51:20 jim
Added macros to simplify C PO implementation.
Revision 1.4 1997/05/19 13:49:36 jim
Added include of time.h.
Revision 1.3 1997/04/27 09:18:23 jim
Added to the CAPI to support subtypes (like Record) that want to
extend attr functions.
Revision 1.2 1997/04/22 02:40:28 jim
Changed object header layout.
Revision 1.1 1997/04/01 17:15:48 jim
*** empty log message ***
*/ */
...@@ -96,24 +15,19 @@ ...@@ -96,24 +15,19 @@
#define cPersistent_HEAD PyObject_HEAD \ #define cPersistent_HEAD PyObject_HEAD \
PyObject *jar; \ PyObject *jar; \
int oid; \ char oid[8]; \
time_t atime; \ unsigned short atime; \
signed char state; \ signed char state; \
#define cPersistent_GHOST_STATE -1 #define cPersistent_GHOST_STATE -1
#define cPersistent_UPTODATE_STATE 0 #define cPersistent_UPTODATE_STATE 0
#define cPersistent_CHANGED_STATE 1 #define cPersistent_CHANGED_STATE 1
#define cPersistent_STICKY_STATE 2
typedef struct { typedef struct {
cPersistent_HEAD cPersistent_HEAD
} cPersistentObject; } cPersistentObject;
typedef struct {
PyObject_HEAD
cPersistentObject *object;
} PATimeobject;
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);
...@@ -121,7 +35,7 @@ typedef struct { ...@@ -121,7 +35,7 @@ typedef struct {
PyMethodChain *methods; PyMethodChain *methods;
getattrofunc getattro; getattrofunc getattro;
setattrofunc setattro; setattrofunc setattro;
int (*changed)(PyObject*); int (*changed)(cPersistentObject*);
int (*setstate)(PyObject*); int (*setstate)(PyObject*);
pergetattr pergetattro; pergetattr pergetattro;
persetattr persetattro; persetattr persetattro;
...@@ -129,16 +43,20 @@ typedef struct { ...@@ -129,16 +43,20 @@ typedef struct {
static cPersistenceCAPIstruct *cPersistenceCAPI; static cPersistenceCAPIstruct *cPersistenceCAPI;
#define PER_USE_OR_RETURN(O,R) { \
if ((O)->state==cPersistent_GHOST_STATE && \
cPersistenceCAPI->setstate((PyObject*)(O)) < 0) \
return (R); \
else if ((O)->state==cPersistent_UPTODATE_STATE) \
(O)->state=cPersistent_STICKY_STATE; \
}
#define PER_USE_OR_RETURN(O,R) \ #define PER_CHANGED(O) (cPersistenceCAPI->changed((cPersistentObject*)(O)))
if(cPersistenceCAPI->setstate((PyObject*)(O)) < 0) return (R)
#define PER_USE(O) (cPersistenceCAPI->setstate((PyObject*)(O)))
#define PER_CHANGED(O) (cPersistenceCAPI->changed((PyObject*)(O))) #define PER_ALLOW_DEACTIVATION(O) \
((O)->state==cPersistent_STICKY_STATE && \
((O)->state=cPersistent_UPTODATE_STATE))
#define PER_PREVENT_DEACTIVATION(O) ((O)->atime=(time_t)1);
#define PER_ALLOW_DEACTIVATION(O) ((O)->atime=time(NULL));
#define PER_DEL(O) Py_XDECREF((O)->jar) #define PER_DEL(O) Py_XDECREF((O)->jar)
#endif #endif
......
/* static char *what_string = "$Id: cPickleCache.c,v 1.16 1998/11/11 02:00:56 jim Exp $";
$Id: cPickleCache.c,v 1.15 1998/07/27 13:09:03 jim Exp $
C implementation of a pickle jar cache.
Copyright
Copyright 1996 Digital Creations, L.C., 910 Princess Anne
Street, Suite 300, Fredericksburg, Virginia 22401 U.S.A. All
rights reserved.
***************************************************************************/
static char *what_string = "$Id: cPickleCache.c,v 1.15 1998/07/27 13:09:03 jim Exp $";
#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))
#define UNLESS_ASSIGN(V,E) ASSIGN(V,E) UNLESS(V) #define UNLESS_ASSIGN(V,E) ASSIGN(V,E) UNLESS(V)
#define Py_ASSIGN(P,E) if(!PyObject_AssignExpression(&(P),(E))) return NULL
#define OBJECT(O) ((PyObject*)O) #define OBJECT(O) ((PyObject*)O)
#include "cPersistence.h" #include "cPersistence.h"
...@@ -25,7 +10,7 @@ static char *what_string = "$Id: cPickleCache.c,v 1.15 1998/07/27 13:09:03 jim E ...@@ -25,7 +10,7 @@ static char *what_string = "$Id: cPickleCache.c,v 1.15 1998/07/27 13:09:03 jim E
#undef Py_FindMethod #undef Py_FindMethod
static PyObject *py_reload, *py__p_jar, *py__p_atime, *py__p_deactivate; static PyObject *py_reload, *py__p_jar, *py__p_deactivate;
/* Declarations for objects of type cCache */ /* Declarations for objects of type cCache */
...@@ -65,60 +50,45 @@ typedef struct { ...@@ -65,60 +50,45 @@ typedef struct {
staticforward PyTypeObject Cctype; staticforward PyTypeObject Cctype;
static PyObject *PATimeType=NULL;
/* ---------------------------------------------------------------- */ /* ---------------------------------------------------------------- */
static int static int
gc_item(ccobject *self, PyObject *key, PyObject *v, time_t now, time_t dt) gc_item(ccobject *self, PyObject *key, PyObject *v, long now, int dt)
{ {
time_t t;
if(v && key) if (v && key)
{ {
self->n++; self->n++;
if(v->ob_type==(PyTypeObject*)PATimeType) if(v->ob_refcnt <= 1)
{ {
if(((PATimeobject*)v)->object->ob_refcnt <= 1) self->sum_deal++;
{ return PyDict_DelItem(self->data, key);
self->sum_deal++; }
UNLESS(-1 != PyDict_DelItem(self->data, key)) return -1;
} if (dt && v->ob_type->tp_basicsize >= sizeof(cPersistentObject) &&
else ((cPersistentObject*)v)->state==cPersistent_UPTODATE_STATE)
{
now -= ((cPersistentObject*)v)->atime;
if (now < 0) now += 65536;
self->na++;
self->sum_age += now;
if (now > dt)
{ {
t=((PATimeobject*)v)->object->atime; /* We have a cPersistent object that hasn't been used in
if(t != (time_t)1) a while. Reinitialize it, hopefully freeing it's
state.
*/
self->sum_deac++;
if(key=PyObject_GetAttr(v,py__p_deactivate))
{ {
self->na++; ASSIGN(key,PyObject_CallObject(key,NULL));
t=now-t; UNLESS(key) return -1;
self->sum_age += t; Py_DECREF(key);
if((! dt || t > dt)) return 0;
{
/* We have a cPersistent object that hasn't been used in
a while. Reinitialize it, hopefully freeing it's
state.
*/
v=(PyObject*)(((PATimeobject*)v)->object);
if(((cPersistentObject*)v)->state !=
cPersistent_UPTODATE_STATE) return 0;
self->sum_deac++;
if(key=PyObject_GetAttr(v,py__p_deactivate))
{
ASSIGN(key,PyObject_CallObject(key,NULL));
UNLESS(key) return -1;
Py_DECREF(key);
}
PyErr_Clear();
}
} }
PyErr_Clear();
} }
} }
else if(v->ob_refcnt <= 1)
{
self->sum_deal++;
UNLESS(-1 != PyDict_DelItem(self->data, key)) return -1;
}
} }
return 0; return 0;
} }
...@@ -135,7 +105,7 @@ update_stats(ccobject *self, time_t now) ...@@ -135,7 +105,7 @@ update_stats(ccobject *self, time_t now)
self->dfa *= WEIGHTING_PERIOD/(WEIGHTING_PERIOD+d); self->dfa *= WEIGHTING_PERIOD/(WEIGHTING_PERIOD+d);
self->mean_age=((self->mean_age*self->dfa+self->sum_age)/ self->mean_age=((self->mean_age*self->dfa+self->sum_age)/
(self->dfa+self->na)); (self->dfa+self->na))*3;
self->sum_age=0; self->sum_age=0;
deac=self->sum_deac/d; deac=self->sum_deac/d;
...@@ -163,16 +133,22 @@ static int ...@@ -163,16 +133,22 @@ static int
fullgc(ccobject *self, int idt) fullgc(ccobject *self, int idt)
{ {
PyObject *key, *v; PyObject *key, *v;
int i; int i, dt;
time_t now, dt; long now;
if(self->cache_size < 1) return 0; if (self->cache_size < 1) return 0;
i=PyDict_Size(self->data)-3/self->cache_size; if ((i=PyDict_Size(self->data)) < 1) return;
if(i < 3) i=3;
dt=self->cache_age*3/i; now=((long)(time(NULL)/3))%65536;
if(dt < 10) dt=10;
now=time(NULL); if (idt) dt=idt*3;
if(idt) dt=idt; else
{
i=PyDict_Size(self->data)-3/self->cache_size;
if(i < 3) i=3;
dt=self->cache_age*3/i;
if(dt < 10) dt=10;
}
for(i=0; PyDict_Next(self->data, &i, &key, &v); ) for(i=0; PyDict_Next(self->data, &i, &key, &v); )
if(gc_item(self,key,v,now,dt) < 0) return -1; if(gc_item(self,key,v,now,dt) < 0) return -1;
...@@ -183,40 +159,6 @@ fullgc(ccobject *self, int idt) ...@@ -183,40 +159,6 @@ fullgc(ccobject *self, int idt)
return 0; return 0;
} }
static PyObject *
ccitems(ccobject *self, PyObject *args)
{
PyObject *r, *key, *v, *item=0;
int i;
UNLESS(PyArg_ParseTuple(args,"")) return NULL;
UNLESS(r=PyList_New(0)) return NULL;
for(i=0; PyDict_Next(self->data, &i, &key, &v); )
{
if(key && v)
{
if(v->ob_type==(PyTypeObject*)PATimeType)
{
ASSIGN(item, Py_BuildValue("OO",key,((PATimeobject*)v)->object));
}
else
{
ASSIGN(item, Py_BuildValue("OO",key,v));
}
UNLESS(item) goto err;
if(PyList_Append(r,item) < 0) goto err;
}
}
Py_XDECREF(item);
return r;
err:
Py_XDECREF(item);
Py_DECREF(r);
return NULL;
}
static int static int
reallyfullgc(ccobject *self, int dt) reallyfullgc(ccobject *self, int dt)
{ {
...@@ -249,31 +191,33 @@ reallyfullgc(ccobject *self, int dt) ...@@ -249,31 +191,33 @@ reallyfullgc(ccobject *self, int dt)
static int static int
maybegc(ccobject *self, PyObject *thisv) maybegc(ccobject *self, PyObject *thisv)
{ {
int n, s, size; int n, s, size, dt;
time_t now,dt; long now;
PyObject *key=0, *v=0; PyObject *key=0, *v=0;
/*printf("m");*/ if (self->cache_size < 1) return 0;
s=PyDict_Size(self->data);
if (s < 1) return s;
now=((long)(time(NULL)/3))%65536;
if(self->cache_size < 1) return 0;
s=PyDict_Size(self->data)-3;
if(s < self->cache_size) return 0;
size=self->cache_size; size=self->cache_size;
self->cache_size=0; self->cache_size=0;
n=(s-size)/10; n=(s-size)/10;
/*n=s/size;*/
if(n < 3) n=3; if (n < 3) n=3;
dt=(long)(self->cache_age*(0.2+0.8*size/s)); s=8*size/s;
if(dt < 10) dt=10; if (s > 100) s=100;
dt=(long)(self->cache_age*(0.2+0.1*s));
if (dt < 10) dt=10;
now=time(NULL); now=time(NULL);
while(--n >= 0) while (--n >= 0)
{ {
if(PyDict_Next(self->data, &(self->position), &key, &v)) if (PyDict_Next(self->data, &(self->position), &key, &v))
{ {
if(v != thisv && gc_item(self,key,v,now,dt) < 0) if (v != thisv && gc_item(self,key,v,now,dt) < 0)
{ {
self->cache_size=size; self->cache_size=size;
return -1; return -1;
...@@ -284,7 +228,7 @@ maybegc(ccobject *self, PyObject *thisv) ...@@ -284,7 +228,7 @@ maybegc(ccobject *self, PyObject *thisv)
} }
self->cache_size=size; self->cache_size=size;
if(now-self->last_check > 1) update_stats(self, now); if (now-self->last_check > 1) update_stats(self, now);
return 0; return 0;
} }
...@@ -309,41 +253,6 @@ cc_reallyfull_sweep(ccobject *self, PyObject *args) ...@@ -309,41 +253,6 @@ cc_reallyfull_sweep(ccobject *self, PyObject *args)
return Py_None; return Py_None;
} }
static PyObject *
cc_report(ccobject *self, PyObject *args)
{
PyObject *key, *v, *t=0;
int i;
if(args) PyArg_ParseTuple(args,"|O", &t);
for(i=0; PyDict_Next(self->data, &i, &key, &v); )
{
if(v->ob_type==(PyTypeObject*)PATimeType
&& (
(t && OBJECT(((PATimeobject*)v)->object->ob_type) == t)
|| ! t))
printf("%d\t%p\t%s\t%ld\t%d\t%ld\n",
(((PATimeobject*)v)->object->oid),
((PATimeobject*)v)->object,
((PATimeobject*)v)->object->ob_type->tp_name,
(long)(((PATimeobject*)v)->object->ob_refcnt),
(((PATimeobject*)v)->object->state),
(long)(((PATimeobject*)v)->object->atime) );
else if((t && OBJECT(((PATimeobject*)v)->object->ob_type) == t)
|| ! t)
printf("%d\t%p\t%s\t%ld\t%d\n",
(((cPersistentObject*)v)->oid),
v,
v->ob_type->tp_name,
(long)(v->ob_refcnt),
(((cPersistentObject*)v)->state)
);
}
if(args) Py_INCREF(Py_None);
return Py_None;
}
static PyObject * static PyObject *
cc_incrgc(ccobject *self, PyObject *args) cc_incrgc(ccobject *self, PyObject *args)
{ {
...@@ -353,31 +262,24 @@ cc_incrgc(ccobject *self, PyObject *args) ...@@ -353,31 +262,24 @@ cc_incrgc(ccobject *self, PyObject *args)
} }
static struct PyMethodDef cc_methods[] = { static struct PyMethodDef cc_methods[] = {
{"full_sweep", (PyCFunction)cc_full_sweep, 1, {"full_sweep", (PyCFunction)cc_full_sweep, METH_VARARGS,
"full_sweep([age]) -- Perform a full sweep of the cache\n\n" "full_sweep([age]) -- Perform a full sweep of the cache\n\n"
"Make a single pass through the cache, removing any objects that are no\n" "Make a single pass through the cache, removing any objects that are no\n"
"longer referenced, and deactivating objects that have not been\n" "longer referenced, and deactivating objects that have not been\n"
"accessed in the number of seconds given by 'age'. " "accessed in the number of seconds given by 'age'. "
"'age defaults to the cache age.\n" "'age defaults to the cache age.\n"
}, },
{"report", (PyCFunction)cc_report, 1, ""}, {"minimize", (PyCFunction)cc_reallyfull_sweep, METH_VARARGS,
{"minimize", (PyCFunction)cc_reallyfull_sweep, 1,
"minimize([age]) -- Remove as many objects as possible\n\n" "minimize([age]) -- Remove as many objects as possible\n\n"
"Make multiple passes through the cache, removing any objects that are no\n" "Make multiple passes through the cache, removing any objects that are no\n"
"longer referenced, and deactivating objects that have not been\n" "longer referenced, and deactivating objects that have not been\n"
"accessed in the number of seconds given by 'age'. 'age defaults to 0.\n" "accessed in the number of seconds given by 'age'. 'age defaults to 0.\n"
}, },
{"items", (PyCFunction)ccitems, 1, {"incrgc", (PyCFunction)cc_incrgc, METH_VARARGS,
"items() -- Return the cache items."
},
{"incrgc", (PyCFunction)cc_incrgc, 1,
"incrgc() -- Perform incremental garbage collection"}, "incrgc() -- Perform incremental garbage collection"},
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };
/* ---------- */
static ccobject * static ccobject *
newccobject(int cache_size, int cache_age) newccobject(int cache_size, int cache_age)
{ {
...@@ -442,7 +344,11 @@ cc_getattr(ccobject *self, char *name) ...@@ -442,7 +344,11 @@ cc_getattr(ccobject *self, char *name)
return self->data; return self->data;
} }
} }
if(*name=='h' && strcmp(name, "has_key")==0) if(
*name=='h' && strcmp(name, "has_key")==0 ||
*name=='i' && strcmp(name, "items")==0 ||
*name=='k' && strcmp(name, "keys")==0
)
return PyObject_GetAttrString(self->data, name); return PyObject_GetAttrString(self->data, name);
if(r=Py_FindMethod(cc_methods, (PyObject *)self, name)) if(r=Py_FindMethod(cc_methods, (PyObject *)self, name))
...@@ -476,22 +382,6 @@ cc_setattr(ccobject *self, char *name, PyObject *value) ...@@ -476,22 +382,6 @@ cc_setattr(ccobject *self, char *name, PyObject *value)
return -1; return -1;
} }
static PyObject *
cc_repr(ccobject *self)
{
return PyObject_Repr(self->data);
}
static PyObject *
cc_str(self)
ccobject *self;
{
return PyObject_Str(self->data);
}
/* Code to access cCache objects as mappings */
static int static int
cc_length(ccobject *self) cc_length(ccobject *self)
{ {
...@@ -503,58 +393,22 @@ cc_subscript(ccobject *self, PyObject *key) ...@@ -503,58 +393,22 @@ cc_subscript(ccobject *self, PyObject *key)
{ {
PyObject *r; PyObject *r;
UNLESS(r=PyObject_GetItem(self->data, key)) UNLESS (r=PyDict_GetItem(self->data, key))
{ {
PyErr_SetObject(PyExc_KeyError, key); PyErr_SetObject(PyExc_KeyError, key);
return NULL; return NULL;
} }
UNLESS(-1 != maybegc(self,r)) if (maybegc(self,r) < 0) return NULL;
{
Py_DECREF(r); Py_INCREF(r);
return NULL;
}
if(r->ob_type==(PyTypeObject *)PATimeType)
{
Py_DECREF(r);
r=(PyObject*)(((PATimeobject*)r)->object);
Py_INCREF(r);
}
return r; return r;
} }
static int static int
cc_ass_sub(ccobject *self, PyObject *key, PyObject *v) cc_ass_sub(ccobject *self, PyObject *key, PyObject *v)
{ {
if(v) return PyDict_SetItem(self->data, key, v);
if(v) return PyDict_DelItem(self->data, key);
{
int r;
PyObject *t=0;
/* Now get and save the access time */
if(t=PyObject_GetAttr(v,py__p_atime))
{
if(t->ob_type != (PyTypeObject *)PATimeType)
{
Py_DECREF(t);
t=0;
}
else
v=t;
}
else
PyErr_Clear();
r=PyDict_SetItem(self->data,key,v);
Py_XDECREF(t);
if(r < 0) return -1;
return maybegc(self, v);
}
else
{
UNLESS(-1 != PyDict_DelItem(self->data,key)) return -1;
return maybegc(self, NULL);
}
} }
static PyMappingMethods cc_as_mapping = { static PyMappingMethods cc_as_mapping = {
...@@ -563,12 +417,6 @@ static PyMappingMethods cc_as_mapping = { ...@@ -563,12 +417,6 @@ static PyMappingMethods cc_as_mapping = {
(objobjargproc)cc_ass_sub, /*mp_ass_subscript*/ (objobjargproc)cc_ass_sub, /*mp_ass_subscript*/
}; };
/* -------------------------------------------------------- */
static char Cctype__doc__[] =
""
;
static PyTypeObject Cctype = { static PyTypeObject Cctype = {
PyObject_HEAD_INIT(NULL) PyObject_HEAD_INIT(NULL)
0, /*ob_size*/ 0, /*ob_size*/
...@@ -581,22 +429,19 @@ static PyTypeObject Cctype = { ...@@ -581,22 +429,19 @@ static PyTypeObject Cctype = {
(getattrfunc)cc_getattr, /*tp_getattr*/ (getattrfunc)cc_getattr, /*tp_getattr*/
(setattrfunc)cc_setattr, /*tp_setattr*/ (setattrfunc)cc_setattr, /*tp_setattr*/
(cmpfunc)0, /*tp_compare*/ (cmpfunc)0, /*tp_compare*/
(reprfunc)cc_repr, /*tp_repr*/ (reprfunc)0, /*tp_repr*/
0, /*tp_as_number*/ 0, /*tp_as_number*/
0, /*tp_as_sequence*/ 0, /*tp_as_sequence*/
&cc_as_mapping, /*tp_as_mapping*/ &cc_as_mapping, /*tp_as_mapping*/
(hashfunc)0, /*tp_hash*/ (hashfunc)0, /*tp_hash*/
(ternaryfunc)0, /*tp_call*/ (ternaryfunc)0, /*tp_call*/
(reprfunc)cc_str, /*tp_str*/ (reprfunc)0, /*tp_str*/
/* Space for future expansion */ /* Space for future expansion */
0L,0L,0L,0L, 0L,0L,0L,0L,
Cctype__doc__ /* Documentation string */ ""
}; };
/* End of code for cCache objects */
/* -------------------------------------------------------- */
static PyObject * static PyObject *
cCM_new(PyObject *self, PyObject *args) cCM_new(PyObject *self, PyObject *args)
{ {
...@@ -605,119 +450,30 @@ cCM_new(PyObject *self, PyObject *args) ...@@ -605,119 +450,30 @@ cCM_new(PyObject *self, PyObject *args)
return (PyObject*)newccobject(cache_size,cache_age); return (PyObject*)newccobject(cache_size,cache_age);
} }
/* List of methods defined in the module */
static struct PyMethodDef cCM_methods[] = { static struct PyMethodDef cCM_methods[] = {
{"PickleCache",(PyCFunction)cCM_new, 1, {"PickleCache",(PyCFunction)cCM_new, METH_VARARGS, ""},
"PickleCache([size,age]) -- Create a pickle jar cache\n\n"
"The cache will attempt to garbage collect items when the cache size is\n"
"greater than the given size, which defaults to 100. Normally, objects\n"
"are garbage collected if their reference count is one, meaning that\n"
"they are only referenced by the cache. In some cases, objects that\n"
"have not been accessed in 'age' seconds may be partially garbage\n"
"collected, meaning that most of their state is freed.\n"
},
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };
/* Initialization function for the module (*must* be called initcCache) */
static char cCache_module_documentation[] =
""
;
void void
initcPickleCache() initcPickleCache()
{ {
PyObject *m, *d; PyObject *m, *d;
char *rev="$Revision: 1.15 $"; char *rev="$Revision: 1.16 $";
Cctype.ob_type=&PyType_Type; Cctype.ob_type=&PyType_Type;
if(PATimeType=PyImport_ImportModule("cPersistence")) m = Py_InitModule4("cPickleCache", cCM_methods, "",
ASSIGN(PATimeType,PyObject_GetAttrString(PATimeType,"atimeType"));
UNLESS(PATimeType) PyErr_Clear();
m = Py_InitModule4("cPickleCache", cCM_methods,
cCache_module_documentation,
(PyObject*)NULL,PYTHON_API_VERSION); (PyObject*)NULL,PYTHON_API_VERSION);
d = PyModule_GetDict(m); d = PyModule_GetDict(m);
py_reload=PyString_FromString("reload"); py_reload=PyString_FromString("reload");
py__p_jar=PyString_FromString("_p_jar"); py__p_jar=PyString_FromString("_p_jar");
py__p_atime=PyString_FromString("_p_atime");
py__p_deactivate=PyString_FromString("_p_deactivate"); py__p_deactivate=PyString_FromString("_p_deactivate");
PyDict_SetItemString(d,"__version__", PyDict_SetItemString(d,"__version__",
PyString_FromStringAndSize(rev+11,strlen(rev+11)-2)); PyString_FromStringAndSize(rev+11,strlen(rev+11)-2));
#include "dcprotect.h"
if (PyErr_Occurred()) Py_FatalError("can't initialize module cCache"); if (PyErr_Occurred()) Py_FatalError("can't initialize module cCache");
} }
/******************************************************************************
$Log: cPickleCache.c,v $
Revision 1.15 1998/07/27 13:09:03 jim
Changed _p___reinit__ to _p_deactivate.
Revision 1.14 1998/02/05 14:43:10 jim
Fixed bug in ibcremental gc method.
Revision 1.13 1998/02/05 14:34:40 jim
Added getattr option to get cache data.
Added method to perform incremental gc.
Changed incremental collection effort algorithm to be based on
difference between actual and target size, rather than ration.
Revision 1.12 1997/12/15 15:25:09 jim
Cleaned up to avoid VC++ warnings.
Revision 1.11 1997/12/10 22:20:43 jim
Added has_key method.
Revision 1.10 1997/07/18 14:30:18 jim
Added reporting method for use during debugging.
Revision 1.9 1997/07/16 20:18:40 jim
*** empty log message ***
Revision 1.8 1997/06/30 15:27:51 jim
Added machinery to track cache statistics.
Fixed bug in garbage collector, which had a nasty habit
of activating inactive objects so that it could deactivate them.
Revision 1.7 1997/05/30 14:29:47 jim
Added new algorithm for adjusting cache age based on cache size. Not,
if the cache size gets really big, the cache age can drop to as low as
20% of the configured cache age. Also made the "minimize" method more
agressive.
Revision 1.6 1997/04/22 02:45:24 jim
Changed object header layout and added sticky feature.
Revision 1.5 1997/04/15 19:03:29 jim
Fixed leak introduced in last revision. :-(
Revision 1.4 1997/04/11 19:13:21 jim
Added code to be more conservative about GCing.
Fixed setattr bugs.
Revision 1.3 1997/03/28 20:18:34 jim
Simplified reinit logic.
Revision 1.2 1997/03/11 20:48:38 jim
Added object-deactivation support. This only works with cPersistent
objects.
Revision 1.1 1997/02/17 18:39:02 jim
*** empty log message ***
******************************************************************************/
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
static char BTree_module_documentation[] = static char BTree_module_documentation[] =
"" ""
"\n$Id: BTree.c,v 1.16 1998/03/24 15:17:44 jim Exp $" "\n$Id: BTree.c,v 1.17 1998/11/11 02:00:55 jim Exp $"
; ;
#define PERSISTENT #define PERSISTENT
...@@ -22,7 +22,7 @@ static char BTree_module_documentation[] = ...@@ -22,7 +22,7 @@ static char BTree_module_documentation[] =
#include "ExtensionClass.h" #include "ExtensionClass.h"
#define PER_USE_OR_RETURN(self, NULL) #define PER_USE_OR_RETURN(self, NULL)
#define PER_ALLOW_DEACTIVATION(self) #define PER_ALLOW_DEACTIVATION(self)
#define PER_PREVENT_DEACTIVATION(self) #define PER_DEL(self)
#endif #endif
...@@ -170,7 +170,7 @@ BTreeItems_item_BTree(char kind, int i, BTree *btree) ...@@ -170,7 +170,7 @@ BTreeItems_item_BTree(char kind, int i, BTree *btree)
if(Bucket_Check(d->value)) if(Bucket_Check(d->value))
{ {
PER_USE_OR_RETURN(d->value, NULL); PER_USE_OR_RETURN((Bucket*)(d->value), NULL);
switch(kind) switch(kind)
{ {
case 'k': case 'k':
...@@ -867,7 +867,7 @@ BTree_grow(BTree *self, int index) ...@@ -867,7 +867,7 @@ BTree_grow(BTree *self, int index)
v=d->value; v=d->value;
UNLESS(e=PyObject_CallObject(OBJECT(v->ob_type), NULL)) return -1; UNLESS(e=PyObject_CallObject(OBJECT(v->ob_type), NULL)) return -1;
PER_USE_OR_RETURN(v, -1); PER_USE_OR_RETURN((Bucket*)v, -1);
if(Bucket_Check(v)) if(Bucket_Check(v))
{ {
...@@ -1273,8 +1273,6 @@ bucket_setstate(Bucket *self, PyObject *args) ...@@ -1273,8 +1273,6 @@ bucket_setstate(Bucket *self, PyObject *args)
char *cv; char *cv;
#endif #endif
PER_PREVENT_DEACTIVATION(self);
UNLESS(PyArg_ParseTuple(args,"O",&r)) goto err; UNLESS(PyArg_ParseTuple(args,"O",&r)) goto err;
UNLESS(PyArg_ParseTuple(r,"OO",&keys,&values)) goto err; UNLESS(PyArg_ParseTuple(r,"OO",&keys,&values)) goto err;
...@@ -1452,8 +1450,6 @@ BTree_setstate(BTree *self, PyObject *args) ...@@ -1452,8 +1450,6 @@ BTree_setstate(BTree *self, PyObject *args)
UNLESS(PyArg_ParseTuple(args,"O",&state)) return NULL; UNLESS(PyArg_ParseTuple(args,"O",&state)) return NULL;
if((l=PyTuple_Size(state))<0) return NULL; if((l=PyTuple_Size(state))<0) return NULL;
PER_PREVENT_DEACTIVATION(self);
if(l>self->size) if(l>self->size)
{ {
if(self->data) if(self->data)
...@@ -1610,6 +1606,7 @@ Bucket_dealloc(Bucket *self) ...@@ -1610,6 +1606,7 @@ Bucket_dealloc(Bucket *self)
DECREF_VALUE(self->data[i].value); DECREF_VALUE(self->data[i].value);
} }
free(self->data); free(self->data);
PER_DEL(self);
PyMem_DEL(self); PyMem_DEL(self);
} }
...@@ -1625,6 +1622,8 @@ BTree_dealloc(BTree *self) ...@@ -1625,6 +1622,8 @@ BTree_dealloc(BTree *self)
Py_DECREF(self->data[i].value); Py_DECREF(self->data[i].value);
} }
free(self->data); free(self->data);
PER_DEL(self);
PyMem_DEL(self); PyMem_DEL(self);
} }
...@@ -1762,7 +1761,7 @@ initBTree() ...@@ -1762,7 +1761,7 @@ initBTree()
#endif #endif
{ {
PyObject *m, *d; PyObject *m, *d;
char *rev="$Revision: 1.16 $"; char *rev="$Revision: 1.17 $";
UNLESS(PyExtensionClassCAPI=PyCObject_Import("ExtensionClass","CAPI")) UNLESS(PyExtensionClassCAPI=PyCObject_Import("ExtensionClass","CAPI"))
return; return;
...@@ -1798,74 +1797,8 @@ initBTree() ...@@ -1798,74 +1797,8 @@ initBTree()
PyDict_SetItemString(d, "__version__", PyDict_SetItemString(d, "__version__",
PyString_FromStringAndSize(rev+11,strlen(rev+11)-2)); PyString_FromStringAndSize(rev+11,strlen(rev+11)-2));
#include "dcprotect.h"
/* Check for errors */ /* Check for errors */
if (PyErr_Occurred()) if (PyErr_Occurred())
Py_FatalError("can't initialize module BTree"); Py_FatalError("can't initialize module BTree");
} }
/*
PER_USE_OR_RETURN(self, NULL);
PER_ALLOW_DEACTIVATION(self);
*/
/*****************************************************************************
Revision Log:
$Log: BTree.c,v $
Revision 1.16 1998/03/24 15:17:44 jim
Brought reinit/deactivate machinery up to date.
Revision 1.15 1998/02/18 22:19:50 jim
Fixed C inheritence problem. Waaaaaaa.
Revision 1.14 1998/02/05 17:46:17 jim
Added get methods.
Revision 1.13 1998/02/04 21:11:26 jim
Fixed two leaks in bucket values.
Revision 1.12 1997/12/31 17:18:04 jim
Fixed bugs related to deleting items.
Revision 1.11 1997/12/12 23:43:05 jim
Added basicnew support.
Revision 1.10 1997/11/13 20:45:51 jim
Fixed some bad return values.
Revision 1.9 1997/11/13 20:38:35 jim
added dcprotect
Revision 1.8 1997/11/03 15:17:53 jim
Fixed stupid bug in has_key methods.
Revision 1.7 1997/10/30 20:58:43 jim
Upped bucket sizes.
Revision 1.6 1997/10/10 18:21:45 jim
Fixed bug in range queries.
Revision 1.5 1997/10/01 02:47:06 jim
Fixed bug in setstate that allocates too much memory.
Revision 1.4 1997/09/17 17:20:32 jim
Fixed bug in deleting members from BTree.
Revision 1.3 1997/09/12 18:35:45 jim
Fixed bug leading to random core dumps.
Revision 1.2 1997/09/10 17:24:47 jim
*** empty log message ***
Revision 1.1 1997/09/08 18:42:21 jim
initial BTree
$Revision 1.1 1997/02/24 23:25:42 jim
$initial
$
*****************************************************************************/
##############################################################################
#
# Copyright (c) 1996-1998, Digital Creations, Fredericksburg, VA, USA.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# o Redistributions of source code must retain the above copyright
# notice, this list of conditions, and the disclaimer that follows.
#
# o Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions, and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# o Neither the name of Digital Creations nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
#
# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS AND CONTRIBUTORS *AS IS*
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL
# CREATIONS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
# DAMAGE.
#
#
# If you have questions regarding this software, contact:
#
# Digital Creations, L.C.
# 910 Princess Ann Street
# Fredericksburge, Virginia 22401
#
# info@digicool.com
#
# (540) 371-6909
#
##############################################################################
"""Implement an bobo_application object that is BoboPOS3 aware
This module provides a wrapper that causes a database connection to be created
and used when bobo publishes a bobo_application object.
"""
__version__='$Revision: 1.1 $'[11:-2]
class BoboApplication:
def __init__(self, db, name, klass= None, klass_args= (),
version_cookie_name=None):
self._stuff = db, name, version_cookie_name
if klass is not None:
conn=db.open()
root=conn.root()
if not root.has_key(name):
root[name]=klass()
get_transaction().commit()
conn.close()
self._klass=klass
# This hack is to overcome a bug in Bobo!
def __getattr__(self, name):
return getattr(self._klass, name)
def __bobo_traverse__(self, REQUEST=None, name=None):
db, aname, version_support = self._stuff
if version_support is not None and REQUEST is not None:
version=REQUEST.get(version_support,'')
else: version=''
conn=db.open(version)
# arrange for the connection to be closed when the request goes away
cleanup=Cleanup()
cleanup.__del__=conn.close
REQUEST[Cleanup]=cleanup
v=conn.root()[aname]
if name is not None:
if hasattr(v,name): return getattr(v,name)
return v[name]
return v
__call__=__bobo_traverse__ # A convenience for command-line use
class Cleanup: pass
##############################################################################
#
# Copyright (c) 1996-1998, Digital Creations, Fredericksburg, VA, USA.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# o Redistributions of source code must retain the above copyright
# notice, this list of conditions, and the disclaimer that follows.
#
# o Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions, and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# o Neither the name of Digital Creations nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
#
# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS AND CONTRIBUTORS *AS IS*
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL
# CREATIONS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
# DAMAGE.
#
#
# If you have questions regarding this software, contact:
#
# Digital Creations, L.C.
# 910 Princess Ann Street
# Fredericksburge, Virginia 22401
#
# info@digicool.com
#
# (540) 371-6909
#
##############################################################################
"""Database connection support
$Id: Connection.py,v 1.1 1998/11/11 02:00:55 jim Exp $"""
__version__='$Revision: 1.1 $'[11:-2]
from PickleCache import PickleCache
from bpthread import allocate_lock
from POSException import ConflictError
from cStringIO import StringIO
from cPickle import Unpickler, Pickler
class HelperClass: pass
ClassType=type(HelperClass)
class Connection:
"""Object managers for individual object space.
An object space is a version of collection of objects. In a
multi-threaded application, each thread get's it's own object
space.
The Connection manages movement of objects in and out of object storage.
"""
def __init__(self, storage, version='', cache_size=400,
cache_deactivate_after=60):
"""Create a new Connection"""
self._storage=storage
self.new_oid=storage.new_oid
self._version=version
self._cache=cache=PickleCache(cache_size, cache_deactivate_after)
self._incrgc=cache.incrgc
self._invalidated={}
lock=allocate_lock()
self._a=lock.acquire
self._r=lock.release
def _breakcr(self):
try: del self._cache
except: pass
try: del self._incrgc
except: pass
def __getitem__(self, oid,
tt=type(()), ct=type(HelperClass)):
cache=self._cache
if cache.has_key(oid): return cache[oid]
__traceback_info__=oid
p=self._storage.load(oid, self._version)
file=StringIO(p)
unpickler=Unpickler(file)
unpickler.persistent_load=self._persistent_load
object = unpickler.load()
if type(object) is tt:
klass, args = object
if (args is None or
not args and not hasattr(klass,'__getinitargs__')):
if type(klass) is ct:
object=HelperClass()
object.__class__=klass
else: object=klass.__basicnew__()
else:
object=apply(klass,args)
object.__dict__.clear()
else:
object.__dict__.clear()
klass=object.__class__
if type(klass) is ct:
d=object.__dict__
d['_p_oid']=oid
d['_p_jar']=self
d['_p_changed']=None
else:
object._p_oid=oid
object._p_jar=self
object._p_changed=None
cache[oid]=object
return object
def _persistent_load(self,oid,
d={'__builtins__':{}},
tt=type(()), st=type(''), ct=type(HelperClass)):
__traceback_info__=oid
cache=self._cache
if type(oid) is tt:
# Quick instance reference. We know all we need to know
# to create the instance wo hitting the db, so go for it!
oid, klass = oid
if cache.has_key(oid): return cache[oid]
if type(klass) is ct:
object=HelperClass()
object.__class__=klass
d=object.__dict__
d['_p_oid']=oid
d['_p_jar']=self
d['_p_changed']=None
else:
object=klass.__basicnew__()
object._p_oid=oid
object._p_jar=self
object._p_changed=None
cache[oid]=object
return object
if type(oid) is st: oid=atoi(oid)
if cache.has_key(oid): return cache[oid]
object=cache[oid]=self[oid]
return object
def _planToStore(self,object,stackp):
oid=object._p_oid
if oid is None or object._p_jar is not self:
oid = self.new_oid()
object._p_jar=self
object._p_oid=oid
stackp(object)
elif object._p_changed:
stackp(object)
return oid
def _setDB(self, odb=None):
"""Begin a new transaction.
Any objects modified since the last transaction are invalidated.
"""
self._db=odb
cache=self._cache
for oid in self._invalidated.keys():
if cache.has_key(oid):
cache[oid]._p_deactivate()
self._invalidated.clear()
return self
def close(self):
self._incrgc()
self._db._closeConnection(self)
del self._db
def commit(self, object, transaction):
oid=object._p_oid
if self._invalidated.has_key(oid): raise ConflictError, oid
self._invalidating.append(oid)
plan=self._planToStore
stack=[]
stackup=stack.append
topoid=plan(object,stackup)
version=self._version
if stack:
# Create a special persistent_id that passes T and the subobject
# stack along:
def persistent_id(object,self=self,stackup=stackup):
if (not hasattr(object, '_p_oid') or
type(object) is ClassType): return None
oid=object._p_oid
if oid is None or object._p_jar is not self:
oid = self.new_oid()
object._p_jar=self
object._p_oid=oid
stackup(object)
if hasattr(object.__class__, '__getinitargs__'): return oid
return oid, object.__class__
file=StringIO()
seek=file.seek
pickler=Pickler(file,1)
pickler.persistent_id=persistent_id
dbstore=self._storage.store
file=file.getvalue
cache=self._cache
dump=pickler.dump
clear_memo=pickler.clear_memo
while stack:
object=stack[-1]
del stack[-1]
oid=object._p_oid
if self._invalidated.has_key(oid): raise ConflictError, oid
cls = object.__class__
if hasattr(cls, '__getinitargs__'):
args = object.__getinitargs__()
len(args) # XXX Assert it's a sequence
else:
args = None # New no-constructor protocol!
seek(0)
clear_memo()
dump((cls,args))
state=object.__getstate__()
dump(state)
p=file()
dbstore(oid,p,version,transaction)
object._p_changed=0
cache[oid]=object
return topoid
def commitVersion(self, destination=''):
raise 'Not Implemented Yet!'
def db(self): return self._db
def getVersion(self): return self._version
def invalidate(self, oid):
"""Invalidate a particular oid
This marks the oid as invalid, but doesn't actually invalidate
it. The object data will be actually invalidated at certain
transaction boundaries.
"""
self._a()
self._invalidated[oid]=1
self._r()
def modifiedInVersion(self, o):
return self._db.modifiedInVersion(o._p_oid)
def root(self): return self['\0\0\0\0\0\0\0\0']
def setstate(self,object):
# Note, we no longer mess with the object's state
# flag, _p_changed. This is the object's job.
oid=object._p_oid
self._a()
if self._invalidated.has_key(oid):
self._r()
raise ConflictError, oid
self._r()
p=self._storage.load(oid, self._version)
file=StringIO(p)
unpickler=Unpickler(file)
unpickler.persistent_load=self._persistent_load
unpickler.load()
state = unpickler.load()
if hasattr(object, '__setstate__'):
object.__setstate__(state)
else:
d=object.__dict__
for k,v in state.items(): d[k]=v
def tpc_abort(self, transaction):
self._storage.tpc_abort(transaction)
cache=self._cache
invalidated=self._invalidated
for oid in invalidated.keys():
if cache.has_key(oid):
cache[oid]._p_deactivate()
invalidated.clear()
for oid in self._invalidating:
cache[oid]._p_deactivate()
def tpc_begin(self, transaction):
self._invalidating=[]
self._storage.tpc_begin(transaction)
def tpc_finish(self, transaction):
self._storage.tpc_finish(transaction, self.tpc_finish_)
cache=self._cache
invalidated=self._invalidated
for oid in invalidated.keys():
if cache.has_key(oid):
cache[oid]._p_deactivate()
invalidated.clear()
def tpc_finish_(self):
invalidate=self._db.invalidate
for oid in self._invalidating: invalidate(oid, self)
class tConnection(Connection):
def close(self):
self._breakcr()
##############################################################################
#
# Copyright (c) 1996-1998, Digital Creations, Fredericksburg, VA, USA.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# o Redistributions of source code must retain the above copyright
# notice, this list of conditions, and the disclaimer that follows.
#
# o Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions, and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# o Neither the name of Digital Creations nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
#
# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS AND CONTRIBUTORS *AS IS*
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL
# CREATIONS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
# DAMAGE.
#
#
# If you have questions regarding this software, contact:
#
# Digital Creations, L.C.
# 910 Princess Ann Street
# Fredericksburge, Virginia 22401
#
# info@digicool.com
#
# (540) 371-6909
#
##############################################################################
"""Database objects
$Id: DB.py,v 1.1 1998/11/11 02:00:55 jim Exp $"""
__version__='$Revision: 1.1 $'[11:-2]
import cPickle, cStringIO, sys
from Connection import Connection
from bpthread import allocate_lock
from Transaction import Transaction
class DB:
"""The Object Database
The Object database coordinates access to and interaction of one
or more connections, which manage object spaces. Most of the actual work
of managing objects is done by the connections.
"""
def __init__(self, storage,
pool_size=7,
cache_size=400,
cache_deactivate_after=60,
version_pool_size=3,
version_cache_size=100,
version_cache_deactivate_after=10,
):
"""Create an object database.
The storage for the object database must be passed in.
Optional arguments are:
pool_size -- The size of the pool of object spaces.
"""
self._storage=storage
try: storage.load('\0\0\0\0\0\0\0\0','')
except:
import PersistentMapping
file=cStringIO.StringIO()
p=cPickle.Pickler(file,1)
p.dump((PersistentMapping.PersistentMapping,None))
p.dump({'_container': {}})
t=Transaction()
t.description='initial database creation'
storage.tpc_begin(t)
storage.store('\0\0\0\0\0\0\0\0', file.getvalue(), '', t)
storage.tpc_finish(t)
# Allocate locks:
l=allocate_lock()
self._a=l.acquire
self._r=l.release
self._pools={},[]
self._temps=[]
self._pool_size=pool_size
self._cache_size=cache_size
self._cache_deactivate_after=cache_deactivate_after
self._version_pool_size=version_pool_size
self._version_cache_size=version_cache_size
self._version_cache_deactivate_after=version_cache_deactivate_after
# Pass through methods:
for m in ('history', 'modifiedInVersion',
'supportsUndo', 'supportsVersions',
'undo', 'undoLog', 'versionEmpty'):
setattr(self, m, getattr(storage, m))
def _cacheMean(self, attr):
m=[0,0]
def f(con, m=m):
t=getattr(con._cache,attr)
m[0]=m[0]+t
m[1]=m[1]+1
self._connectionMap(f)
if m[1]: m=m[0]/m[1]
else: m=None
return m
def _closeConnection(self, connection):
"""Return a connection to the pool"""
self._a()
try:
version=connection._version
pools,pooll=self._pools
pool, allocated, pool_lock = pools[version]
pool.append(connection)
if len(pool)==1:
# Pool now usable again, unlock it.
pool_lock.release()
finally: self._r()
def _connectionMap(f):
self._a()
try:
pools,pooll=self._pools
for pool, allocated in pooll:
for cc in allocated: f(cc)
temps=self._temps
if temps:
t=[]
for cc in temps:
if rc(cc) > 3: f(cc)
self._temps=t
finally: self._r()
def abortVersion(self, version):
raise 'Not Yet Implemented'
def cacheDetail(self):
"""Return information on objects in the various caches
Organized by class."""
detail={}
def f(con,detail=detail,have_detail=detail.has_key):
for oid, ob in con._cache.items():
c="%s.%s" % (ob.__class__.__module__, ob.__class__.__name__)
if have_detail(c): detail[c]=detail[c]+1
else: detail[c]=1
self._connectionMap(f)
detail=detail.items()
detail.sort()
return detail
def cacheExtremeDetail(self):
detail=[]
def f(con, detail=detail, rc=sys.getrefcount):
for oid, ob in con._cache.items():
id=oid
if hasattr(ob,'__dict__'):
d=ob.__dict__
if d.has_key('id'):
id="%s (%s)" % (oid, d['id'])
elif d.has_key('__name__'):
id="%s (%s)" % (oid, d['__name__'])
detail.append({
'oid': id,
'klass': "%s.%s" % (ob.__class__.__module__,
ob.__class__.__name__),
'rc': rc(ob)-4,
'references': con.references(oid),
})
self._connectionMap(f)
return detail
def cacheFullSweep(self, value):
self._connectionMap(lambda c, v=value: c._cache.full_sweep(v))
def cacheLastGCTime(self):
m=[0]
def f(con, m=m):
t=con._cache.cache_last_gc_time
if t > m[0]: m[0]=t
self._connectionMap(f)
return m[0]
def cacheMinimize(self, value):
self._connectionMap(lambda c, v=value: c._cache.minimize(v))
def cacheMeanAge(self): return self._cacheMean('cache_mean_age')
def cacheMeanDeac(self): return self._cacheMean('cache_mean_deac')
def cacheMeanDeal(self): return self._cacheMean('cache_mean_deal')
def cacheSize(self):
m=[0]
def f(con, m=m):
m[0]=m[0]+len(con._cache)
self._connectionMap(f)
return m[0]
def commitVersion(self, source, destination=''):
raise 'Not yet implemented'
def exportFile(self, oid, file=None):
raise 'Not yet implemented'
def getName(self): return self._storage.getName()
def getSize(self): return self._storage.getSize()
def importFile(self, file):
raise 'Not yet implemented'
def invalidate(self, oid, connection=None, rc=sys.getrefcount):
"""Invalidate references to a given oid.
This is used to indicate that one of the connections has committed a
change to the object. The connection commiting the change should be
passed in to prevent useless (but harmless) messages to the
connection.
"""
if connection is not None: version=connection._version
else: version=''
self._a()
try:
pools,pooll=self._pools
for pool, allocated in pooll:
for cc in allocated:
if (cc is not connection and
(not version or cc._version==version)):
if rc(cc) <= 3:
cc.close()
cc.invalidate(oid)
temps=self._temps
if temps:
t=[]
for cc in temps:
if rc(cc) > 3:
if cc is not connection and cc._version==version:
cc.invalidate(oid)
t.append(cc)
else: cc.close()
self._temps=t
finally: self._r()
def objectCount(self): return len(self._storage)
def open(self, version='', transaction=None, temporary=0, force=None,
waitflag=1):
"""Return a object space (AKA connection) to work in
The optional version argument can be used to specify that a
version connection is desired.
The optional transaction argument can be provided to cause the
connection to be automatically closed when a transaction is
terminated. In addition, connections per transaction are
reused, if possible.
Note that the connection pool is managed as a stack, to increate the
likelihood that the connection's stack will include useful objects.
"""
self._a()
try:
if transaction is not None:
connections=transaction._connections
if connections:
if connection.has_key(version) and not temporary:
return connections[version]
else:
transaction._connections=connections={}
transaction=transaction._connections
if temporary:
# This is a temporary connection.
# We won't bother with the pools. This will be
# a one-use connection.
c=Connection(
storage=self._storage,
version=version,
cache_size=self._version_cache_size,
cache_deactivate_after=
self._version_cache_deactivate_after)
c._setDB(self)
self._temps.append(c)
if transaction is not None: transaction[id(c)]=c
return c
pools,pooll=self._pools
if pools.has_key(version):
pool, allocated, pool_lock = pools[version]
else:
pool, allocated, pool_lock = pools[version] = (
[], [], allocate_lock())
pooll.append((pool, allocated))
pool_lock.acquire()
if not pool:
c=None
if version:
if self._version_pool_size < len(allocated) or force:
c=Connection(
storage=self._storage,
version=version,
cache_size=self._version_cache_size,
cache_deactivate_after=
self._version_cache_deactivate_after)
allocated.append(c)
pool.append(c)
elif self._pool_size > len(allocated) or force:
c=Connection(
storage=self._storage,
version=version,
cache_size=self._cache_size,
cache_deactivate_after=
self._cache_deactivate_after)
allocated.append(c)
pool.append(c)
if c is None:
if waitflag:
self._r()
pool_lock.acquire()
self._a()
else: return
elif len(pool)==1:
# Taking last one, lock the pool
# We know that the pool lock is not set.
pool_lock.acquire()
c=pool[-1]
del pool[-1]
c._setDB(self)
for pool, allocated in pooll:
for cc in pool:
cc._incrgc()
if transaction is not None: transaction[version]=c
return c
finally: self._r()
def pack(self, t):
self._storage.pack(t,referencesf,-1)
def setCacheDeactivateAfter(self, v): self._cache_deactivate_after=v
def setCacheSize(self, v): self._cache_size=v
def setPoolSize(self, v): self._pool_size=v
def setVersionCacheDeactivateAfter(self, v):
self._version_cache_deactivate_after=v
def setVersionCacheSize(self, v): self._version_cache_size=v
def setVersionPoolSize(self, v): self._version_pool_size=v
def versionEmpty(self, version):
return self._storage.versionEmpty(version)
def referencesf(p,rootl,
Unpickler=cPickle.Unpickler,
StringIO=cStringIO.StringIO):
u=Unpickler(StringIO(p))
u.persistent_load=rootl
u.noload()
try: u.noload()
except:
# Hm. We failed to do second load. Maybe there wasn't a
# second pickle. Let's check:
f=StringIO(p)
u=Unpickler(f)
u.persistent_load=[]
u.noload()
if len(p) > f.tell(): raise ValueError, 'Error unpickling, %s' % p
##############################################################################
#
# Copyright (c) 1996-1998, Digital Creations, Fredericksburg, VA, USA.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# o Redistributions of source code must retain the above copyright
# notice, this list of conditions, and the disclaimer that follows.
#
# o Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions, and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# o Neither the name of Digital Creations nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
#
# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS AND CONTRIBUTORS *AS IS*
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL
# CREATIONS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
# DAMAGE.
#
#
# If you have questions regarding this software, contact:
#
# Digital Creations, L.C.
# 910 Princess Ann Street
# Fredericksburge, Virginia 22401
#
# info@digicool.com
#
# (540) 371-6909
#
##############################################################################
"""File-based BoboPOS3 storage
"""
__version__='$Revision: 1.1 $'[11:-2]
import struct, time, os, bpthread
now=time.time
from struct import pack, unpack
import POSException
class FileStorageError: pass
class FileStorageFormatError(FileStorageError, POSException.StorageError):
"""Invalid file format
The format of the given file is not valid
"""
class CorruptedFileStorageError(FileStorageError,
POSException.StorageSystemError):
"""Corrupted file storage
"""
class CorruptedTransactionError(CorruptedFileStorageError): pass
class CorruptedDataError(CorruptedFileStorageError): pass
class FileStorage:
_packt=0
_transaction=None
def __init__(self, file_name, create=0):
self.__name__=file_name
self._tfile=open(file_name+'.tmp','w+b')
index, vindex, tindex = self._newIndexes()
self._index=index
self._vindex=vindex
self._tindex=tindex
self._indexpos=index.get
self._vindexpos=vindex.get
self._tappend=tindex.append
# Allocate locks:
l=bpthread.allocate_lock()
self._a=l.acquire
self._r=l.release
l=bpthread.allocate_lock()
self._ca=l.acquire
self._cr=l.release
# Now open the file
if create:
if os.path.exists(file_name): os.remove(file_name)
self._file=file=open(file_name,'w+b')
self._file.write(packed_version)
self._pos=4
self._tpos=0
self._oid='\0\0\0\0\0\0\0\0'
return
if os.path.exists(file_name): file=open(file_name,'r+b')
else: file=open(file_name,'w+b')
self._file=file
self._pos, self._tpos, self._oid = read_index(
file, index, vindex, tindex)
def __len__(self): return len(self._index)
def _newIndexes(self): return {}, {}, []
def abortVersion(self, version):
self._a()
try:
pos=self._vindex[version]
file=self._file
seek=file.seek
read=file.read
file=self._tfile
write=file.write
tell=file.tell
tloc=self._pos
tappend=self._tappend
index=self._index
pack=struct.pack
unpack=struct.unpack
while pos:
seek(pos)
h=read(30)
oid=h[:8]
if index[oid]==pos:
tappend(oid, tell())
pc=h[-8:-4] # Position of committed (non-version) data
write(pack(">8siiHi4s", oid,pos,tloc,0,0,pc))
pos=unpack(">i",h[-4:])[0]
finally: self._r()
def commitVersion(self, src, dest):
self._a()
try:
pos=self._vindex[version]
file=self._file
seek=file.seek
read=file.read
file=self._tfile
write=file.write
tell=file.tell
tloc=self._pos
tappend=self._tappend
index=self._index
pack=struct.pack
unpack=struct.unpack
destlen=len(dest)
while pos:
seek(pos)
h=read(30)
oid=h[:8]
if index[oid]==pos:
tappend(oid, tell())
write(pack(">8siiHi4s", oid,pos,tloc,destlen,0,h[-8:-4]))
write(dest)
write(pack(">i",pos))
pos=unpack(">i",h[-4:])[0]
finally: self._r()
def getName(self): return self.__name__
def getSize(self): return self._pos
def history(self, oid, version, length=1):
self._a()
try:
# not done
index=self._index
file=self._file
seek=file.seek
read=file.read
hist=[]
pos=index[oid]
while length:
seek(pos)
h=read(22)
doid, prev, tloc, vlen, plen = unpack(">8siiHi", h)
if vlen and not hist:
pnc=read(4)
if vlen != len(version) or read(vlen) != version:
pos=unpack(">i", pnc)
contiue
pos=prev
seek(tloc)
h=read(21)
finally: self._r()
def load(self, oid, version, _stuff=None):
self._a()
try:
pos=self._index[oid]
file=self._file
file.seek(pos)
read=file.read
h=read(22)
doid,prev,tloc,vlen,plen = unpack(">8siiHi", h)
if doid != oid: raise CorruptedDataError, h
if vlen:
pnv=read(4)
if (not version or len(version) != vlen or
(read(4) # skip past version link
and version != read(vlen))
):
return _loadBack(file, oid, pnv)
# If we get here, then either this was not a version record,
# or we've already read past the version data!
if plen: return read(plen)
return _loadBack(file, oid, pnv)
finally: self._r()
def modifiedInVersion(self, oid):
self._a()
try:
pos=self._index[oid]
file=self._file
file.seek(pos)
doid,prev,tloc,vlen = unpack(">8siiH", file.read(18))
if doid != oid: raise CorruptedDataError, h
if vlen:
seek(8,1)
return read(vlen)
return ''
finally: self._r()
def new_oid(self, last=None):
if last is None:
self._a()
try:
last=self._oid
d=ord(last[-1])
if d < 255: last=last[:-1]+chr(d+1)
else: last=self.new_oid(last[:-1])
self._oid=last
return last
finally: self._r()
else:
d=ord(last[-1])
if d < 255: return last[:-1]+chr(d+1)+'\0'*(8-len(last))
else: return self.new_oid(last[:-1])
def pack(self, t, rf):
self._a()
try:
# we're going to leave this undone for a while!
# This is hellacious. Hold on to your butts!
# First, prevent undos before t:
self._packt=t
index, vindex, tindex = self._newIndexes()
# Now we know the part of the file containing transactions
# written before t will not be touched. We are free to
# work on it.
self._sync__lock.release()
# Phase 1: pack the old records
ofile=open(self.__name__,'r+b')
import Transaction
stop=Transaction.time2id(t)
opos, otpos, maxoid = read_index(file, index, vindex, tindex, stop)
read=ofile.read
seek=ofile.seek
pfile=open(self.__name__+'.pk','w+b')
write=pfile.write
unpack=struct.unpack
rootl=['\0'*8]
rootd={}
inroot=rootd.has_key
while rootl:
oid=rootl[-1]
del rootl[-1]
if inroot[oid]: continue
pos=index[oid]
seek(pos)
h=read(22)
doid,prev,tloc,vlen,plen = unpack(">8siiHi", h)
if doid != oid: raise CorruptedDataError, h
if vlen:
pnv=read(4)
return _loadBack(file, oid, read(4))
if plen: return read(plen)
return _loadBack(file, oid, pnv)
for oid in rootd.keys(): del index[oid]
del index['\0'*8]
unreachable=index.has_key
seek(4)
pos=4
tpos=0
while 1:
# Read the transaction record
h=read(21)
if not h: break
tid, prev, tl, status, ul, dl = unpack(">8siicHH", h)
if tid >= stop: break
tpos=pos
tend=tpos+tl
if status=='u':
# Undone transaction, skip it
pos=tpos+tl+4
seek(pos)
continue
user=read(ul)
desc=read(dl)
pos=tpos+21+ul+dl
while pos < tend:
# Read the data records for this transaction
h=read(22)
oid,prev,tloc,vlen,plen = unpack(">8siiHi", h)
dlen=22+(plen or 4)+vlen
if vlen:
dlen=vlen+8
seek(8,1)
version=read(vlen)
vindex[version]=pos
pos=pos+dlen
if pos != tend:
raise CorruptedTransactionError, lastp
# Read the (intentionally redundant) transaction length
h=read(4)
if len(h) != 4: raise CorruptedTransactionError, h
if unpack(">i",h)[0] != tl:
raise CorruptedTransactionError, h
pos=pos+4
for oid, p in tindex:
index[oid]=p # Record the position
del tindex[:]
# Phase 2: copy the new records, adjusting all of the
# location pointers. We'll get the commit lock for this part.
finally: self._r()
def store(self, oid, data, version, transaction):
if transaction is not self._transaction:
raise POSException.StorageTransactionError(self, transaction)
self._a()
try:
old=self._indexpos(oid, 0)
pnv=None
if old:
file=self._file
file.seek(old)
h=file.read(22)
doid,prev,tloc,vlen,plen = unpack(">8siiHi", h)
if doid != oid: raise CorruptedDataError, h
if vlen:
pnv=read(4)
if (len(version) != vlen or
(read(4) # skip past version link
and version != read(vlen))
):
raise POSException.VersionLockError, oid
tfile=self._tfile
write=tfile.write
self._tappend(oid, tfile.tell())
pos=self._pos
write(pack(">8siiHi",oid,old,pos,len(version),len(data)))
if version:
if pnv: write(pnv)
else: write(pack(">i",old))
# Link to last record for this version:
vindex=self._vindex
write(pack(">i",vindex[version]))
vindex[version]=pos
write(version)
write(data)
finally: self._r()
def supportsUndo(self): return 0 # for now
def supportsVersions(self): return 1
def tpc_abort(self, transaction):
self._a()
try:
if transaction != self._transaction: return
del self._tindex[:]
self._transaction=None
self._cr()
finally: self._r()
def tpc_begin(self, transaction):
self._a()
try:
if self._transaction is transaction: return
self._r()
self._ca()
self._a()
self._transaction=transaction
del self._tindex[:] # Just to be sure!
self._tfile.seek(0)
finally: self._r()
def tpc_finish(self, transaction, f=None):
self._a()
try:
if transaction != self._transaction: return
if f is not None: f()
file=self._file
write=file.write
tfile=self._tfile
read=tfile.read
dlen=tfile.tell()
tfile.seek(0)
id=transaction.id
user=transaction.user
desc=transaction.description
tlen=21+len(user)+len(desc)
pos=self._pos
file.seek(pos)
tl=tlen+dlen
write(pack(">8siicHH",
id, self._tpos, tl, ' ', len(user), len(desc)))
write(user)
write(desc)
assert dlen >= 0
while dlen > 0:
d=read(min(dlen,8192))
write(d)
d=len(d)
assert dlen >= d
dlen=dlen-d
write(pack(">i", tl))
file.flush()
self._tpos=pos
self._pos=pos+tl+4
index=self._index
dpos=pos+tlen
for oid, pos in self._tindex: index[oid]=pos+dpos
del self._tindex[:]
self._transaction=None
self._cr()
finally: self._r()
def undo(self, transaction_id):
pass
def undoLog(self, version, first, last, path):
return []
def versionEmpty(self, version):
self._a()
try:
pos=self._index[oid]
file=self._file
file.seek(pos)
doid,prev,tloc,vlen = unpack(">8siiH", file.read(18))
if doid != oid: raise CorruptedDataError, h
if not vlen or vlen != len(version): return 1
seek(4,1)
return read(vlen) != version
finally: self._r()
packed_version='FS10'
def read_index(file, index, vindex, tindex, stop='\377'*8):
indexpos=index.get
vndexpos=vindex.get
tappend=tindex.append
read=file.read
seek=file.seek
seek(0,2)
file_size=file.tell()
seek(0)
if file_size:
if file_size < 4: raise FileStorageFormatError, file.name
if read(4) != packed_version:
raise FileStorageFormatError, file_name
else: file.write(packed_version)
pos=4
unpack=struct.unpack
tpos=0
maxoid='\0\0\0\0\0\0\0\0'
while 1:
# Read the transaction record
h=read(21)
if not h: break
if len(h) != 21: raise CorruptedTransactionError, h
tid, prev, tl, status, ul, dl = unpack(">8siicHH",h)
if (prev != tpos
or status not in ' up' or ul > tl or dl > tl
or tl > file_size or tl+pos >= file_size):
raise CorruptedTransactionRecordError, h
if tid >= stop: break
tpos=pos
tend=tpos+tl
if status=='u':
# Undone transaction, skip it
pos=tpos+tl
seek(pos)
h=read(4)
if len(h) != 4: raise CorruptedTransactionError, h
if unpack(">i",h)[0] != tl:
raise CorruptedTransactionError, h
pos=pos+4
continue
pos=tpos+21+ul+dl
while pos < tend:
# Read the data records for this transaction
seek(pos)
h=read(22)
oid,prev,tloc,vlen,plen = unpack(">8siiHi", h)
dlen=22+(plen or 4)+vlen
if pos+dlen > tend or tloc != tpos:
raise CorruptedDataError, h
if indexpos(oid,0) != prev:
raise CorruptedDataError, h
tappend((oid,pos))
if vlen:
dlen=vlen+8
seek(8,1)
version=read(vlen)
vindex[version]=pos
pos=pos+dlen
if pos != tend:
raise CorruptedTransactionError, lastp
# Read the (intentionally redundant) transaction length
seek(pos)
h=read(4)
if len(h) != 4: raise CorruptedTransactionError, h
if unpack(">i",h)[0] != tl:
raise CorruptedTransactionError, h
pos=pos+4
for oid, p in tindex:
maxoid=max(maxoid,oid)
index[oid]=p # Record the position
del tindex[:]
return pos, tpos, maxoid
def _loadBack(file, oid, back):
while 1:
old=unpack(">i",back)[0]
if not old: raise KeyError, oid
file.seek(old)
h=file.read(22)
doid,prev,tloc,vlen,plen = unpack(">8siiHi", h)
if doid != oid or vlen: raise CorruptedDataError, h
if plen: return read(plen)
back=read(4) # We got a back pointer!
##############################################################################
#
# Copyright (c) 1996-1998, Digital Creations, Fredericksburg, VA, USA.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# o Redistributions of source code must retain the above copyright
# notice, this list of conditions, and the disclaimer that follows.
#
# o Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions, and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# o Neither the name of Digital Creations nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
#
# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS AND CONTRIBUTORS *AS IS*
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL
# CREATIONS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
# DAMAGE.
#
#
# If you have questions regarding this software, contact:
#
# Digital Creations, L.C.
# 910 Princess Ann Street
# Fredericksburge, Virginia 22401
#
# info@digicool.com
#
# (540) 371-6909
#
##############################################################################
'''BoboPOS-defined exceptions
$Id: POSException.py,v 1.1 1998/11/11 02:00:55 jim Exp $'''
__version__='$Revision: 1.1 $'[11:-2]
class POSError(Exception):
"""Persistent object system error
"""
class TransactionError(POSError):
"""An error occured due to normal transaction processing
"""
class ConflictError(TransactionError):
"""Two transactions tried to modify the same object at once
This transaction should be resubmitted.
"""
class VersionError(POSError):
"""An error in handling versions occurred
"""
class VersionCommitError(VersionError):
"""An invalid combination of versions was used in a version commit
"""
class VersionLockError(VersionError, TransactionError):
"""An attempt was made to modify an object that has
been modified in an unsaved version"""
class UndoError(POSError):
"""An attempt was made to undo an undoable transaction.
"""
class StorageError(POSError):
pass
class StorageTransactionError(StorageError):
"""An operation was invoked for an invalid transaction or state
"""
class StorageSystemError(StorageError):
"""Panic! Internal storage error!
"""
...@@ -45,149 +45,114 @@ ...@@ -45,149 +45,114 @@
# (540) 371-6909 # (540) 371-6909
# #
############################################################################## ##############################################################################
__doc__='''Python implementation of a persistent base types '''Python implementation of a persistent base types
$Id: Persistence.py,v 1.16 1998/10/23 21:40:15 jim Exp $''' $Id: Persistence.py,v 1.17 1998/11/11 02:00:56 jim Exp $'''
__version__='$Revision: 1.16 $'[11:-2] __version__='$Revision: 1.17 $'[11:-2]
try: _marker=[]
from cPersistence import Persistent class Persistent:
except: """\
class Persistent: Persistent object support mix-in class
"""\
Persistent object support mix-in class When a persistent object is loaded from a database, the object's
data is not immediately loaded. Loading of the objects data is
When a persistent object is loaded from a database, the object's defered until an attempt is made to access an attribute of the
data is not immediately loaded. Loading of the objects data is object.
defered until an attempt is made to access an attribute of the
object. The object also tries to keep track of whether it has changed. It
is easy for this to be done incorrectly. For this reason, methods
The object also tries to keep track of whether it has changed. It of subclasses that change state other than by setting attributes
is easy for this to be done incorrectly. For this reason, methods should: 'self.__changed__(1)' to flag instances as changed.
of subclasses that change state other than by setting attributes
should: 'self.__changed__(1)' to flag instances as changed. You must not override the object's '__getattr__' and '__setattr__'
methods. If you override the objects '__getstate__' method, then
You must not override the object's '__getattr__' and '__setattr__' you must be careful not to include any attributes with names
methods. If you override the objects '__getstate__' method, then starting with '_p_' or '_v_' in the state.
you must be careful not to include any attributes with names
starting with '_p_' or '_v_' in the state. """
_p_oid=None # A Persistent object-id, unique within a jar
""" _p_changed=0 # The object state: None=ghost, 0=normal, 1=changed
_p_oid=None # A Persistent object-id, unique within a jar _p_jar=None # The last jar that this object was stored in.
_p_changed=0 # The object state: None=ghost, 0=normal, 1=changed
_p_jar=None # The last jar that this object was stored in. def _p_deactivate(self):
d=self.__dict__
def _p___init__(self,oid,jar): oid=d['_p_oid']
"""Post creation initialization jar=d['_p_jar']
d.clear()
This is *only* used if we have __getinitargs__! d['_p_oid']=oid
""" d['_p_jar']=jar
d=self.__dict__ d['_p_changed']=None
if d:
newstate={} def __getattr__(self,key):
for key in d.keys(): 'Get an item'
if key[:3] != '_p_': if self._p_changed is None and key[:3] != '_p_':
newstate[key]=d[key] self._p_jar.setstate(self)
del d[key] if self.__dict__.has_key(key): return self.__dict__[key]
if newstate: d['_p_newstate']=newstate
raise AttributeError, key
d['_p_oid']=oid
d['_p_jar']=jar def __setattr__(self,key,value):
d['_p_changed']=None ' '
changed=self._p_changed
def _p_deactivate(self,copy=None): if changed:
if copy is None: newstate=None self.__dict__[key]=value
else: newstate=copy.__dict__ return
d=self.__dict__
oid=self._p_oid k=key[:3]
jar=self._p_jar if k=='_p_' or k=='_v_':
d.clear() if key=='_p_changed':
if newstate: d['_p_newstate']=newstate if changed == value: return
d['_p_oid']=oid if value:
d['_p_jar']=jar if changed: return
d['_p_changed']=None try:
if self._p_jar and self._p_oid:
_p___reinit=_p_deactivate # Back. Comp. get_transaction().register(self)
def __getattr__(self,key):
'Get an item'
if self._p_changed is None and key[:3] != '_p_':
self._p_jar.setstate(self)
if self.__dict__.has_key(key): return self.__dict__[key]
raise AttributeError, key
def __setattr__(self,key,value):
' '
changed=self._p_changed
if changed:
self.__dict__[key]=value
return
k=key[:3]
if k=='_p_' or k=='_v_':
self.__dict__[key]=value
return
jar=self._p_jar
if jar is None:
self.__dict__[key]=value
return
d=self.__dict__
if changed is None:
d['_p_changed']=1
jar.setstate(self)
d[key]=value
try:
get_transaction().register(self)
d['_p_changed']=1
except: pass
def __changed__(self,v=-1):
old=self._p_changed
if v != -1:
if v and not old and self._p_jar is not None:
try: get_transaction().register(self)
except: pass except: pass
self._p_changed = not not v elif value is None:
if self._p_jar and self._p_oid:
return old return self._p_deactivate()
return
def __getstate__(self): value=not not value
self.__dict__[key]=value
# First, update my state, if necessary: return
if self._p_changed is None: self._p_jar.setstate(self)
jar=self._p_jar
state={} if jar is None:
d=self.__dict__ self.__dict__[key]=value
for k,v in d.items(): return
if k[:3] != '_p_' and k[:3] != '_v_': state[k]=v
return state d=self.__dict__
if changed is None:
def __setstate__(self,state): d['_p_changed']=1
d=self.__dict__ jar.setstate(self)
for k,v in state.items(): d[k]=v
return state d[key]=value
try:
def __save__(self): get_transaction().register(self)
'''\ d['_p_changed']=1
Update the object in a persistent database. except: pass
'''
jar=self._p_jar def __changed__(self,v=_marker):
if jar and self._p_changed: jar.store(self) if v is _marker: return not not self._p_changed
self._p_changed = not not v
def __repr__(self):
' ' def __getstate__(self):
return '<%s instance at %s>' % (self.__class__.__name__,
hex(id(self))) # First, update my state, if necessary:
if self._p_changed is None: self._p_jar.setstate(self)
def __inform_commit__(self,T,start_time):
jar=self._p_jar state={}
if jar and self._p_changed: jar.store(self,T) d=self.__dict__
for k,v in d.items():
def __inform_abort__(self,T,start_time): if k[:3] != '_p_' and k[:3] != '_v_': state[k]=v
try: self._p_jar.abort(self,start_time) return state
except: pass
def __setstate__(self,state):
self.__dict__.update(state)
def __repr__(self):
' '
return '<%s instance at %s>' % (self.__class__.__name__,
hex(id(self)))
...@@ -48,12 +48,12 @@ ...@@ -48,12 +48,12 @@
__doc__='''Python implementation of persistent base types __doc__='''Python implementation of persistent base types
$Id: PersistentMapping.py,v 1.2 1998/10/23 21:40:59 jim Exp $''' $Id: PersistentMapping.py,v 1.3 1998/11/11 02:00:56 jim Exp $'''
__version__='$Revision: 1.2 $'[11:-2] __version__='$Revision: 1.3 $'[11:-2]
import Persistence import Persistence
class PersistentMapping(Persistence.Persistent): class PM(Persistence.Persistent):
"""A persistent wrapper for mapping objects. """A persistent wrapper for mapping objects.
This class allows wrapping of mapping objects so that This class allows wrapping of mapping objects so that
...@@ -65,22 +65,36 @@ class PersistentMapping(Persistence.Persistent): ...@@ -65,22 +65,36 @@ class PersistentMapping(Persistence.Persistent):
if container is None: container={} if container is None: container={}
self._container=container self._container=container
def __delitem__(self, key):
del self._container[key]
try: del self._v_keys
except: pass
self.__changed__(1)
def __getitem__(self, key): def __getitem__(self, key):
return self._container[key] return self._container[key]
def __len__(self): return len(self._container)
def __setitem__(self, key, v): def __setitem__(self, key, v):
self._container[key]=v self._container[key]=v
try: del self._v_keys try: del self._v_keys
except: pass except: pass
self.__changed__(1) self.__changed__(1)
def __delitem__(self, key): def clear(self):
del self._container[key] self._container.clear()
try: del self._v_keys self._p_changed=1
except: pass if hasattr(self,'_v_keys'): del self._v_keys
self.__changed__(1)
def __len__(self): return len(self._container) def copy(self): return self.__class__(self._container.copy())
def get(self, key, default): return self._container.get(key, default)
def has_key(self,key): return self._container.has_key(key)
def items(self):
return map(lambda k, d=self: (k,d[k]), self.keys())
def keys(self): def keys(self):
try: return self._v_keys try: return self._v_keys
...@@ -91,14 +105,12 @@ class PersistentMapping(Persistence.Persistent): ...@@ -91,14 +105,12 @@ class PersistentMapping(Persistence.Persistent):
keys.sort() keys.sort()
return keys return keys
def clear(self): def update(self, b):
self._container={} a=self._container
if hasattr(self,'_v_keys'): del self._v_keys for k, v in b.items(): a[k] = v
self._p_changed=1
def values(self): def values(self):
return map(lambda k, d=self: d[k], self.keys()) return map(lambda k, d=self: d[k], self.keys())
def items(self): PersistentMapping=PM
return map(lambda k, d=self: (k,d[k]), self.keys())
def has_key(self,key): return self._container.has_key(key)
...@@ -47,8 +47,8 @@ ...@@ -47,8 +47,8 @@
############################################################################## ##############################################################################
__doc__='''PickleJar Object Cache __doc__='''PickleJar Object Cache
$Id: PickleCache.py,v 1.4 1998/10/23 21:41:24 jim Exp $''' $Id: PickleCache.py,v 1.5 1998/11/11 02:00:56 jim Exp $'''
__version__='$Revision: 1.4 $'[11:-2] __version__='$Revision: 1.5 $'[11:-2]
from sys import getrefcount from sys import getrefcount
...@@ -61,12 +61,14 @@ class PickleCache: ...@@ -61,12 +61,14 @@ class PickleCache:
for a in 'keys', 'items', 'values', 'has_key': for a in 'keys', 'items', 'values', 'has_key':
setattr(self,a,getattr(self.data,a)) setattr(self,a,getattr(self.data,a))
def __getitem__(self, key): def __getitem__(self, key):
cache=self.data v=self.data[key]
v=cache[key] self.incrgc()
return v
def incrgc(self):
# Do cache GC # Do cache GC
cache=self.data
n=min(len(cache)/self.cache_size,10) n=min(len(cache)/self.cache_size,10)
if n: if n:
l=self.cache_location l=self.cache_location
...@@ -82,11 +84,13 @@ class PickleCache: ...@@ -82,11 +84,13 @@ class PickleCache:
del cache[id] del cache[id]
self.cache_location=l self.cache_location=l
return v def __setitem__(self, key, v):
self.data[key]=v
def __setitem__(self, key, v): self.data[key]=v self.incrgc()
def __delitem__(self, key): del self.data[key] def __delitem__(self, key):
del self.data[key]
self.incrgc()
def __len__(self): return len(self.data) def __len__(self): return len(self.data)
......
*shared* *shared*
cPersistence cPersistence.c -I../ExtensionClass -I../python cPersistence cPersistence.c -I../ExtensionClass -I../python
cPickleCache cPickleCache.c -I../ExtensionClass -I../python cPickleCache cPickleCache.c -I../ExtensionClass -I../python
#Record Record.c -I../ExtensionClass cPickleJar ./cPickleJar.c -I../ExtensionClass
iTree ./iTree.c -I../ExtensionClass
intSet ./intSet.c -I/projects/_/ExtensionClass -I/projects/_/cPersistence -I/projects/_/python
BTree ./BTree.c -I/projects/_/ExtensionClass -I/projects/_/cPersistence -I/projects/_/python
IIBTree ./IIBTree.c -I/projects/_/ExtensionClass -I/projects/_/cPersistence -I/projects/_/python
IOBTree ./IOBTree.c -I/projects/_/ExtensionClass -I/projects/_/cPersistence -I/projects/_/python
OIBTree ./OIBTree.c -I/projects/_/ExtensionClass -I/projects/_/cPersistence -I/projects/_/python
#install CacheManager.py
#!/usr/local/bin/python ##############################################################################
# $What$
__doc__='''A very simple HTTP transaction manager.
This module provides a very simple transaction manager for
HTTP requests in a single-threaded application environment.
(Future versions of this module may support multiple transactions.)
This module treats each HTTP request as a transaction.
To use, import the module and then call the 'install' function.
This will install the function 'get_transaction'. This function will
be used by transactional objects to get the current transaction.
$Id: Transaction.py,v 1.2 1997/10/30 20:23:26 brian Exp $'''
# Copyright
#
# Copyright 1996 Digital Creations, L.C., 910 Princess Anne
# Street, Suite 300, Fredericksburg, Virginia 22401 U.S.A. All
# rights reserved. Copyright in this software is owned by DCLC,
# unless otherwise indicated. Permission to use, copy and
# distribute this software is hereby granted, provided that the
# above copyright notice appear in all copies and that both that
# copyright notice and this permission notice appear. Note that
# any product, process or technology described in this software
# may be the subject of other Intellectual Property rights
# reserved by Digital Creations, L.C. and are not licensed
# hereunder.
#
# Trademarks
#
# Digital Creations & DCLC, are trademarks of Digital Creations, L.C..
# All other trademarks are owned by their respective companies.
#
# No Warranty
#
# The software is provided "as is" without warranty of any kind,
# either express or implied, including, but not limited to, the
# implied warranties of merchantability, fitness for a particular
# purpose, or non-infringement. This software could include
# technical inaccuracies or typographical errors. Changes are
# periodically made to the software; these changes will be
# incorporated in new editions of the software. DCLC may make
# improvements and/or changes in this software at any time
# without notice.
# #
# Limitation Of Liability # Copyright (c) 1996-1998, Digital Creations, Fredericksburg, VA, USA.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# o Redistributions of source code must retain the above copyright
# notice, this list of conditions, and the disclaimer that follows.
#
# o Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions, and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# o Neither the name of Digital Creations nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
#
# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS AND CONTRIBUTORS *AS IS*
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL
# CREATIONS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
# DAMAGE.
# #
# In no event will DCLC be liable for direct, indirect, special, #
# incidental, economic, cover, or consequential damages arising # If you have questions regarding this software, contact:
# out of the use of or inability to use this software even if
# advised of the possibility of such damages. Some states do not
# allow the exclusion or limitation of implied warranties or
# limitation of liability for incidental or consequential
# damages, so the above limitation or exclusion may not apply to
# you.
#
# #
# If you have questions regarding this software, # Digital Creations, L.C.
# contact: # 910 Princess Ann Street
# Fredericksburge, Virginia 22401
# #
# Jim Fulton, jim@digicool.com # info@digicool.com
# #
# (540) 371-6909 # (540) 371-6909
# #
# $Log: Transaction.py,v $ ##############################################################################
# Revision 1.2 1997/10/30 20:23:26 brian """Transaction management
# Fixed thread dependency
# $Id: Transaction.py,v 1.3 1998/11/11 02:00:56 jim Exp $"""
# Revision 1.1 1997/04/11 21:42:52 jim __version__='$Revision: 1.3 $'[11:-2]
# *** empty log message ***
# import time, sys, struct
# Revision 1.5 1997/03/25 20:42:58 jim from struct import pack
# Changed to make all persistent objects transactional. from string import split, strip, join
#
# Revision 1.4 1997/02/11 13:23:14 jim ConflictError=""
# Many changes to support both cPickle and LRT.
# class Transaction:
# Revision 1.3 1996/11/14 17:49:38 jim 'Simple transaction objects for single-threaded applications.'
# Removed c-implementation support. user=''
# description=''
# Revision 1.2 1996/10/15 18:32:25 jim _connections=None
# Added support for cSingleThreadedTransaction
# def __init__(self,
# Revision 1.1 1996/09/06 14:35:54 jfulton time=time.time, pack=struct.pack, gmtime=time.gmtime):
# For use with Chris doing installation self._objects=[]
# self._append=self._objects.append
# self.time=now=time()
# y,mo,d,h,m=gmtime(now)[:5]
__version__='$Revision: 1.2 $'[11:-2] s=int((now%60)*1000000)
self.id=pack("<II", (((y*12+mo)*31+d)*24+h)*60+m, s)
self._note=self._user=self._description=''
if self._connections:
for c in self._connections.values(): c.close()
del self._connections
def __str__(self): return "%.3f\t%s" % (self.time,self._note)
def abort(self, freeme=1):
'Abort the transaction.'
t=v=tb=None
try:
for o in self._objects:
try:
if hasattr(o,'_p_jar'): o=o._p_jar
if hasattr(o,'tpc_abort'): o.tpc_abort(self)
except: t,v,tb=sys.exc_info()
if t is not None: raise t,v,tb
finally:
tb=None
if freeme: free_transaction()
def begin(self, info=None):
'''Begin a new transaction.
This aborts any transaction in progres.
'''
if self._objects: self._abort(0)
self.__init__()
if info:
info=split(info,'\t')
self.user=strip(info[0])
self.description=strip(join(info,'\t'))
def commit(self):
'Finalize the transaction'
t=v=tb=None
try:
try:
for o in self._objects:
if hasattr(o,'_p_jar'):
j=o._p_jar
j.tpc_begin(self)
j.commit(o,self)
elif hasattr(o,'tpc_begin'):
o.tpc_begin(self)
except:
t,v,tb=sys.exc_info()
self.abort()
raise t,v,tb
for o in self._objects:
try:
if hasattr(o,'_p_jar'): o=o._p_jar
if hasattr(o,'tpc_finish'): o.tpc_finish(self)
except: t,v,tb=sys.exc_info()
if t is not None: raise t,v,tb
finally:
tb=None
free_transaction()
def register(self,object):
'Register the given object for transaction control.'
self._append(object)
def remark(self, text):
if self.description:
self.description = "%s\n\n%s" % (self.description, strip(text))
else:
self.description = strip(text)
def setUser(self, user_name, path='/'):
self.user="%s %s" % (path, user_name)
############################################################################
# install get_transaction:
# Install myself before anything else happens: try:
import thread
_t={}
def get_transaction(_id=thread.get_ident, _t=_t):
id=_id()
try: t=_t[id]
except KeyError: _t[id]=t=Transaction()
return t
# This both gets everything STT has *and* arranges for our def free_transaction(_id=thread.get_ident, _t=_t):
# get_transaction to override their's id=_id()
from SingleThreadedTransaction import * try: del _t[id]
except KeyError: pass
import SingleThreadedTransaction del thread
try:
import thread
get_id=thread.get_ident
except: except:
def get_id(): return 0 _t=Transaction()
def get_transaction(_t=_t): return _t
theTransactions={} def free_transaction(_t=_t): _t.__init__()
def get_transaction(): del _t
id=get_id()
try: theTransaction=theTransactions[id]
except KeyError: theTransactions[id]=theTransaction=Transaction()
return theTransaction
import __main__ import __main__
__main__.__builtins__.get_transaction=get_transaction __main__.__builtins__.get_transaction=get_transaction
class Transaction(SingleThreadedTransaction.Transaction): def time2id(now, gmtime=time.gmtime, pack=struct.pack):
'''\ y,m,d,h,m=gmtime(now)[:5]
Simple transaction objects. s=int((now%60)*1000000)
return pack("<II", ((y*12+m)*31+d)*24, s)
'''
def abort(self):
'''\
Abort the transaction.
All objects participating in the current transaction will be
informed of the abort so that they can roll back changes or
forget pending changes.
'''
self._abort()
id=get_id()
del theTransactions[id]
##############################################################################
#
# Copyright (c) 1996-1998, Digital Creations, Fredericksburg, VA, USA.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# o Redistributions of source code must retain the above copyright
# notice, this list of conditions, and the disclaimer that follows.
#
# o Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions, and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# o Neither the name of Digital Creations nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
#
# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS AND CONTRIBUTORS *AS IS*
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL
# CREATIONS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
# DAMAGE.
#
#
# If you have questions regarding this software, contact:
#
# Digital Creations, L.C.
# 910 Princess Ann Street
# Fredericksburge, Virginia 22401
#
# info@digicool.com
#
# (540) 371-6909
#
##############################################################################
from DB import DB
from Persistence import Persistent
from POSException import *
##############################################################################
#
# Copyright (c) 1996-1998, Digital Creations, Fredericksburg, VA, USA.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# o Redistributions of source code must retain the above copyright
# notice, this list of conditions, and the disclaimer that follows.
#
# o Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions, and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# o Neither the name of Digital Creations nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
#
# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS AND CONTRIBUTORS *AS IS*
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL
# CREATIONS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
# DAMAGE.
#
#
# If you have questions regarding this software, contact:
#
# Digital Creations, L.C.
# 910 Princess Ann Street
# Fredericksburge, Virginia 22401
#
# info@digicool.com
#
# (540) 371-6909
#
##############################################################################
"""Thread abstraction module
With this, we can run with or wothout threads.
$Id: bpthread.py,v 1.1 1998/11/11 02:00:56 jim Exp $"""
try:
from thread import *
except:
class allocate_lock:
def acquire(self, *args): return args and 1 or None
def release(self): pass
start_new_thread=apply
"""Install C-based replacements for BoboPOS components.
"""
import cPickleCache, cPersistence
import BoboPOS3, BoboPOS3.Persistence, BoboPOS3.Connection
BoboPOS3.Persistence.Persistent=cPersistence.Persistent
BoboPOS3.Persistent=cPersistence.Persistent
BoboPOS3.Connection.PickleCache=cPickleCache.PickleCache
/*********************************************************************** /***********************************************************************
$Id: cPersistence.c,v 1.24 1998/07/27 13:03:21 jim Exp $ $Id: cPersistence.c,v 1.25 1998/11/11 02:00:56 jim Exp $
C Persistence Module C Persistence Module
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
*****************************************************************************/ *****************************************************************************/
static char *what_string = "$Id: cPersistence.c,v 1.24 1998/07/27 13:03:21 jim Exp $"; static char *what_string = "$Id: cPersistence.c,v 1.25 1998/11/11 02:00:56 jim Exp $";
#include <time.h> #include <time.h>
#include "cPersistence.h" #include "cPersistence.h"
...@@ -21,8 +21,7 @@ static char *what_string = "$Id: cPersistence.c,v 1.24 1998/07/27 13:03:21 jim E ...@@ -21,8 +21,7 @@ static char *what_string = "$Id: cPersistence.c,v 1.24 1998/07/27 13:03:21 jim E
#define UNLESS(E) if(!(E)) #define UNLESS(E) if(!(E))
#define UNLESS_ASSIGN(V,E) ASSIGN(V,E) UNLESS(V) #define UNLESS_ASSIGN(V,E) ASSIGN(V,E) UNLESS(V)
static PyObject *py_store, *py_oops, *py_keys, *py_setstate, *py___changed__, static PyObject *py_keys, *py_setstate, *py___dict__;
*py___dict__, *py_mtime, *py_onearg, *py___getinitargs__, *py___init__;
#ifdef DEBUG_LOG #ifdef DEBUG_LOG
static PyObject *debug_log=0; static PyObject *debug_log=0;
...@@ -36,8 +35,9 @@ call_debug(char *event, cPersistentObject *self) ...@@ -36,8 +35,9 @@ call_debug(char *event, cPersistentObject *self)
/* /*
printf("%s %p\n",event,self->ob_type->tp_name); printf("%s %p\n",event,self->ob_type->tp_name);
*/ */
r=PyObject_CallFunction(debug_log,"s(sii)",event, r=PyObject_CallFunction(debug_log,"s(ss#i)",event,
self->ob_type->tp_name, self->oid, self->state); self->ob_type->tp_name, self->oid, 8,
self->state);
Py_XDECREF(r); Py_XDECREF(r);
} }
#endif #endif
...@@ -46,16 +46,9 @@ static void ...@@ -46,16 +46,9 @@ static void
init_strings() init_strings()
{ {
#define INIT_STRING(S) py_ ## S = PyString_FromString(#S) #define INIT_STRING(S) py_ ## S = PyString_FromString(#S)
INIT_STRING(store);
INIT_STRING(oops);
INIT_STRING(keys); INIT_STRING(keys);
INIT_STRING(setstate); INIT_STRING(setstate);
INIT_STRING(mtime);
INIT_STRING(__changed__);
INIT_STRING(__init__);
INIT_STRING(__getinitargs__);
INIT_STRING(__dict__); INIT_STRING(__dict__);
py_onearg=Py_BuildValue("(i)",1);
#undef INIT_STRING #undef INIT_STRING
} }
...@@ -113,6 +106,22 @@ callmethod3(PyObject *self, PyObject *name, ...@@ -113,6 +106,22 @@ callmethod3(PyObject *self, PyObject *name,
return self; return self;
} }
#define UPDATE_STATE_IF_NECESSARY(self, ER) \
if(self->state < 0 && self->jar) \
{ \
PyObject *r; \
\
self->state=cPersistent_STICKY_STATE; \
UNLESS(r=callmethod1(self->jar,py_setstate,(PyObject*)self)) \
{ \
self->state=cPersistent_GHOST_STATE; \
return ER; \
} \
self->state=cPersistent_UPTODATE_STATE; \
Py_DECREF(r); \
}
static PyObject * static PyObject *
#ifdef HAVE_STDARG_PROTOTYPES #ifdef HAVE_STDARG_PROTOTYPES
/* VARARGS 2 */ /* VARARGS 2 */
...@@ -148,254 +157,89 @@ PyString_BuildFormat(va_alist) va_dcl ...@@ -148,254 +157,89 @@ PyString_BuildFormat(va_alist) va_dcl
/****************************************************************************/ /****************************************************************************/
static void
PATime_dealloc(PATimeobject *self)
{
/*printf("D");*/
Py_DECREF(self->object);
PyMem_DEL(self);}
static PyObject *
PATime_repr(PATimeobject *self)
{
return PyString_BuildFormat("<access time: %d>","i",self->object->atime);
}
static PyTypeObject
PATimeType = {
PyObject_HEAD_INIT(NULL) 0,
"PersistentATime", /*tp_name*/
sizeof(PATimeobject), /*tp_basicsize*/
0, /*tp_itemsize*/
/* methods */
(destructor)PATime_dealloc, /*tp_dealloc*/
0L,0L,0L,0L,
(reprfunc)PATime_repr, /*tp_repr*/
0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,
"Values for holding access times for persistent objects"
};
/****************************************************************************/
/* Declarations for objects of type Persistent */
#define GHOST_STATE -1
#define UPTODATE_STATE 0
#define CHANGED_STATE 1
staticforward PyExtensionClass Pertype; staticforward PyExtensionClass Pertype;
staticforward PyExtensionClass TPertype; staticforward PyExtensionClass TPertype;
static char Per___changed____doc__[] = static int
"__changed__([flag]) -- Flag or determine whether an object has changed\n" changed(cPersistentObject *self)
" \n"
"If a value is specified, then it should indicate whether an\n"
"object has or has not been changed. If no value is specified,\n"
"then the return value will indicate whether the object has\n"
"changed.\n"
;
static PyObject *changed_args=(PyObject*)Per___changed____doc__;
static PyObject *
T___changed__(cPersistentObject *self, PyObject *args)
{ {
static PyObject *builtins=0, *get_transaction=0, *py_register=0; static PyObject *builtins=0, *get_transaction=0, *py_register=0;
PyObject *o, *T; PyObject *T;
if(PyArg_Parse(args, "O", &o)) if ((self->state == cPersistent_UPTODATE_STATE ||
self->state == cPersistent_STICKY_STATE)
&& self->jar)
{ {
int t; UNLESS (get_transaction)
t=PyObject_IsTrue(o);
if(t && self->state != cPersistent_CHANGED_STATE && self->jar)
{ {
UNLESS(get_transaction) UNLESS (py_register=PyString_FromString("register")) return -1;
{ UNLESS (T=PyImport_ImportModule("__main__")) return -1;
UNLESS(builtins) ASSIGN(T,PyObject_GetAttrString(T,"__builtins__"));
{ UNLESS (T) return -1;
UNLESS(T=PyImport_ImportModule("__main__")) return NULL; builtins=T;
ASSIGN(T,PyObject_GetAttrString(T,"__builtins__")); UNLESS (get_transaction=PyObject_GetAttrString(builtins,
UNLESS(T) return NULL; "get_transaction"))
UNLESS(py_register=PyString_FromString("register")) goto err; PyErr_Clear();
builtins=T; }
} if (get_transaction)
UNLESS(get_transaction=PyObject_GetAttrString(builtins, {
"get_transaction")) UNLESS (T=PyObject_CallObject(get_transaction,NULL)) return -1;
PyErr_Clear(); ASSIGN(T,PyObject_GetAttr(T,py_register));
} UNLESS (T) return -1;
if(get_transaction) ASSIGN(T, PyObject_CallFunction(T,"O",self));
{ if (T) Py_DECREF(T);
UNLESS(T=PyObject_CallObject(get_transaction,NULL)) return NULL; else return -1;
UNLESS_ASSIGN(T,PyObject_GetAttr(T,py_register)) return NULL;
UNLESS(o=PyTuple_New(1)) goto err;
Py_INCREF(self);
PyTuple_SET_ITEM(o,0,(PyObject*)self);
ASSIGN(o,PyObject_CallObject(T,o));
Py_DECREF(T);
UNLESS(o) return NULL;
Py_DECREF(o);
}
} }
if(self->state != cPersistent_GHOST_STATE) self->state=t;
Py_INCREF(Py_None); self->state=cPersistent_CHANGED_STATE;
return Py_None;
} }
else
{
PyErr_Clear();
UNLESS(PyArg_Parse(args, "")) return NULL;
return PyInt_FromLong(self->state==cPersistent_CHANGED_STATE);
}
err:
Py_DECREF(T);
return NULL;
}
static char Per___save____doc__[] =
"__save__() -- Update the object in a persistent database."
;
static PyObject *
Per___save__(self, args)
cPersistentObject *self;
PyObject *args;
{
if(self->oid && self->jar && self->state == CHANGED_STATE)
return callmethod1(self->jar,py_store,(PyObject*)self);
Py_INCREF(Py_None);
return Py_None;
}
static char Per___inform_commit____doc__[] = return 0;
"__inform_commit__(transaction,start_time) -- Commit object changes"
;
static PyObject *
Per___inform_commit__(self, args)
cPersistentObject *self;
PyObject *args;
{
PyObject *T=0, *t=0;
UNLESS(PyArg_ParseTuple(args, "OO", &T, &t)) return NULL;
if(self->oid && self->jar && self->state == CHANGED_STATE)
return callmethod2(self->jar,py_store,(PyObject*)self,T);
Py_INCREF(Py_None);
return Py_None;
} }
static char Per___inform_abort____doc__[] =
"__inform_abort__(transaction,start_time) -- Abort object changes"
;
static PyObject * static PyObject *
Per___inform_abort__(self, args) Per___changed__(cPersistentObject *self, PyObject *args)
cPersistentObject *self;
PyObject *args;
{ {
PyObject *T, *t; PyObject *v=0;
if (args && ! PyArg_ParseTuple(args, "|O",&v)) return NULL;
UNLESS(PyArg_ParseTuple(args, "OO", &T, &t)) return NULL; if (v && ! PyObject_IsTrue(v))
if(self->oid && self->jar && self->state != GHOST_STATE)
{ {
args=callmethod3(self->jar,py_oops,(PyObject*)self,t,T); PyErr_SetString(PyExc_TypeError,
if(args) "Only true arguments are allowed.");
Py_DECREF(args); return NULL;
else
PyErr_Clear();
} }
if (changed(self) < 0) return NULL;
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
} }
static char Per__p___init____doc__[] =
"_p___init__(oid,jar) -- Initialize persistence management data"
;
static PyObject * static PyObject *
Per__p___init__(self, args) Per__p_deactivate(cPersistentObject *self, PyObject *args)
cPersistentObject *self;
PyObject *args;
{ {
int oid; PyObject *init=0, *copy, *dict;
PyObject *jar;
UNLESS(PyArg_Parse(args, "(iO)", &oid, &jar)) return NULL;
#ifdef DEBUG_LOG #ifdef DEBUG_LOG
if(idebug_log < 0) call_debug("init",self); if (idebug_log < 0) call_debug("reinit",self);
#endif #endif
Py_INCREF(jar);
self->oid=oid;
ASSIGN(self->jar, jar);
self->state=GHOST_STATE;
Py_INCREF(Py_None);
return Py_None;
}
static PyObject * if (args && ! PyArg_ParseTuple(args,"")) return NULL;
Per__p___reinit__(cPersistentObject *self, PyObject *args)
{
PyObject *init=0, *copy, *dict;
#ifdef DEBUG_LOG if (self->state==cPersistent_UPTODATE_STATE && self->jar &&
if(idebug_log < 0) call_debug("reinit",self); HasInstDict(self) && (dict=INSTANCE_DICT(self)))
#endif
if(PyArg_Parse(args,""))
{ {
if(self->state==cPersistent_UPTODATE_STATE) PyDict_Clear(dict);
{ self->state=cPersistent_GHOST_STATE;
if(HasInstDict(self) && (dict=INSTANCE_DICT(self)))
{
PyDict_Clear(dict);
self->state=cPersistent_GHOST_STATE;
}
}
} }
else
{
PyErr_Clear();
UNLESS(PyArg_Parse(args, "O", &copy)) return NULL;
if(HasInstDict(self) && self->state==cPersistent_UPTODATE_STATE)
{
UNLESS(args=PyObject_GetAttr(copy,py___dict__)) return NULL;
ASSIGN(INSTANCE_DICT(self),args);
self->state=GHOST_STATE;
}
}
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
err:
Py_XDECREF(init);
return NULL;
} }
static int static int
Per_setstate(self) Per_setstate(cPersistentObject *self)
cPersistentObject *self;
{ {
self->atime=(time_t)1; /* Mark this object as sticky */ UPDATE_STATE_IF_NECESSARY(self, -1);
if(self->state==GHOST_STATE && self->jar) self->state=cPersistent_STICKY_STATE;
{
PyObject *r;
self->state=UPTODATE_STATE;
UNLESS(r=callmethod1(self->jar,py_setstate,(PyObject*)self))
{
self->state=GHOST_STATE;
self->atime=time(NULL); /* Unmark as sticky */
return -1;
}
Py_DECREF(r);
}
return 0; return 0;
} }
...@@ -406,25 +250,13 @@ Per__getstate__(self,args) ...@@ -406,25 +250,13 @@ Per__getstate__(self,args)
{ {
PyObject *__dict__, *d=0; PyObject *__dict__, *d=0;
UNLESS(PyArg_Parse(args, "")) return NULL; UNLESS(PyArg_ParseTuple(args, "")) return NULL;
#ifdef DEBUG_LOG #ifdef DEBUG_LOG
if(idebug_log < 0) call_debug("get",self); if(idebug_log < 0) call_debug("get",self);
#endif #endif
/* Update state, if necessary */ UPDATE_STATE_IF_NECESSARY(self, NULL);
if(self->state==GHOST_STATE && self->jar)
{
PyObject *r;
self->state=UPTODATE_STATE;
UNLESS(r=callmethod1(self->jar,py_setstate,(PyObject*)self))
{
self->state=GHOST_STATE;
return NULL;
}
Py_DECREF(r);
}
if(HasInstDict(self) && (__dict__=INSTANCE_DICT(self))) if(HasInstDict(self) && (__dict__=INSTANCE_DICT(self)))
{ {
...@@ -465,16 +297,13 @@ Per__setstate__(self,args) ...@@ -465,16 +297,13 @@ Per__setstate__(self,args)
PyObject *__dict__, *v, *keys=0, *key=0, *e=0; PyObject *__dict__, *v, *keys=0, *key=0, *e=0;
int l, i; int l, i;
/*printf("%s(%d) ", self->ob_type->tp_name,self->oid);*/
if(HasInstDict(self)) if(HasInstDict(self))
{ {
UNLESS(PyArg_Parse(args, "O", &v)) return NULL; UNLESS(PyArg_ParseTuple(args, "O", &v)) return NULL;
#ifdef DEBUG_LOG #ifdef DEBUG_LOG
if(idebug_log < 0) call_debug("set",self); if(idebug_log < 0) call_debug("set",self);
#endif #endif
self->state=UPTODATE_STATE;
if(v!=Py_None) if(v!=Py_None)
{ {
__dict__=INSTANCE_DICT(self); __dict__=INSTANCE_DICT(self);
...@@ -483,10 +312,7 @@ Per__setstate__(self,args) ...@@ -483,10 +312,7 @@ Per__setstate__(self,args)
{ {
for(i=0; PyDict_Next(v,&i,&key,&e);) for(i=0; PyDict_Next(v,&i,&key,&e);)
if(PyObject_SetItem(__dict__,key,e) < 0) if(PyObject_SetItem(__dict__,key,e) < 0)
{ return NULL;
self->state=GHOST_STATE;
return NULL;
}
} }
else else
{ {
...@@ -509,7 +335,6 @@ Per__setstate__(self,args) ...@@ -509,7 +335,6 @@ Per__setstate__(self,args)
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
err: err:
self->state=GHOST_STATE;
Py_XDECREF(key); Py_XDECREF(key);
Py_XDECREF(e); Py_XDECREF(e);
Py_XDECREF(keys); Py_XDECREF(keys);
...@@ -518,22 +343,13 @@ err: ...@@ -518,22 +343,13 @@ err:
static struct PyMethodDef Per_methods[] = { static struct PyMethodDef Per_methods[] = {
{"__changed__", (PyCFunction)T___changed__, 0, {"__changed__", (PyCFunction)Per___changed__, METH_VARARGS,
Per___changed____doc__}, "DEPRECATED: use self._p_changed=1"},
{"__save__", (PyCFunction)Per___save__, 1, {"_p_deactivate", (PyCFunction)Per__p_deactivate, METH_VARARGS,
Per___save____doc__}, "_p_deactivate(oid) -- Deactivate the object"},
{"__inform_commit__", (PyCFunction)Per___inform_commit__, 1, {"__getstate__", (PyCFunction)Per__getstate__, METH_VARARGS,
Per___inform_commit____doc__},
{"__inform_abort__", (PyCFunction)Per___inform_abort__, 1,
Per___inform_abort____doc__},
{"_p___init__", (PyCFunction)Per__p___init__, 0,
Per__p___init____doc__},
{"_p_deactivate", (PyCFunction)Per__p___reinit__, 0,
"_p_deactivate(oid[,copy]) -- Deactivate the object"},
{"_p___reinit__", (PyCFunction)Per__p___reinit__, 0,""},
{"__getstate__", (PyCFunction)Per__getstate__, 0,
"__getstate__() -- Return the state of the object" }, "__getstate__() -- Return the state of the object" },
{"__setstate__", (PyCFunction)Per__setstate__, 0, {"__setstate__", (PyCFunction)Per__setstate__, METH_VARARGS,
"__setstate__(v) -- Restore the saved state of the object from v" }, "__setstate__(v) -- Restore the saved state of the object from v" },
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
...@@ -549,28 +365,9 @@ Per_dealloc(self) ...@@ -549,28 +365,9 @@ Per_dealloc(self)
if(idebug_log < 0) call_debug("del",self); if(idebug_log < 0) call_debug("del",self);
#endif #endif
Py_XDECREF(self->jar); Py_XDECREF(self->jar);
/*Py_XDECREF(self->atime);*/
PyMem_DEL(self); PyMem_DEL(self);
} }
static void
Per_set_atime(cPersistentObject *self)
{
if(self->atime == (time_t)1) return;
self->atime = time(NULL);
}
static PyObject *
Per_atime(cPersistentObject *self)
{
PATimeobject *r;
UNLESS(r=PyObject_NEW(PATimeobject,&PATimeType)) return NULL;
Py_INCREF(self);
r->object=self;
return (PyObject*)r;
}
static PyObject * static PyObject *
Per_getattr(cPersistentObject *self, PyObject *oname, char *name, Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
PyObject *(*getattrf)(PyObject *, PyObject*)) PyObject *(*getattrf)(PyObject *, PyObject*))
...@@ -584,17 +381,7 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name, ...@@ -584,17 +381,7 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
{ {
case 'o': case 'o':
if(*n++=='i' && *n++=='d' && ! *n) if(*n++=='i' && *n++=='d' && ! *n)
{ return PyString_FromStringAndSize(self->oid, 8);
if(self->oid)
{
return PyInt_FromLong(self->oid);
}
else
{
Py_INCREF(Py_None);
return Py_None;
}
}
break; break;
case 'j': case 'j':
if(*n++=='a' && *n++=='r' && ! *n) if(*n++=='a' && *n++=='r' && ! *n)
...@@ -614,34 +401,15 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name, ...@@ -614,34 +401,15 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
case 'c': case 'c':
if(strcmp(n,"hanged")==0) if(strcmp(n,"hanged")==0)
{ {
if(self->state == GHOST_STATE) if(self->state < 0)
{ {
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
} }
return PyInt_FromLong(self->state == CHANGED_STATE); return PyInt_FromLong(self->state ==
} cPersistent_CHANGED_STATE);
break;
case 'a':
if(strcmp(n,"time")==0)
{
if(self->state != UPTODATE_STATE) Per_set_atime(self);
return Per_atime(self);
} }
break; break;
case 'm':
if(strcmp(n,"time")==0)
{
if(self->jar)
return callmethod1(self->jar,py_mtime,(PyObject*)self);
Py_INCREF(Py_None);
return Py_None;
}
break;
case 's':
if(strcmp(n,"tate")==0)
return PyInt_FromLong(self->state);
break;
} }
return getattrf((PyObject *)self, oname); return getattrf((PyObject *)self, oname);
...@@ -650,21 +418,9 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name, ...@@ -650,21 +418,9 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
(strcmp(name,"dict__")==0 || strcmp(name,"class__")==0 (strcmp(name,"dict__")==0 || strcmp(name,"class__")==0
|| strcmp(name, "of__")==0))) || strcmp(name, "of__")==0)))
{ {
/* Update state, if necessary */ UPDATE_STATE_IF_NECESSARY(self, NULL);
if(self->state==GHOST_STATE && self->jar)
{
PyObject *r;
self->state=UPTODATE_STATE;
UNLESS(r=callmethod1(self->jar,py_setstate,(PyObject*)self))
{
self->state=GHOST_STATE;
return NULL;
}
Py_DECREF(r);
}
Per_set_atime(self); self->atime=((long)(time(NULL)/3))%65536;
} }
return getattrf((PyObject *)self, oname); return getattrf((PyObject *)self, oname);
...@@ -679,15 +435,12 @@ Per_getattro(cPersistentObject *self, PyObject *name) ...@@ -679,15 +435,12 @@ Per_getattro(cPersistentObject *self, PyObject *name)
return Per_getattr(self,name,s, PyExtensionClassCAPI->getattro); return Per_getattr(self,name,s, PyExtensionClassCAPI->getattro);
} }
static int static int
changed(PyObject *self) bad_delattr()
{ {
PyObject *c; PyErr_SetString(PyExc_AttributeError,
"delete undeletable attribute");
UNLESS(c=PyObject_GetAttr(self,py___changed__)) return -1; return -1;
UNLESS_ASSIGN(c,PyObject_CallObject(c,py_onearg)) return -1;
Py_DECREF(c);
return 0;
} }
static int static int
...@@ -703,8 +456,15 @@ _setattro(cPersistentObject *self, PyObject *oname, PyObject *v, ...@@ -703,8 +456,15 @@ _setattro(cPersistentObject *self, PyObject *oname, PyObject *v,
{ {
if(name[3]=='o' && name[4]=='i' && name[5]=='d' && ! name[6]) if(name[3]=='o' && name[4]=='i' && name[5]=='d' && ! name[6])
{ {
if(v && PyInt_Check(v)) self->oid=PyInt_AsLong(v); if (! v) return bad_delattr();
else self->oid=0; if (PyString_Check(v) && PyString_GET_SIZE(v)==8)
memcpy(self->oid, PyString_AS_STRING(v), 8);
else
{
PyErr_SetString(PyExc_AttributeError,
"_p_oid must be an 8-character string");
return -1;
}
return 0; return 0;
} }
if(name[3]=='j' && name[4]=='a' && name[5]=='r' && ! name[6]) if(name[3]=='j' && name[4]=='a' && name[5]=='r' && ! name[6])
...@@ -715,40 +475,29 @@ _setattro(cPersistentObject *self, PyObject *oname, PyObject *v, ...@@ -715,40 +475,29 @@ _setattro(cPersistentObject *self, PyObject *oname, PyObject *v,
} }
if(strcmp(name+3,"changed")==0) if(strcmp(name+3,"changed")==0)
{ {
if(v==Py_None) self->state=GHOST_STATE; if (! v) return bad_delattr();
else self->state= (v && PyObject_IsTrue(v)); if (v==Py_None)
return 0; {
} if (Per__p_deactivate(self, NULL)) Py_DECREF(Py_None);
if(strcmp(name+3,"atime")==0) return 0;
{ }
self->atime=(time_t)1; if (PyObject_IsTrue(v)) return changed(self);
if (self->state >= 0) self->state=cPersistent_UPTODATE_STATE;
return 0; return 0;
} }
} }
else else
{ {
PyObject *r; PyObject *r;
/* Update state, if necessary */ UPDATE_STATE_IF_NECESSARY(self, -1);
if(self->state==GHOST_STATE && self->jar)
{
self->state=UPTODATE_STATE;
UNLESS(r=callmethod1(self->jar,py_setstate,(PyObject*)self))
{
self->state=GHOST_STATE;
return -1;
}
Py_DECREF(r);
}
/* Record access times */ /* Record access times */
Per_set_atime(self); self->atime=((long)(time(NULL)/3))%65536;
if(! (*name=='_' && name[1]=='v' && name[2]=='_') if(! (*name=='_' && name[1]=='v' && name[2]=='_')
&& self->state != CHANGED_STATE && self->jar) && self->state != cPersistent_CHANGED_STATE && self->jar)
if(changed((PyObject*)self) < 0) return -1; if(changed(self) < 0) return -1;
} }
return setattrf((PyObject*)self,oname,v); return setattrf((PyObject*)self,oname,v);
...@@ -760,28 +509,6 @@ Per_setattro(cPersistentObject *self, PyObject *oname, PyObject *v) ...@@ -760,28 +509,6 @@ Per_setattro(cPersistentObject *self, PyObject *oname, PyObject *v)
return _setattro(self,oname, v, PyExtensionClassCAPI->setattro); return _setattro(self,oname, v, PyExtensionClassCAPI->setattro);
} }
static char Pertype__doc__[] =
"Persistent object support mix-in class\n"
"\n"
"When a persistent object is loaded from a database, the object's\n"
"data is not immediately loaded. Loading of the objects data is\n"
"defered until an attempt is made to access an attribute of the\n"
"object. \n"
"\n"
"The object also tries to keep track of whether it has changed. It\n"
"is easy for this to be done incorrectly. For this reason, methods\n"
"of subclasses that change state other than by setting attributes\n"
"should: 'self.__changed__(1)' to flag instances as changed.\n"
"\n"
"Data are not saved automatically. To save an object's state, call\n"
"the object's '__save__' method.\n"
"\n"
"You must not override the object's '__getattr__' and '__setattr__'\n"
"methods. If you override the objects '__getstate__' method, then\n"
"you must be careful not to include any attributes with names\n"
"starting with '_p_' in the state.\n"
;
static PyExtensionClass Pertype = { static PyExtensionClass Pertype = {
PyObject_HEAD_INIT(NULL) PyObject_HEAD_INIT(NULL)
0, /*ob_size*/ 0, /*ob_size*/
...@@ -804,8 +531,7 @@ static PyExtensionClass Pertype = { ...@@ -804,8 +531,7 @@ static PyExtensionClass Pertype = {
(getattrofunc)Per_getattro, /*tp_getattr with object key*/ (getattrofunc)Per_getattro, /*tp_getattr with object key*/
(setattrofunc)Per_setattro, /*tp_setattr with object key*/ (setattrofunc)Per_setattro, /*tp_setattr with object key*/
/* Space for future expansion */ /* Space for future expansion */
0L,0L, 0L,0L,"",
Pertype__doc__, /* Documentation string */
METHOD_CHAIN(Per_methods), METHOD_CHAIN(Per_methods),
EXTENSIONCLASS_BASICNEW_FLAG, EXTENSIONCLASS_BASICNEW_FLAG,
}; };
...@@ -830,7 +556,7 @@ set_debug_log(PyObject *ignored, PyObject *args) ...@@ -830,7 +556,7 @@ set_debug_log(PyObject *ignored, PyObject *args)
static struct PyMethodDef cP_methods[] = { static struct PyMethodDef cP_methods[] = {
#ifdef DEBUG_LOG #ifdef DEBUG_LOG
{"set_debug_log", (PyCFunction)set_debug_log, 0, {"set_debug_log", (PyCFunction)set_debug_log, METH_VARARGS,
"set_debug_log(callable) -- Provide a function to log events\n" "set_debug_log(callable) -- Provide a function to log events\n"
"\n" "\n"
"The function will be called with an event name and a persistent object.\n" "The function will be called with an event name and a persistent object.\n"
...@@ -859,9 +585,7 @@ void ...@@ -859,9 +585,7 @@ void
initcPersistence() initcPersistence()
{ {
PyObject *m, *d; PyObject *m, *d;
char *rev="$Revision: 1.24 $"; char *rev="$Revision: 1.25 $";
PATimeType.ob_type=&PyType_Type;
m = Py_InitModule4("cPersistence", cP_methods, m = Py_InitModule4("cPersistence", cP_methods,
"", "",
...@@ -873,110 +597,11 @@ initcPersistence() ...@@ -873,110 +597,11 @@ initcPersistence()
PyDict_SetItemString(d,"__version__", PyDict_SetItemString(d,"__version__",
PyString_FromStringAndSize(rev+11,strlen(rev+11)-2)); PyString_FromStringAndSize(rev+11,strlen(rev+11)-2));
PyExtensionClass_Export(d,"Persistent",Pertype); PyExtensionClass_Export(d,"Persistent",Pertype);
PyDict_SetItemString(d,"atimeType",(PyObject*)&PATimeType);
cPersistenceCAPI=&truecPersistenceCAPI; cPersistenceCAPI=&truecPersistenceCAPI;
PyDict_SetItemString(d, "CAPI", PyDict_SetItemString(d, "CAPI",
PyCObject_FromVoidPtr(cPersistenceCAPI,NULL)); PyCObject_FromVoidPtr(cPersistenceCAPI,NULL));
#include "dcprotect.h"
if (PyErr_Occurred()) if (PyErr_Occurred())
Py_FatalError("can't initialize module cDocumentTemplate"); Py_FatalError("can't initialize module cDocumentTemplate");
} }
/****************************************************************************
$Log: cPersistence.c,v $
Revision 1.24 1998/07/27 13:03:21 jim
Added __of__ to list of attributes that don't activate object on access.
Revision 1.23 1998/01/09 22:19:28 jim
Fixed bug in deactivation logic. The stupid thing was calling a
constructor when deactivating.
Revision 1.22 1997/12/15 15:28:09 jim
Some cleanup. Removed unused old routine.
Renamed _p___reinit__ to _p_deactivate.
Updated _p_changed attribute protocol. This will allow us to get rid
of _p_state and maybe someday __changed__().
Revision 1.21 1997/12/11 16:03:30 jim
Set EXTENSIONCLASS_BASICNEW_FLAG to support __basicnew__ protocol.
Revision 1.20 1997/11/13 19:46:24 jim
Fixed minor error handling bug in reinit.
Revision 1.19 1997/09/18 19:53:46 jim
Added attribute, _p_state.
Revision 1.18 1997/07/18 14:14:02 jim
Fixed bug in handling delete of certain special attributes.
Revision 1.17 1997/07/16 20:18:32 jim
*** empty log message ***
Revision 1.16 1997/06/30 15:26:35 jim
Changed so getting an object's __class__ does not cause it's
activation.
Revision 1.15 1997/06/06 19:04:40 jim
Modified so that C API setstate makes object temporarily
undeactivatable.
Revision 1.14 1997/05/01 20:33:58 jim
I made (and restored) some optimizations. The effect is probably
minor, but who knows.
Revision 1.13 1997/04/27 09:18:01 jim
Added to the CAPI to support subtypes (like Record) that want to
extend attr functions.
Revision 1.12 1997/04/24 12:48:48 jim
Fixed bug in reinit
Revision 1.11 1997/04/22 02:46:50 jim
Took out debugging info.
Revision 1.10 1997/04/22 02:40:03 jim
Changed object header layout and added sticky feature.
Revision 1.9 1997/04/03 17:34:14 jim
Changed to pass transaction to jar store method during commit.
Revision 1.8 1997/03/28 20:24:52 jim
Added login to really minimice cache size and to
make cache attributes changeable.
Revision 1.7 1997/03/25 20:43:21 jim
Changed to make all persistent objects transactional.
Revision 1.6 1997/03/20 20:58:25 jim
Fixed bug in reinit.
Revision 1.5 1997/03/14 22:59:34 jim
Changed the way Per_setstate was exported to get rid of compilation
error.
Revision 1.4 1997/03/14 22:51:40 jim
Added exported C interface, so that other C classes could subclass
from it.
Added _p_mtime attribute, which returns the persistent modification
time.
Revision 1.3 1997/03/11 20:53:07 jim
Added access-time tracking and special type for efficient access time
management.
Revision 1.2 1997/02/21 20:49:09 jim
Added logic to treat attributes starting with _v_ as volatile.
Changes in these attributes to not make the object thing it's been
saved and these attributes are not saved by the default __getstate__
method.
Revision 1.1 1997/02/14 20:24:55 jim
*** empty log message ***
****************************************************************************/
/* /*
$Id: cPersistence.h,v 1.9 1997/12/15 15:55:16 jim Exp $ $Id: cPersistence.h,v 1.10 1998/11/11 02:00:56 jim Exp $
Definitions to facilitate making cPersistent subclasses in C. Definitions to facilitate making cPersistent subclasses in C.
Copyright
Copyright 1996 Digital Creations, L.C., 910 Princess Anne
Street, Suite 300, Fredericksburg, Virginia 22401 U.S.A. All
rights reserved. Copyright in this software is owned by DCLC,
unless otherwise indicated. Permission to use, copy and
distribute this software is hereby granted, provided that the
above copyright notice appear in all copies and that both that
copyright notice and this permission notice appear. Note that
any product, process or technology described in this software
may be the subject of other Intellectual Property rights
reserved by Digital Creations, L.C. and are not licensed
hereunder.
Trademarks
Digital Creations & DCLC, are trademarks of Digital Creations, L.C..
All other trademarks are owned by their respective companies.
No Warranty
The software is provided "as is" without warranty of any kind,
either express or implied, including, but not limited to, the
implied warranties of merchantability, fitness for a particular
purpose, or non-infringement. This software could include
technical inaccuracies or typographical errors. Changes are
periodically made to the software; these changes will be
incorporated in new editions of the software. DCLC may make
improvements and/or changes in this software at any time
without notice.
Limitation Of Liability
In no event will DCLC be liable for direct, indirect, special,
incidental, economic, cover, or consequential damages arising
out of the use of or inability to use this software even if
advised of the possibility of such damages. Some states do not
allow the exclusion or limitation of implied warranties or
limitation of liability for incidental or consequential
damages, so the above limitation or exclusion may not apply to
you.
If you have questions regarding this software,
contact:
Digital Creations L.C.
info@digicool.com
(540) 371-6909
$Log: cPersistence.h,v $
Revision 1.9 1997/12/15 15:55:16 jim
Changed persistent object header layout. This will require recompile
of all C Persistent objects.
Revision 1.8 1997/12/10 22:19:24 jim
Added PER_USE macro.
Revision 1.7 1997/07/18 14:15:39 jim
Added PER_DEL so that subclasses can handle deallocation correctly.
Revision 1.6 1997/06/06 19:13:32 jim
Changed/fixed convenience macros.
Revision 1.5 1997/05/19 17:51:20 jim
Added macros to simplify C PO implementation.
Revision 1.4 1997/05/19 13:49:36 jim
Added include of time.h.
Revision 1.3 1997/04/27 09:18:23 jim
Added to the CAPI to support subtypes (like Record) that want to
extend attr functions.
Revision 1.2 1997/04/22 02:40:28 jim
Changed object header layout.
Revision 1.1 1997/04/01 17:15:48 jim
*** empty log message ***
*/ */
...@@ -96,24 +15,19 @@ ...@@ -96,24 +15,19 @@
#define cPersistent_HEAD PyObject_HEAD \ #define cPersistent_HEAD PyObject_HEAD \
PyObject *jar; \ PyObject *jar; \
int oid; \ char oid[8]; \
time_t atime; \ unsigned short atime; \
signed char state; \ signed char state; \
#define cPersistent_GHOST_STATE -1 #define cPersistent_GHOST_STATE -1
#define cPersistent_UPTODATE_STATE 0 #define cPersistent_UPTODATE_STATE 0
#define cPersistent_CHANGED_STATE 1 #define cPersistent_CHANGED_STATE 1
#define cPersistent_STICKY_STATE 2
typedef struct { typedef struct {
cPersistent_HEAD cPersistent_HEAD
} cPersistentObject; } cPersistentObject;
typedef struct {
PyObject_HEAD
cPersistentObject *object;
} PATimeobject;
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);
...@@ -121,7 +35,7 @@ typedef struct { ...@@ -121,7 +35,7 @@ typedef struct {
PyMethodChain *methods; PyMethodChain *methods;
getattrofunc getattro; getattrofunc getattro;
setattrofunc setattro; setattrofunc setattro;
int (*changed)(PyObject*); int (*changed)(cPersistentObject*);
int (*setstate)(PyObject*); int (*setstate)(PyObject*);
pergetattr pergetattro; pergetattr pergetattro;
persetattr persetattro; persetattr persetattro;
...@@ -129,16 +43,20 @@ typedef struct { ...@@ -129,16 +43,20 @@ typedef struct {
static cPersistenceCAPIstruct *cPersistenceCAPI; static cPersistenceCAPIstruct *cPersistenceCAPI;
#define PER_USE_OR_RETURN(O,R) { \
if ((O)->state==cPersistent_GHOST_STATE && \
cPersistenceCAPI->setstate((PyObject*)(O)) < 0) \
return (R); \
else if ((O)->state==cPersistent_UPTODATE_STATE) \
(O)->state=cPersistent_STICKY_STATE; \
}
#define PER_USE_OR_RETURN(O,R) \ #define PER_CHANGED(O) (cPersistenceCAPI->changed((cPersistentObject*)(O)))
if(cPersistenceCAPI->setstate((PyObject*)(O)) < 0) return (R)
#define PER_USE(O) (cPersistenceCAPI->setstate((PyObject*)(O)))
#define PER_CHANGED(O) (cPersistenceCAPI->changed((PyObject*)(O))) #define PER_ALLOW_DEACTIVATION(O) \
((O)->state==cPersistent_STICKY_STATE && \
((O)->state=cPersistent_UPTODATE_STATE))
#define PER_PREVENT_DEACTIVATION(O) ((O)->atime=(time_t)1);
#define PER_ALLOW_DEACTIVATION(O) ((O)->atime=time(NULL));
#define PER_DEL(O) Py_XDECREF((O)->jar) #define PER_DEL(O) Py_XDECREF((O)->jar)
#endif #endif
......
/* static char *what_string = "$Id: cPickleCache.c,v 1.16 1998/11/11 02:00:56 jim Exp $";
$Id: cPickleCache.c,v 1.15 1998/07/27 13:09:03 jim Exp $
C implementation of a pickle jar cache.
Copyright
Copyright 1996 Digital Creations, L.C., 910 Princess Anne
Street, Suite 300, Fredericksburg, Virginia 22401 U.S.A. All
rights reserved.
***************************************************************************/
static char *what_string = "$Id: cPickleCache.c,v 1.15 1998/07/27 13:09:03 jim Exp $";
#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))
#define UNLESS_ASSIGN(V,E) ASSIGN(V,E) UNLESS(V) #define UNLESS_ASSIGN(V,E) ASSIGN(V,E) UNLESS(V)
#define Py_ASSIGN(P,E) if(!PyObject_AssignExpression(&(P),(E))) return NULL
#define OBJECT(O) ((PyObject*)O) #define OBJECT(O) ((PyObject*)O)
#include "cPersistence.h" #include "cPersistence.h"
...@@ -25,7 +10,7 @@ static char *what_string = "$Id: cPickleCache.c,v 1.15 1998/07/27 13:09:03 jim E ...@@ -25,7 +10,7 @@ static char *what_string = "$Id: cPickleCache.c,v 1.15 1998/07/27 13:09:03 jim E
#undef Py_FindMethod #undef Py_FindMethod
static PyObject *py_reload, *py__p_jar, *py__p_atime, *py__p_deactivate; static PyObject *py_reload, *py__p_jar, *py__p_deactivate;
/* Declarations for objects of type cCache */ /* Declarations for objects of type cCache */
...@@ -65,60 +50,45 @@ typedef struct { ...@@ -65,60 +50,45 @@ typedef struct {
staticforward PyTypeObject Cctype; staticforward PyTypeObject Cctype;
static PyObject *PATimeType=NULL;
/* ---------------------------------------------------------------- */ /* ---------------------------------------------------------------- */
static int static int
gc_item(ccobject *self, PyObject *key, PyObject *v, time_t now, time_t dt) gc_item(ccobject *self, PyObject *key, PyObject *v, long now, int dt)
{ {
time_t t;
if(v && key) if (v && key)
{ {
self->n++; self->n++;
if(v->ob_type==(PyTypeObject*)PATimeType) if(v->ob_refcnt <= 1)
{ {
if(((PATimeobject*)v)->object->ob_refcnt <= 1) self->sum_deal++;
{ return PyDict_DelItem(self->data, key);
self->sum_deal++; }
UNLESS(-1 != PyDict_DelItem(self->data, key)) return -1;
} if (dt && v->ob_type->tp_basicsize >= sizeof(cPersistentObject) &&
else ((cPersistentObject*)v)->state==cPersistent_UPTODATE_STATE)
{
now -= ((cPersistentObject*)v)->atime;
if (now < 0) now += 65536;
self->na++;
self->sum_age += now;
if (now > dt)
{ {
t=((PATimeobject*)v)->object->atime; /* We have a cPersistent object that hasn't been used in
if(t != (time_t)1) a while. Reinitialize it, hopefully freeing it's
state.
*/
self->sum_deac++;
if(key=PyObject_GetAttr(v,py__p_deactivate))
{ {
self->na++; ASSIGN(key,PyObject_CallObject(key,NULL));
t=now-t; UNLESS(key) return -1;
self->sum_age += t; Py_DECREF(key);
if((! dt || t > dt)) return 0;
{
/* We have a cPersistent object that hasn't been used in
a while. Reinitialize it, hopefully freeing it's
state.
*/
v=(PyObject*)(((PATimeobject*)v)->object);
if(((cPersistentObject*)v)->state !=
cPersistent_UPTODATE_STATE) return 0;
self->sum_deac++;
if(key=PyObject_GetAttr(v,py__p_deactivate))
{
ASSIGN(key,PyObject_CallObject(key,NULL));
UNLESS(key) return -1;
Py_DECREF(key);
}
PyErr_Clear();
}
} }
PyErr_Clear();
} }
} }
else if(v->ob_refcnt <= 1)
{
self->sum_deal++;
UNLESS(-1 != PyDict_DelItem(self->data, key)) return -1;
}
} }
return 0; return 0;
} }
...@@ -135,7 +105,7 @@ update_stats(ccobject *self, time_t now) ...@@ -135,7 +105,7 @@ update_stats(ccobject *self, time_t now)
self->dfa *= WEIGHTING_PERIOD/(WEIGHTING_PERIOD+d); self->dfa *= WEIGHTING_PERIOD/(WEIGHTING_PERIOD+d);
self->mean_age=((self->mean_age*self->dfa+self->sum_age)/ self->mean_age=((self->mean_age*self->dfa+self->sum_age)/
(self->dfa+self->na)); (self->dfa+self->na))*3;
self->sum_age=0; self->sum_age=0;
deac=self->sum_deac/d; deac=self->sum_deac/d;
...@@ -163,16 +133,22 @@ static int ...@@ -163,16 +133,22 @@ static int
fullgc(ccobject *self, int idt) fullgc(ccobject *self, int idt)
{ {
PyObject *key, *v; PyObject *key, *v;
int i; int i, dt;
time_t now, dt; long now;
if(self->cache_size < 1) return 0; if (self->cache_size < 1) return 0;
i=PyDict_Size(self->data)-3/self->cache_size; if ((i=PyDict_Size(self->data)) < 1) return;
if(i < 3) i=3;
dt=self->cache_age*3/i; now=((long)(time(NULL)/3))%65536;
if(dt < 10) dt=10;
now=time(NULL); if (idt) dt=idt*3;
if(idt) dt=idt; else
{
i=PyDict_Size(self->data)-3/self->cache_size;
if(i < 3) i=3;
dt=self->cache_age*3/i;
if(dt < 10) dt=10;
}
for(i=0; PyDict_Next(self->data, &i, &key, &v); ) for(i=0; PyDict_Next(self->data, &i, &key, &v); )
if(gc_item(self,key,v,now,dt) < 0) return -1; if(gc_item(self,key,v,now,dt) < 0) return -1;
...@@ -183,40 +159,6 @@ fullgc(ccobject *self, int idt) ...@@ -183,40 +159,6 @@ fullgc(ccobject *self, int idt)
return 0; return 0;
} }
static PyObject *
ccitems(ccobject *self, PyObject *args)
{
PyObject *r, *key, *v, *item=0;
int i;
UNLESS(PyArg_ParseTuple(args,"")) return NULL;
UNLESS(r=PyList_New(0)) return NULL;
for(i=0; PyDict_Next(self->data, &i, &key, &v); )
{
if(key && v)
{
if(v->ob_type==(PyTypeObject*)PATimeType)
{
ASSIGN(item, Py_BuildValue("OO",key,((PATimeobject*)v)->object));
}
else
{
ASSIGN(item, Py_BuildValue("OO",key,v));
}
UNLESS(item) goto err;
if(PyList_Append(r,item) < 0) goto err;
}
}
Py_XDECREF(item);
return r;
err:
Py_XDECREF(item);
Py_DECREF(r);
return NULL;
}
static int static int
reallyfullgc(ccobject *self, int dt) reallyfullgc(ccobject *self, int dt)
{ {
...@@ -249,31 +191,33 @@ reallyfullgc(ccobject *self, int dt) ...@@ -249,31 +191,33 @@ reallyfullgc(ccobject *self, int dt)
static int static int
maybegc(ccobject *self, PyObject *thisv) maybegc(ccobject *self, PyObject *thisv)
{ {
int n, s, size; int n, s, size, dt;
time_t now,dt; long now;
PyObject *key=0, *v=0; PyObject *key=0, *v=0;
/*printf("m");*/ if (self->cache_size < 1) return 0;
s=PyDict_Size(self->data);
if (s < 1) return s;
now=((long)(time(NULL)/3))%65536;
if(self->cache_size < 1) return 0;
s=PyDict_Size(self->data)-3;
if(s < self->cache_size) return 0;
size=self->cache_size; size=self->cache_size;
self->cache_size=0; self->cache_size=0;
n=(s-size)/10; n=(s-size)/10;
/*n=s/size;*/
if(n < 3) n=3; if (n < 3) n=3;
dt=(long)(self->cache_age*(0.2+0.8*size/s)); s=8*size/s;
if(dt < 10) dt=10; if (s > 100) s=100;
dt=(long)(self->cache_age*(0.2+0.1*s));
if (dt < 10) dt=10;
now=time(NULL); now=time(NULL);
while(--n >= 0) while (--n >= 0)
{ {
if(PyDict_Next(self->data, &(self->position), &key, &v)) if (PyDict_Next(self->data, &(self->position), &key, &v))
{ {
if(v != thisv && gc_item(self,key,v,now,dt) < 0) if (v != thisv && gc_item(self,key,v,now,dt) < 0)
{ {
self->cache_size=size; self->cache_size=size;
return -1; return -1;
...@@ -284,7 +228,7 @@ maybegc(ccobject *self, PyObject *thisv) ...@@ -284,7 +228,7 @@ maybegc(ccobject *self, PyObject *thisv)
} }
self->cache_size=size; self->cache_size=size;
if(now-self->last_check > 1) update_stats(self, now); if (now-self->last_check > 1) update_stats(self, now);
return 0; return 0;
} }
...@@ -309,41 +253,6 @@ cc_reallyfull_sweep(ccobject *self, PyObject *args) ...@@ -309,41 +253,6 @@ cc_reallyfull_sweep(ccobject *self, PyObject *args)
return Py_None; return Py_None;
} }
static PyObject *
cc_report(ccobject *self, PyObject *args)
{
PyObject *key, *v, *t=0;
int i;
if(args) PyArg_ParseTuple(args,"|O", &t);
for(i=0; PyDict_Next(self->data, &i, &key, &v); )
{
if(v->ob_type==(PyTypeObject*)PATimeType
&& (
(t && OBJECT(((PATimeobject*)v)->object->ob_type) == t)
|| ! t))
printf("%d\t%p\t%s\t%ld\t%d\t%ld\n",
(((PATimeobject*)v)->object->oid),
((PATimeobject*)v)->object,
((PATimeobject*)v)->object->ob_type->tp_name,
(long)(((PATimeobject*)v)->object->ob_refcnt),
(((PATimeobject*)v)->object->state),
(long)(((PATimeobject*)v)->object->atime) );
else if((t && OBJECT(((PATimeobject*)v)->object->ob_type) == t)
|| ! t)
printf("%d\t%p\t%s\t%ld\t%d\n",
(((cPersistentObject*)v)->oid),
v,
v->ob_type->tp_name,
(long)(v->ob_refcnt),
(((cPersistentObject*)v)->state)
);
}
if(args) Py_INCREF(Py_None);
return Py_None;
}
static PyObject * static PyObject *
cc_incrgc(ccobject *self, PyObject *args) cc_incrgc(ccobject *self, PyObject *args)
{ {
...@@ -353,31 +262,24 @@ cc_incrgc(ccobject *self, PyObject *args) ...@@ -353,31 +262,24 @@ cc_incrgc(ccobject *self, PyObject *args)
} }
static struct PyMethodDef cc_methods[] = { static struct PyMethodDef cc_methods[] = {
{"full_sweep", (PyCFunction)cc_full_sweep, 1, {"full_sweep", (PyCFunction)cc_full_sweep, METH_VARARGS,
"full_sweep([age]) -- Perform a full sweep of the cache\n\n" "full_sweep([age]) -- Perform a full sweep of the cache\n\n"
"Make a single pass through the cache, removing any objects that are no\n" "Make a single pass through the cache, removing any objects that are no\n"
"longer referenced, and deactivating objects that have not been\n" "longer referenced, and deactivating objects that have not been\n"
"accessed in the number of seconds given by 'age'. " "accessed in the number of seconds given by 'age'. "
"'age defaults to the cache age.\n" "'age defaults to the cache age.\n"
}, },
{"report", (PyCFunction)cc_report, 1, ""}, {"minimize", (PyCFunction)cc_reallyfull_sweep, METH_VARARGS,
{"minimize", (PyCFunction)cc_reallyfull_sweep, 1,
"minimize([age]) -- Remove as many objects as possible\n\n" "minimize([age]) -- Remove as many objects as possible\n\n"
"Make multiple passes through the cache, removing any objects that are no\n" "Make multiple passes through the cache, removing any objects that are no\n"
"longer referenced, and deactivating objects that have not been\n" "longer referenced, and deactivating objects that have not been\n"
"accessed in the number of seconds given by 'age'. 'age defaults to 0.\n" "accessed in the number of seconds given by 'age'. 'age defaults to 0.\n"
}, },
{"items", (PyCFunction)ccitems, 1, {"incrgc", (PyCFunction)cc_incrgc, METH_VARARGS,
"items() -- Return the cache items."
},
{"incrgc", (PyCFunction)cc_incrgc, 1,
"incrgc() -- Perform incremental garbage collection"}, "incrgc() -- Perform incremental garbage collection"},
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };
/* ---------- */
static ccobject * static ccobject *
newccobject(int cache_size, int cache_age) newccobject(int cache_size, int cache_age)
{ {
...@@ -442,7 +344,11 @@ cc_getattr(ccobject *self, char *name) ...@@ -442,7 +344,11 @@ cc_getattr(ccobject *self, char *name)
return self->data; return self->data;
} }
} }
if(*name=='h' && strcmp(name, "has_key")==0) if(
*name=='h' && strcmp(name, "has_key")==0 ||
*name=='i' && strcmp(name, "items")==0 ||
*name=='k' && strcmp(name, "keys")==0
)
return PyObject_GetAttrString(self->data, name); return PyObject_GetAttrString(self->data, name);
if(r=Py_FindMethod(cc_methods, (PyObject *)self, name)) if(r=Py_FindMethod(cc_methods, (PyObject *)self, name))
...@@ -476,22 +382,6 @@ cc_setattr(ccobject *self, char *name, PyObject *value) ...@@ -476,22 +382,6 @@ cc_setattr(ccobject *self, char *name, PyObject *value)
return -1; return -1;
} }
static PyObject *
cc_repr(ccobject *self)
{
return PyObject_Repr(self->data);
}
static PyObject *
cc_str(self)
ccobject *self;
{
return PyObject_Str(self->data);
}
/* Code to access cCache objects as mappings */
static int static int
cc_length(ccobject *self) cc_length(ccobject *self)
{ {
...@@ -503,58 +393,22 @@ cc_subscript(ccobject *self, PyObject *key) ...@@ -503,58 +393,22 @@ cc_subscript(ccobject *self, PyObject *key)
{ {
PyObject *r; PyObject *r;
UNLESS(r=PyObject_GetItem(self->data, key)) UNLESS (r=PyDict_GetItem(self->data, key))
{ {
PyErr_SetObject(PyExc_KeyError, key); PyErr_SetObject(PyExc_KeyError, key);
return NULL; return NULL;
} }
UNLESS(-1 != maybegc(self,r)) if (maybegc(self,r) < 0) return NULL;
{
Py_DECREF(r); Py_INCREF(r);
return NULL;
}
if(r->ob_type==(PyTypeObject *)PATimeType)
{
Py_DECREF(r);
r=(PyObject*)(((PATimeobject*)r)->object);
Py_INCREF(r);
}
return r; return r;
} }
static int static int
cc_ass_sub(ccobject *self, PyObject *key, PyObject *v) cc_ass_sub(ccobject *self, PyObject *key, PyObject *v)
{ {
if(v) return PyDict_SetItem(self->data, key, v);
if(v) return PyDict_DelItem(self->data, key);
{
int r;
PyObject *t=0;
/* Now get and save the access time */
if(t=PyObject_GetAttr(v,py__p_atime))
{
if(t->ob_type != (PyTypeObject *)PATimeType)
{
Py_DECREF(t);
t=0;
}
else
v=t;
}
else
PyErr_Clear();
r=PyDict_SetItem(self->data,key,v);
Py_XDECREF(t);
if(r < 0) return -1;
return maybegc(self, v);
}
else
{
UNLESS(-1 != PyDict_DelItem(self->data,key)) return -1;
return maybegc(self, NULL);
}
} }
static PyMappingMethods cc_as_mapping = { static PyMappingMethods cc_as_mapping = {
...@@ -563,12 +417,6 @@ static PyMappingMethods cc_as_mapping = { ...@@ -563,12 +417,6 @@ static PyMappingMethods cc_as_mapping = {
(objobjargproc)cc_ass_sub, /*mp_ass_subscript*/ (objobjargproc)cc_ass_sub, /*mp_ass_subscript*/
}; };
/* -------------------------------------------------------- */
static char Cctype__doc__[] =
""
;
static PyTypeObject Cctype = { static PyTypeObject Cctype = {
PyObject_HEAD_INIT(NULL) PyObject_HEAD_INIT(NULL)
0, /*ob_size*/ 0, /*ob_size*/
...@@ -581,22 +429,19 @@ static PyTypeObject Cctype = { ...@@ -581,22 +429,19 @@ static PyTypeObject Cctype = {
(getattrfunc)cc_getattr, /*tp_getattr*/ (getattrfunc)cc_getattr, /*tp_getattr*/
(setattrfunc)cc_setattr, /*tp_setattr*/ (setattrfunc)cc_setattr, /*tp_setattr*/
(cmpfunc)0, /*tp_compare*/ (cmpfunc)0, /*tp_compare*/
(reprfunc)cc_repr, /*tp_repr*/ (reprfunc)0, /*tp_repr*/
0, /*tp_as_number*/ 0, /*tp_as_number*/
0, /*tp_as_sequence*/ 0, /*tp_as_sequence*/
&cc_as_mapping, /*tp_as_mapping*/ &cc_as_mapping, /*tp_as_mapping*/
(hashfunc)0, /*tp_hash*/ (hashfunc)0, /*tp_hash*/
(ternaryfunc)0, /*tp_call*/ (ternaryfunc)0, /*tp_call*/
(reprfunc)cc_str, /*tp_str*/ (reprfunc)0, /*tp_str*/
/* Space for future expansion */ /* Space for future expansion */
0L,0L,0L,0L, 0L,0L,0L,0L,
Cctype__doc__ /* Documentation string */ ""
}; };
/* End of code for cCache objects */
/* -------------------------------------------------------- */
static PyObject * static PyObject *
cCM_new(PyObject *self, PyObject *args) cCM_new(PyObject *self, PyObject *args)
{ {
...@@ -605,119 +450,30 @@ cCM_new(PyObject *self, PyObject *args) ...@@ -605,119 +450,30 @@ cCM_new(PyObject *self, PyObject *args)
return (PyObject*)newccobject(cache_size,cache_age); return (PyObject*)newccobject(cache_size,cache_age);
} }
/* List of methods defined in the module */
static struct PyMethodDef cCM_methods[] = { static struct PyMethodDef cCM_methods[] = {
{"PickleCache",(PyCFunction)cCM_new, 1, {"PickleCache",(PyCFunction)cCM_new, METH_VARARGS, ""},
"PickleCache([size,age]) -- Create a pickle jar cache\n\n"
"The cache will attempt to garbage collect items when the cache size is\n"
"greater than the given size, which defaults to 100. Normally, objects\n"
"are garbage collected if their reference count is one, meaning that\n"
"they are only referenced by the cache. In some cases, objects that\n"
"have not been accessed in 'age' seconds may be partially garbage\n"
"collected, meaning that most of their state is freed.\n"
},
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };
/* Initialization function for the module (*must* be called initcCache) */
static char cCache_module_documentation[] =
""
;
void void
initcPickleCache() initcPickleCache()
{ {
PyObject *m, *d; PyObject *m, *d;
char *rev="$Revision: 1.15 $"; char *rev="$Revision: 1.16 $";
Cctype.ob_type=&PyType_Type; Cctype.ob_type=&PyType_Type;
if(PATimeType=PyImport_ImportModule("cPersistence")) m = Py_InitModule4("cPickleCache", cCM_methods, "",
ASSIGN(PATimeType,PyObject_GetAttrString(PATimeType,"atimeType"));
UNLESS(PATimeType) PyErr_Clear();
m = Py_InitModule4("cPickleCache", cCM_methods,
cCache_module_documentation,
(PyObject*)NULL,PYTHON_API_VERSION); (PyObject*)NULL,PYTHON_API_VERSION);
d = PyModule_GetDict(m); d = PyModule_GetDict(m);
py_reload=PyString_FromString("reload"); py_reload=PyString_FromString("reload");
py__p_jar=PyString_FromString("_p_jar"); py__p_jar=PyString_FromString("_p_jar");
py__p_atime=PyString_FromString("_p_atime");
py__p_deactivate=PyString_FromString("_p_deactivate"); py__p_deactivate=PyString_FromString("_p_deactivate");
PyDict_SetItemString(d,"__version__", PyDict_SetItemString(d,"__version__",
PyString_FromStringAndSize(rev+11,strlen(rev+11)-2)); PyString_FromStringAndSize(rev+11,strlen(rev+11)-2));
#include "dcprotect.h"
if (PyErr_Occurred()) Py_FatalError("can't initialize module cCache"); if (PyErr_Occurred()) Py_FatalError("can't initialize module cCache");
} }
/******************************************************************************
$Log: cPickleCache.c,v $
Revision 1.15 1998/07/27 13:09:03 jim
Changed _p___reinit__ to _p_deactivate.
Revision 1.14 1998/02/05 14:43:10 jim
Fixed bug in ibcremental gc method.
Revision 1.13 1998/02/05 14:34:40 jim
Added getattr option to get cache data.
Added method to perform incremental gc.
Changed incremental collection effort algorithm to be based on
difference between actual and target size, rather than ration.
Revision 1.12 1997/12/15 15:25:09 jim
Cleaned up to avoid VC++ warnings.
Revision 1.11 1997/12/10 22:20:43 jim
Added has_key method.
Revision 1.10 1997/07/18 14:30:18 jim
Added reporting method for use during debugging.
Revision 1.9 1997/07/16 20:18:40 jim
*** empty log message ***
Revision 1.8 1997/06/30 15:27:51 jim
Added machinery to track cache statistics.
Fixed bug in garbage collector, which had a nasty habit
of activating inactive objects so that it could deactivate them.
Revision 1.7 1997/05/30 14:29:47 jim
Added new algorithm for adjusting cache age based on cache size. Not,
if the cache size gets really big, the cache age can drop to as low as
20% of the configured cache age. Also made the "minimize" method more
agressive.
Revision 1.6 1997/04/22 02:45:24 jim
Changed object header layout and added sticky feature.
Revision 1.5 1997/04/15 19:03:29 jim
Fixed leak introduced in last revision. :-(
Revision 1.4 1997/04/11 19:13:21 jim
Added code to be more conservative about GCing.
Fixed setattr bugs.
Revision 1.3 1997/03/28 20:18:34 jim
Simplified reinit logic.
Revision 1.2 1997/03/11 20:48:38 jim
Added object-deactivation support. This only works with cPersistent
objects.
Revision 1.1 1997/02/17 18:39:02 jim
*** empty log message ***
******************************************************************************/
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
static char intSet_module_documentation[] = static char intSet_module_documentation[] =
"" ""
"\n$Id: intSet.c,v 1.9 1998/03/24 15:17:34 jim Exp $" "\n$Id: intSet.c,v 1.10 1998/11/11 02:00:56 jim Exp $"
; ;
#include <limits.h> #include <limits.h>
...@@ -225,8 +225,6 @@ intSet___setstate__(intSet *self, PyObject *args) ...@@ -225,8 +225,6 @@ intSet___setstate__(intSet *self, PyObject *args)
char *c; char *c;
INTSET_DATA_TYPE k; INTSET_DATA_TYPE k;
PER_PREVENT_DEACTIVATION(self);
UNLESS(PyArg_ParseTuple(args,"O",&data)) return PER_RETURN(self, NULL); UNLESS(PyArg_ParseTuple(args,"O",&data)) return PER_RETURN(self, NULL);
UNLESS(c=PyString_AsString(data)) return PER_RETURN(self, NULL); UNLESS(c=PyString_AsString(data)) return PER_RETURN(self, NULL);
...@@ -271,7 +269,7 @@ intSet_set_operation(intSet *self, PyObject *other, ...@@ -271,7 +269,7 @@ intSet_set_operation(intSet *self, PyObject *other,
o=INTSET(other); o=INTSET(other);
PER_USE_OR_RETURN(self, NULL); PER_USE_OR_RETURN(self, NULL);
PER_USE_OR_RETURN(other, NULL); PER_USE_OR_RETURN((intSet*)other, NULL);
od=o->data; od=o->data;
...@@ -537,7 +535,7 @@ void ...@@ -537,7 +535,7 @@ void
initintSet() initintSet()
{ {
PyObject *m, *d; PyObject *m, *d;
char *rev="$Revision: 1.9 $"; char *rev="$Revision: 1.10 $";
UNLESS(ExtensionClassImported) return; UNLESS(ExtensionClassImported) return;
...@@ -575,6 +573,9 @@ initintSet() ...@@ -575,6 +573,9 @@ initintSet()
Revision Log: Revision Log:
$Log: intSet.c,v $ $Log: intSet.c,v $
Revision 1.10 1998/11/11 02:00:56 jim
alpha1
Revision 1.9 1998/03/24 15:17:34 jim Revision 1.9 1998/03/24 15:17:34 jim
*** empty log message *** *** empty log message ***
......
/*********************************************************************** /***********************************************************************
$Id: cPersistence.c,v 1.24 1998/07/27 13:03:21 jim Exp $ $Id: cPersistence.c,v 1.25 1998/11/11 02:00:56 jim Exp $
C Persistence Module C Persistence Module
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
*****************************************************************************/ *****************************************************************************/
static char *what_string = "$Id: cPersistence.c,v 1.24 1998/07/27 13:03:21 jim Exp $"; static char *what_string = "$Id: cPersistence.c,v 1.25 1998/11/11 02:00:56 jim Exp $";
#include <time.h> #include <time.h>
#include "cPersistence.h" #include "cPersistence.h"
...@@ -21,8 +21,7 @@ static char *what_string = "$Id: cPersistence.c,v 1.24 1998/07/27 13:03:21 jim E ...@@ -21,8 +21,7 @@ static char *what_string = "$Id: cPersistence.c,v 1.24 1998/07/27 13:03:21 jim E
#define UNLESS(E) if(!(E)) #define UNLESS(E) if(!(E))
#define UNLESS_ASSIGN(V,E) ASSIGN(V,E) UNLESS(V) #define UNLESS_ASSIGN(V,E) ASSIGN(V,E) UNLESS(V)
static PyObject *py_store, *py_oops, *py_keys, *py_setstate, *py___changed__, static PyObject *py_keys, *py_setstate, *py___dict__;
*py___dict__, *py_mtime, *py_onearg, *py___getinitargs__, *py___init__;
#ifdef DEBUG_LOG #ifdef DEBUG_LOG
static PyObject *debug_log=0; static PyObject *debug_log=0;
...@@ -36,8 +35,9 @@ call_debug(char *event, cPersistentObject *self) ...@@ -36,8 +35,9 @@ call_debug(char *event, cPersistentObject *self)
/* /*
printf("%s %p\n",event,self->ob_type->tp_name); printf("%s %p\n",event,self->ob_type->tp_name);
*/ */
r=PyObject_CallFunction(debug_log,"s(sii)",event, r=PyObject_CallFunction(debug_log,"s(ss#i)",event,
self->ob_type->tp_name, self->oid, self->state); self->ob_type->tp_name, self->oid, 8,
self->state);
Py_XDECREF(r); Py_XDECREF(r);
} }
#endif #endif
...@@ -46,16 +46,9 @@ static void ...@@ -46,16 +46,9 @@ static void
init_strings() init_strings()
{ {
#define INIT_STRING(S) py_ ## S = PyString_FromString(#S) #define INIT_STRING(S) py_ ## S = PyString_FromString(#S)
INIT_STRING(store);
INIT_STRING(oops);
INIT_STRING(keys); INIT_STRING(keys);
INIT_STRING(setstate); INIT_STRING(setstate);
INIT_STRING(mtime);
INIT_STRING(__changed__);
INIT_STRING(__init__);
INIT_STRING(__getinitargs__);
INIT_STRING(__dict__); INIT_STRING(__dict__);
py_onearg=Py_BuildValue("(i)",1);
#undef INIT_STRING #undef INIT_STRING
} }
...@@ -113,6 +106,22 @@ callmethod3(PyObject *self, PyObject *name, ...@@ -113,6 +106,22 @@ callmethod3(PyObject *self, PyObject *name,
return self; return self;
} }
#define UPDATE_STATE_IF_NECESSARY(self, ER) \
if(self->state < 0 && self->jar) \
{ \
PyObject *r; \
\
self->state=cPersistent_STICKY_STATE; \
UNLESS(r=callmethod1(self->jar,py_setstate,(PyObject*)self)) \
{ \
self->state=cPersistent_GHOST_STATE; \
return ER; \
} \
self->state=cPersistent_UPTODATE_STATE; \
Py_DECREF(r); \
}
static PyObject * static PyObject *
#ifdef HAVE_STDARG_PROTOTYPES #ifdef HAVE_STDARG_PROTOTYPES
/* VARARGS 2 */ /* VARARGS 2 */
...@@ -148,254 +157,89 @@ PyString_BuildFormat(va_alist) va_dcl ...@@ -148,254 +157,89 @@ PyString_BuildFormat(va_alist) va_dcl
/****************************************************************************/ /****************************************************************************/
static void
PATime_dealloc(PATimeobject *self)
{
/*printf("D");*/
Py_DECREF(self->object);
PyMem_DEL(self);}
static PyObject *
PATime_repr(PATimeobject *self)
{
return PyString_BuildFormat("<access time: %d>","i",self->object->atime);
}
static PyTypeObject
PATimeType = {
PyObject_HEAD_INIT(NULL) 0,
"PersistentATime", /*tp_name*/
sizeof(PATimeobject), /*tp_basicsize*/
0, /*tp_itemsize*/
/* methods */
(destructor)PATime_dealloc, /*tp_dealloc*/
0L,0L,0L,0L,
(reprfunc)PATime_repr, /*tp_repr*/
0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,
"Values for holding access times for persistent objects"
};
/****************************************************************************/
/* Declarations for objects of type Persistent */
#define GHOST_STATE -1
#define UPTODATE_STATE 0
#define CHANGED_STATE 1
staticforward PyExtensionClass Pertype; staticforward PyExtensionClass Pertype;
staticforward PyExtensionClass TPertype; staticforward PyExtensionClass TPertype;
static char Per___changed____doc__[] = static int
"__changed__([flag]) -- Flag or determine whether an object has changed\n" changed(cPersistentObject *self)
" \n"
"If a value is specified, then it should indicate whether an\n"
"object has or has not been changed. If no value is specified,\n"
"then the return value will indicate whether the object has\n"
"changed.\n"
;
static PyObject *changed_args=(PyObject*)Per___changed____doc__;
static PyObject *
T___changed__(cPersistentObject *self, PyObject *args)
{ {
static PyObject *builtins=0, *get_transaction=0, *py_register=0; static PyObject *builtins=0, *get_transaction=0, *py_register=0;
PyObject *o, *T; PyObject *T;
if(PyArg_Parse(args, "O", &o)) if ((self->state == cPersistent_UPTODATE_STATE ||
self->state == cPersistent_STICKY_STATE)
&& self->jar)
{ {
int t; UNLESS (get_transaction)
t=PyObject_IsTrue(o);
if(t && self->state != cPersistent_CHANGED_STATE && self->jar)
{ {
UNLESS(get_transaction) UNLESS (py_register=PyString_FromString("register")) return -1;
{ UNLESS (T=PyImport_ImportModule("__main__")) return -1;
UNLESS(builtins) ASSIGN(T,PyObject_GetAttrString(T,"__builtins__"));
{ UNLESS (T) return -1;
UNLESS(T=PyImport_ImportModule("__main__")) return NULL; builtins=T;
ASSIGN(T,PyObject_GetAttrString(T,"__builtins__")); UNLESS (get_transaction=PyObject_GetAttrString(builtins,
UNLESS(T) return NULL; "get_transaction"))
UNLESS(py_register=PyString_FromString("register")) goto err; PyErr_Clear();
builtins=T; }
} if (get_transaction)
UNLESS(get_transaction=PyObject_GetAttrString(builtins, {
"get_transaction")) UNLESS (T=PyObject_CallObject(get_transaction,NULL)) return -1;
PyErr_Clear(); ASSIGN(T,PyObject_GetAttr(T,py_register));
} UNLESS (T) return -1;
if(get_transaction) ASSIGN(T, PyObject_CallFunction(T,"O",self));
{ if (T) Py_DECREF(T);
UNLESS(T=PyObject_CallObject(get_transaction,NULL)) return NULL; else return -1;
UNLESS_ASSIGN(T,PyObject_GetAttr(T,py_register)) return NULL;
UNLESS(o=PyTuple_New(1)) goto err;
Py_INCREF(self);
PyTuple_SET_ITEM(o,0,(PyObject*)self);
ASSIGN(o,PyObject_CallObject(T,o));
Py_DECREF(T);
UNLESS(o) return NULL;
Py_DECREF(o);
}
} }
if(self->state != cPersistent_GHOST_STATE) self->state=t;
Py_INCREF(Py_None); self->state=cPersistent_CHANGED_STATE;
return Py_None;
} }
else
{
PyErr_Clear();
UNLESS(PyArg_Parse(args, "")) return NULL;
return PyInt_FromLong(self->state==cPersistent_CHANGED_STATE);
}
err:
Py_DECREF(T);
return NULL;
}
static char Per___save____doc__[] =
"__save__() -- Update the object in a persistent database."
;
static PyObject *
Per___save__(self, args)
cPersistentObject *self;
PyObject *args;
{
if(self->oid && self->jar && self->state == CHANGED_STATE)
return callmethod1(self->jar,py_store,(PyObject*)self);
Py_INCREF(Py_None);
return Py_None;
}
static char Per___inform_commit____doc__[] = return 0;
"__inform_commit__(transaction,start_time) -- Commit object changes"
;
static PyObject *
Per___inform_commit__(self, args)
cPersistentObject *self;
PyObject *args;
{
PyObject *T=0, *t=0;
UNLESS(PyArg_ParseTuple(args, "OO", &T, &t)) return NULL;
if(self->oid && self->jar && self->state == CHANGED_STATE)
return callmethod2(self->jar,py_store,(PyObject*)self,T);
Py_INCREF(Py_None);
return Py_None;
} }
static char Per___inform_abort____doc__[] =
"__inform_abort__(transaction,start_time) -- Abort object changes"
;
static PyObject * static PyObject *
Per___inform_abort__(self, args) Per___changed__(cPersistentObject *self, PyObject *args)
cPersistentObject *self;
PyObject *args;
{ {
PyObject *T, *t; PyObject *v=0;
if (args && ! PyArg_ParseTuple(args, "|O",&v)) return NULL;
UNLESS(PyArg_ParseTuple(args, "OO", &T, &t)) return NULL; if (v && ! PyObject_IsTrue(v))
if(self->oid && self->jar && self->state != GHOST_STATE)
{ {
args=callmethod3(self->jar,py_oops,(PyObject*)self,t,T); PyErr_SetString(PyExc_TypeError,
if(args) "Only true arguments are allowed.");
Py_DECREF(args); return NULL;
else
PyErr_Clear();
} }
if (changed(self) < 0) return NULL;
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
} }
static char Per__p___init____doc__[] =
"_p___init__(oid,jar) -- Initialize persistence management data"
;
static PyObject * static PyObject *
Per__p___init__(self, args) Per__p_deactivate(cPersistentObject *self, PyObject *args)
cPersistentObject *self;
PyObject *args;
{ {
int oid; PyObject *init=0, *copy, *dict;
PyObject *jar;
UNLESS(PyArg_Parse(args, "(iO)", &oid, &jar)) return NULL;
#ifdef DEBUG_LOG #ifdef DEBUG_LOG
if(idebug_log < 0) call_debug("init",self); if (idebug_log < 0) call_debug("reinit",self);
#endif #endif
Py_INCREF(jar);
self->oid=oid;
ASSIGN(self->jar, jar);
self->state=GHOST_STATE;
Py_INCREF(Py_None);
return Py_None;
}
static PyObject * if (args && ! PyArg_ParseTuple(args,"")) return NULL;
Per__p___reinit__(cPersistentObject *self, PyObject *args)
{
PyObject *init=0, *copy, *dict;
#ifdef DEBUG_LOG if (self->state==cPersistent_UPTODATE_STATE && self->jar &&
if(idebug_log < 0) call_debug("reinit",self); HasInstDict(self) && (dict=INSTANCE_DICT(self)))
#endif
if(PyArg_Parse(args,""))
{ {
if(self->state==cPersistent_UPTODATE_STATE) PyDict_Clear(dict);
{ self->state=cPersistent_GHOST_STATE;
if(HasInstDict(self) && (dict=INSTANCE_DICT(self)))
{
PyDict_Clear(dict);
self->state=cPersistent_GHOST_STATE;
}
}
} }
else
{
PyErr_Clear();
UNLESS(PyArg_Parse(args, "O", &copy)) return NULL;
if(HasInstDict(self) && self->state==cPersistent_UPTODATE_STATE)
{
UNLESS(args=PyObject_GetAttr(copy,py___dict__)) return NULL;
ASSIGN(INSTANCE_DICT(self),args);
self->state=GHOST_STATE;
}
}
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
err:
Py_XDECREF(init);
return NULL;
} }
static int static int
Per_setstate(self) Per_setstate(cPersistentObject *self)
cPersistentObject *self;
{ {
self->atime=(time_t)1; /* Mark this object as sticky */ UPDATE_STATE_IF_NECESSARY(self, -1);
if(self->state==GHOST_STATE && self->jar) self->state=cPersistent_STICKY_STATE;
{
PyObject *r;
self->state=UPTODATE_STATE;
UNLESS(r=callmethod1(self->jar,py_setstate,(PyObject*)self))
{
self->state=GHOST_STATE;
self->atime=time(NULL); /* Unmark as sticky */
return -1;
}
Py_DECREF(r);
}
return 0; return 0;
} }
...@@ -406,25 +250,13 @@ Per__getstate__(self,args) ...@@ -406,25 +250,13 @@ Per__getstate__(self,args)
{ {
PyObject *__dict__, *d=0; PyObject *__dict__, *d=0;
UNLESS(PyArg_Parse(args, "")) return NULL; UNLESS(PyArg_ParseTuple(args, "")) return NULL;
#ifdef DEBUG_LOG #ifdef DEBUG_LOG
if(idebug_log < 0) call_debug("get",self); if(idebug_log < 0) call_debug("get",self);
#endif #endif
/* Update state, if necessary */ UPDATE_STATE_IF_NECESSARY(self, NULL);
if(self->state==GHOST_STATE && self->jar)
{
PyObject *r;
self->state=UPTODATE_STATE;
UNLESS(r=callmethod1(self->jar,py_setstate,(PyObject*)self))
{
self->state=GHOST_STATE;
return NULL;
}
Py_DECREF(r);
}
if(HasInstDict(self) && (__dict__=INSTANCE_DICT(self))) if(HasInstDict(self) && (__dict__=INSTANCE_DICT(self)))
{ {
...@@ -465,16 +297,13 @@ Per__setstate__(self,args) ...@@ -465,16 +297,13 @@ Per__setstate__(self,args)
PyObject *__dict__, *v, *keys=0, *key=0, *e=0; PyObject *__dict__, *v, *keys=0, *key=0, *e=0;
int l, i; int l, i;
/*printf("%s(%d) ", self->ob_type->tp_name,self->oid);*/
if(HasInstDict(self)) if(HasInstDict(self))
{ {
UNLESS(PyArg_Parse(args, "O", &v)) return NULL; UNLESS(PyArg_ParseTuple(args, "O", &v)) return NULL;
#ifdef DEBUG_LOG #ifdef DEBUG_LOG
if(idebug_log < 0) call_debug("set",self); if(idebug_log < 0) call_debug("set",self);
#endif #endif
self->state=UPTODATE_STATE;
if(v!=Py_None) if(v!=Py_None)
{ {
__dict__=INSTANCE_DICT(self); __dict__=INSTANCE_DICT(self);
...@@ -483,10 +312,7 @@ Per__setstate__(self,args) ...@@ -483,10 +312,7 @@ Per__setstate__(self,args)
{ {
for(i=0; PyDict_Next(v,&i,&key,&e);) for(i=0; PyDict_Next(v,&i,&key,&e);)
if(PyObject_SetItem(__dict__,key,e) < 0) if(PyObject_SetItem(__dict__,key,e) < 0)
{ return NULL;
self->state=GHOST_STATE;
return NULL;
}
} }
else else
{ {
...@@ -509,7 +335,6 @@ Per__setstate__(self,args) ...@@ -509,7 +335,6 @@ Per__setstate__(self,args)
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
err: err:
self->state=GHOST_STATE;
Py_XDECREF(key); Py_XDECREF(key);
Py_XDECREF(e); Py_XDECREF(e);
Py_XDECREF(keys); Py_XDECREF(keys);
...@@ -518,22 +343,13 @@ err: ...@@ -518,22 +343,13 @@ err:
static struct PyMethodDef Per_methods[] = { static struct PyMethodDef Per_methods[] = {
{"__changed__", (PyCFunction)T___changed__, 0, {"__changed__", (PyCFunction)Per___changed__, METH_VARARGS,
Per___changed____doc__}, "DEPRECATED: use self._p_changed=1"},
{"__save__", (PyCFunction)Per___save__, 1, {"_p_deactivate", (PyCFunction)Per__p_deactivate, METH_VARARGS,
Per___save____doc__}, "_p_deactivate(oid) -- Deactivate the object"},
{"__inform_commit__", (PyCFunction)Per___inform_commit__, 1, {"__getstate__", (PyCFunction)Per__getstate__, METH_VARARGS,
Per___inform_commit____doc__},
{"__inform_abort__", (PyCFunction)Per___inform_abort__, 1,
Per___inform_abort____doc__},
{"_p___init__", (PyCFunction)Per__p___init__, 0,
Per__p___init____doc__},
{"_p_deactivate", (PyCFunction)Per__p___reinit__, 0,
"_p_deactivate(oid[,copy]) -- Deactivate the object"},
{"_p___reinit__", (PyCFunction)Per__p___reinit__, 0,""},
{"__getstate__", (PyCFunction)Per__getstate__, 0,
"__getstate__() -- Return the state of the object" }, "__getstate__() -- Return the state of the object" },
{"__setstate__", (PyCFunction)Per__setstate__, 0, {"__setstate__", (PyCFunction)Per__setstate__, METH_VARARGS,
"__setstate__(v) -- Restore the saved state of the object from v" }, "__setstate__(v) -- Restore the saved state of the object from v" },
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
...@@ -549,28 +365,9 @@ Per_dealloc(self) ...@@ -549,28 +365,9 @@ Per_dealloc(self)
if(idebug_log < 0) call_debug("del",self); if(idebug_log < 0) call_debug("del",self);
#endif #endif
Py_XDECREF(self->jar); Py_XDECREF(self->jar);
/*Py_XDECREF(self->atime);*/
PyMem_DEL(self); PyMem_DEL(self);
} }
static void
Per_set_atime(cPersistentObject *self)
{
if(self->atime == (time_t)1) return;
self->atime = time(NULL);
}
static PyObject *
Per_atime(cPersistentObject *self)
{
PATimeobject *r;
UNLESS(r=PyObject_NEW(PATimeobject,&PATimeType)) return NULL;
Py_INCREF(self);
r->object=self;
return (PyObject*)r;
}
static PyObject * static PyObject *
Per_getattr(cPersistentObject *self, PyObject *oname, char *name, Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
PyObject *(*getattrf)(PyObject *, PyObject*)) PyObject *(*getattrf)(PyObject *, PyObject*))
...@@ -584,17 +381,7 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name, ...@@ -584,17 +381,7 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
{ {
case 'o': case 'o':
if(*n++=='i' && *n++=='d' && ! *n) if(*n++=='i' && *n++=='d' && ! *n)
{ return PyString_FromStringAndSize(self->oid, 8);
if(self->oid)
{
return PyInt_FromLong(self->oid);
}
else
{
Py_INCREF(Py_None);
return Py_None;
}
}
break; break;
case 'j': case 'j':
if(*n++=='a' && *n++=='r' && ! *n) if(*n++=='a' && *n++=='r' && ! *n)
...@@ -614,34 +401,15 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name, ...@@ -614,34 +401,15 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
case 'c': case 'c':
if(strcmp(n,"hanged")==0) if(strcmp(n,"hanged")==0)
{ {
if(self->state == GHOST_STATE) if(self->state < 0)
{ {
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
} }
return PyInt_FromLong(self->state == CHANGED_STATE); return PyInt_FromLong(self->state ==
} cPersistent_CHANGED_STATE);
break;
case 'a':
if(strcmp(n,"time")==0)
{
if(self->state != UPTODATE_STATE) Per_set_atime(self);
return Per_atime(self);
} }
break; break;
case 'm':
if(strcmp(n,"time")==0)
{
if(self->jar)
return callmethod1(self->jar,py_mtime,(PyObject*)self);
Py_INCREF(Py_None);
return Py_None;
}
break;
case 's':
if(strcmp(n,"tate")==0)
return PyInt_FromLong(self->state);
break;
} }
return getattrf((PyObject *)self, oname); return getattrf((PyObject *)self, oname);
...@@ -650,21 +418,9 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name, ...@@ -650,21 +418,9 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
(strcmp(name,"dict__")==0 || strcmp(name,"class__")==0 (strcmp(name,"dict__")==0 || strcmp(name,"class__")==0
|| strcmp(name, "of__")==0))) || strcmp(name, "of__")==0)))
{ {
/* Update state, if necessary */ UPDATE_STATE_IF_NECESSARY(self, NULL);
if(self->state==GHOST_STATE && self->jar)
{
PyObject *r;
self->state=UPTODATE_STATE;
UNLESS(r=callmethod1(self->jar,py_setstate,(PyObject*)self))
{
self->state=GHOST_STATE;
return NULL;
}
Py_DECREF(r);
}
Per_set_atime(self); self->atime=((long)(time(NULL)/3))%65536;
} }
return getattrf((PyObject *)self, oname); return getattrf((PyObject *)self, oname);
...@@ -679,15 +435,12 @@ Per_getattro(cPersistentObject *self, PyObject *name) ...@@ -679,15 +435,12 @@ Per_getattro(cPersistentObject *self, PyObject *name)
return Per_getattr(self,name,s, PyExtensionClassCAPI->getattro); return Per_getattr(self,name,s, PyExtensionClassCAPI->getattro);
} }
static int static int
changed(PyObject *self) bad_delattr()
{ {
PyObject *c; PyErr_SetString(PyExc_AttributeError,
"delete undeletable attribute");
UNLESS(c=PyObject_GetAttr(self,py___changed__)) return -1; return -1;
UNLESS_ASSIGN(c,PyObject_CallObject(c,py_onearg)) return -1;
Py_DECREF(c);
return 0;
} }
static int static int
...@@ -703,8 +456,15 @@ _setattro(cPersistentObject *self, PyObject *oname, PyObject *v, ...@@ -703,8 +456,15 @@ _setattro(cPersistentObject *self, PyObject *oname, PyObject *v,
{ {
if(name[3]=='o' && name[4]=='i' && name[5]=='d' && ! name[6]) if(name[3]=='o' && name[4]=='i' && name[5]=='d' && ! name[6])
{ {
if(v && PyInt_Check(v)) self->oid=PyInt_AsLong(v); if (! v) return bad_delattr();
else self->oid=0; if (PyString_Check(v) && PyString_GET_SIZE(v)==8)
memcpy(self->oid, PyString_AS_STRING(v), 8);
else
{
PyErr_SetString(PyExc_AttributeError,
"_p_oid must be an 8-character string");
return -1;
}
return 0; return 0;
} }
if(name[3]=='j' && name[4]=='a' && name[5]=='r' && ! name[6]) if(name[3]=='j' && name[4]=='a' && name[5]=='r' && ! name[6])
...@@ -715,40 +475,29 @@ _setattro(cPersistentObject *self, PyObject *oname, PyObject *v, ...@@ -715,40 +475,29 @@ _setattro(cPersistentObject *self, PyObject *oname, PyObject *v,
} }
if(strcmp(name+3,"changed")==0) if(strcmp(name+3,"changed")==0)
{ {
if(v==Py_None) self->state=GHOST_STATE; if (! v) return bad_delattr();
else self->state= (v && PyObject_IsTrue(v)); if (v==Py_None)
return 0; {
} if (Per__p_deactivate(self, NULL)) Py_DECREF(Py_None);
if(strcmp(name+3,"atime")==0) return 0;
{ }
self->atime=(time_t)1; if (PyObject_IsTrue(v)) return changed(self);
if (self->state >= 0) self->state=cPersistent_UPTODATE_STATE;
return 0; return 0;
} }
} }
else else
{ {
PyObject *r; PyObject *r;
/* Update state, if necessary */ UPDATE_STATE_IF_NECESSARY(self, -1);
if(self->state==GHOST_STATE && self->jar)
{
self->state=UPTODATE_STATE;
UNLESS(r=callmethod1(self->jar,py_setstate,(PyObject*)self))
{
self->state=GHOST_STATE;
return -1;
}
Py_DECREF(r);
}
/* Record access times */ /* Record access times */
Per_set_atime(self); self->atime=((long)(time(NULL)/3))%65536;
if(! (*name=='_' && name[1]=='v' && name[2]=='_') if(! (*name=='_' && name[1]=='v' && name[2]=='_')
&& self->state != CHANGED_STATE && self->jar) && self->state != cPersistent_CHANGED_STATE && self->jar)
if(changed((PyObject*)self) < 0) return -1; if(changed(self) < 0) return -1;
} }
return setattrf((PyObject*)self,oname,v); return setattrf((PyObject*)self,oname,v);
...@@ -760,28 +509,6 @@ Per_setattro(cPersistentObject *self, PyObject *oname, PyObject *v) ...@@ -760,28 +509,6 @@ Per_setattro(cPersistentObject *self, PyObject *oname, PyObject *v)
return _setattro(self,oname, v, PyExtensionClassCAPI->setattro); return _setattro(self,oname, v, PyExtensionClassCAPI->setattro);
} }
static char Pertype__doc__[] =
"Persistent object support mix-in class\n"
"\n"
"When a persistent object is loaded from a database, the object's\n"
"data is not immediately loaded. Loading of the objects data is\n"
"defered until an attempt is made to access an attribute of the\n"
"object. \n"
"\n"
"The object also tries to keep track of whether it has changed. It\n"
"is easy for this to be done incorrectly. For this reason, methods\n"
"of subclasses that change state other than by setting attributes\n"
"should: 'self.__changed__(1)' to flag instances as changed.\n"
"\n"
"Data are not saved automatically. To save an object's state, call\n"
"the object's '__save__' method.\n"
"\n"
"You must not override the object's '__getattr__' and '__setattr__'\n"
"methods. If you override the objects '__getstate__' method, then\n"
"you must be careful not to include any attributes with names\n"
"starting with '_p_' in the state.\n"
;
static PyExtensionClass Pertype = { static PyExtensionClass Pertype = {
PyObject_HEAD_INIT(NULL) PyObject_HEAD_INIT(NULL)
0, /*ob_size*/ 0, /*ob_size*/
...@@ -804,8 +531,7 @@ static PyExtensionClass Pertype = { ...@@ -804,8 +531,7 @@ static PyExtensionClass Pertype = {
(getattrofunc)Per_getattro, /*tp_getattr with object key*/ (getattrofunc)Per_getattro, /*tp_getattr with object key*/
(setattrofunc)Per_setattro, /*tp_setattr with object key*/ (setattrofunc)Per_setattro, /*tp_setattr with object key*/
/* Space for future expansion */ /* Space for future expansion */
0L,0L, 0L,0L,"",
Pertype__doc__, /* Documentation string */
METHOD_CHAIN(Per_methods), METHOD_CHAIN(Per_methods),
EXTENSIONCLASS_BASICNEW_FLAG, EXTENSIONCLASS_BASICNEW_FLAG,
}; };
...@@ -830,7 +556,7 @@ set_debug_log(PyObject *ignored, PyObject *args) ...@@ -830,7 +556,7 @@ set_debug_log(PyObject *ignored, PyObject *args)
static struct PyMethodDef cP_methods[] = { static struct PyMethodDef cP_methods[] = {
#ifdef DEBUG_LOG #ifdef DEBUG_LOG
{"set_debug_log", (PyCFunction)set_debug_log, 0, {"set_debug_log", (PyCFunction)set_debug_log, METH_VARARGS,
"set_debug_log(callable) -- Provide a function to log events\n" "set_debug_log(callable) -- Provide a function to log events\n"
"\n" "\n"
"The function will be called with an event name and a persistent object.\n" "The function will be called with an event name and a persistent object.\n"
...@@ -859,9 +585,7 @@ void ...@@ -859,9 +585,7 @@ void
initcPersistence() initcPersistence()
{ {
PyObject *m, *d; PyObject *m, *d;
char *rev="$Revision: 1.24 $"; char *rev="$Revision: 1.25 $";
PATimeType.ob_type=&PyType_Type;
m = Py_InitModule4("cPersistence", cP_methods, m = Py_InitModule4("cPersistence", cP_methods,
"", "",
...@@ -873,110 +597,11 @@ initcPersistence() ...@@ -873,110 +597,11 @@ initcPersistence()
PyDict_SetItemString(d,"__version__", PyDict_SetItemString(d,"__version__",
PyString_FromStringAndSize(rev+11,strlen(rev+11)-2)); PyString_FromStringAndSize(rev+11,strlen(rev+11)-2));
PyExtensionClass_Export(d,"Persistent",Pertype); PyExtensionClass_Export(d,"Persistent",Pertype);
PyDict_SetItemString(d,"atimeType",(PyObject*)&PATimeType);
cPersistenceCAPI=&truecPersistenceCAPI; cPersistenceCAPI=&truecPersistenceCAPI;
PyDict_SetItemString(d, "CAPI", PyDict_SetItemString(d, "CAPI",
PyCObject_FromVoidPtr(cPersistenceCAPI,NULL)); PyCObject_FromVoidPtr(cPersistenceCAPI,NULL));
#include "dcprotect.h"
if (PyErr_Occurred()) if (PyErr_Occurred())
Py_FatalError("can't initialize module cDocumentTemplate"); Py_FatalError("can't initialize module cDocumentTemplate");
} }
/****************************************************************************
$Log: cPersistence.c,v $
Revision 1.24 1998/07/27 13:03:21 jim
Added __of__ to list of attributes that don't activate object on access.
Revision 1.23 1998/01/09 22:19:28 jim
Fixed bug in deactivation logic. The stupid thing was calling a
constructor when deactivating.
Revision 1.22 1997/12/15 15:28:09 jim
Some cleanup. Removed unused old routine.
Renamed _p___reinit__ to _p_deactivate.
Updated _p_changed attribute protocol. This will allow us to get rid
of _p_state and maybe someday __changed__().
Revision 1.21 1997/12/11 16:03:30 jim
Set EXTENSIONCLASS_BASICNEW_FLAG to support __basicnew__ protocol.
Revision 1.20 1997/11/13 19:46:24 jim
Fixed minor error handling bug in reinit.
Revision 1.19 1997/09/18 19:53:46 jim
Added attribute, _p_state.
Revision 1.18 1997/07/18 14:14:02 jim
Fixed bug in handling delete of certain special attributes.
Revision 1.17 1997/07/16 20:18:32 jim
*** empty log message ***
Revision 1.16 1997/06/30 15:26:35 jim
Changed so getting an object's __class__ does not cause it's
activation.
Revision 1.15 1997/06/06 19:04:40 jim
Modified so that C API setstate makes object temporarily
undeactivatable.
Revision 1.14 1997/05/01 20:33:58 jim
I made (and restored) some optimizations. The effect is probably
minor, but who knows.
Revision 1.13 1997/04/27 09:18:01 jim
Added to the CAPI to support subtypes (like Record) that want to
extend attr functions.
Revision 1.12 1997/04/24 12:48:48 jim
Fixed bug in reinit
Revision 1.11 1997/04/22 02:46:50 jim
Took out debugging info.
Revision 1.10 1997/04/22 02:40:03 jim
Changed object header layout and added sticky feature.
Revision 1.9 1997/04/03 17:34:14 jim
Changed to pass transaction to jar store method during commit.
Revision 1.8 1997/03/28 20:24:52 jim
Added login to really minimice cache size and to
make cache attributes changeable.
Revision 1.7 1997/03/25 20:43:21 jim
Changed to make all persistent objects transactional.
Revision 1.6 1997/03/20 20:58:25 jim
Fixed bug in reinit.
Revision 1.5 1997/03/14 22:59:34 jim
Changed the way Per_setstate was exported to get rid of compilation
error.
Revision 1.4 1997/03/14 22:51:40 jim
Added exported C interface, so that other C classes could subclass
from it.
Added _p_mtime attribute, which returns the persistent modification
time.
Revision 1.3 1997/03/11 20:53:07 jim
Added access-time tracking and special type for efficient access time
management.
Revision 1.2 1997/02/21 20:49:09 jim
Added logic to treat attributes starting with _v_ as volatile.
Changes in these attributes to not make the object thing it's been
saved and these attributes are not saved by the default __getstate__
method.
Revision 1.1 1997/02/14 20:24:55 jim
*** empty log message ***
****************************************************************************/
/* /*
$Id: cPersistence.h,v 1.9 1997/12/15 15:55:16 jim Exp $ $Id: cPersistence.h,v 1.10 1998/11/11 02:00:56 jim Exp $
Definitions to facilitate making cPersistent subclasses in C. Definitions to facilitate making cPersistent subclasses in C.
Copyright
Copyright 1996 Digital Creations, L.C., 910 Princess Anne
Street, Suite 300, Fredericksburg, Virginia 22401 U.S.A. All
rights reserved. Copyright in this software is owned by DCLC,
unless otherwise indicated. Permission to use, copy and
distribute this software is hereby granted, provided that the
above copyright notice appear in all copies and that both that
copyright notice and this permission notice appear. Note that
any product, process or technology described in this software
may be the subject of other Intellectual Property rights
reserved by Digital Creations, L.C. and are not licensed
hereunder.
Trademarks
Digital Creations & DCLC, are trademarks of Digital Creations, L.C..
All other trademarks are owned by their respective companies.
No Warranty
The software is provided "as is" without warranty of any kind,
either express or implied, including, but not limited to, the
implied warranties of merchantability, fitness for a particular
purpose, or non-infringement. This software could include
technical inaccuracies or typographical errors. Changes are
periodically made to the software; these changes will be
incorporated in new editions of the software. DCLC may make
improvements and/or changes in this software at any time
without notice.
Limitation Of Liability
In no event will DCLC be liable for direct, indirect, special,
incidental, economic, cover, or consequential damages arising
out of the use of or inability to use this software even if
advised of the possibility of such damages. Some states do not
allow the exclusion or limitation of implied warranties or
limitation of liability for incidental or consequential
damages, so the above limitation or exclusion may not apply to
you.
If you have questions regarding this software,
contact:
Digital Creations L.C.
info@digicool.com
(540) 371-6909
$Log: cPersistence.h,v $
Revision 1.9 1997/12/15 15:55:16 jim
Changed persistent object header layout. This will require recompile
of all C Persistent objects.
Revision 1.8 1997/12/10 22:19:24 jim
Added PER_USE macro.
Revision 1.7 1997/07/18 14:15:39 jim
Added PER_DEL so that subclasses can handle deallocation correctly.
Revision 1.6 1997/06/06 19:13:32 jim
Changed/fixed convenience macros.
Revision 1.5 1997/05/19 17:51:20 jim
Added macros to simplify C PO implementation.
Revision 1.4 1997/05/19 13:49:36 jim
Added include of time.h.
Revision 1.3 1997/04/27 09:18:23 jim
Added to the CAPI to support subtypes (like Record) that want to
extend attr functions.
Revision 1.2 1997/04/22 02:40:28 jim
Changed object header layout.
Revision 1.1 1997/04/01 17:15:48 jim
*** empty log message ***
*/ */
...@@ -96,24 +15,19 @@ ...@@ -96,24 +15,19 @@
#define cPersistent_HEAD PyObject_HEAD \ #define cPersistent_HEAD PyObject_HEAD \
PyObject *jar; \ PyObject *jar; \
int oid; \ char oid[8]; \
time_t atime; \ unsigned short atime; \
signed char state; \ signed char state; \
#define cPersistent_GHOST_STATE -1 #define cPersistent_GHOST_STATE -1
#define cPersistent_UPTODATE_STATE 0 #define cPersistent_UPTODATE_STATE 0
#define cPersistent_CHANGED_STATE 1 #define cPersistent_CHANGED_STATE 1
#define cPersistent_STICKY_STATE 2
typedef struct { typedef struct {
cPersistent_HEAD cPersistent_HEAD
} cPersistentObject; } cPersistentObject;
typedef struct {
PyObject_HEAD
cPersistentObject *object;
} PATimeobject;
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);
...@@ -121,7 +35,7 @@ typedef struct { ...@@ -121,7 +35,7 @@ typedef struct {
PyMethodChain *methods; PyMethodChain *methods;
getattrofunc getattro; getattrofunc getattro;
setattrofunc setattro; setattrofunc setattro;
int (*changed)(PyObject*); int (*changed)(cPersistentObject*);
int (*setstate)(PyObject*); int (*setstate)(PyObject*);
pergetattr pergetattro; pergetattr pergetattro;
persetattr persetattro; persetattr persetattro;
...@@ -129,16 +43,20 @@ typedef struct { ...@@ -129,16 +43,20 @@ typedef struct {
static cPersistenceCAPIstruct *cPersistenceCAPI; static cPersistenceCAPIstruct *cPersistenceCAPI;
#define PER_USE_OR_RETURN(O,R) { \
if ((O)->state==cPersistent_GHOST_STATE && \
cPersistenceCAPI->setstate((PyObject*)(O)) < 0) \
return (R); \
else if ((O)->state==cPersistent_UPTODATE_STATE) \
(O)->state=cPersistent_STICKY_STATE; \
}
#define PER_USE_OR_RETURN(O,R) \ #define PER_CHANGED(O) (cPersistenceCAPI->changed((cPersistentObject*)(O)))
if(cPersistenceCAPI->setstate((PyObject*)(O)) < 0) return (R)
#define PER_USE(O) (cPersistenceCAPI->setstate((PyObject*)(O)))
#define PER_CHANGED(O) (cPersistenceCAPI->changed((PyObject*)(O))) #define PER_ALLOW_DEACTIVATION(O) \
((O)->state==cPersistent_STICKY_STATE && \
((O)->state=cPersistent_UPTODATE_STATE))
#define PER_PREVENT_DEACTIVATION(O) ((O)->atime=(time_t)1);
#define PER_ALLOW_DEACTIVATION(O) ((O)->atime=time(NULL));
#define PER_DEL(O) Py_XDECREF((O)->jar) #define PER_DEL(O) Py_XDECREF((O)->jar)
#endif #endif
......
/* static char *what_string = "$Id: cPickleCache.c,v 1.16 1998/11/11 02:00:56 jim Exp $";
$Id: cPickleCache.c,v 1.15 1998/07/27 13:09:03 jim Exp $
C implementation of a pickle jar cache.
Copyright
Copyright 1996 Digital Creations, L.C., 910 Princess Anne
Street, Suite 300, Fredericksburg, Virginia 22401 U.S.A. All
rights reserved.
***************************************************************************/
static char *what_string = "$Id: cPickleCache.c,v 1.15 1998/07/27 13:09:03 jim Exp $";
#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))
#define UNLESS_ASSIGN(V,E) ASSIGN(V,E) UNLESS(V) #define UNLESS_ASSIGN(V,E) ASSIGN(V,E) UNLESS(V)
#define Py_ASSIGN(P,E) if(!PyObject_AssignExpression(&(P),(E))) return NULL
#define OBJECT(O) ((PyObject*)O) #define OBJECT(O) ((PyObject*)O)
#include "cPersistence.h" #include "cPersistence.h"
...@@ -25,7 +10,7 @@ static char *what_string = "$Id: cPickleCache.c,v 1.15 1998/07/27 13:09:03 jim E ...@@ -25,7 +10,7 @@ static char *what_string = "$Id: cPickleCache.c,v 1.15 1998/07/27 13:09:03 jim E
#undef Py_FindMethod #undef Py_FindMethod
static PyObject *py_reload, *py__p_jar, *py__p_atime, *py__p_deactivate; static PyObject *py_reload, *py__p_jar, *py__p_deactivate;
/* Declarations for objects of type cCache */ /* Declarations for objects of type cCache */
...@@ -65,60 +50,45 @@ typedef struct { ...@@ -65,60 +50,45 @@ typedef struct {
staticforward PyTypeObject Cctype; staticforward PyTypeObject Cctype;
static PyObject *PATimeType=NULL;
/* ---------------------------------------------------------------- */ /* ---------------------------------------------------------------- */
static int static int
gc_item(ccobject *self, PyObject *key, PyObject *v, time_t now, time_t dt) gc_item(ccobject *self, PyObject *key, PyObject *v, long now, int dt)
{ {
time_t t;
if(v && key) if (v && key)
{ {
self->n++; self->n++;
if(v->ob_type==(PyTypeObject*)PATimeType) if(v->ob_refcnt <= 1)
{ {
if(((PATimeobject*)v)->object->ob_refcnt <= 1) self->sum_deal++;
{ return PyDict_DelItem(self->data, key);
self->sum_deal++; }
UNLESS(-1 != PyDict_DelItem(self->data, key)) return -1;
} if (dt && v->ob_type->tp_basicsize >= sizeof(cPersistentObject) &&
else ((cPersistentObject*)v)->state==cPersistent_UPTODATE_STATE)
{
now -= ((cPersistentObject*)v)->atime;
if (now < 0) now += 65536;
self->na++;
self->sum_age += now;
if (now > dt)
{ {
t=((PATimeobject*)v)->object->atime; /* We have a cPersistent object that hasn't been used in
if(t != (time_t)1) a while. Reinitialize it, hopefully freeing it's
state.
*/
self->sum_deac++;
if(key=PyObject_GetAttr(v,py__p_deactivate))
{ {
self->na++; ASSIGN(key,PyObject_CallObject(key,NULL));
t=now-t; UNLESS(key) return -1;
self->sum_age += t; Py_DECREF(key);
if((! dt || t > dt)) return 0;
{
/* We have a cPersistent object that hasn't been used in
a while. Reinitialize it, hopefully freeing it's
state.
*/
v=(PyObject*)(((PATimeobject*)v)->object);
if(((cPersistentObject*)v)->state !=
cPersistent_UPTODATE_STATE) return 0;
self->sum_deac++;
if(key=PyObject_GetAttr(v,py__p_deactivate))
{
ASSIGN(key,PyObject_CallObject(key,NULL));
UNLESS(key) return -1;
Py_DECREF(key);
}
PyErr_Clear();
}
} }
PyErr_Clear();
} }
} }
else if(v->ob_refcnt <= 1)
{
self->sum_deal++;
UNLESS(-1 != PyDict_DelItem(self->data, key)) return -1;
}
} }
return 0; return 0;
} }
...@@ -135,7 +105,7 @@ update_stats(ccobject *self, time_t now) ...@@ -135,7 +105,7 @@ update_stats(ccobject *self, time_t now)
self->dfa *= WEIGHTING_PERIOD/(WEIGHTING_PERIOD+d); self->dfa *= WEIGHTING_PERIOD/(WEIGHTING_PERIOD+d);
self->mean_age=((self->mean_age*self->dfa+self->sum_age)/ self->mean_age=((self->mean_age*self->dfa+self->sum_age)/
(self->dfa+self->na)); (self->dfa+self->na))*3;
self->sum_age=0; self->sum_age=0;
deac=self->sum_deac/d; deac=self->sum_deac/d;
...@@ -163,16 +133,22 @@ static int ...@@ -163,16 +133,22 @@ static int
fullgc(ccobject *self, int idt) fullgc(ccobject *self, int idt)
{ {
PyObject *key, *v; PyObject *key, *v;
int i; int i, dt;
time_t now, dt; long now;
if(self->cache_size < 1) return 0; if (self->cache_size < 1) return 0;
i=PyDict_Size(self->data)-3/self->cache_size; if ((i=PyDict_Size(self->data)) < 1) return;
if(i < 3) i=3;
dt=self->cache_age*3/i; now=((long)(time(NULL)/3))%65536;
if(dt < 10) dt=10;
now=time(NULL); if (idt) dt=idt*3;
if(idt) dt=idt; else
{
i=PyDict_Size(self->data)-3/self->cache_size;
if(i < 3) i=3;
dt=self->cache_age*3/i;
if(dt < 10) dt=10;
}
for(i=0; PyDict_Next(self->data, &i, &key, &v); ) for(i=0; PyDict_Next(self->data, &i, &key, &v); )
if(gc_item(self,key,v,now,dt) < 0) return -1; if(gc_item(self,key,v,now,dt) < 0) return -1;
...@@ -183,40 +159,6 @@ fullgc(ccobject *self, int idt) ...@@ -183,40 +159,6 @@ fullgc(ccobject *self, int idt)
return 0; return 0;
} }
static PyObject *
ccitems(ccobject *self, PyObject *args)
{
PyObject *r, *key, *v, *item=0;
int i;
UNLESS(PyArg_ParseTuple(args,"")) return NULL;
UNLESS(r=PyList_New(0)) return NULL;
for(i=0; PyDict_Next(self->data, &i, &key, &v); )
{
if(key && v)
{
if(v->ob_type==(PyTypeObject*)PATimeType)
{
ASSIGN(item, Py_BuildValue("OO",key,((PATimeobject*)v)->object));
}
else
{
ASSIGN(item, Py_BuildValue("OO",key,v));
}
UNLESS(item) goto err;
if(PyList_Append(r,item) < 0) goto err;
}
}
Py_XDECREF(item);
return r;
err:
Py_XDECREF(item);
Py_DECREF(r);
return NULL;
}
static int static int
reallyfullgc(ccobject *self, int dt) reallyfullgc(ccobject *self, int dt)
{ {
...@@ -249,31 +191,33 @@ reallyfullgc(ccobject *self, int dt) ...@@ -249,31 +191,33 @@ reallyfullgc(ccobject *self, int dt)
static int static int
maybegc(ccobject *self, PyObject *thisv) maybegc(ccobject *self, PyObject *thisv)
{ {
int n, s, size; int n, s, size, dt;
time_t now,dt; long now;
PyObject *key=0, *v=0; PyObject *key=0, *v=0;
/*printf("m");*/ if (self->cache_size < 1) return 0;
s=PyDict_Size(self->data);
if (s < 1) return s;
now=((long)(time(NULL)/3))%65536;
if(self->cache_size < 1) return 0;
s=PyDict_Size(self->data)-3;
if(s < self->cache_size) return 0;
size=self->cache_size; size=self->cache_size;
self->cache_size=0; self->cache_size=0;
n=(s-size)/10; n=(s-size)/10;
/*n=s/size;*/
if(n < 3) n=3; if (n < 3) n=3;
dt=(long)(self->cache_age*(0.2+0.8*size/s)); s=8*size/s;
if(dt < 10) dt=10; if (s > 100) s=100;
dt=(long)(self->cache_age*(0.2+0.1*s));
if (dt < 10) dt=10;
now=time(NULL); now=time(NULL);
while(--n >= 0) while (--n >= 0)
{ {
if(PyDict_Next(self->data, &(self->position), &key, &v)) if (PyDict_Next(self->data, &(self->position), &key, &v))
{ {
if(v != thisv && gc_item(self,key,v,now,dt) < 0) if (v != thisv && gc_item(self,key,v,now,dt) < 0)
{ {
self->cache_size=size; self->cache_size=size;
return -1; return -1;
...@@ -284,7 +228,7 @@ maybegc(ccobject *self, PyObject *thisv) ...@@ -284,7 +228,7 @@ maybegc(ccobject *self, PyObject *thisv)
} }
self->cache_size=size; self->cache_size=size;
if(now-self->last_check > 1) update_stats(self, now); if (now-self->last_check > 1) update_stats(self, now);
return 0; return 0;
} }
...@@ -309,41 +253,6 @@ cc_reallyfull_sweep(ccobject *self, PyObject *args) ...@@ -309,41 +253,6 @@ cc_reallyfull_sweep(ccobject *self, PyObject *args)
return Py_None; return Py_None;
} }
static PyObject *
cc_report(ccobject *self, PyObject *args)
{
PyObject *key, *v, *t=0;
int i;
if(args) PyArg_ParseTuple(args,"|O", &t);
for(i=0; PyDict_Next(self->data, &i, &key, &v); )
{
if(v->ob_type==(PyTypeObject*)PATimeType
&& (
(t && OBJECT(((PATimeobject*)v)->object->ob_type) == t)
|| ! t))
printf("%d\t%p\t%s\t%ld\t%d\t%ld\n",
(((PATimeobject*)v)->object->oid),
((PATimeobject*)v)->object,
((PATimeobject*)v)->object->ob_type->tp_name,
(long)(((PATimeobject*)v)->object->ob_refcnt),
(((PATimeobject*)v)->object->state),
(long)(((PATimeobject*)v)->object->atime) );
else if((t && OBJECT(((PATimeobject*)v)->object->ob_type) == t)
|| ! t)
printf("%d\t%p\t%s\t%ld\t%d\n",
(((cPersistentObject*)v)->oid),
v,
v->ob_type->tp_name,
(long)(v->ob_refcnt),
(((cPersistentObject*)v)->state)
);
}
if(args) Py_INCREF(Py_None);
return Py_None;
}
static PyObject * static PyObject *
cc_incrgc(ccobject *self, PyObject *args) cc_incrgc(ccobject *self, PyObject *args)
{ {
...@@ -353,31 +262,24 @@ cc_incrgc(ccobject *self, PyObject *args) ...@@ -353,31 +262,24 @@ cc_incrgc(ccobject *self, PyObject *args)
} }
static struct PyMethodDef cc_methods[] = { static struct PyMethodDef cc_methods[] = {
{"full_sweep", (PyCFunction)cc_full_sweep, 1, {"full_sweep", (PyCFunction)cc_full_sweep, METH_VARARGS,
"full_sweep([age]) -- Perform a full sweep of the cache\n\n" "full_sweep([age]) -- Perform a full sweep of the cache\n\n"
"Make a single pass through the cache, removing any objects that are no\n" "Make a single pass through the cache, removing any objects that are no\n"
"longer referenced, and deactivating objects that have not been\n" "longer referenced, and deactivating objects that have not been\n"
"accessed in the number of seconds given by 'age'. " "accessed in the number of seconds given by 'age'. "
"'age defaults to the cache age.\n" "'age defaults to the cache age.\n"
}, },
{"report", (PyCFunction)cc_report, 1, ""}, {"minimize", (PyCFunction)cc_reallyfull_sweep, METH_VARARGS,
{"minimize", (PyCFunction)cc_reallyfull_sweep, 1,
"minimize([age]) -- Remove as many objects as possible\n\n" "minimize([age]) -- Remove as many objects as possible\n\n"
"Make multiple passes through the cache, removing any objects that are no\n" "Make multiple passes through the cache, removing any objects that are no\n"
"longer referenced, and deactivating objects that have not been\n" "longer referenced, and deactivating objects that have not been\n"
"accessed in the number of seconds given by 'age'. 'age defaults to 0.\n" "accessed in the number of seconds given by 'age'. 'age defaults to 0.\n"
}, },
{"items", (PyCFunction)ccitems, 1, {"incrgc", (PyCFunction)cc_incrgc, METH_VARARGS,
"items() -- Return the cache items."
},
{"incrgc", (PyCFunction)cc_incrgc, 1,
"incrgc() -- Perform incremental garbage collection"}, "incrgc() -- Perform incremental garbage collection"},
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };
/* ---------- */
static ccobject * static ccobject *
newccobject(int cache_size, int cache_age) newccobject(int cache_size, int cache_age)
{ {
...@@ -442,7 +344,11 @@ cc_getattr(ccobject *self, char *name) ...@@ -442,7 +344,11 @@ cc_getattr(ccobject *self, char *name)
return self->data; return self->data;
} }
} }
if(*name=='h' && strcmp(name, "has_key")==0) if(
*name=='h' && strcmp(name, "has_key")==0 ||
*name=='i' && strcmp(name, "items")==0 ||
*name=='k' && strcmp(name, "keys")==0
)
return PyObject_GetAttrString(self->data, name); return PyObject_GetAttrString(self->data, name);
if(r=Py_FindMethod(cc_methods, (PyObject *)self, name)) if(r=Py_FindMethod(cc_methods, (PyObject *)self, name))
...@@ -476,22 +382,6 @@ cc_setattr(ccobject *self, char *name, PyObject *value) ...@@ -476,22 +382,6 @@ cc_setattr(ccobject *self, char *name, PyObject *value)
return -1; return -1;
} }
static PyObject *
cc_repr(ccobject *self)
{
return PyObject_Repr(self->data);
}
static PyObject *
cc_str(self)
ccobject *self;
{
return PyObject_Str(self->data);
}
/* Code to access cCache objects as mappings */
static int static int
cc_length(ccobject *self) cc_length(ccobject *self)
{ {
...@@ -503,58 +393,22 @@ cc_subscript(ccobject *self, PyObject *key) ...@@ -503,58 +393,22 @@ cc_subscript(ccobject *self, PyObject *key)
{ {
PyObject *r; PyObject *r;
UNLESS(r=PyObject_GetItem(self->data, key)) UNLESS (r=PyDict_GetItem(self->data, key))
{ {
PyErr_SetObject(PyExc_KeyError, key); PyErr_SetObject(PyExc_KeyError, key);
return NULL; return NULL;
} }
UNLESS(-1 != maybegc(self,r)) if (maybegc(self,r) < 0) return NULL;
{
Py_DECREF(r); Py_INCREF(r);
return NULL;
}
if(r->ob_type==(PyTypeObject *)PATimeType)
{
Py_DECREF(r);
r=(PyObject*)(((PATimeobject*)r)->object);
Py_INCREF(r);
}
return r; return r;
} }
static int static int
cc_ass_sub(ccobject *self, PyObject *key, PyObject *v) cc_ass_sub(ccobject *self, PyObject *key, PyObject *v)
{ {
if(v) return PyDict_SetItem(self->data, key, v);
if(v) return PyDict_DelItem(self->data, key);
{
int r;
PyObject *t=0;
/* Now get and save the access time */
if(t=PyObject_GetAttr(v,py__p_atime))
{
if(t->ob_type != (PyTypeObject *)PATimeType)
{
Py_DECREF(t);
t=0;
}
else
v=t;
}
else
PyErr_Clear();
r=PyDict_SetItem(self->data,key,v);
Py_XDECREF(t);
if(r < 0) return -1;
return maybegc(self, v);
}
else
{
UNLESS(-1 != PyDict_DelItem(self->data,key)) return -1;
return maybegc(self, NULL);
}
} }
static PyMappingMethods cc_as_mapping = { static PyMappingMethods cc_as_mapping = {
...@@ -563,12 +417,6 @@ static PyMappingMethods cc_as_mapping = { ...@@ -563,12 +417,6 @@ static PyMappingMethods cc_as_mapping = {
(objobjargproc)cc_ass_sub, /*mp_ass_subscript*/ (objobjargproc)cc_ass_sub, /*mp_ass_subscript*/
}; };
/* -------------------------------------------------------- */
static char Cctype__doc__[] =
""
;
static PyTypeObject Cctype = { static PyTypeObject Cctype = {
PyObject_HEAD_INIT(NULL) PyObject_HEAD_INIT(NULL)
0, /*ob_size*/ 0, /*ob_size*/
...@@ -581,22 +429,19 @@ static PyTypeObject Cctype = { ...@@ -581,22 +429,19 @@ static PyTypeObject Cctype = {
(getattrfunc)cc_getattr, /*tp_getattr*/ (getattrfunc)cc_getattr, /*tp_getattr*/
(setattrfunc)cc_setattr, /*tp_setattr*/ (setattrfunc)cc_setattr, /*tp_setattr*/
(cmpfunc)0, /*tp_compare*/ (cmpfunc)0, /*tp_compare*/
(reprfunc)cc_repr, /*tp_repr*/ (reprfunc)0, /*tp_repr*/
0, /*tp_as_number*/ 0, /*tp_as_number*/
0, /*tp_as_sequence*/ 0, /*tp_as_sequence*/
&cc_as_mapping, /*tp_as_mapping*/ &cc_as_mapping, /*tp_as_mapping*/
(hashfunc)0, /*tp_hash*/ (hashfunc)0, /*tp_hash*/
(ternaryfunc)0, /*tp_call*/ (ternaryfunc)0, /*tp_call*/
(reprfunc)cc_str, /*tp_str*/ (reprfunc)0, /*tp_str*/
/* Space for future expansion */ /* Space for future expansion */
0L,0L,0L,0L, 0L,0L,0L,0L,
Cctype__doc__ /* Documentation string */ ""
}; };
/* End of code for cCache objects */
/* -------------------------------------------------------- */
static PyObject * static PyObject *
cCM_new(PyObject *self, PyObject *args) cCM_new(PyObject *self, PyObject *args)
{ {
...@@ -605,119 +450,30 @@ cCM_new(PyObject *self, PyObject *args) ...@@ -605,119 +450,30 @@ cCM_new(PyObject *self, PyObject *args)
return (PyObject*)newccobject(cache_size,cache_age); return (PyObject*)newccobject(cache_size,cache_age);
} }
/* List of methods defined in the module */
static struct PyMethodDef cCM_methods[] = { static struct PyMethodDef cCM_methods[] = {
{"PickleCache",(PyCFunction)cCM_new, 1, {"PickleCache",(PyCFunction)cCM_new, METH_VARARGS, ""},
"PickleCache([size,age]) -- Create a pickle jar cache\n\n"
"The cache will attempt to garbage collect items when the cache size is\n"
"greater than the given size, which defaults to 100. Normally, objects\n"
"are garbage collected if their reference count is one, meaning that\n"
"they are only referenced by the cache. In some cases, objects that\n"
"have not been accessed in 'age' seconds may be partially garbage\n"
"collected, meaning that most of their state is freed.\n"
},
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };
/* Initialization function for the module (*must* be called initcCache) */
static char cCache_module_documentation[] =
""
;
void void
initcPickleCache() initcPickleCache()
{ {
PyObject *m, *d; PyObject *m, *d;
char *rev="$Revision: 1.15 $"; char *rev="$Revision: 1.16 $";
Cctype.ob_type=&PyType_Type; Cctype.ob_type=&PyType_Type;
if(PATimeType=PyImport_ImportModule("cPersistence")) m = Py_InitModule4("cPickleCache", cCM_methods, "",
ASSIGN(PATimeType,PyObject_GetAttrString(PATimeType,"atimeType"));
UNLESS(PATimeType) PyErr_Clear();
m = Py_InitModule4("cPickleCache", cCM_methods,
cCache_module_documentation,
(PyObject*)NULL,PYTHON_API_VERSION); (PyObject*)NULL,PYTHON_API_VERSION);
d = PyModule_GetDict(m); d = PyModule_GetDict(m);
py_reload=PyString_FromString("reload"); py_reload=PyString_FromString("reload");
py__p_jar=PyString_FromString("_p_jar"); py__p_jar=PyString_FromString("_p_jar");
py__p_atime=PyString_FromString("_p_atime");
py__p_deactivate=PyString_FromString("_p_deactivate"); py__p_deactivate=PyString_FromString("_p_deactivate");
PyDict_SetItemString(d,"__version__", PyDict_SetItemString(d,"__version__",
PyString_FromStringAndSize(rev+11,strlen(rev+11)-2)); PyString_FromStringAndSize(rev+11,strlen(rev+11)-2));
#include "dcprotect.h"
if (PyErr_Occurred()) Py_FatalError("can't initialize module cCache"); if (PyErr_Occurred()) Py_FatalError("can't initialize module cCache");
} }
/******************************************************************************
$Log: cPickleCache.c,v $
Revision 1.15 1998/07/27 13:09:03 jim
Changed _p___reinit__ to _p_deactivate.
Revision 1.14 1998/02/05 14:43:10 jim
Fixed bug in ibcremental gc method.
Revision 1.13 1998/02/05 14:34:40 jim
Added getattr option to get cache data.
Added method to perform incremental gc.
Changed incremental collection effort algorithm to be based on
difference between actual and target size, rather than ration.
Revision 1.12 1997/12/15 15:25:09 jim
Cleaned up to avoid VC++ warnings.
Revision 1.11 1997/12/10 22:20:43 jim
Added has_key method.
Revision 1.10 1997/07/18 14:30:18 jim
Added reporting method for use during debugging.
Revision 1.9 1997/07/16 20:18:40 jim
*** empty log message ***
Revision 1.8 1997/06/30 15:27:51 jim
Added machinery to track cache statistics.
Fixed bug in garbage collector, which had a nasty habit
of activating inactive objects so that it could deactivate them.
Revision 1.7 1997/05/30 14:29:47 jim
Added new algorithm for adjusting cache age based on cache size. Not,
if the cache size gets really big, the cache age can drop to as low as
20% of the configured cache age. Also made the "minimize" method more
agressive.
Revision 1.6 1997/04/22 02:45:24 jim
Changed object header layout and added sticky feature.
Revision 1.5 1997/04/15 19:03:29 jim
Fixed leak introduced in last revision. :-(
Revision 1.4 1997/04/11 19:13:21 jim
Added code to be more conservative about GCing.
Fixed setattr bugs.
Revision 1.3 1997/03/28 20:18:34 jim
Simplified reinit logic.
Revision 1.2 1997/03/11 20:48:38 jim
Added object-deactivation support. This only works with cPersistent
objects.
Revision 1.1 1997/02/17 18:39:02 jim
*** empty log message ***
******************************************************************************/
...@@ -48,12 +48,12 @@ ...@@ -48,12 +48,12 @@
__doc__='''Python implementation of persistent base types __doc__='''Python implementation of persistent base types
$Id: mapping.py,v 1.2 1998/10/23 21:40:59 jim Exp $''' $Id: mapping.py,v 1.3 1998/11/11 02:00:56 jim Exp $'''
__version__='$Revision: 1.2 $'[11:-2] __version__='$Revision: 1.3 $'[11:-2]
import Persistence import Persistence
class PersistentMapping(Persistence.Persistent): class PM(Persistence.Persistent):
"""A persistent wrapper for mapping objects. """A persistent wrapper for mapping objects.
This class allows wrapping of mapping objects so that This class allows wrapping of mapping objects so that
...@@ -65,22 +65,36 @@ class PersistentMapping(Persistence.Persistent): ...@@ -65,22 +65,36 @@ class PersistentMapping(Persistence.Persistent):
if container is None: container={} if container is None: container={}
self._container=container self._container=container
def __delitem__(self, key):
del self._container[key]
try: del self._v_keys
except: pass
self.__changed__(1)
def __getitem__(self, key): def __getitem__(self, key):
return self._container[key] return self._container[key]
def __len__(self): return len(self._container)
def __setitem__(self, key, v): def __setitem__(self, key, v):
self._container[key]=v self._container[key]=v
try: del self._v_keys try: del self._v_keys
except: pass except: pass
self.__changed__(1) self.__changed__(1)
def __delitem__(self, key): def clear(self):
del self._container[key] self._container.clear()
try: del self._v_keys self._p_changed=1
except: pass if hasattr(self,'_v_keys'): del self._v_keys
self.__changed__(1)
def __len__(self): return len(self._container) def copy(self): return self.__class__(self._container.copy())
def get(self, key, default): return self._container.get(key, default)
def has_key(self,key): return self._container.has_key(key)
def items(self):
return map(lambda k, d=self: (k,d[k]), self.keys())
def keys(self): def keys(self):
try: return self._v_keys try: return self._v_keys
...@@ -91,14 +105,12 @@ class PersistentMapping(Persistence.Persistent): ...@@ -91,14 +105,12 @@ class PersistentMapping(Persistence.Persistent):
keys.sort() keys.sort()
return keys return keys
def clear(self): def update(self, b):
self._container={} a=self._container
if hasattr(self,'_v_keys'): del self._v_keys for k, v in b.items(): a[k] = v
self._p_changed=1
def values(self): def values(self):
return map(lambda k, d=self: d[k], self.keys()) return map(lambda k, d=self: d[k], self.keys())
def items(self): PersistentMapping=PM
return map(lambda k, d=self: (k,d[k]), self.keys())
def has_key(self,key): return self._container.has_key(key)
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