Commit e6c1e00d authored by Jim Fulton's avatar Jim Fulton

alpha1

parent 618a2277
......@@ -48,12 +48,12 @@
__doc__='''Python implementation of persistent base types
$Id: PersistentMapping.py,v 1.2 1998/10/23 21:40:59 jim Exp $'''
__version__='$Revision: 1.2 $'[11:-2]
$Id: PersistentMapping.py,v 1.3 1998/11/11 02:00:56 jim Exp $'''
__version__='$Revision: 1.3 $'[11:-2]
import Persistence
class PersistentMapping(Persistence.Persistent):
class PM(Persistence.Persistent):
"""A persistent wrapper for mapping objects.
This class allows wrapping of mapping objects so that
......@@ -65,22 +65,36 @@ class PersistentMapping(Persistence.Persistent):
if container is None: 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):
return self._container[key]
def __len__(self): return len(self._container)
def __setitem__(self, key, v):
self._container[key]=v
try: del self._v_keys
except: pass
self.__changed__(1)
def __delitem__(self, key):
del self._container[key]
try: del self._v_keys
except: pass
self.__changed__(1)
def clear(self):
self._container.clear()
self._p_changed=1
if hasattr(self,'_v_keys'): del self._v_keys
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):
try: return self._v_keys
......@@ -91,14 +105,12 @@ class PersistentMapping(Persistence.Persistent):
keys.sort()
return keys
def clear(self):
self._container={}
if hasattr(self,'_v_keys'): del self._v_keys
def update(self, b):
a=self._container
for k, v in b.items(): a[k] = v
self._p_changed=1
def values(self):
return map(lambda k, d=self: d[k], self.keys())
def items(self):
return map(lambda k, d=self: (k,d[k]), self.keys())
def has_key(self,key): return self._container.has_key(key)
PersistentMapping=PM
/***********************************************************************
$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
......@@ -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 "cPersistence.h"
......@@ -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_ASSIGN(V,E) ASSIGN(V,E) UNLESS(V)
static PyObject *py_store, *py_oops, *py_keys, *py_setstate, *py___changed__,
*py___dict__, *py_mtime, *py_onearg, *py___getinitargs__, *py___init__;
static PyObject *py_keys, *py_setstate, *py___dict__;
#ifdef DEBUG_LOG
static PyObject *debug_log=0;
......@@ -36,8 +35,9 @@ call_debug(char *event, cPersistentObject *self)
/*
printf("%s %p\n",event,self->ob_type->tp_name);
*/
r=PyObject_CallFunction(debug_log,"s(sii)",event,
self->ob_type->tp_name, self->oid, self->state);
r=PyObject_CallFunction(debug_log,"s(ss#i)",event,
self->ob_type->tp_name, self->oid, 8,
self->state);
Py_XDECREF(r);
}
#endif
......@@ -46,16 +46,9 @@ static void
init_strings()
{
#define INIT_STRING(S) py_ ## S = PyString_FromString(#S)
INIT_STRING(store);
INIT_STRING(oops);
INIT_STRING(keys);
INIT_STRING(setstate);
INIT_STRING(mtime);
INIT_STRING(__changed__);
INIT_STRING(__init__);
INIT_STRING(__getinitargs__);
INIT_STRING(__dict__);
py_onearg=Py_BuildValue("(i)",1);
#undef INIT_STRING
}
......@@ -113,6 +106,22 @@ callmethod3(PyObject *self, PyObject *name,
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 *
#ifdef HAVE_STDARG_PROTOTYPES
/* VARARGS 2 */
......@@ -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 TPertype;
static char Per___changed____doc__[] =
"__changed__([flag]) -- Flag or determine whether an object has changed\n"
" \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 int
changed(cPersistentObject *self)
{
static PyObject *builtins=0, *get_transaction=0, *py_register=0;
PyObject *o, *T;
if(PyArg_Parse(args, "O", &o))
{
int t;
t=PyObject_IsTrue(o);
PyObject *T;
if(t && self->state != cPersistent_CHANGED_STATE && self->jar)
{
UNLESS(get_transaction)
if ((self->state == cPersistent_UPTODATE_STATE ||
self->state == cPersistent_STICKY_STATE)
&& self->jar)
{
UNLESS(builtins)
UNLESS (get_transaction)
{
UNLESS(T=PyImport_ImportModule("__main__")) return NULL;
UNLESS (py_register=PyString_FromString("register")) return -1;
UNLESS (T=PyImport_ImportModule("__main__")) return -1;
ASSIGN(T,PyObject_GetAttrString(T,"__builtins__"));
UNLESS(T) return NULL;
UNLESS(py_register=PyString_FromString("register")) goto err;
UNLESS (T) return -1;
builtins=T;
}
UNLESS(get_transaction=PyObject_GetAttrString(builtins,
UNLESS (get_transaction=PyObject_GetAttrString(builtins,
"get_transaction"))
PyErr_Clear();
}
if(get_transaction)
if (get_transaction)
{
UNLESS(T=PyObject_CallObject(get_transaction,NULL)) return NULL;
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);
}
UNLESS (T=PyObject_CallObject(get_transaction,NULL)) return -1;
ASSIGN(T,PyObject_GetAttr(T,py_register));
UNLESS (T) return -1;
ASSIGN(T, PyObject_CallFunction(T,"O",self));
if (T) Py_DECREF(T);
else return -1;
}
if(self->state != cPersistent_GHOST_STATE) self->state=t;
Py_INCREF(Py_None);
return Py_None;
}
else
{
PyErr_Clear();
UNLESS(PyArg_Parse(args, "")) return NULL;
return PyInt_FromLong(self->state==cPersistent_CHANGED_STATE);
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__[] =
"__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;
return 0;
}
static char Per___inform_abort____doc__[] =
"__inform_abort__(transaction,start_time) -- Abort object changes"
;
static PyObject *
Per___inform_abort__(self, args)
cPersistentObject *self;
PyObject *args;
Per___changed__(cPersistentObject *self, PyObject *args)
{
PyObject *T, *t;
UNLESS(PyArg_ParseTuple(args, "OO", &T, &t)) return NULL;
if(self->oid && self->jar && self->state != GHOST_STATE)
PyObject *v=0;
if (args && ! PyArg_ParseTuple(args, "|O",&v)) return NULL;
if (v && ! PyObject_IsTrue(v))
{
args=callmethod3(self->jar,py_oops,(PyObject*)self,t,T);
if(args)
Py_DECREF(args);
else
PyErr_Clear();
PyErr_SetString(PyExc_TypeError,
"Only true arguments are allowed.");
return NULL;
}
if (changed(self) < 0) return NULL;
Py_INCREF(Py_None);
return Py_None;
}
static char Per__p___init____doc__[] =
"_p___init__(oid,jar) -- Initialize persistence management data"
;
static PyObject *
Per__p___init__(self, args)
cPersistentObject *self;
PyObject *args;
Per__p_deactivate(cPersistentObject *self, PyObject *args)
{
int oid;
PyObject *jar;
PyObject *init=0, *copy, *dict;
UNLESS(PyArg_Parse(args, "(iO)", &oid, &jar)) return NULL;
#ifdef DEBUG_LOG
if(idebug_log < 0) call_debug("init",self);
if (idebug_log < 0) call_debug("reinit",self);
#endif
Py_INCREF(jar);
self->oid=oid;
ASSIGN(self->jar, jar);
self->state=GHOST_STATE;
Py_INCREF(Py_None);
return Py_None;
}
static PyObject *
Per__p___reinit__(cPersistentObject *self, PyObject *args)
{
PyObject *init=0, *copy, *dict;
if (args && ! PyArg_ParseTuple(args,"")) return NULL;
#ifdef DEBUG_LOG
if(idebug_log < 0) call_debug("reinit",self);
#endif
if(PyArg_Parse(args,""))
{
if(self->state==cPersistent_UPTODATE_STATE)
{
if(HasInstDict(self) && (dict=INSTANCE_DICT(self)))
if (self->state==cPersistent_UPTODATE_STATE && self->jar &&
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);
return Py_None;
err:
Py_XDECREF(init);
return NULL;
}
static int
Per_setstate(self)
cPersistentObject *self;
Per_setstate(cPersistentObject *self)
{
self->atime=(time_t)1; /* Mark this object as sticky */
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;
self->atime=time(NULL); /* Unmark as sticky */
return -1;
}
Py_DECREF(r);
}
UPDATE_STATE_IF_NECESSARY(self, -1);
self->state=cPersistent_STICKY_STATE;
return 0;
}
......@@ -406,25 +250,13 @@ Per__getstate__(self,args)
{
PyObject *__dict__, *d=0;
UNLESS(PyArg_Parse(args, "")) return NULL;
UNLESS(PyArg_ParseTuple(args, "")) return NULL;
#ifdef DEBUG_LOG
if(idebug_log < 0) call_debug("get",self);
#endif
/* Update state, if necessary */
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);
}
UPDATE_STATE_IF_NECESSARY(self, NULL);
if(HasInstDict(self) && (__dict__=INSTANCE_DICT(self)))
{
......@@ -465,16 +297,13 @@ Per__setstate__(self,args)
PyObject *__dict__, *v, *keys=0, *key=0, *e=0;
int l, i;
/*printf("%s(%d) ", self->ob_type->tp_name,self->oid);*/
if(HasInstDict(self))
{
UNLESS(PyArg_Parse(args, "O", &v)) return NULL;
UNLESS(PyArg_ParseTuple(args, "O", &v)) return NULL;
#ifdef DEBUG_LOG
if(idebug_log < 0) call_debug("set",self);
#endif
self->state=UPTODATE_STATE;
if(v!=Py_None)
{
__dict__=INSTANCE_DICT(self);
......@@ -483,11 +312,8 @@ Per__setstate__(self,args)
{
for(i=0; PyDict_Next(v,&i,&key,&e);)
if(PyObject_SetItem(__dict__,key,e) < 0)
{
self->state=GHOST_STATE;
return NULL;
}
}
else
{
UNLESS(keys=callmethod(v,py_keys)) goto err;
......@@ -509,7 +335,6 @@ Per__setstate__(self,args)
Py_INCREF(Py_None);
return Py_None;
err:
self->state=GHOST_STATE;
Py_XDECREF(key);
Py_XDECREF(e);
Py_XDECREF(keys);
......@@ -518,22 +343,13 @@ err:
static struct PyMethodDef Per_methods[] = {
{"__changed__", (PyCFunction)T___changed__, 0,
Per___changed____doc__},
{"__save__", (PyCFunction)Per___save__, 1,
Per___save____doc__},
{"__inform_commit__", (PyCFunction)Per___inform_commit__, 1,
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,
{"__changed__", (PyCFunction)Per___changed__, METH_VARARGS,
"DEPRECATED: use self._p_changed=1"},
{"_p_deactivate", (PyCFunction)Per__p_deactivate, METH_VARARGS,
"_p_deactivate(oid) -- Deactivate the object"},
{"__getstate__", (PyCFunction)Per__getstate__, METH_VARARGS,
"__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" },
{NULL, NULL} /* sentinel */
......@@ -549,28 +365,9 @@ Per_dealloc(self)
if(idebug_log < 0) call_debug("del",self);
#endif
Py_XDECREF(self->jar);
/*Py_XDECREF(self->atime);*/
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 *
Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
PyObject *(*getattrf)(PyObject *, PyObject*))
......@@ -584,17 +381,7 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
{
case 'o':
if(*n++=='i' && *n++=='d' && ! *n)
{
if(self->oid)
{
return PyInt_FromLong(self->oid);
}
else
{
Py_INCREF(Py_None);
return Py_None;
}
}
return PyString_FromStringAndSize(self->oid, 8);
break;
case 'j':
if(*n++=='a' && *n++=='r' && ! *n)
......@@ -614,34 +401,15 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
case 'c':
if(strcmp(n,"hanged")==0)
{
if(self->state == GHOST_STATE)
if(self->state < 0)
{
Py_INCREF(Py_None);
return Py_None;
}
return PyInt_FromLong(self->state == CHANGED_STATE);
}
break;
case 'a':
if(strcmp(n,"time")==0)
{
if(self->state != UPTODATE_STATE) Per_set_atime(self);
return Per_atime(self);
}
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;
return PyInt_FromLong(self->state ==
cPersistent_CHANGED_STATE);
}
break;
case 's':
if(strcmp(n,"tate")==0)
return PyInt_FromLong(self->state);
break;
}
return getattrf((PyObject *)self, oname);
......@@ -650,21 +418,9 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
(strcmp(name,"dict__")==0 || strcmp(name,"class__")==0
|| strcmp(name, "of__")==0)))
{
/* Update state, if necessary */
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);
}
UPDATE_STATE_IF_NECESSARY(self, NULL);
Per_set_atime(self);
self->atime=((long)(time(NULL)/3))%65536;
}
return getattrf((PyObject *)self, oname);
......@@ -680,14 +436,11 @@ Per_getattro(cPersistentObject *self, PyObject *name)
}
static int
changed(PyObject *self)
bad_delattr()
{
PyObject *c;
UNLESS(c=PyObject_GetAttr(self,py___changed__)) return -1;
UNLESS_ASSIGN(c,PyObject_CallObject(c,py_onearg)) return -1;
Py_DECREF(c);
return 0;
PyErr_SetString(PyExc_AttributeError,
"delete undeletable attribute");
return -1;
}
static int
......@@ -703,8 +456,15 @@ _setattro(cPersistentObject *self, PyObject *oname, PyObject *v,
{
if(name[3]=='o' && name[4]=='i' && name[5]=='d' && ! name[6])
{
if(v && PyInt_Check(v)) self->oid=PyInt_AsLong(v);
else self->oid=0;
if (! v) return bad_delattr();
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;
}
if(name[3]=='j' && name[4]=='a' && name[5]=='r' && ! name[6])
......@@ -715,40 +475,29 @@ _setattro(cPersistentObject *self, PyObject *oname, PyObject *v,
}
if(strcmp(name+3,"changed")==0)
{
if(v==Py_None) self->state=GHOST_STATE;
else self->state= (v && PyObject_IsTrue(v));
if (! v) return bad_delattr();
if (v==Py_None)
{
if (Per__p_deactivate(self, NULL)) Py_DECREF(Py_None);
return 0;
}
if(strcmp(name+3,"atime")==0)
{
self->atime=(time_t)1;
if (PyObject_IsTrue(v)) return changed(self);
if (self->state >= 0) self->state=cPersistent_UPTODATE_STATE;
return 0;
}
}
else
{
PyObject *r;
/* Update state, if necessary */
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);
}
UPDATE_STATE_IF_NECESSARY(self, -1);
/* Record access times */
Per_set_atime(self);
self->atime=((long)(time(NULL)/3))%65536;
if(! (*name=='_' && name[1]=='v' && name[2]=='_')
&& self->state != CHANGED_STATE && self->jar)
if(changed((PyObject*)self) < 0) return -1;
&& self->state != cPersistent_CHANGED_STATE && self->jar)
if(changed(self) < 0) return -1;
}
return setattrf((PyObject*)self,oname,v);
......@@ -760,28 +509,6 @@ Per_setattro(cPersistentObject *self, PyObject *oname, PyObject *v)
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 = {
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
......@@ -804,8 +531,7 @@ static PyExtensionClass Pertype = {
(getattrofunc)Per_getattro, /*tp_getattr with object key*/
(setattrofunc)Per_setattro, /*tp_setattr with object key*/
/* Space for future expansion */
0L,0L,
Pertype__doc__, /* Documentation string */
0L,0L,"",
METHOD_CHAIN(Per_methods),
EXTENSIONCLASS_BASICNEW_FLAG,
};
......@@ -830,7 +556,7 @@ set_debug_log(PyObject *ignored, PyObject *args)
static struct PyMethodDef cP_methods[] = {
#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"
"\n"
"The function will be called with an event name and a persistent object.\n"
......@@ -859,9 +585,7 @@ void
initcPersistence()
{
PyObject *m, *d;
char *rev="$Revision: 1.24 $";
PATimeType.ob_type=&PyType_Type;
char *rev="$Revision: 1.25 $";
m = Py_InitModule4("cPersistence", cP_methods,
"",
......@@ -873,110 +597,11 @@ initcPersistence()
PyDict_SetItemString(d,"__version__",
PyString_FromStringAndSize(rev+11,strlen(rev+11)-2));
PyExtensionClass_Export(d,"Persistent",Pertype);
PyDict_SetItemString(d,"atimeType",(PyObject*)&PATimeType);
cPersistenceCAPI=&truecPersistenceCAPI;
PyDict_SetItemString(d, "CAPI",
PyCObject_FromVoidPtr(cPersistenceCAPI,NULL));
#include "dcprotect.h"
if (PyErr_Occurred())
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.
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 @@
#define cPersistent_HEAD PyObject_HEAD \
PyObject *jar; \
int oid; \
time_t atime; \
char oid[8]; \
unsigned short atime; \
signed char state; \
#define cPersistent_GHOST_STATE -1
#define cPersistent_UPTODATE_STATE 0
#define cPersistent_CHANGED_STATE 1
#define cPersistent_STICKY_STATE 2
typedef struct {
cPersistent_HEAD
} cPersistentObject;
typedef struct {
PyObject_HEAD
cPersistentObject *object;
} PATimeobject;
typedef int (*persetattr)(PyObject *, PyObject*, PyObject *, setattrofunc);
typedef PyObject *(*pergetattr)(PyObject *, PyObject*, char *, getattrofunc);
......@@ -121,7 +35,7 @@ typedef struct {
PyMethodChain *methods;
getattrofunc getattro;
setattrofunc setattro;
int (*changed)(PyObject*);
int (*changed)(cPersistentObject*);
int (*setstate)(PyObject*);
pergetattr pergetattro;
persetattr persetattro;
......@@ -129,16 +43,20 @@ typedef struct {
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) \
if(cPersistenceCAPI->setstate((PyObject*)(O)) < 0) return (R)
#define PER_USE(O) (cPersistenceCAPI->setstate((PyObject*)(O)))
#define PER_CHANGED(O) (cPersistenceCAPI->changed((cPersistentObject*)(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)
#endif
......
/*
$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 $";
static char *what_string = "$Id: cPickleCache.c,v 1.16 1998/11/11 02:00:56 jim Exp $";
#define ASSIGN(V,E) {PyObject *__e; __e=(E); Py_XDECREF(V); (V)=__e;}
#define UNLESS(E) if(!(E))
#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)
#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
#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 */
......@@ -65,61 +50,46 @@ typedef struct {
staticforward PyTypeObject Cctype;
static PyObject *PATimeType=NULL;
/* ---------------------------------------------------------------- */
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++;
if(v->ob_type==(PyTypeObject*)PATimeType)
{
if(((PATimeobject*)v)->object->ob_refcnt <= 1)
if(v->ob_refcnt <= 1)
{
self->sum_deal++;
UNLESS(-1 != PyDict_DelItem(self->data, key)) return -1;
return PyDict_DelItem(self->data, key);
}
else
{
t=((PATimeobject*)v)->object->atime;
if(t != (time_t)1)
if (dt && v->ob_type->tp_basicsize >= sizeof(cPersistentObject) &&
((cPersistentObject*)v)->state==cPersistent_UPTODATE_STATE)
{
now -= ((cPersistentObject*)v)->atime;
if (now < 0) now += 65536;
self->na++;
t=now-t;
self->sum_age += t;
if((! dt || t > dt))
self->sum_age += now;
if (now > dt)
{
/* 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);
return 0;
}
PyErr_Clear();
}
}
}
}
else if(v->ob_refcnt <= 1)
{
self->sum_deal++;
UNLESS(-1 != PyDict_DelItem(self->data, key)) return -1;
}
}
return 0;
}
......@@ -135,7 +105,7 @@ update_stats(ccobject *self, time_t now)
self->dfa *= WEIGHTING_PERIOD/(WEIGHTING_PERIOD+d);
self->mean_age=((self->mean_age*self->dfa+self->sum_age)/
(self->dfa+self->na));
(self->dfa+self->na))*3;
self->sum_age=0;
deac=self->sum_deac/d;
......@@ -163,16 +133,22 @@ static int
fullgc(ccobject *self, int idt)
{
PyObject *key, *v;
int i;
time_t now, dt;
int i, dt;
long now;
if(self->cache_size < 1) return 0;
if (self->cache_size < 1) return 0;
if ((i=PyDict_Size(self->data)) < 1) return;
now=((long)(time(NULL)/3))%65536;
if (idt) dt=idt*3;
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;
now=time(NULL);
if(idt) dt=idt;
}
for(i=0; PyDict_Next(self->data, &i, &key, &v); )
if(gc_item(self,key,v,now,dt) < 0) return -1;
......@@ -183,40 +159,6 @@ fullgc(ccobject *self, int idt)
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
reallyfullgc(ccobject *self, int dt)
{
......@@ -249,31 +191,33 @@ reallyfullgc(ccobject *self, int dt)
static int
maybegc(ccobject *self, PyObject *thisv)
{
int n, s, size;
time_t now,dt;
int n, s, size, dt;
long now;
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;
self->cache_size=0;
n=(s-size)/10;
/*n=s/size;*/
if(n < 3) n=3;
dt=(long)(self->cache_age*(0.2+0.8*size/s));
if(dt < 10) dt=10;
if (n < 3) n=3;
s=8*size/s;
if (s > 100) s=100;
dt=(long)(self->cache_age*(0.2+0.1*s));
if (dt < 10) dt=10;
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;
return -1;
......@@ -284,7 +228,7 @@ maybegc(ccobject *self, PyObject *thisv)
}
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;
}
......@@ -309,41 +253,6 @@ cc_reallyfull_sweep(ccobject *self, PyObject *args)
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 *
cc_incrgc(ccobject *self, PyObject *args)
{
......@@ -353,31 +262,24 @@ cc_incrgc(ccobject *self, PyObject *args)
}
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"
"Make a single pass through the cache, removing any objects that are no\n"
"longer referenced, and deactivating objects that have not been\n"
"accessed in the number of seconds given by 'age'. "
"'age defaults to the cache age.\n"
},
{"report", (PyCFunction)cc_report, 1, ""},
{"minimize", (PyCFunction)cc_reallyfull_sweep, 1,
{"minimize", (PyCFunction)cc_reallyfull_sweep, METH_VARARGS,
"minimize([age]) -- Remove as many objects as possible\n\n"
"Make multiple passes through the cache, removing any objects that are no\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"
},
{"items", (PyCFunction)ccitems, 1,
"items() -- Return the cache items."
},
{"incrgc", (PyCFunction)cc_incrgc, 1,
{"incrgc", (PyCFunction)cc_incrgc, METH_VARARGS,
"incrgc() -- Perform incremental garbage collection"},
{NULL, NULL} /* sentinel */
};
/* ---------- */
static ccobject *
newccobject(int cache_size, int cache_age)
{
......@@ -442,7 +344,11 @@ cc_getattr(ccobject *self, char *name)
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);
if(r=Py_FindMethod(cc_methods, (PyObject *)self, name))
......@@ -476,22 +382,6 @@ cc_setattr(ccobject *self, char *name, PyObject *value)
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
cc_length(ccobject *self)
{
......@@ -503,58 +393,22 @@ cc_subscript(ccobject *self, PyObject *key)
{
PyObject *r;
UNLESS(r=PyObject_GetItem(self->data, key))
UNLESS (r=PyDict_GetItem(self->data, key))
{
PyErr_SetObject(PyExc_KeyError, key);
return NULL;
}
UNLESS(-1 != maybegc(self,r))
{
Py_DECREF(r);
return NULL;
}
if(r->ob_type==(PyTypeObject *)PATimeType)
{
Py_DECREF(r);
r=(PyObject*)(((PATimeobject*)r)->object);
if (maybegc(self,r) < 0) return NULL;
Py_INCREF(r);
}
return r;
}
static int
cc_ass_sub(ccobject *self, PyObject *key, PyObject *v)
{
if(v)
{
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);
}
if(v) return PyDict_SetItem(self->data, key, v);
return PyDict_DelItem(self->data, key);
}
static PyMappingMethods cc_as_mapping = {
......@@ -563,12 +417,6 @@ static PyMappingMethods cc_as_mapping = {
(objobjargproc)cc_ass_sub, /*mp_ass_subscript*/
};
/* -------------------------------------------------------- */
static char Cctype__doc__[] =
""
;
static PyTypeObject Cctype = {
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
......@@ -581,22 +429,19 @@ static PyTypeObject Cctype = {
(getattrfunc)cc_getattr, /*tp_getattr*/
(setattrfunc)cc_setattr, /*tp_setattr*/
(cmpfunc)0, /*tp_compare*/
(reprfunc)cc_repr, /*tp_repr*/
(reprfunc)0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
&cc_as_mapping, /*tp_as_mapping*/
(hashfunc)0, /*tp_hash*/
(ternaryfunc)0, /*tp_call*/
(reprfunc)cc_str, /*tp_str*/
(reprfunc)0, /*tp_str*/
/* Space for future expansion */
0L,0L,0L,0L,
Cctype__doc__ /* Documentation string */
""
};
/* End of code for cCache objects */
/* -------------------------------------------------------- */
static PyObject *
cCM_new(PyObject *self, PyObject *args)
{
......@@ -605,119 +450,30 @@ cCM_new(PyObject *self, PyObject *args)
return (PyObject*)newccobject(cache_size,cache_age);
}
/* List of methods defined in the module */
static struct PyMethodDef cCM_methods[] = {
{"PickleCache",(PyCFunction)cCM_new, 1,
"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"
},
{"PickleCache",(PyCFunction)cCM_new, METH_VARARGS, ""},
{NULL, NULL} /* sentinel */
};
/* Initialization function for the module (*must* be called initcCache) */
static char cCache_module_documentation[] =
""
;
void
initcPickleCache()
{
PyObject *m, *d;
char *rev="$Revision: 1.15 $";
char *rev="$Revision: 1.16 $";
Cctype.ob_type=&PyType_Type;
if(PATimeType=PyImport_ImportModule("cPersistence"))
ASSIGN(PATimeType,PyObject_GetAttrString(PATimeType,"atimeType"));
UNLESS(PATimeType) PyErr_Clear();
m = Py_InitModule4("cPickleCache", cCM_methods,
cCache_module_documentation,
m = Py_InitModule4("cPickleCache", cCM_methods, "",
(PyObject*)NULL,PYTHON_API_VERSION);
d = PyModule_GetDict(m);
py_reload=PyString_FromString("reload");
py__p_jar=PyString_FromString("_p_jar");
py__p_atime=PyString_FromString("_p_atime");
py__p_deactivate=PyString_FromString("_p_deactivate");
PyDict_SetItemString(d,"__version__",
PyString_FromStringAndSize(rev+11,strlen(rev+11)-2));
#include "dcprotect.h"
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 @@
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
......@@ -22,7 +22,7 @@ static char BTree_module_documentation[] =
#include "ExtensionClass.h"
#define PER_USE_OR_RETURN(self, NULL)
#define PER_ALLOW_DEACTIVATION(self)
#define PER_PREVENT_DEACTIVATION(self)
#define PER_DEL(self)
#endif
......@@ -170,7 +170,7 @@ BTreeItems_item_BTree(char kind, int i, BTree *btree)
if(Bucket_Check(d->value))
{
PER_USE_OR_RETURN(d->value, NULL);
PER_USE_OR_RETURN((Bucket*)(d->value), NULL);
switch(kind)
{
case 'k':
......@@ -867,7 +867,7 @@ BTree_grow(BTree *self, int index)
v=d->value;
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))
{
......@@ -1273,8 +1273,6 @@ bucket_setstate(Bucket *self, PyObject *args)
char *cv;
#endif
PER_PREVENT_DEACTIVATION(self);
UNLESS(PyArg_ParseTuple(args,"O",&r)) goto err;
UNLESS(PyArg_ParseTuple(r,"OO",&keys,&values)) goto err;
......@@ -1452,8 +1450,6 @@ BTree_setstate(BTree *self, PyObject *args)
UNLESS(PyArg_ParseTuple(args,"O",&state)) return NULL;
if((l=PyTuple_Size(state))<0) return NULL;
PER_PREVENT_DEACTIVATION(self);
if(l>self->size)
{
if(self->data)
......@@ -1610,6 +1606,7 @@ Bucket_dealloc(Bucket *self)
DECREF_VALUE(self->data[i].value);
}
free(self->data);
PER_DEL(self);
PyMem_DEL(self);
}
......@@ -1625,6 +1622,8 @@ BTree_dealloc(BTree *self)
Py_DECREF(self->data[i].value);
}
free(self->data);
PER_DEL(self);
PyMem_DEL(self);
}
......@@ -1762,7 +1761,7 @@ initBTree()
#endif
{
PyObject *m, *d;
char *rev="$Revision: 1.16 $";
char *rev="$Revision: 1.17 $";
UNLESS(PyExtensionClassCAPI=PyCObject_Import("ExtensionClass","CAPI"))
return;
......@@ -1799,73 +1798,7 @@ initBTree()
PyDict_SetItemString(d, "__version__",
PyString_FromStringAndSize(rev+11,strlen(rev+11)-2));
#include "dcprotect.h"
/* Check for errors */
if (PyErr_Occurred())
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,15 +45,13 @@
# (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 $'''
__version__='$Revision: 1.16 $'[11:-2]
$Id: Persistence.py,v 1.17 1998/11/11 02:00:56 jim Exp $'''
__version__='$Revision: 1.17 $'[11:-2]
try:
from cPersistence import Persistent
except:
class Persistent:
_marker=[]
class Persistent:
"""\
Persistent object support mix-in class
......@@ -77,38 +75,15 @@ except:
_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___init__(self,oid,jar):
"""Post creation initialization
This is *only* used if we have __getinitargs__!
"""
d=self.__dict__
if d:
newstate={}
for key in d.keys():
if key[:3] != '_p_':
newstate[key]=d[key]
del d[key]
if newstate: d['_p_newstate']=newstate
d['_p_oid']=oid
d['_p_jar']=jar
d['_p_changed']=None
def _p_deactivate(self,copy=None):
if copy is None: newstate=None
else: newstate=copy.__dict__
def _p_deactivate(self):
d=self.__dict__
oid=self._p_oid
jar=self._p_jar
oid=d['_p_oid']
jar=d['_p_jar']
d.clear()
if newstate: d['_p_newstate']=newstate
d['_p_oid']=oid
d['_p_jar']=jar
d['_p_changed']=None
_p___reinit=_p_deactivate # Back. Comp.
def __getattr__(self,key):
'Get an item'
if self._p_changed is None and key[:3] != '_p_':
......@@ -126,6 +101,19 @@ except:
k=key[:3]
if k=='_p_' or k=='_v_':
if key=='_p_changed':
if changed == value: return
if value:
if changed: return
try:
if self._p_jar and self._p_oid:
get_transaction().register(self)
except: pass
elif value is None:
if self._p_jar and self._p_oid:
return self._p_deactivate()
return
value=not not value
self.__dict__[key]=value
return
......@@ -145,16 +133,10 @@ except:
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
def __changed__(self,v=_marker):
if v is _marker: return not not self._p_changed
self._p_changed = not not v
return old
def __getstate__(self):
# First, update my state, if necessary:
......@@ -167,27 +149,10 @@ except:
return state
def __setstate__(self,state):
d=self.__dict__
for k,v in state.items(): d[k]=v
return state
def __save__(self):
'''\
Update the object in a persistent database.
'''
jar=self._p_jar
if jar and self._p_changed: jar.store(self)
self.__dict__.update(state)
def __repr__(self):
' '
return '<%s instance at %s>' % (self.__class__.__name__,
hex(id(self)))
def __inform_commit__(self,T,start_time):
jar=self._p_jar
if jar and self._p_changed: jar.store(self,T)
def __inform_abort__(self,T,start_time):
try: self._p_jar.abort(self,start_time)
except: pass
......@@ -48,12 +48,12 @@
__doc__='''Python implementation of persistent base types
$Id: PersistentMapping.py,v 1.2 1998/10/23 21:40:59 jim Exp $'''
__version__='$Revision: 1.2 $'[11:-2]
$Id: PersistentMapping.py,v 1.3 1998/11/11 02:00:56 jim Exp $'''
__version__='$Revision: 1.3 $'[11:-2]
import Persistence
class PersistentMapping(Persistence.Persistent):
class PM(Persistence.Persistent):
"""A persistent wrapper for mapping objects.
This class allows wrapping of mapping objects so that
......@@ -65,22 +65,36 @@ class PersistentMapping(Persistence.Persistent):
if container is None: 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):
return self._container[key]
def __len__(self): return len(self._container)
def __setitem__(self, key, v):
self._container[key]=v
try: del self._v_keys
except: pass
self.__changed__(1)
def __delitem__(self, key):
del self._container[key]
try: del self._v_keys
except: pass
self.__changed__(1)
def clear(self):
self._container.clear()
self._p_changed=1
if hasattr(self,'_v_keys'): del self._v_keys
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):
try: return self._v_keys
......@@ -91,14 +105,12 @@ class PersistentMapping(Persistence.Persistent):
keys.sort()
return keys
def clear(self):
self._container={}
if hasattr(self,'_v_keys'): del self._v_keys
def update(self, b):
a=self._container
for k, v in b.items(): a[k] = v
self._p_changed=1
def values(self):
return map(lambda k, d=self: d[k], self.keys())
def items(self):
return map(lambda k, d=self: (k,d[k]), self.keys())
def has_key(self,key): return self._container.has_key(key)
PersistentMapping=PM
......@@ -47,8 +47,8 @@
##############################################################################
__doc__='''PickleJar Object Cache
$Id: PickleCache.py,v 1.4 1998/10/23 21:41:24 jim Exp $'''
__version__='$Revision: 1.4 $'[11:-2]
$Id: PickleCache.py,v 1.5 1998/11/11 02:00:56 jim Exp $'''
__version__='$Revision: 1.5 $'[11:-2]
from sys import getrefcount
......@@ -61,12 +61,14 @@ class PickleCache:
for a in 'keys', 'items', 'values', 'has_key':
setattr(self,a,getattr(self.data,a))
def __getitem__(self, key):
cache=self.data
v=cache[key]
v=self.data[key]
self.incrgc()
return v
def incrgc(self):
# Do cache GC
cache=self.data
n=min(len(cache)/self.cache_size,10)
if n:
l=self.cache_location
......@@ -82,11 +84,13 @@ class PickleCache:
del cache[id]
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)
......
*shared*
cPersistence cPersistence.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
# Copyright (c) 1996-1998, Digital Creations, Fredericksburg, VA, USA.
# All rights reserved.
#
# Digital Creations & DCLC, are trademarks of Digital Creations, L.C..
# All other trademarks are owned by their respective companies.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# No Warranty
# o Redistributions of source code must retain the above copyright
# notice, this list of conditions, and the disclaimer that follows.
#
# 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.
# 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.
#
# Limitation Of Liability
# 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.
#
# 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.
#
# 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:
#
# Jim Fulton, jim@digicool.com
#
# (540) 371-6909
# If you have questions regarding this software, contact:
#
# $Log: Transaction.py,v $
# Revision 1.2 1997/10/30 20:23:26 brian
# Fixed thread dependency
#
# Revision 1.1 1997/04/11 21:42:52 jim
# *** empty log message ***
#
# Revision 1.5 1997/03/25 20:42:58 jim
# Changed to make all persistent objects transactional.
#
# Revision 1.4 1997/02/11 13:23:14 jim
# Many changes to support both cPickle and LRT.
#
# Revision 1.3 1996/11/14 17:49:38 jim
# Removed c-implementation support.
#
# Revision 1.2 1996/10/15 18:32:25 jim
# Added support for cSingleThreadedTransaction
#
# Revision 1.1 1996/09/06 14:35:54 jfulton
# For use with Chris doing installation
# Digital Creations, L.C.
# 910 Princess Ann Street
# Fredericksburge, Virginia 22401
#
# info@digicool.com
#
# (540) 371-6909
#
__version__='$Revision: 1.2 $'[11:-2]
##############################################################################
"""Transaction management
$Id: Transaction.py,v 1.3 1998/11/11 02:00:56 jim Exp $"""
__version__='$Revision: 1.3 $'[11:-2]
import time, sys, struct
from struct import pack
from string import split, strip, join
ConflictError=""
class Transaction:
'Simple transaction objects for single-threaded applications.'
user=''
description=''
_connections=None
def __init__(self,
time=time.time, pack=struct.pack, gmtime=time.gmtime):
self._objects=[]
self._append=self._objects.append
self.time=now=time()
y,mo,d,h,m=gmtime(now)[:5]
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
# get_transaction to override their's
from SingleThreadedTransaction import *
def free_transaction(_id=thread.get_ident, _t=_t):
id=_id()
try: del _t[id]
except KeyError: pass
import SingleThreadedTransaction
del thread
try:
import thread
get_id=thread.get_ident
except:
def get_id(): return 0
theTransactions={}
_t=Transaction()
def get_transaction(_t=_t): return _t
def free_transaction(_t=_t): _t.__init__()
def get_transaction():
id=get_id()
try: theTransaction=theTransactions[id]
except KeyError: theTransactions[id]=theTransaction=Transaction()
return theTransaction
del _t
import __main__
__main__.__builtins__.get_transaction=get_transaction
class Transaction(SingleThreadedTransaction.Transaction):
'''\
Simple transaction objects.
'''
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]
def time2id(now, gmtime=time.gmtime, pack=struct.pack):
y,m,d,h,m=gmtime(now)[:5]
s=int((now%60)*1000000)
return pack("<II", ((y*12+m)*31+d)*24, s)
##############################################################################
#
# 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
......@@ -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 "cPersistence.h"
......@@ -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_ASSIGN(V,E) ASSIGN(V,E) UNLESS(V)
static PyObject *py_store, *py_oops, *py_keys, *py_setstate, *py___changed__,
*py___dict__, *py_mtime, *py_onearg, *py___getinitargs__, *py___init__;
static PyObject *py_keys, *py_setstate, *py___dict__;
#ifdef DEBUG_LOG
static PyObject *debug_log=0;
......@@ -36,8 +35,9 @@ call_debug(char *event, cPersistentObject *self)
/*
printf("%s %p\n",event,self->ob_type->tp_name);
*/
r=PyObject_CallFunction(debug_log,"s(sii)",event,
self->ob_type->tp_name, self->oid, self->state);
r=PyObject_CallFunction(debug_log,"s(ss#i)",event,
self->ob_type->tp_name, self->oid, 8,
self->state);
Py_XDECREF(r);
}
#endif
......@@ -46,16 +46,9 @@ static void
init_strings()
{
#define INIT_STRING(S) py_ ## S = PyString_FromString(#S)
INIT_STRING(store);
INIT_STRING(oops);
INIT_STRING(keys);
INIT_STRING(setstate);
INIT_STRING(mtime);
INIT_STRING(__changed__);
INIT_STRING(__init__);
INIT_STRING(__getinitargs__);
INIT_STRING(__dict__);
py_onearg=Py_BuildValue("(i)",1);
#undef INIT_STRING
}
......@@ -113,6 +106,22 @@ callmethod3(PyObject *self, PyObject *name,
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 *
#ifdef HAVE_STDARG_PROTOTYPES
/* VARARGS 2 */
......@@ -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 TPertype;
static char Per___changed____doc__[] =
"__changed__([flag]) -- Flag or determine whether an object has changed\n"
" \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 int
changed(cPersistentObject *self)
{
static PyObject *builtins=0, *get_transaction=0, *py_register=0;
PyObject *o, *T;
if(PyArg_Parse(args, "O", &o))
{
int t;
t=PyObject_IsTrue(o);
PyObject *T;
if(t && self->state != cPersistent_CHANGED_STATE && self->jar)
{
UNLESS(get_transaction)
if ((self->state == cPersistent_UPTODATE_STATE ||
self->state == cPersistent_STICKY_STATE)
&& self->jar)
{
UNLESS(builtins)
UNLESS (get_transaction)
{
UNLESS(T=PyImport_ImportModule("__main__")) return NULL;
UNLESS (py_register=PyString_FromString("register")) return -1;
UNLESS (T=PyImport_ImportModule("__main__")) return -1;
ASSIGN(T,PyObject_GetAttrString(T,"__builtins__"));
UNLESS(T) return NULL;
UNLESS(py_register=PyString_FromString("register")) goto err;
UNLESS (T) return -1;
builtins=T;
}
UNLESS(get_transaction=PyObject_GetAttrString(builtins,
UNLESS (get_transaction=PyObject_GetAttrString(builtins,
"get_transaction"))
PyErr_Clear();
}
if(get_transaction)
if (get_transaction)
{
UNLESS(T=PyObject_CallObject(get_transaction,NULL)) return NULL;
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);
}
UNLESS (T=PyObject_CallObject(get_transaction,NULL)) return -1;
ASSIGN(T,PyObject_GetAttr(T,py_register));
UNLESS (T) return -1;
ASSIGN(T, PyObject_CallFunction(T,"O",self));
if (T) Py_DECREF(T);
else return -1;
}
if(self->state != cPersistent_GHOST_STATE) self->state=t;
Py_INCREF(Py_None);
return Py_None;
}
else
{
PyErr_Clear();
UNLESS(PyArg_Parse(args, "")) return NULL;
return PyInt_FromLong(self->state==cPersistent_CHANGED_STATE);
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__[] =
"__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;
return 0;
}
static char Per___inform_abort____doc__[] =
"__inform_abort__(transaction,start_time) -- Abort object changes"
;
static PyObject *
Per___inform_abort__(self, args)
cPersistentObject *self;
PyObject *args;
Per___changed__(cPersistentObject *self, PyObject *args)
{
PyObject *T, *t;
UNLESS(PyArg_ParseTuple(args, "OO", &T, &t)) return NULL;
if(self->oid && self->jar && self->state != GHOST_STATE)
PyObject *v=0;
if (args && ! PyArg_ParseTuple(args, "|O",&v)) return NULL;
if (v && ! PyObject_IsTrue(v))
{
args=callmethod3(self->jar,py_oops,(PyObject*)self,t,T);
if(args)
Py_DECREF(args);
else
PyErr_Clear();
PyErr_SetString(PyExc_TypeError,
"Only true arguments are allowed.");
return NULL;
}
if (changed(self) < 0) return NULL;
Py_INCREF(Py_None);
return Py_None;
}
static char Per__p___init____doc__[] =
"_p___init__(oid,jar) -- Initialize persistence management data"
;
static PyObject *
Per__p___init__(self, args)
cPersistentObject *self;
PyObject *args;
Per__p_deactivate(cPersistentObject *self, PyObject *args)
{
int oid;
PyObject *jar;
PyObject *init=0, *copy, *dict;
UNLESS(PyArg_Parse(args, "(iO)", &oid, &jar)) return NULL;
#ifdef DEBUG_LOG
if(idebug_log < 0) call_debug("init",self);
if (idebug_log < 0) call_debug("reinit",self);
#endif
Py_INCREF(jar);
self->oid=oid;
ASSIGN(self->jar, jar);
self->state=GHOST_STATE;
Py_INCREF(Py_None);
return Py_None;
}
static PyObject *
Per__p___reinit__(cPersistentObject *self, PyObject *args)
{
PyObject *init=0, *copy, *dict;
if (args && ! PyArg_ParseTuple(args,"")) return NULL;
#ifdef DEBUG_LOG
if(idebug_log < 0) call_debug("reinit",self);
#endif
if(PyArg_Parse(args,""))
{
if(self->state==cPersistent_UPTODATE_STATE)
{
if(HasInstDict(self) && (dict=INSTANCE_DICT(self)))
if (self->state==cPersistent_UPTODATE_STATE && self->jar &&
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);
return Py_None;
err:
Py_XDECREF(init);
return NULL;
}
static int
Per_setstate(self)
cPersistentObject *self;
Per_setstate(cPersistentObject *self)
{
self->atime=(time_t)1; /* Mark this object as sticky */
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;
self->atime=time(NULL); /* Unmark as sticky */
return -1;
}
Py_DECREF(r);
}
UPDATE_STATE_IF_NECESSARY(self, -1);
self->state=cPersistent_STICKY_STATE;
return 0;
}
......@@ -406,25 +250,13 @@ Per__getstate__(self,args)
{
PyObject *__dict__, *d=0;
UNLESS(PyArg_Parse(args, "")) return NULL;
UNLESS(PyArg_ParseTuple(args, "")) return NULL;
#ifdef DEBUG_LOG
if(idebug_log < 0) call_debug("get",self);
#endif
/* Update state, if necessary */
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);
}
UPDATE_STATE_IF_NECESSARY(self, NULL);
if(HasInstDict(self) && (__dict__=INSTANCE_DICT(self)))
{
......@@ -465,16 +297,13 @@ Per__setstate__(self,args)
PyObject *__dict__, *v, *keys=0, *key=0, *e=0;
int l, i;
/*printf("%s(%d) ", self->ob_type->tp_name,self->oid);*/
if(HasInstDict(self))
{
UNLESS(PyArg_Parse(args, "O", &v)) return NULL;
UNLESS(PyArg_ParseTuple(args, "O", &v)) return NULL;
#ifdef DEBUG_LOG
if(idebug_log < 0) call_debug("set",self);
#endif
self->state=UPTODATE_STATE;
if(v!=Py_None)
{
__dict__=INSTANCE_DICT(self);
......@@ -483,11 +312,8 @@ Per__setstate__(self,args)
{
for(i=0; PyDict_Next(v,&i,&key,&e);)
if(PyObject_SetItem(__dict__,key,e) < 0)
{
self->state=GHOST_STATE;
return NULL;
}
}
else
{
UNLESS(keys=callmethod(v,py_keys)) goto err;
......@@ -509,7 +335,6 @@ Per__setstate__(self,args)
Py_INCREF(Py_None);
return Py_None;
err:
self->state=GHOST_STATE;
Py_XDECREF(key);
Py_XDECREF(e);
Py_XDECREF(keys);
......@@ -518,22 +343,13 @@ err:
static struct PyMethodDef Per_methods[] = {
{"__changed__", (PyCFunction)T___changed__, 0,
Per___changed____doc__},
{"__save__", (PyCFunction)Per___save__, 1,
Per___save____doc__},
{"__inform_commit__", (PyCFunction)Per___inform_commit__, 1,
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,
{"__changed__", (PyCFunction)Per___changed__, METH_VARARGS,
"DEPRECATED: use self._p_changed=1"},
{"_p_deactivate", (PyCFunction)Per__p_deactivate, METH_VARARGS,
"_p_deactivate(oid) -- Deactivate the object"},
{"__getstate__", (PyCFunction)Per__getstate__, METH_VARARGS,
"__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" },
{NULL, NULL} /* sentinel */
......@@ -549,28 +365,9 @@ Per_dealloc(self)
if(idebug_log < 0) call_debug("del",self);
#endif
Py_XDECREF(self->jar);
/*Py_XDECREF(self->atime);*/
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 *
Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
PyObject *(*getattrf)(PyObject *, PyObject*))
......@@ -584,17 +381,7 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
{
case 'o':
if(*n++=='i' && *n++=='d' && ! *n)
{
if(self->oid)
{
return PyInt_FromLong(self->oid);
}
else
{
Py_INCREF(Py_None);
return Py_None;
}
}
return PyString_FromStringAndSize(self->oid, 8);
break;
case 'j':
if(*n++=='a' && *n++=='r' && ! *n)
......@@ -614,34 +401,15 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
case 'c':
if(strcmp(n,"hanged")==0)
{
if(self->state == GHOST_STATE)
if(self->state < 0)
{
Py_INCREF(Py_None);
return Py_None;
}
return PyInt_FromLong(self->state == CHANGED_STATE);
}
break;
case 'a':
if(strcmp(n,"time")==0)
{
if(self->state != UPTODATE_STATE) Per_set_atime(self);
return Per_atime(self);
}
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;
return PyInt_FromLong(self->state ==
cPersistent_CHANGED_STATE);
}
break;
case 's':
if(strcmp(n,"tate")==0)
return PyInt_FromLong(self->state);
break;
}
return getattrf((PyObject *)self, oname);
......@@ -650,21 +418,9 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
(strcmp(name,"dict__")==0 || strcmp(name,"class__")==0
|| strcmp(name, "of__")==0)))
{
/* Update state, if necessary */
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);
}
UPDATE_STATE_IF_NECESSARY(self, NULL);
Per_set_atime(self);
self->atime=((long)(time(NULL)/3))%65536;
}
return getattrf((PyObject *)self, oname);
......@@ -680,14 +436,11 @@ Per_getattro(cPersistentObject *self, PyObject *name)
}
static int
changed(PyObject *self)
bad_delattr()
{
PyObject *c;
UNLESS(c=PyObject_GetAttr(self,py___changed__)) return -1;
UNLESS_ASSIGN(c,PyObject_CallObject(c,py_onearg)) return -1;
Py_DECREF(c);
return 0;
PyErr_SetString(PyExc_AttributeError,
"delete undeletable attribute");
return -1;
}
static int
......@@ -703,8 +456,15 @@ _setattro(cPersistentObject *self, PyObject *oname, PyObject *v,
{
if(name[3]=='o' && name[4]=='i' && name[5]=='d' && ! name[6])
{
if(v && PyInt_Check(v)) self->oid=PyInt_AsLong(v);
else self->oid=0;
if (! v) return bad_delattr();
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;
}
if(name[3]=='j' && name[4]=='a' && name[5]=='r' && ! name[6])
......@@ -715,40 +475,29 @@ _setattro(cPersistentObject *self, PyObject *oname, PyObject *v,
}
if(strcmp(name+3,"changed")==0)
{
if(v==Py_None) self->state=GHOST_STATE;
else self->state= (v && PyObject_IsTrue(v));
if (! v) return bad_delattr();
if (v==Py_None)
{
if (Per__p_deactivate(self, NULL)) Py_DECREF(Py_None);
return 0;
}
if(strcmp(name+3,"atime")==0)
{
self->atime=(time_t)1;
if (PyObject_IsTrue(v)) return changed(self);
if (self->state >= 0) self->state=cPersistent_UPTODATE_STATE;
return 0;
}
}
else
{
PyObject *r;
/* Update state, if necessary */
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);
}
UPDATE_STATE_IF_NECESSARY(self, -1);
/* Record access times */
Per_set_atime(self);
self->atime=((long)(time(NULL)/3))%65536;
if(! (*name=='_' && name[1]=='v' && name[2]=='_')
&& self->state != CHANGED_STATE && self->jar)
if(changed((PyObject*)self) < 0) return -1;
&& self->state != cPersistent_CHANGED_STATE && self->jar)
if(changed(self) < 0) return -1;
}
return setattrf((PyObject*)self,oname,v);
......@@ -760,28 +509,6 @@ Per_setattro(cPersistentObject *self, PyObject *oname, PyObject *v)
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 = {
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
......@@ -804,8 +531,7 @@ static PyExtensionClass Pertype = {
(getattrofunc)Per_getattro, /*tp_getattr with object key*/
(setattrofunc)Per_setattro, /*tp_setattr with object key*/
/* Space for future expansion */
0L,0L,
Pertype__doc__, /* Documentation string */
0L,0L,"",
METHOD_CHAIN(Per_methods),
EXTENSIONCLASS_BASICNEW_FLAG,
};
......@@ -830,7 +556,7 @@ set_debug_log(PyObject *ignored, PyObject *args)
static struct PyMethodDef cP_methods[] = {
#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"
"\n"
"The function will be called with an event name and a persistent object.\n"
......@@ -859,9 +585,7 @@ void
initcPersistence()
{
PyObject *m, *d;
char *rev="$Revision: 1.24 $";
PATimeType.ob_type=&PyType_Type;
char *rev="$Revision: 1.25 $";
m = Py_InitModule4("cPersistence", cP_methods,
"",
......@@ -873,110 +597,11 @@ initcPersistence()
PyDict_SetItemString(d,"__version__",
PyString_FromStringAndSize(rev+11,strlen(rev+11)-2));
PyExtensionClass_Export(d,"Persistent",Pertype);
PyDict_SetItemString(d,"atimeType",(PyObject*)&PATimeType);
cPersistenceCAPI=&truecPersistenceCAPI;
PyDict_SetItemString(d, "CAPI",
PyCObject_FromVoidPtr(cPersistenceCAPI,NULL));
#include "dcprotect.h"
if (PyErr_Occurred())
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.
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 @@
#define cPersistent_HEAD PyObject_HEAD \
PyObject *jar; \
int oid; \
time_t atime; \
char oid[8]; \
unsigned short atime; \
signed char state; \
#define cPersistent_GHOST_STATE -1
#define cPersistent_UPTODATE_STATE 0
#define cPersistent_CHANGED_STATE 1
#define cPersistent_STICKY_STATE 2
typedef struct {
cPersistent_HEAD
} cPersistentObject;
typedef struct {
PyObject_HEAD
cPersistentObject *object;
} PATimeobject;
typedef int (*persetattr)(PyObject *, PyObject*, PyObject *, setattrofunc);
typedef PyObject *(*pergetattr)(PyObject *, PyObject*, char *, getattrofunc);
......@@ -121,7 +35,7 @@ typedef struct {
PyMethodChain *methods;
getattrofunc getattro;
setattrofunc setattro;
int (*changed)(PyObject*);
int (*changed)(cPersistentObject*);
int (*setstate)(PyObject*);
pergetattr pergetattro;
persetattr persetattro;
......@@ -129,16 +43,20 @@ typedef struct {
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) \
if(cPersistenceCAPI->setstate((PyObject*)(O)) < 0) return (R)
#define PER_USE(O) (cPersistenceCAPI->setstate((PyObject*)(O)))
#define PER_CHANGED(O) (cPersistenceCAPI->changed((cPersistentObject*)(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)
#endif
......
/*
$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 $";
static char *what_string = "$Id: cPickleCache.c,v 1.16 1998/11/11 02:00:56 jim Exp $";
#define ASSIGN(V,E) {PyObject *__e; __e=(E); Py_XDECREF(V); (V)=__e;}
#define UNLESS(E) if(!(E))
#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)
#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
#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 */
......@@ -65,61 +50,46 @@ typedef struct {
staticforward PyTypeObject Cctype;
static PyObject *PATimeType=NULL;
/* ---------------------------------------------------------------- */
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++;
if(v->ob_type==(PyTypeObject*)PATimeType)
{
if(((PATimeobject*)v)->object->ob_refcnt <= 1)
if(v->ob_refcnt <= 1)
{
self->sum_deal++;
UNLESS(-1 != PyDict_DelItem(self->data, key)) return -1;
return PyDict_DelItem(self->data, key);
}
else
{
t=((PATimeobject*)v)->object->atime;
if(t != (time_t)1)
if (dt && v->ob_type->tp_basicsize >= sizeof(cPersistentObject) &&
((cPersistentObject*)v)->state==cPersistent_UPTODATE_STATE)
{
now -= ((cPersistentObject*)v)->atime;
if (now < 0) now += 65536;
self->na++;
t=now-t;
self->sum_age += t;
if((! dt || t > dt))
self->sum_age += now;
if (now > dt)
{
/* 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);
return 0;
}
PyErr_Clear();
}
}
}
}
else if(v->ob_refcnt <= 1)
{
self->sum_deal++;
UNLESS(-1 != PyDict_DelItem(self->data, key)) return -1;
}
}
return 0;
}
......@@ -135,7 +105,7 @@ update_stats(ccobject *self, time_t now)
self->dfa *= WEIGHTING_PERIOD/(WEIGHTING_PERIOD+d);
self->mean_age=((self->mean_age*self->dfa+self->sum_age)/
(self->dfa+self->na));
(self->dfa+self->na))*3;
self->sum_age=0;
deac=self->sum_deac/d;
......@@ -163,16 +133,22 @@ static int
fullgc(ccobject *self, int idt)
{
PyObject *key, *v;
int i;
time_t now, dt;
int i, dt;
long now;
if(self->cache_size < 1) return 0;
if (self->cache_size < 1) return 0;
if ((i=PyDict_Size(self->data)) < 1) return;
now=((long)(time(NULL)/3))%65536;
if (idt) dt=idt*3;
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;
now=time(NULL);
if(idt) dt=idt;
}
for(i=0; PyDict_Next(self->data, &i, &key, &v); )
if(gc_item(self,key,v,now,dt) < 0) return -1;
......@@ -183,40 +159,6 @@ fullgc(ccobject *self, int idt)
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
reallyfullgc(ccobject *self, int dt)
{
......@@ -249,31 +191,33 @@ reallyfullgc(ccobject *self, int dt)
static int
maybegc(ccobject *self, PyObject *thisv)
{
int n, s, size;
time_t now,dt;
int n, s, size, dt;
long now;
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;
self->cache_size=0;
n=(s-size)/10;
/*n=s/size;*/
if(n < 3) n=3;
dt=(long)(self->cache_age*(0.2+0.8*size/s));
if(dt < 10) dt=10;
if (n < 3) n=3;
s=8*size/s;
if (s > 100) s=100;
dt=(long)(self->cache_age*(0.2+0.1*s));
if (dt < 10) dt=10;
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;
return -1;
......@@ -284,7 +228,7 @@ maybegc(ccobject *self, PyObject *thisv)
}
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;
}
......@@ -309,41 +253,6 @@ cc_reallyfull_sweep(ccobject *self, PyObject *args)
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 *
cc_incrgc(ccobject *self, PyObject *args)
{
......@@ -353,31 +262,24 @@ cc_incrgc(ccobject *self, PyObject *args)
}
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"
"Make a single pass through the cache, removing any objects that are no\n"
"longer referenced, and deactivating objects that have not been\n"
"accessed in the number of seconds given by 'age'. "
"'age defaults to the cache age.\n"
},
{"report", (PyCFunction)cc_report, 1, ""},
{"minimize", (PyCFunction)cc_reallyfull_sweep, 1,
{"minimize", (PyCFunction)cc_reallyfull_sweep, METH_VARARGS,
"minimize([age]) -- Remove as many objects as possible\n\n"
"Make multiple passes through the cache, removing any objects that are no\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"
},
{"items", (PyCFunction)ccitems, 1,
"items() -- Return the cache items."
},
{"incrgc", (PyCFunction)cc_incrgc, 1,
{"incrgc", (PyCFunction)cc_incrgc, METH_VARARGS,
"incrgc() -- Perform incremental garbage collection"},
{NULL, NULL} /* sentinel */
};
/* ---------- */
static ccobject *
newccobject(int cache_size, int cache_age)
{
......@@ -442,7 +344,11 @@ cc_getattr(ccobject *self, char *name)
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);
if(r=Py_FindMethod(cc_methods, (PyObject *)self, name))
......@@ -476,22 +382,6 @@ cc_setattr(ccobject *self, char *name, PyObject *value)
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
cc_length(ccobject *self)
{
......@@ -503,58 +393,22 @@ cc_subscript(ccobject *self, PyObject *key)
{
PyObject *r;
UNLESS(r=PyObject_GetItem(self->data, key))
UNLESS (r=PyDict_GetItem(self->data, key))
{
PyErr_SetObject(PyExc_KeyError, key);
return NULL;
}
UNLESS(-1 != maybegc(self,r))
{
Py_DECREF(r);
return NULL;
}
if(r->ob_type==(PyTypeObject *)PATimeType)
{
Py_DECREF(r);
r=(PyObject*)(((PATimeobject*)r)->object);
if (maybegc(self,r) < 0) return NULL;
Py_INCREF(r);
}
return r;
}
static int
cc_ass_sub(ccobject *self, PyObject *key, PyObject *v)
{
if(v)
{
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);
}
if(v) return PyDict_SetItem(self->data, key, v);
return PyDict_DelItem(self->data, key);
}
static PyMappingMethods cc_as_mapping = {
......@@ -563,12 +417,6 @@ static PyMappingMethods cc_as_mapping = {
(objobjargproc)cc_ass_sub, /*mp_ass_subscript*/
};
/* -------------------------------------------------------- */
static char Cctype__doc__[] =
""
;
static PyTypeObject Cctype = {
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
......@@ -581,22 +429,19 @@ static PyTypeObject Cctype = {
(getattrfunc)cc_getattr, /*tp_getattr*/
(setattrfunc)cc_setattr, /*tp_setattr*/
(cmpfunc)0, /*tp_compare*/
(reprfunc)cc_repr, /*tp_repr*/
(reprfunc)0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
&cc_as_mapping, /*tp_as_mapping*/
(hashfunc)0, /*tp_hash*/
(ternaryfunc)0, /*tp_call*/
(reprfunc)cc_str, /*tp_str*/
(reprfunc)0, /*tp_str*/
/* Space for future expansion */
0L,0L,0L,0L,
Cctype__doc__ /* Documentation string */
""
};
/* End of code for cCache objects */
/* -------------------------------------------------------- */
static PyObject *
cCM_new(PyObject *self, PyObject *args)
{
......@@ -605,119 +450,30 @@ cCM_new(PyObject *self, PyObject *args)
return (PyObject*)newccobject(cache_size,cache_age);
}
/* List of methods defined in the module */
static struct PyMethodDef cCM_methods[] = {
{"PickleCache",(PyCFunction)cCM_new, 1,
"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"
},
{"PickleCache",(PyCFunction)cCM_new, METH_VARARGS, ""},
{NULL, NULL} /* sentinel */
};
/* Initialization function for the module (*must* be called initcCache) */
static char cCache_module_documentation[] =
""
;
void
initcPickleCache()
{
PyObject *m, *d;
char *rev="$Revision: 1.15 $";
char *rev="$Revision: 1.16 $";
Cctype.ob_type=&PyType_Type;
if(PATimeType=PyImport_ImportModule("cPersistence"))
ASSIGN(PATimeType,PyObject_GetAttrString(PATimeType,"atimeType"));
UNLESS(PATimeType) PyErr_Clear();
m = Py_InitModule4("cPickleCache", cCM_methods,
cCache_module_documentation,
m = Py_InitModule4("cPickleCache", cCM_methods, "",
(PyObject*)NULL,PYTHON_API_VERSION);
d = PyModule_GetDict(m);
py_reload=PyString_FromString("reload");
py__p_jar=PyString_FromString("_p_jar");
py__p_atime=PyString_FromString("_p_atime");
py__p_deactivate=PyString_FromString("_p_deactivate");
PyDict_SetItemString(d,"__version__",
PyString_FromStringAndSize(rev+11,strlen(rev+11)-2));
#include "dcprotect.h"
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 @@
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>
......@@ -225,8 +225,6 @@ intSet___setstate__(intSet *self, PyObject *args)
char *c;
INTSET_DATA_TYPE k;
PER_PREVENT_DEACTIVATION(self);
UNLESS(PyArg_ParseTuple(args,"O",&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,
o=INTSET(other);
PER_USE_OR_RETURN(self, NULL);
PER_USE_OR_RETURN(other, NULL);
PER_USE_OR_RETURN((intSet*)other, NULL);
od=o->data;
......@@ -537,7 +535,7 @@ void
initintSet()
{
PyObject *m, *d;
char *rev="$Revision: 1.9 $";
char *rev="$Revision: 1.10 $";
UNLESS(ExtensionClassImported) return;
......@@ -575,6 +573,9 @@ initintSet()
Revision Log:
$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
*** 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
......@@ -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 "cPersistence.h"
......@@ -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_ASSIGN(V,E) ASSIGN(V,E) UNLESS(V)
static PyObject *py_store, *py_oops, *py_keys, *py_setstate, *py___changed__,
*py___dict__, *py_mtime, *py_onearg, *py___getinitargs__, *py___init__;
static PyObject *py_keys, *py_setstate, *py___dict__;
#ifdef DEBUG_LOG
static PyObject *debug_log=0;
......@@ -36,8 +35,9 @@ call_debug(char *event, cPersistentObject *self)
/*
printf("%s %p\n",event,self->ob_type->tp_name);
*/
r=PyObject_CallFunction(debug_log,"s(sii)",event,
self->ob_type->tp_name, self->oid, self->state);
r=PyObject_CallFunction(debug_log,"s(ss#i)",event,
self->ob_type->tp_name, self->oid, 8,
self->state);
Py_XDECREF(r);
}
#endif
......@@ -46,16 +46,9 @@ static void
init_strings()
{
#define INIT_STRING(S) py_ ## S = PyString_FromString(#S)
INIT_STRING(store);
INIT_STRING(oops);
INIT_STRING(keys);
INIT_STRING(setstate);
INIT_STRING(mtime);
INIT_STRING(__changed__);
INIT_STRING(__init__);
INIT_STRING(__getinitargs__);
INIT_STRING(__dict__);
py_onearg=Py_BuildValue("(i)",1);
#undef INIT_STRING
}
......@@ -113,6 +106,22 @@ callmethod3(PyObject *self, PyObject *name,
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 *
#ifdef HAVE_STDARG_PROTOTYPES
/* VARARGS 2 */
......@@ -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 TPertype;
static char Per___changed____doc__[] =
"__changed__([flag]) -- Flag or determine whether an object has changed\n"
" \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 int
changed(cPersistentObject *self)
{
static PyObject *builtins=0, *get_transaction=0, *py_register=0;
PyObject *o, *T;
if(PyArg_Parse(args, "O", &o))
{
int t;
t=PyObject_IsTrue(o);
PyObject *T;
if(t && self->state != cPersistent_CHANGED_STATE && self->jar)
{
UNLESS(get_transaction)
if ((self->state == cPersistent_UPTODATE_STATE ||
self->state == cPersistent_STICKY_STATE)
&& self->jar)
{
UNLESS(builtins)
UNLESS (get_transaction)
{
UNLESS(T=PyImport_ImportModule("__main__")) return NULL;
UNLESS (py_register=PyString_FromString("register")) return -1;
UNLESS (T=PyImport_ImportModule("__main__")) return -1;
ASSIGN(T,PyObject_GetAttrString(T,"__builtins__"));
UNLESS(T) return NULL;
UNLESS(py_register=PyString_FromString("register")) goto err;
UNLESS (T) return -1;
builtins=T;
}
UNLESS(get_transaction=PyObject_GetAttrString(builtins,
UNLESS (get_transaction=PyObject_GetAttrString(builtins,
"get_transaction"))
PyErr_Clear();
}
if(get_transaction)
if (get_transaction)
{
UNLESS(T=PyObject_CallObject(get_transaction,NULL)) return NULL;
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);
}
UNLESS (T=PyObject_CallObject(get_transaction,NULL)) return -1;
ASSIGN(T,PyObject_GetAttr(T,py_register));
UNLESS (T) return -1;
ASSIGN(T, PyObject_CallFunction(T,"O",self));
if (T) Py_DECREF(T);
else return -1;
}
if(self->state != cPersistent_GHOST_STATE) self->state=t;
Py_INCREF(Py_None);
return Py_None;
}
else
{
PyErr_Clear();
UNLESS(PyArg_Parse(args, "")) return NULL;
return PyInt_FromLong(self->state==cPersistent_CHANGED_STATE);
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__[] =
"__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;
return 0;
}
static char Per___inform_abort____doc__[] =
"__inform_abort__(transaction,start_time) -- Abort object changes"
;
static PyObject *
Per___inform_abort__(self, args)
cPersistentObject *self;
PyObject *args;
Per___changed__(cPersistentObject *self, PyObject *args)
{
PyObject *T, *t;
UNLESS(PyArg_ParseTuple(args, "OO", &T, &t)) return NULL;
if(self->oid && self->jar && self->state != GHOST_STATE)
PyObject *v=0;
if (args && ! PyArg_ParseTuple(args, "|O",&v)) return NULL;
if (v && ! PyObject_IsTrue(v))
{
args=callmethod3(self->jar,py_oops,(PyObject*)self,t,T);
if(args)
Py_DECREF(args);
else
PyErr_Clear();
PyErr_SetString(PyExc_TypeError,
"Only true arguments are allowed.");
return NULL;
}
if (changed(self) < 0) return NULL;
Py_INCREF(Py_None);
return Py_None;
}
static char Per__p___init____doc__[] =
"_p___init__(oid,jar) -- Initialize persistence management data"
;
static PyObject *
Per__p___init__(self, args)
cPersistentObject *self;
PyObject *args;
Per__p_deactivate(cPersistentObject *self, PyObject *args)
{
int oid;
PyObject *jar;
PyObject *init=0, *copy, *dict;
UNLESS(PyArg_Parse(args, "(iO)", &oid, &jar)) return NULL;
#ifdef DEBUG_LOG
if(idebug_log < 0) call_debug("init",self);
if (idebug_log < 0) call_debug("reinit",self);
#endif
Py_INCREF(jar);
self->oid=oid;
ASSIGN(self->jar, jar);
self->state=GHOST_STATE;
Py_INCREF(Py_None);
return Py_None;
}
static PyObject *
Per__p___reinit__(cPersistentObject *self, PyObject *args)
{
PyObject *init=0, *copy, *dict;
if (args && ! PyArg_ParseTuple(args,"")) return NULL;
#ifdef DEBUG_LOG
if(idebug_log < 0) call_debug("reinit",self);
#endif
if(PyArg_Parse(args,""))
{
if(self->state==cPersistent_UPTODATE_STATE)
{
if(HasInstDict(self) && (dict=INSTANCE_DICT(self)))
if (self->state==cPersistent_UPTODATE_STATE && self->jar &&
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);
return Py_None;
err:
Py_XDECREF(init);
return NULL;
}
static int
Per_setstate(self)
cPersistentObject *self;
Per_setstate(cPersistentObject *self)
{
self->atime=(time_t)1; /* Mark this object as sticky */
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;
self->atime=time(NULL); /* Unmark as sticky */
return -1;
}
Py_DECREF(r);
}
UPDATE_STATE_IF_NECESSARY(self, -1);
self->state=cPersistent_STICKY_STATE;
return 0;
}
......@@ -406,25 +250,13 @@ Per__getstate__(self,args)
{
PyObject *__dict__, *d=0;
UNLESS(PyArg_Parse(args, "")) return NULL;
UNLESS(PyArg_ParseTuple(args, "")) return NULL;
#ifdef DEBUG_LOG
if(idebug_log < 0) call_debug("get",self);
#endif
/* Update state, if necessary */
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);
}
UPDATE_STATE_IF_NECESSARY(self, NULL);
if(HasInstDict(self) && (__dict__=INSTANCE_DICT(self)))
{
......@@ -465,16 +297,13 @@ Per__setstate__(self,args)
PyObject *__dict__, *v, *keys=0, *key=0, *e=0;
int l, i;
/*printf("%s(%d) ", self->ob_type->tp_name,self->oid);*/
if(HasInstDict(self))
{
UNLESS(PyArg_Parse(args, "O", &v)) return NULL;
UNLESS(PyArg_ParseTuple(args, "O", &v)) return NULL;
#ifdef DEBUG_LOG
if(idebug_log < 0) call_debug("set",self);
#endif
self->state=UPTODATE_STATE;
if(v!=Py_None)
{
__dict__=INSTANCE_DICT(self);
......@@ -483,11 +312,8 @@ Per__setstate__(self,args)
{
for(i=0; PyDict_Next(v,&i,&key,&e);)
if(PyObject_SetItem(__dict__,key,e) < 0)
{
self->state=GHOST_STATE;
return NULL;
}
}
else
{
UNLESS(keys=callmethod(v,py_keys)) goto err;
......@@ -509,7 +335,6 @@ Per__setstate__(self,args)
Py_INCREF(Py_None);
return Py_None;
err:
self->state=GHOST_STATE;
Py_XDECREF(key);
Py_XDECREF(e);
Py_XDECREF(keys);
......@@ -518,22 +343,13 @@ err:
static struct PyMethodDef Per_methods[] = {
{"__changed__", (PyCFunction)T___changed__, 0,
Per___changed____doc__},
{"__save__", (PyCFunction)Per___save__, 1,
Per___save____doc__},
{"__inform_commit__", (PyCFunction)Per___inform_commit__, 1,
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,
{"__changed__", (PyCFunction)Per___changed__, METH_VARARGS,
"DEPRECATED: use self._p_changed=1"},
{"_p_deactivate", (PyCFunction)Per__p_deactivate, METH_VARARGS,
"_p_deactivate(oid) -- Deactivate the object"},
{"__getstate__", (PyCFunction)Per__getstate__, METH_VARARGS,
"__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" },
{NULL, NULL} /* sentinel */
......@@ -549,28 +365,9 @@ Per_dealloc(self)
if(idebug_log < 0) call_debug("del",self);
#endif
Py_XDECREF(self->jar);
/*Py_XDECREF(self->atime);*/
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 *
Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
PyObject *(*getattrf)(PyObject *, PyObject*))
......@@ -584,17 +381,7 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
{
case 'o':
if(*n++=='i' && *n++=='d' && ! *n)
{
if(self->oid)
{
return PyInt_FromLong(self->oid);
}
else
{
Py_INCREF(Py_None);
return Py_None;
}
}
return PyString_FromStringAndSize(self->oid, 8);
break;
case 'j':
if(*n++=='a' && *n++=='r' && ! *n)
......@@ -614,34 +401,15 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
case 'c':
if(strcmp(n,"hanged")==0)
{
if(self->state == GHOST_STATE)
if(self->state < 0)
{
Py_INCREF(Py_None);
return Py_None;
}
return PyInt_FromLong(self->state == CHANGED_STATE);
}
break;
case 'a':
if(strcmp(n,"time")==0)
{
if(self->state != UPTODATE_STATE) Per_set_atime(self);
return Per_atime(self);
}
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;
return PyInt_FromLong(self->state ==
cPersistent_CHANGED_STATE);
}
break;
case 's':
if(strcmp(n,"tate")==0)
return PyInt_FromLong(self->state);
break;
}
return getattrf((PyObject *)self, oname);
......@@ -650,21 +418,9 @@ Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
(strcmp(name,"dict__")==0 || strcmp(name,"class__")==0
|| strcmp(name, "of__")==0)))
{
/* Update state, if necessary */
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);
}
UPDATE_STATE_IF_NECESSARY(self, NULL);
Per_set_atime(self);
self->atime=((long)(time(NULL)/3))%65536;
}
return getattrf((PyObject *)self, oname);
......@@ -680,14 +436,11 @@ Per_getattro(cPersistentObject *self, PyObject *name)
}
static int
changed(PyObject *self)
bad_delattr()
{
PyObject *c;
UNLESS(c=PyObject_GetAttr(self,py___changed__)) return -1;
UNLESS_ASSIGN(c,PyObject_CallObject(c,py_onearg)) return -1;
Py_DECREF(c);
return 0;
PyErr_SetString(PyExc_AttributeError,
"delete undeletable attribute");
return -1;
}
static int
......@@ -703,8 +456,15 @@ _setattro(cPersistentObject *self, PyObject *oname, PyObject *v,
{
if(name[3]=='o' && name[4]=='i' && name[5]=='d' && ! name[6])
{
if(v && PyInt_Check(v)) self->oid=PyInt_AsLong(v);
else self->oid=0;
if (! v) return bad_delattr();
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;
}
if(name[3]=='j' && name[4]=='a' && name[5]=='r' && ! name[6])
......@@ -715,40 +475,29 @@ _setattro(cPersistentObject *self, PyObject *oname, PyObject *v,
}
if(strcmp(name+3,"changed")==0)
{
if(v==Py_None) self->state=GHOST_STATE;
else self->state= (v && PyObject_IsTrue(v));
if (! v) return bad_delattr();
if (v==Py_None)
{
if (Per__p_deactivate(self, NULL)) Py_DECREF(Py_None);
return 0;
}
if(strcmp(name+3,"atime")==0)
{
self->atime=(time_t)1;
if (PyObject_IsTrue(v)) return changed(self);
if (self->state >= 0) self->state=cPersistent_UPTODATE_STATE;
return 0;
}
}
else
{
PyObject *r;
/* Update state, if necessary */
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);
}
UPDATE_STATE_IF_NECESSARY(self, -1);
/* Record access times */
Per_set_atime(self);
self->atime=((long)(time(NULL)/3))%65536;
if(! (*name=='_' && name[1]=='v' && name[2]=='_')
&& self->state != CHANGED_STATE && self->jar)
if(changed((PyObject*)self) < 0) return -1;
&& self->state != cPersistent_CHANGED_STATE && self->jar)
if(changed(self) < 0) return -1;
}
return setattrf((PyObject*)self,oname,v);
......@@ -760,28 +509,6 @@ Per_setattro(cPersistentObject *self, PyObject *oname, PyObject *v)
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 = {
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
......@@ -804,8 +531,7 @@ static PyExtensionClass Pertype = {
(getattrofunc)Per_getattro, /*tp_getattr with object key*/
(setattrofunc)Per_setattro, /*tp_setattr with object key*/
/* Space for future expansion */
0L,0L,
Pertype__doc__, /* Documentation string */
0L,0L,"",
METHOD_CHAIN(Per_methods),
EXTENSIONCLASS_BASICNEW_FLAG,
};
......@@ -830,7 +556,7 @@ set_debug_log(PyObject *ignored, PyObject *args)
static struct PyMethodDef cP_methods[] = {
#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"
"\n"
"The function will be called with an event name and a persistent object.\n"
......@@ -859,9 +585,7 @@ void
initcPersistence()
{
PyObject *m, *d;
char *rev="$Revision: 1.24 $";
PATimeType.ob_type=&PyType_Type;
char *rev="$Revision: 1.25 $";
m = Py_InitModule4("cPersistence", cP_methods,
"",
......@@ -873,110 +597,11 @@ initcPersistence()
PyDict_SetItemString(d,"__version__",
PyString_FromStringAndSize(rev+11,strlen(rev+11)-2));
PyExtensionClass_Export(d,"Persistent",Pertype);
PyDict_SetItemString(d,"atimeType",(PyObject*)&PATimeType);
cPersistenceCAPI=&truecPersistenceCAPI;
PyDict_SetItemString(d, "CAPI",
PyCObject_FromVoidPtr(cPersistenceCAPI,NULL));
#include "dcprotect.h"
if (PyErr_Occurred())
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.
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 @@
#define cPersistent_HEAD PyObject_HEAD \
PyObject *jar; \
int oid; \
time_t atime; \
char oid[8]; \
unsigned short atime; \
signed char state; \
#define cPersistent_GHOST_STATE -1
#define cPersistent_UPTODATE_STATE 0
#define cPersistent_CHANGED_STATE 1
#define cPersistent_STICKY_STATE 2
typedef struct {
cPersistent_HEAD
} cPersistentObject;
typedef struct {
PyObject_HEAD
cPersistentObject *object;
} PATimeobject;
typedef int (*persetattr)(PyObject *, PyObject*, PyObject *, setattrofunc);
typedef PyObject *(*pergetattr)(PyObject *, PyObject*, char *, getattrofunc);
......@@ -121,7 +35,7 @@ typedef struct {
PyMethodChain *methods;
getattrofunc getattro;
setattrofunc setattro;
int (*changed)(PyObject*);
int (*changed)(cPersistentObject*);
int (*setstate)(PyObject*);
pergetattr pergetattro;
persetattr persetattro;
......@@ -129,16 +43,20 @@ typedef struct {
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) \
if(cPersistenceCAPI->setstate((PyObject*)(O)) < 0) return (R)
#define PER_USE(O) (cPersistenceCAPI->setstate((PyObject*)(O)))
#define PER_CHANGED(O) (cPersistenceCAPI->changed((cPersistentObject*)(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)
#endif
......
/*
$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 $";
static char *what_string = "$Id: cPickleCache.c,v 1.16 1998/11/11 02:00:56 jim Exp $";
#define ASSIGN(V,E) {PyObject *__e; __e=(E); Py_XDECREF(V); (V)=__e;}
#define UNLESS(E) if(!(E))
#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)
#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
#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 */
......@@ -65,61 +50,46 @@ typedef struct {
staticforward PyTypeObject Cctype;
static PyObject *PATimeType=NULL;
/* ---------------------------------------------------------------- */
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++;
if(v->ob_type==(PyTypeObject*)PATimeType)
{
if(((PATimeobject*)v)->object->ob_refcnt <= 1)
if(v->ob_refcnt <= 1)
{
self->sum_deal++;
UNLESS(-1 != PyDict_DelItem(self->data, key)) return -1;
return PyDict_DelItem(self->data, key);
}
else
{
t=((PATimeobject*)v)->object->atime;
if(t != (time_t)1)
if (dt && v->ob_type->tp_basicsize >= sizeof(cPersistentObject) &&
((cPersistentObject*)v)->state==cPersistent_UPTODATE_STATE)
{
now -= ((cPersistentObject*)v)->atime;
if (now < 0) now += 65536;
self->na++;
t=now-t;
self->sum_age += t;
if((! dt || t > dt))
self->sum_age += now;
if (now > dt)
{
/* 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);
return 0;
}
PyErr_Clear();
}
}
}
}
else if(v->ob_refcnt <= 1)
{
self->sum_deal++;
UNLESS(-1 != PyDict_DelItem(self->data, key)) return -1;
}
}
return 0;
}
......@@ -135,7 +105,7 @@ update_stats(ccobject *self, time_t now)
self->dfa *= WEIGHTING_PERIOD/(WEIGHTING_PERIOD+d);
self->mean_age=((self->mean_age*self->dfa+self->sum_age)/
(self->dfa+self->na));
(self->dfa+self->na))*3;
self->sum_age=0;
deac=self->sum_deac/d;
......@@ -163,16 +133,22 @@ static int
fullgc(ccobject *self, int idt)
{
PyObject *key, *v;
int i;
time_t now, dt;
int i, dt;
long now;
if(self->cache_size < 1) return 0;
if (self->cache_size < 1) return 0;
if ((i=PyDict_Size(self->data)) < 1) return;
now=((long)(time(NULL)/3))%65536;
if (idt) dt=idt*3;
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;
now=time(NULL);
if(idt) dt=idt;
}
for(i=0; PyDict_Next(self->data, &i, &key, &v); )
if(gc_item(self,key,v,now,dt) < 0) return -1;
......@@ -183,40 +159,6 @@ fullgc(ccobject *self, int idt)
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
reallyfullgc(ccobject *self, int dt)
{
......@@ -249,31 +191,33 @@ reallyfullgc(ccobject *self, int dt)
static int
maybegc(ccobject *self, PyObject *thisv)
{
int n, s, size;
time_t now,dt;
int n, s, size, dt;
long now;
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;
self->cache_size=0;
n=(s-size)/10;
/*n=s/size;*/
if(n < 3) n=3;
dt=(long)(self->cache_age*(0.2+0.8*size/s));
if(dt < 10) dt=10;
if (n < 3) n=3;
s=8*size/s;
if (s > 100) s=100;
dt=(long)(self->cache_age*(0.2+0.1*s));
if (dt < 10) dt=10;
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;
return -1;
......@@ -284,7 +228,7 @@ maybegc(ccobject *self, PyObject *thisv)
}
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;
}
......@@ -309,41 +253,6 @@ cc_reallyfull_sweep(ccobject *self, PyObject *args)
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 *
cc_incrgc(ccobject *self, PyObject *args)
{
......@@ -353,31 +262,24 @@ cc_incrgc(ccobject *self, PyObject *args)
}
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"
"Make a single pass through the cache, removing any objects that are no\n"
"longer referenced, and deactivating objects that have not been\n"
"accessed in the number of seconds given by 'age'. "
"'age defaults to the cache age.\n"
},
{"report", (PyCFunction)cc_report, 1, ""},
{"minimize", (PyCFunction)cc_reallyfull_sweep, 1,
{"minimize", (PyCFunction)cc_reallyfull_sweep, METH_VARARGS,
"minimize([age]) -- Remove as many objects as possible\n\n"
"Make multiple passes through the cache, removing any objects that are no\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"
},
{"items", (PyCFunction)ccitems, 1,
"items() -- Return the cache items."
},
{"incrgc", (PyCFunction)cc_incrgc, 1,
{"incrgc", (PyCFunction)cc_incrgc, METH_VARARGS,
"incrgc() -- Perform incremental garbage collection"},
{NULL, NULL} /* sentinel */
};
/* ---------- */
static ccobject *
newccobject(int cache_size, int cache_age)
{
......@@ -442,7 +344,11 @@ cc_getattr(ccobject *self, char *name)
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);
if(r=Py_FindMethod(cc_methods, (PyObject *)self, name))
......@@ -476,22 +382,6 @@ cc_setattr(ccobject *self, char *name, PyObject *value)
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
cc_length(ccobject *self)
{
......@@ -503,58 +393,22 @@ cc_subscript(ccobject *self, PyObject *key)
{
PyObject *r;
UNLESS(r=PyObject_GetItem(self->data, key))
UNLESS (r=PyDict_GetItem(self->data, key))
{
PyErr_SetObject(PyExc_KeyError, key);
return NULL;
}
UNLESS(-1 != maybegc(self,r))
{
Py_DECREF(r);
return NULL;
}
if(r->ob_type==(PyTypeObject *)PATimeType)
{
Py_DECREF(r);
r=(PyObject*)(((PATimeobject*)r)->object);
if (maybegc(self,r) < 0) return NULL;
Py_INCREF(r);
}
return r;
}
static int
cc_ass_sub(ccobject *self, PyObject *key, PyObject *v)
{
if(v)
{
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);
}
if(v) return PyDict_SetItem(self->data, key, v);
return PyDict_DelItem(self->data, key);
}
static PyMappingMethods cc_as_mapping = {
......@@ -563,12 +417,6 @@ static PyMappingMethods cc_as_mapping = {
(objobjargproc)cc_ass_sub, /*mp_ass_subscript*/
};
/* -------------------------------------------------------- */
static char Cctype__doc__[] =
""
;
static PyTypeObject Cctype = {
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
......@@ -581,22 +429,19 @@ static PyTypeObject Cctype = {
(getattrfunc)cc_getattr, /*tp_getattr*/
(setattrfunc)cc_setattr, /*tp_setattr*/
(cmpfunc)0, /*tp_compare*/
(reprfunc)cc_repr, /*tp_repr*/
(reprfunc)0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
&cc_as_mapping, /*tp_as_mapping*/
(hashfunc)0, /*tp_hash*/
(ternaryfunc)0, /*tp_call*/
(reprfunc)cc_str, /*tp_str*/
(reprfunc)0, /*tp_str*/
/* Space for future expansion */
0L,0L,0L,0L,
Cctype__doc__ /* Documentation string */
""
};
/* End of code for cCache objects */
/* -------------------------------------------------------- */
static PyObject *
cCM_new(PyObject *self, PyObject *args)
{
......@@ -605,119 +450,30 @@ cCM_new(PyObject *self, PyObject *args)
return (PyObject*)newccobject(cache_size,cache_age);
}
/* List of methods defined in the module */
static struct PyMethodDef cCM_methods[] = {
{"PickleCache",(PyCFunction)cCM_new, 1,
"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"
},
{"PickleCache",(PyCFunction)cCM_new, METH_VARARGS, ""},
{NULL, NULL} /* sentinel */
};
/* Initialization function for the module (*must* be called initcCache) */
static char cCache_module_documentation[] =
""
;
void
initcPickleCache()
{
PyObject *m, *d;
char *rev="$Revision: 1.15 $";
char *rev="$Revision: 1.16 $";
Cctype.ob_type=&PyType_Type;
if(PATimeType=PyImport_ImportModule("cPersistence"))
ASSIGN(PATimeType,PyObject_GetAttrString(PATimeType,"atimeType"));
UNLESS(PATimeType) PyErr_Clear();
m = Py_InitModule4("cPickleCache", cCM_methods,
cCache_module_documentation,
m = Py_InitModule4("cPickleCache", cCM_methods, "",
(PyObject*)NULL,PYTHON_API_VERSION);
d = PyModule_GetDict(m);
py_reload=PyString_FromString("reload");
py__p_jar=PyString_FromString("_p_jar");
py__p_atime=PyString_FromString("_p_atime");
py__p_deactivate=PyString_FromString("_p_deactivate");
PyDict_SetItemString(d,"__version__",
PyString_FromStringAndSize(rev+11,strlen(rev+11)-2));
#include "dcprotect.h"
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 @@
__doc__='''Python implementation of persistent base types
$Id: mapping.py,v 1.2 1998/10/23 21:40:59 jim Exp $'''
__version__='$Revision: 1.2 $'[11:-2]
$Id: mapping.py,v 1.3 1998/11/11 02:00:56 jim Exp $'''
__version__='$Revision: 1.3 $'[11:-2]
import Persistence
class PersistentMapping(Persistence.Persistent):
class PM(Persistence.Persistent):
"""A persistent wrapper for mapping objects.
This class allows wrapping of mapping objects so that
......@@ -65,22 +65,36 @@ class PersistentMapping(Persistence.Persistent):
if container is None: 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):
return self._container[key]
def __len__(self): return len(self._container)
def __setitem__(self, key, v):
self._container[key]=v
try: del self._v_keys
except: pass
self.__changed__(1)
def __delitem__(self, key):
del self._container[key]
try: del self._v_keys
except: pass
self.__changed__(1)
def clear(self):
self._container.clear()
self._p_changed=1
if hasattr(self,'_v_keys'): del self._v_keys
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):
try: return self._v_keys
......@@ -91,14 +105,12 @@ class PersistentMapping(Persistence.Persistent):
keys.sort()
return keys
def clear(self):
self._container={}
if hasattr(self,'_v_keys'): del self._v_keys
def update(self, b):
a=self._container
for k, v in b.items(): a[k] = v
self._p_changed=1
def values(self):
return map(lambda k, d=self: d[k], self.keys())
def items(self):
return map(lambda k, d=self: (k,d[k]), self.keys())
def has_key(self,key): return self._container.has_key(key)
PersistentMapping=PM
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