Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Z
ZODB
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Nicolas Wavrant
ZODB
Commits
b983a6b5
Commit
b983a6b5
authored
Jul 01, 2003
by
Jim Fulton
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Remove files being moved here so we can add them on a branch.
parent
4d22ca2b
Changes
10
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
0 additions
and
4360 deletions
+0
-4360
src/Persistence/PersistentList.py
src/Persistence/PersistentList.py
+0
-96
src/Persistence/PersistentMapping.py
src/Persistence/PersistentMapping.py
+0
-106
src/Persistence/cPersistence.c
src/Persistence/cPersistence.c
+0
-888
src/Persistence/cPersistence.h
src/Persistence/cPersistence.h
+0
-109
src/Persistence/cPickleCache.c
src/Persistence/cPickleCache.c
+0
-981
src/persistent/cPersistence.c
src/persistent/cPersistence.c
+0
-888
src/persistent/cPersistence.h
src/persistent/cPersistence.h
+0
-109
src/persistent/cPickleCache.c
src/persistent/cPickleCache.c
+0
-981
src/persistent/list.py
src/persistent/list.py
+0
-96
src/persistent/mapping.py
src/persistent/mapping.py
+0
-106
No files found.
src/Persistence/PersistentList.py
deleted
100644 → 0
View file @
4d22ca2b
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""Python implementation of persistent list.
$Id: PersistentList.py,v 1.3 2002/08/14 22:07:09 mj Exp $"""
__version__
=
'$Revision: 1.3 $'
[
11
:
-
2
]
import
Persistence
from
UserList
import
UserList
class
PersistentList
(
UserList
,
Persistence
.
Persistent
):
__super_setitem
=
UserList
.
__setitem__
__super_delitem
=
UserList
.
__delitem__
__super_setslice
=
UserList
.
__setslice__
__super_delslice
=
UserList
.
__delslice__
__super_iadd
=
UserList
.
__iadd__
__super_imul
=
UserList
.
__imul__
__super_append
=
UserList
.
append
__super_insert
=
UserList
.
insert
__super_pop
=
UserList
.
pop
__super_remove
=
UserList
.
remove
__super_reverse
=
UserList
.
reverse
__super_sort
=
UserList
.
sort
__super_extend
=
UserList
.
extend
def
__setitem__
(
self
,
i
,
item
):
self
.
__super_setitem
(
i
,
item
)
self
.
_p_changed
=
1
def
__delitem__
(
self
,
i
):
self
.
__super_delitem
(
i
)
self
.
_p_changed
=
1
def
__setslice__
(
self
,
i
,
j
,
other
):
self
.
__super_setslice
(
i
,
j
,
other
)
self
.
_p_changed
=
1
def
__delslice__
(
self
,
i
,
j
):
self
.
__super_delslice
(
i
,
j
)
self
.
_p_changed
=
1
def
__iadd__
(
self
,
other
):
self
.
__super_iadd
(
other
)
self
.
_p_changed
=
1
def
__imul__
(
self
,
n
):
self
.
__super_imul
(
n
)
self
.
_p_changed
=
1
def
append
(
self
,
item
):
self
.
__super_append
(
item
)
self
.
_p_changed
=
1
def
insert
(
self
,
i
,
item
):
self
.
__super_insert
(
i
,
item
)
self
.
_p_changed
=
1
def
pop
(
self
,
i
=-
1
):
rtn
=
self
.
__super_pop
(
i
)
self
.
_p_changed
=
1
return
rtn
def
remove
(
self
,
item
):
self
.
__super_remove
(
item
)
self
.
_p_changed
=
1
def
reverse
(
self
):
self
.
__super_reverse
()
self
.
_p_changed
=
1
def
sort
(
self
,
*
args
):
self
.
__super_sort
(
*
args
)
self
.
_p_changed
=
1
def
extend
(
self
,
other
):
self
.
__super_extend
(
other
)
self
.
_p_changed
=
1
# This works around a bug in Python 2.1.x (up to 2.1.2 at least) where the
# __cmp__ bogusly raises a RuntimeError, and because this is an extension
# class, none of the rich comparison stuff works anyway.
def
__cmp__
(
self
,
other
):
return
cmp
(
self
.
data
,
self
.
_UserList__cast
(
other
))
src/Persistence/PersistentMapping.py
deleted
100644 → 0
View file @
4d22ca2b
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""Python implementation of persistent base types
$Id: PersistentMapping.py,v 1.20 2002/08/14 22:07:09 mj Exp $"""
__version__
=
'$Revision: 1.20 $'
[
11
:
-
2
]
import
Persistence
from
UserDict
import
UserDict
class
PersistentMapping
(
UserDict
,
Persistence
.
Persistent
):
"""A persistent wrapper for mapping objects.
This class allows wrapping of mapping objects so that object
changes are registered. As a side effect, mapping objects may be
subclassed.
A subclass of PersistentMapping or any code that adds new
attributes should not create an attribute named _container. This
is reserved for backwards compatibility reasons.
"""
# UserDict provides all of the mapping behavior. The
# PersistentMapping class is responsible marking the persistent
# state as changed when a method actually changes the state. At
# the mapping API evolves, we may need to add more methods here.
__super_delitem
=
UserDict
.
__delitem__
__super_setitem
=
UserDict
.
__setitem__
__super_clear
=
UserDict
.
clear
__super_update
=
UserDict
.
update
__super_setdefault
=
UserDict
.
setdefault
def
__delitem__
(
self
,
key
):
self
.
__super_delitem
(
key
)
self
.
_p_changed
=
1
def
__setitem__
(
self
,
key
,
v
):
self
.
__super_setitem
(
key
,
v
)
self
.
_p_changed
=
1
def
clear
(
self
):
self
.
__super_clear
()
self
.
_p_changed
=
1
def
update
(
self
,
b
):
self
.
__super_update
(
b
)
self
.
_p_changed
=
1
def
setdefault
(
self
,
key
,
failobj
=
None
):
# We could inline all of UserDict's implementation into the
# method here, but I'd rather not depend at all on the
# implementation in UserDict (simple as it is).
if
not
self
.
has_key
(
key
):
self
.
_p_changed
=
1
return
self
.
__super_setdefault
(
key
,
failobj
)
try
:
__super_popitem
=
UserDict
.
popitem
except
AttributeError
:
pass
else
:
def
popitem
(
self
):
self
.
_p_changed
=
1
return
self
.
__super_popitem
()
# If the internal representation of PersistentMapping changes,
# it causes compatibility problems for pickles generated by
# different versions of the code. Compatibility works in both
# directions, because an application may want to share a database
# between applications using different versions of the code.
# Effectively, the original rep is part of the "API." To provide
# full compatibility, the getstate and setstate must read and
# right objects using the old rep.
# As a result, the PersistentMapping must save and restore the
# actual internal dictionary using the name _container.
def
__getstate__
(
self
):
state
=
{}
state
.
update
(
self
.
__dict__
)
state
[
'_container'
]
=
state
[
'data'
]
del
state
[
'data'
]
return
state
def
__setstate__
(
self
,
state
):
if
state
.
has_key
(
'_container'
):
self
.
data
=
state
[
'_container'
]
del
state
[
'_container'
]
elif
not
state
.
has_key
(
'data'
):
self
.
data
=
{}
self
.
__dict__
.
update
(
state
)
src/Persistence/cPersistence.c
deleted
100644 → 0
View file @
4d22ca2b
/*****************************************************************************
Copyright (c) 2001, 2002 Zope Corporation and Contributors.
All Rights Reserved.
This software is subject to the provisions of the Zope Public License,
Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
FOR A PARTICULAR PURPOSE
****************************************************************************/
static
char
cPersistence_doc_string
[]
=
"Defines Persistent mixin class for persistent objects.
\n
"
"
\n
"
"$Id: cPersistence.c,v 1.72 2003/05/30 19:20:55 jeremy Exp $
\n
"
;
#include "cPersistence.h"
struct
ccobject_head_struct
{
CACHE_HEAD
};
#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 OBJECT(V) ((PyObject*)(V))
static
PyObject
*
py_keys
,
*
py_setstate
,
*
py___dict__
,
*
py_timeTime
;
static
PyObject
*
py__p_changed
,
*
py__p_deactivate
;
static
PyObject
*
py___getattr__
,
*
py___setattr__
,
*
py___delattr__
;
static
PyObject
*
TimeStamp
;
#ifdef DEBUG_LOG
static
PyObject
*
debug_log
=
0
;
static
int
idebug_log
=
0
;
static
void
*
call_debug
(
char
*
event
,
cPersistentObject
*
self
)
{
PyObject
*
r
;
/*
printf("%s %p\n",event,self->ob_type->tp_name);
*/
r
=
PyObject_CallFunction
(
debug_log
,
"s(sOi)"
,
event
,
self
->
ob_type
->
tp_name
,
self
->
oid
,
self
->
state
);
Py_XDECREF
(
r
);
}
#endif
static
int
init_strings
(
void
)
{
#define INIT_STRING(S) if (! (py_ ## S = PyString_FromString(#S))) return -1;
INIT_STRING
(
keys
);
INIT_STRING
(
setstate
);
INIT_STRING
(
timeTime
);
INIT_STRING
(
__dict__
);
INIT_STRING
(
_p_changed
);
INIT_STRING
(
_p_deactivate
);
INIT_STRING
(
__getattr__
);
INIT_STRING
(
__setattr__
);
INIT_STRING
(
__delattr__
);
#undef INIT_STRING
return
0
;
}
static
int
checknoargs
(
PyObject
*
args
)
{
if
(
!
PyTuple_Check
(
args
))
return
0
;
if
(
PyTuple_GET_SIZE
(
args
)
!=
0
)
{
PyErr_Format
(
PyExc_TypeError
,
"function takes exactly 0 arguments (%d given)"
,
PyTuple_GET_SIZE
(
args
));
return
0
;
}
return
1
;
}
static
PyObject
*
callmethod
(
PyObject
*
self
,
PyObject
*
name
)
{
self
=
PyObject_GetAttr
(
self
,
name
);
if
(
self
)
ASSIGN
(
self
,
PyObject_CallObject
(
self
,
NULL
));
return
self
;
}
static
PyObject
*
callmethod1
(
PyObject
*
self
,
PyObject
*
name
,
PyObject
*
arg
)
{
self
=
PyObject_GetAttr
(
self
,
name
);
UNLESS
(
self
)
return
NULL
;
name
=
PyTuple_New
(
1
);
UNLESS
(
name
)
{
Py_DECREF
(
self
);
return
NULL
;
}
PyTuple_SET_ITEM
(
name
,
0
,
arg
);
ASSIGN
(
self
,
PyObject_CallObject
(
self
,
name
));
PyTuple_SET_ITEM
(
name
,
0
,
NULL
);
Py_DECREF
(
name
);
return
self
;
}
static
void
ghostify
(
cPersistentObject
*
);
/* Load the state of the object, unghostifying it. Upon success, return 1.
* If an error occurred, re-ghostify the object and return 0.
*/
static
int
unghostify
(
cPersistentObject
*
self
)
{
if
(
self
->
state
<
0
&&
self
->
jar
)
{
PyObject
*
r
;
/* XXX Is it ever possibly to not have a cache? */
if
(
self
->
cache
)
{
CPersistentRing
*
home
=
&
self
->
cache
->
ring_home
;
/* Create a node in the ring for this unghostified object. */
self
->
cache
->
non_ghost_count
++
;
self
->
ring
.
next
=
home
;
self
->
ring
.
prev
=
home
->
prev
;
home
->
prev
->
next
=
&
self
->
ring
;
home
->
prev
=
&
self
->
ring
;
Py_INCREF
(
self
);
}
self
->
state
=
cPersistent_CHANGED_STATE
;
/* Call the object's __setstate__() */
r
=
callmethod1
(
self
->
jar
,
py_setstate
,
(
PyObject
*
)
self
);
if
(
r
==
NULL
)
{
ghostify
(
self
);
return
0
;
}
self
->
state
=
cPersistent_UPTODATE_STATE
;
Py_DECREF
(
r
);
}
return
1
;
}
/****************************************************************************/
staticforward
PyExtensionClass
Pertype
;
static
void
accessed
(
cPersistentObject
*
self
)
{
/* Do nothing unless the object is in a cache and not a ghost. */
if
(
self
->
cache
&&
self
->
state
>=
0
&&
self
->
ring
.
next
)
{
CPersistentRing
*
home
=
&
self
->
cache
->
ring_home
;
self
->
ring
.
prev
->
next
=
self
->
ring
.
next
;
self
->
ring
.
next
->
prev
=
self
->
ring
.
prev
;
self
->
ring
.
next
=
home
;
self
->
ring
.
prev
=
home
->
prev
;
home
->
prev
->
next
=
&
self
->
ring
;
home
->
prev
=
&
self
->
ring
;
}
}
static
void
ghostify
(
cPersistentObject
*
self
)
{
/* are we already a ghost? */
if
(
self
->
state
==
cPersistent_GHOST_STATE
)
return
;
/* XXX is it ever possible to not have a cache? */
if
(
self
->
cache
==
NULL
)
{
self
->
state
=
cPersistent_GHOST_STATE
;
return
;
}
/* If the cache has been cleared, then a non-ghost object
isn't in the ring any longer.
*/
if
(
self
->
ring
.
next
==
NULL
)
return
;
/* if we're ghostifying an object, we better have some non-ghosts */
assert
(
self
->
cache
->
non_ghost_count
>
0
);
self
->
cache
->
non_ghost_count
--
;
self
->
ring
.
next
->
prev
=
self
->
ring
.
prev
;
self
->
ring
.
prev
->
next
=
self
->
ring
.
next
;
self
->
ring
.
prev
=
NULL
;
self
->
ring
.
next
=
NULL
;
self
->
state
=
cPersistent_GHOST_STATE
;
/* We remove the reference to the just ghosted object that the ring
* holds. Note that the dictionary of oids->objects has an uncounted
* reference, so if the ring's reference was the only one, this frees
* the ghost object. Note further that the object's dealloc knows to
* inform the dictionary that it is going away.
*/
Py_DECREF
(
self
);
}
static
void
deallocated
(
cPersistentObject
*
self
)
{
if
(
self
->
state
>=
0
)
ghostify
(
self
);
if
(
self
->
cache
)
{
/* XXX This function shouldn't be able to fail? If not, maybe
it shouldn't set an exception either.
*/
if
(
cPersistenceCAPI
->
percachedel
(
self
->
cache
,
self
->
oid
)
<
0
)
PyErr_Clear
();
/* I don't think this should ever happen */
}
Py_XDECREF
(
self
->
jar
);
Py_XDECREF
(
self
->
oid
);
}
static
int
changed
(
cPersistentObject
*
self
)
{
if
((
self
->
state
==
cPersistent_UPTODATE_STATE
||
self
->
state
==
cPersistent_STICKY_STATE
)
&&
self
->
jar
)
{
PyObject
*
meth
,
*
arg
,
*
result
;
static
PyObject
*
s_register
;
if
(
s_register
==
NULL
)
s_register
=
PyString_InternFromString
(
"register"
);
meth
=
PyObject_GetAttr
((
PyObject
*
)
self
->
jar
,
s_register
);
if
(
meth
==
NULL
)
return
-
1
;
arg
=
PyTuple_New
(
1
);
if
(
arg
==
NULL
)
{
Py_DECREF
(
meth
);
return
-
1
;
}
PyTuple_SET_ITEM
(
arg
,
0
,
(
PyObject
*
)
self
);
result
=
PyEval_CallObject
(
meth
,
arg
);
PyTuple_SET_ITEM
(
arg
,
0
,
NULL
);
Py_DECREF
(
arg
);
Py_DECREF
(
meth
);
if
(
result
==
NULL
)
return
-
1
;
Py_DECREF
(
result
);
self
->
state
=
cPersistent_CHANGED_STATE
;
}
return
0
;
}
static
PyObject
*
Per___changed__
(
cPersistentObject
*
self
,
PyObject
*
args
)
{
PyObject
*
v
=
NULL
;
if
(
args
&&
!
PyArg_ParseTuple
(
args
,
"|O:__changed__"
,
&
v
))
return
NULL
;
if
(
!
v
)
return
PyObject_GetAttr
(
OBJECT
(
self
),
py__p_changed
);
if
(
PyObject_IsTrue
(
v
))
{
if
(
changed
(
self
)
<
0
)
return
NULL
;
}
else
if
(
self
->
state
>=
0
)
self
->
state
=
cPersistent_UPTODATE_STATE
;
Py_INCREF
(
Py_None
);
return
Py_None
;
}
static
PyObject
*
Per__p_deactivate
(
cPersistentObject
*
self
,
PyObject
*
args
)
{
PyObject
*
dict
,
*
dict2
=
NULL
;
#ifdef DEBUG_LOG
if
(
idebug_log
<
0
)
call_debug
(
"reinit"
,
self
);
#endif
if
(
args
&&
!
checknoargs
(
args
))
return
NULL
;
if
(
self
->
state
==
cPersistent_UPTODATE_STATE
&&
self
->
jar
&&
HasInstDict
(
self
)
&&
(
dict
=
INSTANCE_DICT
(
self
)))
{
dict2
=
PyDict_Copy
(
dict
);
PyDict_Clear
(
dict
);
/* Note that we need to set to ghost state unless we are
called directly. Methods that override this need to
do the same! */
ghostify
(
self
);
}
/* need to delay releasing the last reference on instance attributes
until after we have finished accounting for losing our state */
if
(
dict2
)
{
PyDict_Clear
(
dict2
);
Py_DECREF
(
dict2
);
}
Py_INCREF
(
Py_None
);
return
Py_None
;
}
/* Load the object's state if necessary and become sticky */
static
int
Per_setstate
(
cPersistentObject
*
self
)
{
if
(
!
unghostify
(
self
))
return
-
1
;
self
->
state
=
cPersistent_STICKY_STATE
;
return
0
;
}
static
PyObject
*
Per__getstate__
(
cPersistentObject
*
self
,
PyObject
*
args
)
{
PyObject
*
__dict__
,
*
d
=
0
;
if
(
!
checknoargs
(
args
))
return
NULL
;
#ifdef DEBUG_LOG
if
(
idebug_log
<
0
)
call_debug
(
"get"
,
self
);
#endif
if
(
!
unghostify
(
self
))
return
NULL
;
if
(
HasInstDict
(
self
)
&&
(
__dict__
=
INSTANCE_DICT
(
self
)))
{
PyObject
*
k
,
*
v
;
int
pos
;
char
*
ck
;
for
(
pos
=
0
;
PyDict_Next
(
__dict__
,
&
pos
,
&
k
,
&
v
);
)
{
if
(
PyString_Check
(
k
)
&&
(
ck
=
PyString_AS_STRING
(
k
))
&&
(
*
ck
==
'_'
&&
ck
[
1
]
==
'v'
&&
ck
[
2
]
==
'_'
))
{
if
((
d
=
PyDict_New
())
==
NULL
)
goto
err
;
for
(
pos
=
0
;
PyDict_Next
(
__dict__
,
&
pos
,
&
k
,
&
v
);
)
UNLESS
(
PyString_Check
(
k
)
&&
(
ck
=
PyString_AS_STRING
(
k
))
&&
(
*
ck
==
'_'
&&
ck
[
1
]
==
'v'
&&
ck
[
2
]
==
'_'
))
{
if
(
PyDict_SetItem
(
d
,
k
,
v
)
<
0
)
goto
err
;
}
return
d
;
}
}
}
else
__dict__
=
Py_None
;
Py_INCREF
(
__dict__
);
return
__dict__
;
err:
Py_XDECREF
(
d
);
return
NULL
;
}
static
PyObject
*
Per__setstate__
(
cPersistentObject
*
self
,
PyObject
*
args
)
{
PyObject
*
__dict__
,
*
v
,
*
keys
=
0
,
*
key
=
0
,
*
e
=
0
;
int
l
,
i
;
if
(
HasInstDict
(
self
))
{
UNLESS
(
PyArg_ParseTuple
(
args
,
"O"
,
&
v
))
return
NULL
;
#ifdef DEBUG_LOG
if
(
idebug_log
<
0
)
call_debug
(
"set"
,
self
);
#endif
if
(
v
!=
Py_None
)
{
__dict__
=
INSTANCE_DICT
(
self
);
if
(
PyDict_Check
(
v
))
{
for
(
i
=
0
;
PyDict_Next
(
v
,
&
i
,
&
key
,
&
e
);)
if
(
PyDict_SetItem
(
__dict__
,
key
,
e
)
<
0
)
return
NULL
;
}
else
{
UNLESS
(
keys
=
callmethod
(
v
,
py_keys
))
goto
err
;
UNLESS
(
-
1
!=
(
l
=
PyObject_Length
(
keys
)))
goto
err
;
for
(
i
=
0
;
i
<
l
;
i
++
)
{
UNLESS_ASSIGN
(
key
,
PySequence_GetItem
(
keys
,
i
))
goto
err
;
UNLESS_ASSIGN
(
e
,
PyObject_GetItem
(
v
,
key
))
goto
err
;
UNLESS
(
-
1
!=
PyDict_SetItem
(
__dict__
,
key
,
e
))
goto
err
;
}
Py_XDECREF
(
key
);
Py_XDECREF
(
e
);
Py_DECREF
(
keys
);
}
}
}
Py_INCREF
(
Py_None
);
return
Py_None
;
err:
Py_XDECREF
(
key
);
Py_XDECREF
(
e
);
Py_XDECREF
(
keys
);
return
NULL
;
}
static
struct
PyMethodDef
Per_methods
[]
=
{
{
"__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__
,
METH_VARARGS
,
"__setstate__(v) -- Restore the saved state of the object from v"
},
{
NULL
,
NULL
}
/* sentinel */
};
/* ---------- */
static
void
Per_dealloc
(
cPersistentObject
*
self
)
{
#ifdef DEBUG_LOG
if
(
idebug_log
<
0
)
call_debug
(
"del"
,
self
);
#endif
deallocated
(
self
);
Py_DECREF
(
self
->
ob_type
);
PyObject_DEL
(
self
);
}
static
PyObject
*
orNothing
(
PyObject
*
v
)
{
if
(
!
v
)
v
=
Py_None
;
Py_INCREF
(
v
);
return
v
;
}
static
PyObject
*
Per_getattr
(
cPersistentObject
*
self
,
PyObject
*
oname
,
char
*
name
,
PyObject
*
(
*
getattrf
)(
PyObject
*
,
PyObject
*
))
{
char
*
n
=
name
;
if
(
n
&&
*
n
++==
'_'
)
if
(
*
n
++==
'p'
&&
*
n
++==
'_'
)
{
switch
(
*
n
++
)
{
case
'o'
:
if
(
*
n
++==
'i'
&&
*
n
++==
'd'
&&
!
*
n
)
return
orNothing
(
self
->
oid
);
break
;
case
'j'
:
if
(
*
n
++==
'a'
&&
*
n
++==
'r'
&&
!
*
n
)
return
orNothing
(
self
->
jar
);
break
;
case
'c'
:
if
(
strcmp
(
n
,
"hanged"
)
==
0
)
{
if
(
self
->
state
<
0
)
{
Py_INCREF
(
Py_None
);
return
Py_None
;
}
return
PyInt_FromLong
(
self
->
state
==
cPersistent_CHANGED_STATE
);
}
break
;
case
's'
:
if
(
strcmp
(
n
,
"erial"
)
==
0
)
return
PyString_FromStringAndSize
(
self
->
serial
,
8
);
if
(
strcmp
(
n
,
"elf"
)
==
0
)
return
orNothing
(
OBJECT
(
self
));
break
;
case
'm'
:
if
(
strcmp
(
n
,
"time"
)
==
0
)
{
if
(
!
unghostify
(
self
))
return
NULL
;
accessed
(
self
);
if
(
self
->
serial
[
7
]
==
'\0'
&&
self
->
serial
[
6
]
==
'\0'
&&
self
->
serial
[
5
]
==
'\0'
&&
self
->
serial
[
4
]
==
'\0'
&&
self
->
serial
[
3
]
==
'\0'
&&
self
->
serial
[
2
]
==
'\0'
&&
self
->
serial
[
1
]
==
'\0'
&&
self
->
serial
[
0
]
==
'\0'
)
{
Py_INCREF
(
Py_None
);
return
Py_None
;
}
oname
=
PyString_FromStringAndSize
(
self
->
serial
,
8
);
if
(
!
oname
)
return
oname
;
ASSIGN
(
oname
,
PyObject_CallFunction
(
TimeStamp
,
"O"
,
oname
));
if
(
!
oname
)
return
oname
;
ASSIGN
(
oname
,
PyObject_GetAttr
(
oname
,
py_timeTime
));
if
(
!
oname
)
return
oname
;
ASSIGN
(
oname
,
PyObject_CallObject
(
oname
,
NULL
));
return
oname
;
}
break
;
}
return
getattrf
((
PyObject
*
)
self
,
oname
);
}
if
(
!
(
name
&&
*
name
++==
'_'
&&
*
name
++==
'_'
&&
(
strcmp
(
name
,
"dict__"
)
==
0
||
strcmp
(
name
,
"class__"
)
==
0
||
strcmp
(
name
,
"of__"
)
==
0
)))
{
if
(
!
unghostify
(
self
))
return
NULL
;
accessed
(
self
);
}
return
getattrf
((
PyObject
*
)
self
,
oname
);
}
static
PyObject
*
Per_getattro
(
cPersistentObject
*
self
,
PyObject
*
name
)
{
char
*
s
=
NULL
;
PyObject
*
r
;
if
(
PyString_Check
(
name
))
UNLESS
(
s
=
PyString_AS_STRING
(
name
))
return
NULL
;
r
=
Per_getattr
(
self
,
name
,
s
,
PyExtensionClassCAPI
->
getattro
);
if
(
!
r
&&
self
->
state
!=
cPersistent_GHOST_STATE
&&
(((
PyExtensionClass
*
)(
self
->
ob_type
))
->
class_flags
&
EXTENSIONCLASS_USERGETATTR_FLAG
)
)
{
PyErr_Clear
();
r
=
PyObject_GetAttr
(
OBJECT
(
self
),
py___getattr__
);
if
(
r
)
{
ASSIGN
(
r
,
PyObject_CallFunction
(
r
,
"O"
,
name
));
}
else
PyErr_SetObject
(
PyExc_AttributeError
,
name
);
}
return
r
;
}
static
int
_setattro
(
cPersistentObject
*
self
,
PyObject
*
oname
,
PyObject
*
v
,
int
(
*
setattrf
)(
PyObject
*
,
PyObject
*
,
PyObject
*
))
{
char
*
name
=
""
;
if
(
oname
==
NULL
)
return
-
1
;
if
(
!
PyString_Check
(
oname
))
return
-
1
;
name
=
PyString_AS_STRING
(
oname
);
if
(
name
==
NULL
)
return
-
1
;
if
(
*
name
==
'_'
&&
name
[
1
]
==
'p'
&&
name
[
2
]
==
'_'
)
{
if
(
strcmp
(
name
+
3
,
"oid"
)
==
0
)
{
if
(
self
->
cache
)
{
int
result
;
if
(
v
==
NULL
)
{
PyErr_SetString
(
PyExc_ValueError
,
"can not delete oid of cached object"
);
return
-
1
;
}
if
(
PyObject_Cmp
(
self
->
oid
,
v
,
&
result
)
<
0
)
return
-
1
;
if
(
result
)
{
PyErr_SetString
(
PyExc_ValueError
,
"can not change oid of cached object"
);
return
-
1
;
}
}
Py_XINCREF
(
v
);
ASSIGN
(
self
->
oid
,
v
);
return
0
;
}
else
if
(
strcmp
(
name
+
3
,
"jar"
)
==
0
)
{
if
(
self
->
cache
&&
self
->
jar
)
{
int
result
;
if
(
v
==
NULL
)
{
PyErr_SetString
(
PyExc_ValueError
,
"can not delete jar of cached object"
);
return
-
1
;
}
if
(
PyObject_Cmp
(
self
->
jar
,
v
,
&
result
)
<
0
)
return
-
1
;
if
(
result
)
{
PyErr_SetString
(
PyExc_ValueError
,
"can not change jar of cached object"
);
return
-
1
;
}
}
Py_XINCREF
(
v
);
ASSIGN
(
self
->
jar
,
v
);
return
0
;
}
else
if
(
strcmp
(
name
+
3
,
"serial"
)
==
0
)
{
if
(
v
)
{
if
(
PyString_Check
(
v
)
&&
PyString_GET_SIZE
(
v
)
==
8
)
memcpy
(
self
->
serial
,
PyString_AS_STRING
(
v
),
8
);
else
{
PyErr_SetString
(
PyExc_ValueError
,
"_p_serial must be an 8-character string"
);
return
-
1
;
}
}
else
memset
(
self
->
serial
,
0
,
8
);
return
0
;
}
else
if
(
strcmp
(
name
+
3
,
"changed"
)
==
0
)
{
int
deactivate
=
0
;
if
(
!
v
)
{
/* delatter is used to invalidate the object
*even* if it has changed.
*/
if
(
self
->
state
!=
cPersistent_GHOST_STATE
)
self
->
state
=
cPersistent_UPTODATE_STATE
;
deactivate
=
1
;
}
else
if
(
v
==
Py_None
)
deactivate
=
1
;
if
(
deactivate
)
{
PyObject
*
res
;
PyObject
*
meth
=
PyObject_GetAttr
(
OBJECT
(
self
),
py__p_deactivate
);
if
(
meth
==
NULL
)
return
-
1
;
res
=
PyObject_CallObject
(
meth
,
NULL
);
if
(
res
)
{
Py_DECREF
(
res
);
}
else
{
/* an error occured in _p_deactivate().
It's not clear what we should do here. The code is
obviously ignoring the exception, but it shouldn't
return 0 for a getattr and set an exception. The
simplest change is to clear the exception, but that
simply masks the error.
XXX We'll print an error to stderr just like
exceptions in __del__(). It would probably be
better to log it but that would be painful from C.
*/
PyErr_WriteUnraisable
(
meth
);
}
Py_DECREF
(
meth
);
return
0
;
}
if
(
PyObject_IsTrue
(
v
))
return
changed
(
self
);
if
(
self
->
state
>=
0
)
self
->
state
=
cPersistent_UPTODATE_STATE
;
return
0
;
}
}
else
{
if
(
!
unghostify
(
self
))
return
-
1
;
accessed
(
self
);
if
((
!
(
*
name
==
'_'
&&
name
[
1
]
==
'v'
&&
name
[
2
]
==
'_'
))
&&
(
self
->
state
!=
cPersistent_CHANGED_STATE
&&
self
->
jar
)
&&
setattrf
)
if
(
changed
(
self
)
<
0
)
return
-
1
;
}
if
(
setattrf
)
return
setattrf
((
PyObject
*
)
self
,
oname
,
v
);
return
1
;
/* Ready for user setattr */
}
static
int
Per_setattro
(
cPersistentObject
*
self
,
PyObject
*
oname
,
PyObject
*
v
)
{
int
r
;
PyObject
*
m
;
if
(
v
&&
(((
PyExtensionClass
*
)
self
->
ob_type
)
->
class_flags
&
EXTENSIONCLASS_USERSETATTR_FLAG
))
{
r
=
_setattro
(
self
,
oname
,
v
,
NULL
);
if
(
r
<
1
)
return
r
;
m
=
PyObject_GetAttr
(
OBJECT
(
self
),
py___setattr__
);
if
(
m
)
{
ASSIGN
(
m
,
PyObject_CallFunction
(
m
,
"OO"
,
oname
,
v
));
}
else
PyErr_SetObject
(
PyExc_AttributeError
,
oname
);
}
else
if
(
!
v
&&
(((
PyExtensionClass
*
)
self
->
ob_type
)
->
class_flags
&
EXTENSIONCLASS_USERDELATTR_FLAG
)
)
{
r
=
_setattro
(
self
,
oname
,
v
,
NULL
);
if
(
r
<
1
)
return
r
;
m
=
PyObject_GetAttr
(
OBJECT
(
self
),
py___delattr__
);
if
(
m
)
{
ASSIGN
(
m
,
PyObject_CallFunction
(
m
,
"O"
,
oname
));
}
else
PyErr_SetObject
(
PyExc_AttributeError
,
oname
);
}
else
return
_setattro
(
self
,
oname
,
v
,
PyExtensionClassCAPI
->
setattro
);
if
(
m
)
{
Py_DECREF
(
m
);
return
0
;
}
return
-
1
;
}
static
PyExtensionClass
Pertype
=
{
PyObject_HEAD_INIT
(
NULL
)
0
,
/*ob_size*/
"Persistent"
,
/*tp_name*/
sizeof
(
cPersistentObject
),
/*tp_basicsize*/
0
,
/*tp_itemsize*/
/* methods */
(
destructor
)
Per_dealloc
,
/*tp_dealloc*/
(
printfunc
)
0
,
/*tp_print*/
(
getattrfunc
)
0
,
/*tp_getattr*/
(
setattrfunc
)
0
,
/*tp_setattr*/
(
cmpfunc
)
0
,
/*tp_compare*/
(
reprfunc
)
0
,
/*tp_repr*/
0
,
/*tp_as_number*/
0
,
/*tp_as_sequence*/
0
,
/*tp_as_mapping*/
(
hashfunc
)
0
,
/*tp_hash*/
(
ternaryfunc
)
0
,
/*tp_call*/
(
reprfunc
)
0
,
/*tp_str*/
(
getattrofunc
)
Per_getattro
,
/*tp_getattr with object key*/
(
setattrofunc
)
Per_setattro
,
/*tp_setattr with object key*/
/* Space for future expansion */
0L
,
0L
,
"Base class for objects that are stored in their own database records"
,
METHOD_CHAIN
(
Per_methods
),
PERSISTENCE_FLAGS
,
};
static
PyExtensionClass
Overridable
=
{
PyObject_HEAD_INIT
(
NULL
)
0
,
/*ob_size*/
"Overridable"
,
/*tp_name*/
sizeof
(
cPersistentObject
),
/*tp_basicsize*/
0
,
/*tp_itemsize*/
/* methods */
(
destructor
)
Per_dealloc
,
/*tp_dealloc*/
(
printfunc
)
0
,
/*tp_print*/
(
getattrfunc
)
0
,
/*tp_getattr*/
(
setattrfunc
)
0
,
/*tp_setattr*/
(
cmpfunc
)
0
,
/*tp_compare*/
(
reprfunc
)
0
,
/*tp_repr*/
0
,
/*tp_as_number*/
0
,
/*tp_as_sequence*/
0
,
/*tp_as_mapping*/
(
hashfunc
)
0
,
/*tp_hash*/
(
ternaryfunc
)
0
,
/*tp_call*/
(
reprfunc
)
0
,
/*tp_str*/
(
getattrofunc
)
Per_getattro
,
/*tp_getattr with object key*/
(
setattrofunc
)
Per_setattro
,
/*tp_setattr with object key*/
/* Space for future expansion */
0L
,
0L
,
"Hacked base class used in Zope's App.Uninstalled.BrokenClass
\n\n
"
"Not sure if this is still needed"
,
METHOD_CHAIN
(
Per_methods
),
EXTENSIONCLASS_BASICNEW_FLAG
|
PERSISTENT_TYPE_FLAG
};
/* End of code for Persistent objects */
/* -------------------------------------------------------- */
/* List of methods defined in the module */
#ifdef DEBUG_LOG
static
PyObject
*
set_debug_log
(
PyObject
*
ignored
,
PyObject
*
args
)
{
Py_INCREF
(
args
);
ASSIGN
(
debug_log
,
args
);
if
(
debug_log
)
idebug_log
=-
1
;
else
idebug_log
=
0
;
Py_INCREF
(
Py_None
);
return
Py_None
;
}
#endif
static
struct
PyMethodDef
cP_methods
[]
=
{
#ifdef DEBUG_LOG
{
"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
"
},
#endif
{
NULL
,
NULL
}
/* sentinel */
};
/* Initialization function for the module (*must* be called initcPersistence) */
typedef
int
(
*
intfunctionwithpythonarg
)(
PyObject
*
);
static
cPersistenceCAPIstruct
truecPersistenceCAPI
=
{
&
(
Pertype
.
methods
),
(
getattrofunc
)
Per_getattro
,
/*tp_getattr with object key*/
(
setattrofunc
)
Per_setattro
,
/*tp_setattr with object key*/
changed
,
accessed
,
ghostify
,
deallocated
,
(
intfunctionwithpythonarg
)
Per_setstate
,
(
pergetattr
)
Per_getattr
,
(
persetattr
)
_setattro
,
NULL
/* The percachedel slot is initialized in cPickleCache.c when
the module is loaded. It uses a function in a different
shared library. */
};
void
initcPersistence
(
void
)
{
PyObject
*
m
,
*
d
,
*
s
;
s
=
PyString_FromString
(
"ZODB.TimeStamp"
);
if
(
s
==
NULL
)
return
;
m
=
PyImport_Import
(
s
);
if
(
m
==
NULL
)
{
Py_DECREF
(
s
);
return
;
}
TimeStamp
=
PyObject_GetAttrString
(
m
,
"TimeStamp"
);
assert
(
TimeStamp
);
Py_DECREF
(
m
);
Py_DECREF
(
s
);
if
(
init_strings
()
<
0
)
return
;
m
=
Py_InitModule4
(
"cPersistence"
,
cP_methods
,
cPersistence_doc_string
,
(
PyObject
*
)
NULL
,
PYTHON_API_VERSION
);
d
=
PyModule_GetDict
(
m
);
PyExtensionClass_Export
(
d
,
"Persistent"
,
Pertype
);
PyExtensionClass_Export
(
d
,
"Overridable"
,
Overridable
);
cPersistenceCAPI
=
&
truecPersistenceCAPI
;
s
=
PyCObject_FromVoidPtr
(
cPersistenceCAPI
,
NULL
);
PyDict_SetItemString
(
d
,
"CAPI"
,
s
);
Py_XDECREF
(
s
);
}
src/Persistence/cPersistence.h
deleted
100644 → 0
View file @
4d22ca2b
/*****************************************************************************
Copyright (c) 2001, 2002 Zope Corporation and Contributors.
All Rights Reserved.
This software is subject to the provisions of the Zope Public License,
Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
FOR A PARTICULAR PURPOSE
****************************************************************************/
#ifndef CPERSISTENCE_H
#define CPERSISTENCE_H
#include "ExtensionClass.h"
#include <time.h>
typedef
struct
CPersistentRing_struct
{
struct
CPersistentRing_struct
*
prev
;
struct
CPersistentRing_struct
*
next
;
}
CPersistentRing
;
#define CACHE_HEAD \
PyObject_HEAD \
CPersistentRing ring_home; \
int non_ghost_count;
struct
ccobject_head_struct
;
typedef
struct
ccobject_head_struct
PerCache
;
#define cPersistent_HEAD \
PyObject_HEAD \
PyObject *jar; \
PyObject *oid; \
PerCache *cache; \
CPersistentRing ring; \
char serial[8]; \
signed char state; \
unsigned char reserved[3];
#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
int
(
*
persetattr
)(
PyObject
*
,
PyObject
*
,
PyObject
*
,
setattrofunc
);
typedef
PyObject
*
(
*
pergetattr
)(
PyObject
*
,
PyObject
*
,
char
*
,
getattrofunc
);
typedef
int
(
*
percachedelfunc
)(
PerCache
*
,
PyObject
*
);
typedef
struct
{
PyMethodChain
*
methods
;
getattrofunc
getattro
;
setattrofunc
setattro
;
int
(
*
changed
)(
cPersistentObject
*
);
void
(
*
accessed
)(
cPersistentObject
*
);
void
(
*
ghostify
)(
cPersistentObject
*
);
void
(
*
deallocated
)(
cPersistentObject
*
);
int
(
*
setstate
)(
PyObject
*
);
pergetattr
pergetattro
;
persetattr
persetattro
;
percachedelfunc
percachedel
;
}
cPersistenceCAPIstruct
;
#ifndef DONT_USE_CPERSISTENCECAPI
static
cPersistenceCAPIstruct
*
cPersistenceCAPI
;
#endif
#define cPersistanceModuleName "cPersistence"
#define PERSISTENT_TYPE_FLAG EXTENSIONCLASS_USER_FLAG8
/* ExtensionClass class flags for persistent base classes should
include PERSISTENCE_FLAGS.
*/
#define PERSISTENCE_FLAGS EXTENSIONCLASS_BASICNEW_FLAG | PERSISTENT_TYPE_FLAG \
| EXTENSIONCLASS_PYTHONICATTR_FLAG
#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_CHANGED(O) (cPersistenceCAPI->changed((cPersistentObject*)(O)))
#define PER_GHOSTIFY(O) (cPersistenceCAPI->ghostify((cPersistentObject*)(O)))
#define PER_ALLOW_DEACTIVATION(O) ((O)->state==cPersistent_STICKY_STATE && ((O)->state=cPersistent_UPTODATE_STATE))
#define PER_PREVENT_DEACTIVATION(O) ((O)->state==cPersistent_UPTODATE_STATE && ((O)->state=cPersistent_STICKY_STATE))
#define PER_DEL(O) (cPersistenceCAPI->deallocated((cPersistentObject*)(O)))
#define PER_USE(O) \
(((O)->state != cPersistent_GHOST_STATE \
|| (cPersistenceCAPI->setstate((PyObject*)(O)) >= 0)) \
? (((O)->state==cPersistent_UPTODATE_STATE) \
? ((O)->state=cPersistent_STICKY_STATE) : 1) : 0)
#define PER_ACCESSED(O) (cPersistenceCAPI->accessed((cPersistentObject*)(O)))
#endif
src/Persistence/cPickleCache.c
deleted
100644 → 0
View file @
4d22ca2b
/*****************************************************************************
Copyright (c) 2001, 2002 Zope Corporation and Contributors.
All Rights Reserved.
This software is subject to the provisions of the Zope Public License,
Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
FOR A PARTICULAR PURPOSE
****************************************************************************/
/*
Objects are stored under three different regimes:
Regime 1: Persistent Classes
Persistent Classes are part of ZClasses. They are stored in the
self->data dictionary, and are never garbage collected.
The klass_items() method returns a sequence of (oid,object) tuples for
every Persistent Class, which should make it possible to implement
garbage collection in Python if necessary.
Regime 2: Ghost Objects
There is no benefit to keeping a ghost object which has no external
references, therefore a weak reference scheme is used to ensure that
ghost objects are removed from memory as soon as possible, when the
last external reference is lost.
Ghost objects are stored in the self->data dictionary. Normally a
dictionary keeps a strong reference on its values, however this
reference count is 'stolen'.
This weak reference scheme leaves a dangling reference, in the
dictionary, when the last external reference is lost. To clean up this
dangling reference the persistent object dealloc function calls
self->cache->_oid_unreferenced(self->oid). The cache looks up the oid
in the dictionary, ensures it points to an object whose reference
count is zero, then removes it from the dictionary. Before removing
the object from the dictionary it must temporarily resurrect the
object in much the same way that class instances are resurrected
before their __del__ is called.
Since ghost objects are stored under a different regime to non-ghost
objects, an extra ghostify function in cPersistenceAPI replaces
self->state=GHOST_STATE assignments that were common in other
persistent classes (such as BTrees).
Regime 3: Non-Ghost Objects
Non-ghost objects are stored in two data structures: the dictionary
mapping oids to objects and a doubly-linked list that encodes the
order in which the objects were accessed. The dictionary reference is
borrowed, as it is for ghosts. The list reference is a new reference;
the list stores recently used objects, even if they are otherwise
unreferenced, to avoid loading the object from the database again.
The doubly-link-list nodes contain next and previous pointers linking
together the cache and all non-ghost persistent objects.
The node embedded in the cache is the home position. On every
attribute access a non-ghost object will relink itself just behind the
home position in the ring. Objects accessed least recently will
eventually find themselves positioned after the home position.
Occasionally other nodes are temporarily inserted in the ring as
position markers. The cache contains a ring_lock flag which must be
set and unset before and after doing so. Only if the flag is unset can
the cache assume that all nodes are either his own home node, or nodes
from persistent objects. This assumption is useful during the garbage
collection process.
The number of non-ghost objects is counted in self->non_ghost_count.
The garbage collection process consists of traversing the ring, and
deactivating (that is, turning into a ghost) every object until
self->non_ghost_count is down to the target size, or until it
reaches the home position again.
Note that objects in the sticky or changed states are still kept in
the ring, however they can not be deactivated. The garbage collection
process must skip such objects, rather than deactivating them.
*/
static
char
cPickleCache_doc_string
[]
=
"Defines the PickleCache used by ZODB Connection objects.
\n
"
"
\n
"
"$Id: cPickleCache.c,v 1.85 2003/05/30 19:20:55 jeremy Exp $
\n
"
;
#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 OBJECT(O) ((PyObject*)O)
#define DONT_USE_CPERSISTENCECAPI
#include "cPersistence.h"
#include <time.h>
#include <stddef.h>
#undef Py_FindMethod
static
PyObject
*
py__p_oid
,
*
py_reload
,
*
py__p_jar
,
*
py__p_changed
;
static
cPersistenceCAPIstruct
*
capi
;
/* Do we want 'engine noise'.... abstract debugging output useful for
visualizing cache behavior */
#if 0
#define ENGINE_NOISE(A) printf(A)
#else
#define ENGINE_NOISE(A) ((void)A)
#endif
/* This object is the pickle cache. The CACHE_HEAD macro guarantees
that layout of this struct is the same as the start of
ccobject_head in cPersistence.c */
typedef
struct
{
CACHE_HEAD
int
klass_count
;
/* count of persistent classes */
PyObject
*
data
;
/* oid -> object dict */
PyObject
*
jar
;
/* Connection object */
PyObject
*
setklassstate
;
/* ??? */
int
cache_size
;
/* target number of items in cache */
/* Most of the time the ring contains only:
* many nodes corresponding to persistent objects
* one 'home' node from the cache.
In some cases it is handy to temporarily add other types
of node into the ring as placeholders. 'ring_lock' is a boolean
indicating that someone has already done this. Currently this
is only used by the garbage collection code. */
int
ring_lock
;
/* 'cache_drain_resistance' controls how quickly the cache size will drop
when it is smaller than the configured size. A value of zero means it will
not drop below the configured size (suitable for most caches). Otherwise,
it will remove cache_non_ghost_count/cache_drain_resistance items from
the cache every time (suitable for rarely used caches, such as those
associated with Zope versions. */
int
cache_drain_resistance
;
}
ccobject
;
static
int
cc_ass_sub
(
ccobject
*
self
,
PyObject
*
key
,
PyObject
*
v
);
/* ---------------------------------------------------------------- */
#define OBJECT_FROM_RING(SELF, HERE, CTX) \
((cPersistentObject *)(((char *)here) - offsetof(cPersistentObject, ring)))
static
int
scan_gc_items
(
ccobject
*
self
,
int
target
)
{
/* This function must only be called with the ring lock held,
because it places a non-object placeholder in the ring.
*/
cPersistentObject
*
object
;
int
error
;
CPersistentRing
placeholder
;
CPersistentRing
*
here
=
self
->
ring_home
.
next
;
/* Scan through the ring until we either find the ring_home (i.e. start
* of the ring, or we've ghosted enough objects to reach the target
* size.
*/
while
(
1
)
{
/* back to the home position. stop looking */
if
(
here
==
&
self
->
ring_home
)
return
0
;
/* At this point we know that the ring only contains nodes
from persistent objects, plus our own home node. We know
this because the ring lock is held. We can safely assume
the current ring node is a persistent object now we know it
is not the home */
object
=
OBJECT_FROM_RING
(
self
,
here
,
"scan_gc_items"
);
if
(
!
object
)
return
-
1
;
/* we are small enough */
if
(
self
->
non_ghost_count
<=
target
)
return
0
;
else
if
(
object
->
state
==
cPersistent_UPTODATE_STATE
)
{
/* deactivate it. This is the main memory saver. */
/* Add a placeholder; a dummy node in the ring. We need
to do this to mark our position in the ring. It is
possible that the PyObject_SetAttr() call below will
invoke an __setattr__() hook in Python. If it does,
another thread might run; if that thread accesses a
persistent object and moves it to the head of the ring,
it might cause the gc scan to start working from the
head of the list.
*/
placeholder
.
next
=
here
->
next
;
placeholder
.
prev
=
here
;
here
->
next
->
prev
=
&
placeholder
;
here
->
next
=
&
placeholder
;
ENGINE_NOISE
(
"G"
);
/* In Python, "obj._p_changed = None" spells, ghostify */
error
=
PyObject_SetAttr
((
PyObject
*
)
object
,
py__p_changed
,
Py_None
);
/* unlink the placeholder */
placeholder
.
next
->
prev
=
placeholder
.
prev
;
placeholder
.
prev
->
next
=
placeholder
.
next
;
here
=
placeholder
.
next
;
if
(
error
)
return
-
1
;
/* problem */
}
else
{
ENGINE_NOISE
(
"."
);
here
=
here
->
next
;
}
}
}
static
PyObject
*
lockgc
(
ccobject
*
self
,
int
target_size
)
{
/* This is thread-safe because of the GIL, and there's nothing
* in between checking the ring_lock and acquiring it that calls back
* into Python.
*/
if
(
self
->
ring_lock
)
{
Py_INCREF
(
Py_None
);
return
Py_None
;
}
ENGINE_NOISE
(
"<"
);
self
->
ring_lock
=
1
;
if
(
scan_gc_items
(
self
,
target_size
))
{
self
->
ring_lock
=
0
;
return
NULL
;
}
self
->
ring_lock
=
0
;
ENGINE_NOISE
(
">
\n
"
);
Py_INCREF
(
Py_None
);
return
Py_None
;
}
static
PyObject
*
cc_incrgc
(
ccobject
*
self
,
PyObject
*
args
)
{
int
n
=
1
;
int
starting_size
=
self
->
non_ghost_count
;
int
target_size
=
self
->
cache_size
;
if
(
self
->
cache_drain_resistance
>=
1
)
{
/* This cache will gradually drain down to a small size. Check
a (small) number of objects proportional to the current size */
int
target_size_2
=
(
starting_size
-
1
-
starting_size
/
self
->
cache_drain_resistance
);
if
(
target_size_2
<
target_size
)
target_size
=
target_size_2
;
}
if
(
!
PyArg_ParseTuple
(
args
,
"|i:incrgc"
,
&
n
))
return
NULL
;
return
lockgc
(
self
,
target_size
);
}
static
PyObject
*
cc_full_sweep
(
ccobject
*
self
,
PyObject
*
args
)
{
int
dt
=
0
;
if
(
!
PyArg_ParseTuple
(
args
,
"|i:full_sweep"
,
&
dt
))
return
NULL
;
if
(
dt
==
0
)
return
lockgc
(
self
,
0
);
else
return
cc_incrgc
(
self
,
args
);
}
static
PyObject
*
cc_minimize
(
ccobject
*
self
,
PyObject
*
args
)
{
int
ignored
;
if
(
!
PyArg_ParseTuple
(
args
,
"|i:minimize"
,
&
ignored
))
return
NULL
;
return
lockgc
(
self
,
0
);
}
static
void
_invalidate
(
ccobject
*
self
,
PyObject
*
key
)
{
PyObject
*
v
=
PyDict_GetItem
(
self
->
data
,
key
);
if
(
!
v
)
return
;
if
(
PyExtensionClass_Check
(
v
))
{
if
(
v
->
ob_refcnt
<=
1
)
{
self
->
klass_count
--
;
if
(
PyDict_DelItem
(
self
->
data
,
key
)
<
0
)
PyErr_Clear
();
}
else
{
v
=
PyObject_CallFunction
(
self
->
setklassstate
,
"O"
,
v
);
if
(
v
)
Py_DECREF
(
v
);
else
PyErr_Clear
();
}
}
else
{
if
(
PyObject_DelAttr
(
v
,
py__p_changed
)
<
0
)
PyErr_Clear
();
}
}
static
PyObject
*
cc_invalidate
(
ccobject
*
self
,
PyObject
*
args
)
{
PyObject
*
inv
,
*
key
,
*
v
;
int
i
=
0
;
if
(
PyArg_ParseTuple
(
args
,
"O!"
,
&
PyDict_Type
,
&
inv
))
{
while
(
PyDict_Next
(
inv
,
&
i
,
&
key
,
&
v
))
_invalidate
(
self
,
key
);
PyDict_Clear
(
inv
);
}
else
{
PyErr_Clear
();
if
(
!
PyArg_ParseTuple
(
args
,
"O:invalidate"
,
&
inv
))
return
NULL
;
if
(
PyString_Check
(
inv
))
_invalidate
(
self
,
inv
);
else
{
int
l
;
PyErr_Clear
();
l
=
PyObject_Length
(
inv
);
if
(
l
<
0
)
return
NULL
;
for
(
i
=
l
;
--
i
>=
0
;
)
{
key
=
PySequence_GetItem
(
inv
,
i
);
if
(
!
key
)
return
NULL
;
_invalidate
(
self
,
key
);
Py_DECREF
(
key
);
}
/* XXX Do we really want to modify the input? */
PySequence_DelSlice
(
inv
,
0
,
l
);
}
}
Py_INCREF
(
Py_None
);
return
Py_None
;
}
static
PyObject
*
cc_get
(
ccobject
*
self
,
PyObject
*
args
)
{
PyObject
*
r
,
*
key
,
*
d
=
NULL
;
if
(
!
PyArg_ParseTuple
(
args
,
"O|O:get"
,
&
key
,
&
d
))
return
NULL
;
r
=
PyDict_GetItem
(
self
->
data
,
key
);
if
(
!
r
)
{
if
(
d
)
{
r
=
d
;
}
else
{
PyErr_SetObject
(
PyExc_KeyError
,
key
);
return
NULL
;
}
}
Py_INCREF
(
r
);
return
r
;
}
static
PyObject
*
cc_klass_items
(
ccobject
*
self
,
PyObject
*
args
)
{
PyObject
*
l
,
*
k
,
*
v
;
int
p
=
0
;
if
(
!
PyArg_ParseTuple
(
args
,
":klass_items"
))
return
NULL
;
l
=
PyList_New
(
PyDict_Size
(
self
->
data
));
if
(
l
==
NULL
)
return
NULL
;
while
(
PyDict_Next
(
self
->
data
,
&
p
,
&
k
,
&
v
))
{
if
(
PyExtensionClass_Check
(
v
))
{
v
=
Py_BuildValue
(
"OO"
,
k
,
v
);
if
(
v
==
NULL
)
{
Py_DECREF
(
l
);
return
NULL
;
}
if
(
PyList_Append
(
l
,
v
)
<
0
)
{
Py_DECREF
(
v
);
Py_DECREF
(
l
);
return
NULL
;
}
Py_DECREF
(
v
);
}
}
return
l
;
}
static
PyObject
*
cc_lru_items
(
ccobject
*
self
,
PyObject
*
args
)
{
PyObject
*
l
;
CPersistentRing
*
here
;
if
(
!
PyArg_ParseTuple
(
args
,
":lru_items"
))
return
NULL
;
if
(
self
->
ring_lock
)
{
/* When the ring lock is held, we have no way of know which
ring nodes belong to persistent objects, and which a
placeholders. */
PyErr_SetString
(
PyExc_ValueError
,
".lru_items() is unavailable during garbage collection"
);
return
NULL
;
}
l
=
PyList_New
(
0
);
if
(
l
==
NULL
)
return
NULL
;
here
=
self
->
ring_home
.
next
;
while
(
here
!=
&
self
->
ring_home
)
{
PyObject
*
v
;
cPersistentObject
*
object
=
OBJECT_FROM_RING
(
self
,
here
,
"cc_items"
);
if
(
object
==
NULL
)
{
Py_DECREF
(
l
);
return
NULL
;
}
v
=
Py_BuildValue
(
"OO"
,
object
->
oid
,
object
);
if
(
v
==
NULL
)
{
Py_DECREF
(
l
);
return
NULL
;
}
if
(
PyList_Append
(
l
,
v
)
<
0
)
{
Py_DECREF
(
v
);
Py_DECREF
(
l
);
return
NULL
;
}
Py_DECREF
(
v
);
here
=
here
->
next
;
}
return
l
;
}
/* Be very careful about calling clear().
It removes all non-ghost objects from the ring without otherwise
removing them from the cache. The method should only be called
after the cache is no longer in use.
*/
static
PyObject
*
cc_clear
(
ccobject
*
self
,
PyObject
*
args
)
{
CPersistentRing
*
here
;
if
(
!
PyArg_ParseTuple
(
args
,
":clear"
))
return
NULL
;
if
(
self
->
ring_lock
)
{
/* When the ring lock is held, we have no way of know which
ring nodes belong to persistent objects, and which a
placeholders. */
PyErr_SetString
(
PyExc_ValueError
,
".lru_items() is unavailable during garbage collection"
);
return
NULL
;
}
self
->
ring_lock
=
1
;
while
((
here
=
self
->
ring_home
.
next
)
!=
&
self
->
ring_home
)
{
cPersistentObject
*
o
=
OBJECT_FROM_RING
(
self
,
here
,
"clear"
);
self
->
non_ghost_count
--
;
o
->
ring
.
next
->
prev
=
&
self
->
ring_home
;
self
->
ring_home
.
next
=
o
->
ring
.
next
;
o
->
ring
.
next
=
NULL
;
o
->
ring
.
prev
=
NULL
;
Py_DECREF
(
o
);
}
self
->
ring_lock
=
0
;
Py_INCREF
(
Py_None
);
return
Py_None
;
}
static
int
cc_oid_unreferenced
(
ccobject
*
self
,
PyObject
*
oid
)
{
/* This is called by the persistent object deallocation function
when the reference count on a persistent object reaches
zero. We need to fix up our dictionary; its reference is now
dangling because we stole its reference count. Be careful to
not release the global interpreter lock until this is
complete. */
PyObject
*
v
;
v
=
PyDict_GetItem
(
self
->
data
,
oid
);
if
(
v
==
NULL
)
{
PyErr_SetObject
(
PyExc_KeyError
,
oid
);
return
-
1
;
}
assert
(
v
->
ob_refcnt
==
0
);
/* Need to be very hairy here because a dictionary is about
to decref an already deleted object.
*/
#ifdef Py_TRACE_REFS
/* This is called from the deallocation function after the
interpreter has untracked the reference. Track it again.
*/
_Py_NewReference
(
v
);
/* Don't increment total refcount as a result of the
shenanigans played in this function. The _Py_NewReference()
call above creates artificial references to v.
*/
_Py_RefTotal
--
;
assert
(
v
->
ob_type
);
#else
Py_INCREF
(
v
);
#endif
assert
(
v
->
ob_refcnt
==
1
);
/* Incremement the refcount again, because delitem is going to
DECREF it. If it's refcount reached zero again, we'd call back to
the dealloc function that called us.
*/
Py_INCREF
(
v
);
/* XXX Should we call _Py_ForgetReference() on error exit? */
if
(
PyDict_DelItem
(
self
->
data
,
oid
)
<
0
)
return
-
1
;
Py_DECREF
((
ccobject
*
)((
cPersistentObject
*
)
v
)
->
cache
);
if
(
v
->
ob_refcnt
!=
1
)
{
PyErr_SetString
(
PyExc_ValueError
,
"refcount is not 1 after removal from dict"
);
return
-
1
;
}
/* Undo the temporary resurrection.
Don't DECREF the object, because this function is called from
the object's dealloc function. If the refcnt reaches zero, it
will all be invoked recursively.
*/
_Py_ForgetReference
(
v
);
return
0
;
}
static
PyObject
*
cc_ringlen
(
ccobject
*
self
,
PyObject
*
args
)
{
CPersistentRing
*
here
;
int
c
=
0
;
if
(
!
PyArg_ParseTuple
(
args
,
":ringlen"
))
return
NULL
;
for
(
here
=
self
->
ring_home
.
next
;
here
!=
&
self
->
ring_home
;
here
=
here
->
next
)
c
++
;
return
PyInt_FromLong
(
c
);
}
static
struct
PyMethodDef
cc_methods
[]
=
{
{
"lru_items"
,
(
PyCFunction
)
cc_lru_items
,
METH_VARARGS
,
"List (oid, object) pairs from the lru list, as 2-tuples.
\n
"
},
{
"klass_items"
,
(
PyCFunction
)
cc_klass_items
,
METH_VARARGS
,
"List (oid, object) pairs of cached persistent classes.
\n
"
},
{
"full_sweep"
,
(
PyCFunction
)
cc_full_sweep
,
METH_VARARGS
,
"full_sweep([age]) -- Perform a full sweep of the cache
\n\n
"
"Supported for backwards compatibility. If the age argument is 0,
\n
"
"behaves like minimize(). Otherwise, behaves like incrgc()."
},
{
"minimize"
,
(
PyCFunction
)
cc_minimize
,
METH_VARARGS
,
"minimize([ignored]) -- Remove as many objects as possible
\n\n
"
"Ghostify all objects that are not modified. Takes an optional
\n
"
"argument, but ignores it."
},
{
"incrgc"
,
(
PyCFunction
)
cc_incrgc
,
METH_VARARGS
,
"incrgc([n]) -- Perform incremental garbage collection
\n\n
"
"Some other implementations support an optional parameter 'n' which
\n
"
"indicates a repetition count; this value is ignored.
\n
"
},
{
"invalidate"
,
(
PyCFunction
)
cc_invalidate
,
METH_VARARGS
,
"invalidate(oids) -- invalidate one, many, or all ids"
},
{
"get"
,
(
PyCFunction
)
cc_get
,
METH_VARARGS
,
"get(key [, default]) -- get an item, or a default"
},
{
"ringlen"
,
(
PyCFunction
)
cc_ringlen
,
METH_VARARGS
,
"ringlen() -- Returns number of non-ghost items in cache."
},
{
"clear"
,
(
PyCFunction
)
cc_clear
,
METH_VARARGS
,
"clear() -- remove all objects from the cache"
},
{
NULL
,
NULL
}
/* sentinel */
};
static
void
cc_dealloc
(
ccobject
*
self
)
{
Py_XDECREF
(
self
->
data
);
Py_XDECREF
(
self
->
jar
);
Py_XDECREF
(
self
->
setklassstate
);
PyMem_DEL
(
self
);
}
static
PyObject
*
cc_getattr
(
ccobject
*
self
,
char
*
name
)
{
if
(
*
name
==
'c'
)
{
if
(
strcmp
(
name
,
"cache_age"
)
==
0
)
return
PyInt_FromLong
(
0
);
/* this cache does not use this value */
if
(
strcmp
(
name
,
"cache_size"
)
==
0
)
return
PyInt_FromLong
(
self
->
cache_size
);
if
(
strcmp
(
name
,
"cache_drain_resistance"
)
==
0
)
return
PyInt_FromLong
(
self
->
cache_drain_resistance
);
if
(
strcmp
(
name
,
"cache_non_ghost_count"
)
==
0
)
return
PyInt_FromLong
(
self
->
non_ghost_count
);
if
(
strcmp
(
name
,
"cache_klass_count"
)
==
0
)
return
PyInt_FromLong
(
self
->
klass_count
);
if
(
strcmp
(
name
,
"cache_data"
)
==
0
)
{
/* now a copy of our data; the ring is too fragile */
return
PyDict_Copy
(
self
->
data
);
}
}
if
(
strcmp
(
name
,
"items"
)
==
0
)
return
PyObject_GetAttrString
(
self
->
data
,
name
);
return
Py_FindMethod
(
cc_methods
,
(
PyObject
*
)
self
,
name
);
}
static
int
cc_setattr
(
ccobject
*
self
,
char
*
name
,
PyObject
*
value
)
{
if
(
value
)
{
int
v
;
if
(
strcmp
(
name
,
"cache_age"
)
==
0
)
{
/* this cache doesnt use the age */
return
0
;
}
if
(
strcmp
(
name
,
"cache_size"
)
==
0
)
{
UNLESS
(
PyArg_Parse
(
value
,
"i"
,
&
v
))
return
-
1
;
self
->
cache_size
=
v
;
return
0
;
}
if
(
strcmp
(
name
,
"cache_drain_resistance"
)
==
0
)
{
UNLESS
(
PyArg_Parse
(
value
,
"i"
,
&
v
))
return
-
1
;
self
->
cache_drain_resistance
=
v
;
return
0
;
}
}
PyErr_SetString
(
PyExc_AttributeError
,
name
);
return
-
1
;
}
static
int
cc_length
(
ccobject
*
self
)
{
return
PyObject_Length
(
self
->
data
);
}
static
PyObject
*
cc_subscript
(
ccobject
*
self
,
PyObject
*
key
)
{
PyObject
*
r
;
r
=
PyDict_GetItem
(
self
->
data
,
key
);
if
(
r
==
NULL
)
{
PyErr_SetObject
(
PyExc_KeyError
,
key
);
return
NULL
;
}
Py_INCREF
(
r
);
return
r
;
}
static
int
cc_add_item
(
ccobject
*
self
,
PyObject
*
key
,
PyObject
*
v
)
{
int
result
;
PyObject
*
oid
,
*
object_again
,
*
jar
;
cPersistentObject
*
p
;
if
(
PyExtensionClass_Check
(
v
))
{
/* Its a persistent class, such as a ZClass. Thats ok. */
}
else
if
(
PyExtensionInstance_Check
(
v
)
&&
(((
PyExtensionClass
*
)(
v
->
ob_type
))
->
class_flags
&
PERSISTENT_TYPE_FLAG
)
&&
(
v
->
ob_type
->
tp_basicsize
>=
sizeof
(
cPersistentObject
))
)
{
/* Its and instance of a persistent class, (ie Python classeses that
derive from Persistence.Persistent, BTrees, etc). Thats ok. */
}
else
{
PyErr_SetString
(
PyExc_TypeError
,
"Cache values must be persistent objects."
);
return
-
1
;
}
/* Can't access v->oid directly because the object might be a
* persistent class.
*/
oid
=
PyObject_GetAttr
(
v
,
py__p_oid
);
if
(
oid
==
NULL
)
return
-
1
;
if
(
!
PyString_Check
(
oid
))
{
PyErr_Format
(
PyExc_TypeError
,
"Cached object oid must be a string, not a %s"
,
oid
->
ob_type
->
tp_name
);
return
-
1
;
}
/* we know they are both strings.
* now check if they are the same string.
*/
result
=
PyObject_Compare
(
key
,
oid
);
if
(
PyErr_Occurred
())
{
Py_DECREF
(
oid
);
return
-
1
;
}
Py_DECREF
(
oid
);
if
(
result
)
{
PyErr_SetString
(
PyExc_ValueError
,
"Cache key does not match oid"
);
return
-
1
;
}
/* useful sanity check, but not strictly an invariant of this class */
jar
=
PyObject_GetAttr
(
v
,
py__p_jar
);
if
(
jar
==
NULL
)
return
-
1
;
if
(
jar
==
Py_None
)
{
Py_DECREF
(
jar
);
PyErr_SetString
(
PyExc_ValueError
,
"Cached object jar missing"
);
return
-
1
;
}
Py_DECREF
(
jar
);
object_again
=
PyDict_GetItem
(
self
->
data
,
key
);
if
(
object_again
)
{
if
(
object_again
!=
v
)
{
PyErr_SetString
(
PyExc_ValueError
,
"Can not re-register object under a different oid"
);
return
-
1
;
}
else
{
/* re-register under the same oid - no work needed */
return
0
;
}
}
if
(
PyExtensionClass_Check
(
v
))
{
if
(
PyDict_SetItem
(
self
->
data
,
key
,
v
)
<
0
)
return
-
1
;
self
->
klass_count
++
;
return
0
;
}
else
{
PerCache
*
cache
=
((
cPersistentObject
*
)
v
)
->
cache
;
if
(
cache
)
{
if
(
cache
!=
(
PerCache
*
)
self
)
/* This object is already in a different cache. */
PyErr_SetString
(
PyExc_ValueError
,
"Cache values may only be in one cache."
);
return
-
1
;
}
/* else:
This object is already one of ours, which is ok. It
would be very strange if someone was trying to register
the same object under a different key.
*/
}
if
(
PyDict_SetItem
(
self
->
data
,
key
,
v
)
<
0
)
return
-
1
;
/* the dict should have a borrowed reference */
Py_DECREF
(
v
);
p
=
(
cPersistentObject
*
)
v
;
Py_INCREF
(
self
);
p
->
cache
=
(
PerCache
*
)
self
;
if
(
p
->
state
>=
0
)
{
/* insert this non-ghost object into the ring just
behind the home position. */
self
->
non_ghost_count
++
;
p
->
ring
.
next
=
&
self
->
ring_home
;
p
->
ring
.
prev
=
self
->
ring_home
.
prev
;
self
->
ring_home
.
prev
->
next
=
&
p
->
ring
;
self
->
ring_home
.
prev
=
&
p
->
ring
;
/* this list should have a new reference to the object */
Py_INCREF
(
v
);
}
return
0
;
}
static
int
cc_del_item
(
ccobject
*
self
,
PyObject
*
key
)
{
PyObject
*
v
;
cPersistentObject
*
p
;
/* unlink this item from the ring */
v
=
PyDict_GetItem
(
self
->
data
,
key
);
if
(
v
==
NULL
)
return
-
1
;
if
(
PyExtensionClass_Check
(
v
))
{
self
->
klass_count
--
;
}
else
{
p
=
(
cPersistentObject
*
)
v
;
if
(
p
->
state
>=
0
)
{
self
->
non_ghost_count
--
;
p
->
ring
.
next
->
prev
=
p
->
ring
.
prev
;
p
->
ring
.
prev
->
next
=
p
->
ring
.
next
;
p
->
ring
.
prev
=
NULL
;
p
->
ring
.
next
=
NULL
;
/* The DelItem below will account for the reference
held by the list. */
}
else
{
/* This is a ghost object, so we havent kept a reference
count on it. For it have stayed alive this long
someone else must be keeping a reference to
it. Therefore we need to temporarily give it back a
reference count before calling DelItem below */
Py_INCREF
(
v
);
}
Py_DECREF
((
PyObject
*
)
p
->
cache
);
p
->
cache
=
NULL
;
}
if
(
PyDict_DelItem
(
self
->
data
,
key
)
<
0
)
{
PyErr_SetString
(
PyExc_RuntimeError
,
"unexpectedly couldn't remove key in cc_ass_sub"
);
return
-
1
;
}
return
0
;
}
static
int
cc_ass_sub
(
ccobject
*
self
,
PyObject
*
key
,
PyObject
*
v
)
{
if
(
!
PyString_Check
(
key
))
{
PyErr_Format
(
PyExc_TypeError
,
"cPickleCache key must be a string, not a %s"
,
key
->
ob_type
->
tp_name
);
return
-
1
;
}
if
(
v
)
return
cc_add_item
(
self
,
key
,
v
);
else
return
cc_del_item
(
self
,
key
);
}
static
PyMappingMethods
cc_as_mapping
=
{
(
inquiry
)
cc_length
,
/*mp_length*/
(
binaryfunc
)
cc_subscript
,
/*mp_subscript*/
(
objobjargproc
)
cc_ass_sub
,
/*mp_ass_subscript*/
};
static
PyTypeObject
Cctype
=
{
PyObject_HEAD_INIT
(
NULL
)
0
,
/*ob_size*/
"cPickleCache"
,
/*tp_name*/
sizeof
(
ccobject
),
/*tp_basicsize*/
0
,
/*tp_itemsize*/
/* methods */
(
destructor
)
cc_dealloc
,
/*tp_dealloc*/
(
printfunc
)
0
,
/*tp_print*/
(
getattrfunc
)
cc_getattr
,
/*tp_getattr*/
(
setattrfunc
)
cc_setattr
,
/*tp_setattr*/
(
cmpfunc
)
0
,
/*tp_compare*/
(
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
)
0
,
/*tp_str*/
};
static
ccobject
*
newccobject
(
PyObject
*
jar
,
int
cache_size
)
{
ccobject
*
self
;
self
=
PyObject_NEW
(
ccobject
,
&
Cctype
);
if
(
self
==
NULL
)
return
NULL
;
self
->
setklassstate
=
self
->
jar
=
NULL
;
self
->
data
=
PyDict_New
();
if
(
self
->
data
==
NULL
)
{
Py_DECREF
(
self
);
return
NULL
;
}
self
->
setklassstate
=
PyObject_GetAttrString
(
jar
,
"setklassstate"
);
if
(
self
->
setklassstate
==
NULL
)
{
Py_DECREF
(
self
);
return
NULL
;
}
self
->
jar
=
jar
;
Py_INCREF
(
jar
);
self
->
cache_size
=
cache_size
;
self
->
non_ghost_count
=
0
;
self
->
klass_count
=
0
;
self
->
cache_drain_resistance
=
0
;
self
->
ring_lock
=
0
;
self
->
ring_home
.
next
=
&
self
->
ring_home
;
self
->
ring_home
.
prev
=
&
self
->
ring_home
;
return
self
;
}
static
PyObject
*
cCM_new
(
PyObject
*
self
,
PyObject
*
args
)
{
int
cache_size
=
100
;
PyObject
*
jar
;
if
(
!
PyArg_ParseTuple
(
args
,
"O|i"
,
&
jar
,
&
cache_size
))
return
NULL
;
return
(
PyObject
*
)
newccobject
(
jar
,
cache_size
);
}
static
struct
PyMethodDef
cCM_methods
[]
=
{
{
"PickleCache"
,
(
PyCFunction
)
cCM_new
,
METH_VARARGS
},
{
NULL
,
NULL
}
/* sentinel */
};
void
initcPickleCache
(
void
)
{
PyObject
*
m
,
*
d
;
Cctype
.
ob_type
=
&
PyType_Type
;
if
(
!
ExtensionClassImported
)
return
;
capi
=
(
cPersistenceCAPIstruct
*
)
PyCObject_Import
(
"cPersistence"
,
"CAPI"
);
if
(
!
capi
)
return
;
capi
->
percachedel
=
(
percachedelfunc
)
cc_oid_unreferenced
;
m
=
Py_InitModule4
(
"cPickleCache"
,
cCM_methods
,
cPickleCache_doc_string
,
(
PyObject
*
)
NULL
,
PYTHON_API_VERSION
);
py_reload
=
PyString_InternFromString
(
"reload"
);
py__p_jar
=
PyString_InternFromString
(
"_p_jar"
);
py__p_changed
=
PyString_InternFromString
(
"_p_changed"
);
py__p_oid
=
PyString_InternFromString
(
"_p_oid"
);
d
=
PyModule_GetDict
(
m
);
PyDict_SetItemString
(
d
,
"cache_variant"
,
PyString_FromString
(
"stiff/c"
));
}
src/persistent/cPersistence.c
deleted
100644 → 0
View file @
4d22ca2b
/*****************************************************************************
Copyright (c) 2001, 2002 Zope Corporation and Contributors.
All Rights Reserved.
This software is subject to the provisions of the Zope Public License,
Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
FOR A PARTICULAR PURPOSE
****************************************************************************/
static
char
cPersistence_doc_string
[]
=
"Defines Persistent mixin class for persistent objects.
\n
"
"
\n
"
"$Id: cPersistence.c,v 1.72 2003/05/30 19:20:55 jeremy Exp $
\n
"
;
#include "cPersistence.h"
struct
ccobject_head_struct
{
CACHE_HEAD
};
#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 OBJECT(V) ((PyObject*)(V))
static
PyObject
*
py_keys
,
*
py_setstate
,
*
py___dict__
,
*
py_timeTime
;
static
PyObject
*
py__p_changed
,
*
py__p_deactivate
;
static
PyObject
*
py___getattr__
,
*
py___setattr__
,
*
py___delattr__
;
static
PyObject
*
TimeStamp
;
#ifdef DEBUG_LOG
static
PyObject
*
debug_log
=
0
;
static
int
idebug_log
=
0
;
static
void
*
call_debug
(
char
*
event
,
cPersistentObject
*
self
)
{
PyObject
*
r
;
/*
printf("%s %p\n",event,self->ob_type->tp_name);
*/
r
=
PyObject_CallFunction
(
debug_log
,
"s(sOi)"
,
event
,
self
->
ob_type
->
tp_name
,
self
->
oid
,
self
->
state
);
Py_XDECREF
(
r
);
}
#endif
static
int
init_strings
(
void
)
{
#define INIT_STRING(S) if (! (py_ ## S = PyString_FromString(#S))) return -1;
INIT_STRING
(
keys
);
INIT_STRING
(
setstate
);
INIT_STRING
(
timeTime
);
INIT_STRING
(
__dict__
);
INIT_STRING
(
_p_changed
);
INIT_STRING
(
_p_deactivate
);
INIT_STRING
(
__getattr__
);
INIT_STRING
(
__setattr__
);
INIT_STRING
(
__delattr__
);
#undef INIT_STRING
return
0
;
}
static
int
checknoargs
(
PyObject
*
args
)
{
if
(
!
PyTuple_Check
(
args
))
return
0
;
if
(
PyTuple_GET_SIZE
(
args
)
!=
0
)
{
PyErr_Format
(
PyExc_TypeError
,
"function takes exactly 0 arguments (%d given)"
,
PyTuple_GET_SIZE
(
args
));
return
0
;
}
return
1
;
}
static
PyObject
*
callmethod
(
PyObject
*
self
,
PyObject
*
name
)
{
self
=
PyObject_GetAttr
(
self
,
name
);
if
(
self
)
ASSIGN
(
self
,
PyObject_CallObject
(
self
,
NULL
));
return
self
;
}
static
PyObject
*
callmethod1
(
PyObject
*
self
,
PyObject
*
name
,
PyObject
*
arg
)
{
self
=
PyObject_GetAttr
(
self
,
name
);
UNLESS
(
self
)
return
NULL
;
name
=
PyTuple_New
(
1
);
UNLESS
(
name
)
{
Py_DECREF
(
self
);
return
NULL
;
}
PyTuple_SET_ITEM
(
name
,
0
,
arg
);
ASSIGN
(
self
,
PyObject_CallObject
(
self
,
name
));
PyTuple_SET_ITEM
(
name
,
0
,
NULL
);
Py_DECREF
(
name
);
return
self
;
}
static
void
ghostify
(
cPersistentObject
*
);
/* Load the state of the object, unghostifying it. Upon success, return 1.
* If an error occurred, re-ghostify the object and return 0.
*/
static
int
unghostify
(
cPersistentObject
*
self
)
{
if
(
self
->
state
<
0
&&
self
->
jar
)
{
PyObject
*
r
;
/* XXX Is it ever possibly to not have a cache? */
if
(
self
->
cache
)
{
CPersistentRing
*
home
=
&
self
->
cache
->
ring_home
;
/* Create a node in the ring for this unghostified object. */
self
->
cache
->
non_ghost_count
++
;
self
->
ring
.
next
=
home
;
self
->
ring
.
prev
=
home
->
prev
;
home
->
prev
->
next
=
&
self
->
ring
;
home
->
prev
=
&
self
->
ring
;
Py_INCREF
(
self
);
}
self
->
state
=
cPersistent_CHANGED_STATE
;
/* Call the object's __setstate__() */
r
=
callmethod1
(
self
->
jar
,
py_setstate
,
(
PyObject
*
)
self
);
if
(
r
==
NULL
)
{
ghostify
(
self
);
return
0
;
}
self
->
state
=
cPersistent_UPTODATE_STATE
;
Py_DECREF
(
r
);
}
return
1
;
}
/****************************************************************************/
staticforward
PyExtensionClass
Pertype
;
static
void
accessed
(
cPersistentObject
*
self
)
{
/* Do nothing unless the object is in a cache and not a ghost. */
if
(
self
->
cache
&&
self
->
state
>=
0
&&
self
->
ring
.
next
)
{
CPersistentRing
*
home
=
&
self
->
cache
->
ring_home
;
self
->
ring
.
prev
->
next
=
self
->
ring
.
next
;
self
->
ring
.
next
->
prev
=
self
->
ring
.
prev
;
self
->
ring
.
next
=
home
;
self
->
ring
.
prev
=
home
->
prev
;
home
->
prev
->
next
=
&
self
->
ring
;
home
->
prev
=
&
self
->
ring
;
}
}
static
void
ghostify
(
cPersistentObject
*
self
)
{
/* are we already a ghost? */
if
(
self
->
state
==
cPersistent_GHOST_STATE
)
return
;
/* XXX is it ever possible to not have a cache? */
if
(
self
->
cache
==
NULL
)
{
self
->
state
=
cPersistent_GHOST_STATE
;
return
;
}
/* If the cache has been cleared, then a non-ghost object
isn't in the ring any longer.
*/
if
(
self
->
ring
.
next
==
NULL
)
return
;
/* if we're ghostifying an object, we better have some non-ghosts */
assert
(
self
->
cache
->
non_ghost_count
>
0
);
self
->
cache
->
non_ghost_count
--
;
self
->
ring
.
next
->
prev
=
self
->
ring
.
prev
;
self
->
ring
.
prev
->
next
=
self
->
ring
.
next
;
self
->
ring
.
prev
=
NULL
;
self
->
ring
.
next
=
NULL
;
self
->
state
=
cPersistent_GHOST_STATE
;
/* We remove the reference to the just ghosted object that the ring
* holds. Note that the dictionary of oids->objects has an uncounted
* reference, so if the ring's reference was the only one, this frees
* the ghost object. Note further that the object's dealloc knows to
* inform the dictionary that it is going away.
*/
Py_DECREF
(
self
);
}
static
void
deallocated
(
cPersistentObject
*
self
)
{
if
(
self
->
state
>=
0
)
ghostify
(
self
);
if
(
self
->
cache
)
{
/* XXX This function shouldn't be able to fail? If not, maybe
it shouldn't set an exception either.
*/
if
(
cPersistenceCAPI
->
percachedel
(
self
->
cache
,
self
->
oid
)
<
0
)
PyErr_Clear
();
/* I don't think this should ever happen */
}
Py_XDECREF
(
self
->
jar
);
Py_XDECREF
(
self
->
oid
);
}
static
int
changed
(
cPersistentObject
*
self
)
{
if
((
self
->
state
==
cPersistent_UPTODATE_STATE
||
self
->
state
==
cPersistent_STICKY_STATE
)
&&
self
->
jar
)
{
PyObject
*
meth
,
*
arg
,
*
result
;
static
PyObject
*
s_register
;
if
(
s_register
==
NULL
)
s_register
=
PyString_InternFromString
(
"register"
);
meth
=
PyObject_GetAttr
((
PyObject
*
)
self
->
jar
,
s_register
);
if
(
meth
==
NULL
)
return
-
1
;
arg
=
PyTuple_New
(
1
);
if
(
arg
==
NULL
)
{
Py_DECREF
(
meth
);
return
-
1
;
}
PyTuple_SET_ITEM
(
arg
,
0
,
(
PyObject
*
)
self
);
result
=
PyEval_CallObject
(
meth
,
arg
);
PyTuple_SET_ITEM
(
arg
,
0
,
NULL
);
Py_DECREF
(
arg
);
Py_DECREF
(
meth
);
if
(
result
==
NULL
)
return
-
1
;
Py_DECREF
(
result
);
self
->
state
=
cPersistent_CHANGED_STATE
;
}
return
0
;
}
static
PyObject
*
Per___changed__
(
cPersistentObject
*
self
,
PyObject
*
args
)
{
PyObject
*
v
=
NULL
;
if
(
args
&&
!
PyArg_ParseTuple
(
args
,
"|O:__changed__"
,
&
v
))
return
NULL
;
if
(
!
v
)
return
PyObject_GetAttr
(
OBJECT
(
self
),
py__p_changed
);
if
(
PyObject_IsTrue
(
v
))
{
if
(
changed
(
self
)
<
0
)
return
NULL
;
}
else
if
(
self
->
state
>=
0
)
self
->
state
=
cPersistent_UPTODATE_STATE
;
Py_INCREF
(
Py_None
);
return
Py_None
;
}
static
PyObject
*
Per__p_deactivate
(
cPersistentObject
*
self
,
PyObject
*
args
)
{
PyObject
*
dict
,
*
dict2
=
NULL
;
#ifdef DEBUG_LOG
if
(
idebug_log
<
0
)
call_debug
(
"reinit"
,
self
);
#endif
if
(
args
&&
!
checknoargs
(
args
))
return
NULL
;
if
(
self
->
state
==
cPersistent_UPTODATE_STATE
&&
self
->
jar
&&
HasInstDict
(
self
)
&&
(
dict
=
INSTANCE_DICT
(
self
)))
{
dict2
=
PyDict_Copy
(
dict
);
PyDict_Clear
(
dict
);
/* Note that we need to set to ghost state unless we are
called directly. Methods that override this need to
do the same! */
ghostify
(
self
);
}
/* need to delay releasing the last reference on instance attributes
until after we have finished accounting for losing our state */
if
(
dict2
)
{
PyDict_Clear
(
dict2
);
Py_DECREF
(
dict2
);
}
Py_INCREF
(
Py_None
);
return
Py_None
;
}
/* Load the object's state if necessary and become sticky */
static
int
Per_setstate
(
cPersistentObject
*
self
)
{
if
(
!
unghostify
(
self
))
return
-
1
;
self
->
state
=
cPersistent_STICKY_STATE
;
return
0
;
}
static
PyObject
*
Per__getstate__
(
cPersistentObject
*
self
,
PyObject
*
args
)
{
PyObject
*
__dict__
,
*
d
=
0
;
if
(
!
checknoargs
(
args
))
return
NULL
;
#ifdef DEBUG_LOG
if
(
idebug_log
<
0
)
call_debug
(
"get"
,
self
);
#endif
if
(
!
unghostify
(
self
))
return
NULL
;
if
(
HasInstDict
(
self
)
&&
(
__dict__
=
INSTANCE_DICT
(
self
)))
{
PyObject
*
k
,
*
v
;
int
pos
;
char
*
ck
;
for
(
pos
=
0
;
PyDict_Next
(
__dict__
,
&
pos
,
&
k
,
&
v
);
)
{
if
(
PyString_Check
(
k
)
&&
(
ck
=
PyString_AS_STRING
(
k
))
&&
(
*
ck
==
'_'
&&
ck
[
1
]
==
'v'
&&
ck
[
2
]
==
'_'
))
{
if
((
d
=
PyDict_New
())
==
NULL
)
goto
err
;
for
(
pos
=
0
;
PyDict_Next
(
__dict__
,
&
pos
,
&
k
,
&
v
);
)
UNLESS
(
PyString_Check
(
k
)
&&
(
ck
=
PyString_AS_STRING
(
k
))
&&
(
*
ck
==
'_'
&&
ck
[
1
]
==
'v'
&&
ck
[
2
]
==
'_'
))
{
if
(
PyDict_SetItem
(
d
,
k
,
v
)
<
0
)
goto
err
;
}
return
d
;
}
}
}
else
__dict__
=
Py_None
;
Py_INCREF
(
__dict__
);
return
__dict__
;
err:
Py_XDECREF
(
d
);
return
NULL
;
}
static
PyObject
*
Per__setstate__
(
cPersistentObject
*
self
,
PyObject
*
args
)
{
PyObject
*
__dict__
,
*
v
,
*
keys
=
0
,
*
key
=
0
,
*
e
=
0
;
int
l
,
i
;
if
(
HasInstDict
(
self
))
{
UNLESS
(
PyArg_ParseTuple
(
args
,
"O"
,
&
v
))
return
NULL
;
#ifdef DEBUG_LOG
if
(
idebug_log
<
0
)
call_debug
(
"set"
,
self
);
#endif
if
(
v
!=
Py_None
)
{
__dict__
=
INSTANCE_DICT
(
self
);
if
(
PyDict_Check
(
v
))
{
for
(
i
=
0
;
PyDict_Next
(
v
,
&
i
,
&
key
,
&
e
);)
if
(
PyDict_SetItem
(
__dict__
,
key
,
e
)
<
0
)
return
NULL
;
}
else
{
UNLESS
(
keys
=
callmethod
(
v
,
py_keys
))
goto
err
;
UNLESS
(
-
1
!=
(
l
=
PyObject_Length
(
keys
)))
goto
err
;
for
(
i
=
0
;
i
<
l
;
i
++
)
{
UNLESS_ASSIGN
(
key
,
PySequence_GetItem
(
keys
,
i
))
goto
err
;
UNLESS_ASSIGN
(
e
,
PyObject_GetItem
(
v
,
key
))
goto
err
;
UNLESS
(
-
1
!=
PyDict_SetItem
(
__dict__
,
key
,
e
))
goto
err
;
}
Py_XDECREF
(
key
);
Py_XDECREF
(
e
);
Py_DECREF
(
keys
);
}
}
}
Py_INCREF
(
Py_None
);
return
Py_None
;
err:
Py_XDECREF
(
key
);
Py_XDECREF
(
e
);
Py_XDECREF
(
keys
);
return
NULL
;
}
static
struct
PyMethodDef
Per_methods
[]
=
{
{
"__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__
,
METH_VARARGS
,
"__setstate__(v) -- Restore the saved state of the object from v"
},
{
NULL
,
NULL
}
/* sentinel */
};
/* ---------- */
static
void
Per_dealloc
(
cPersistentObject
*
self
)
{
#ifdef DEBUG_LOG
if
(
idebug_log
<
0
)
call_debug
(
"del"
,
self
);
#endif
deallocated
(
self
);
Py_DECREF
(
self
->
ob_type
);
PyObject_DEL
(
self
);
}
static
PyObject
*
orNothing
(
PyObject
*
v
)
{
if
(
!
v
)
v
=
Py_None
;
Py_INCREF
(
v
);
return
v
;
}
static
PyObject
*
Per_getattr
(
cPersistentObject
*
self
,
PyObject
*
oname
,
char
*
name
,
PyObject
*
(
*
getattrf
)(
PyObject
*
,
PyObject
*
))
{
char
*
n
=
name
;
if
(
n
&&
*
n
++==
'_'
)
if
(
*
n
++==
'p'
&&
*
n
++==
'_'
)
{
switch
(
*
n
++
)
{
case
'o'
:
if
(
*
n
++==
'i'
&&
*
n
++==
'd'
&&
!
*
n
)
return
orNothing
(
self
->
oid
);
break
;
case
'j'
:
if
(
*
n
++==
'a'
&&
*
n
++==
'r'
&&
!
*
n
)
return
orNothing
(
self
->
jar
);
break
;
case
'c'
:
if
(
strcmp
(
n
,
"hanged"
)
==
0
)
{
if
(
self
->
state
<
0
)
{
Py_INCREF
(
Py_None
);
return
Py_None
;
}
return
PyInt_FromLong
(
self
->
state
==
cPersistent_CHANGED_STATE
);
}
break
;
case
's'
:
if
(
strcmp
(
n
,
"erial"
)
==
0
)
return
PyString_FromStringAndSize
(
self
->
serial
,
8
);
if
(
strcmp
(
n
,
"elf"
)
==
0
)
return
orNothing
(
OBJECT
(
self
));
break
;
case
'm'
:
if
(
strcmp
(
n
,
"time"
)
==
0
)
{
if
(
!
unghostify
(
self
))
return
NULL
;
accessed
(
self
);
if
(
self
->
serial
[
7
]
==
'\0'
&&
self
->
serial
[
6
]
==
'\0'
&&
self
->
serial
[
5
]
==
'\0'
&&
self
->
serial
[
4
]
==
'\0'
&&
self
->
serial
[
3
]
==
'\0'
&&
self
->
serial
[
2
]
==
'\0'
&&
self
->
serial
[
1
]
==
'\0'
&&
self
->
serial
[
0
]
==
'\0'
)
{
Py_INCREF
(
Py_None
);
return
Py_None
;
}
oname
=
PyString_FromStringAndSize
(
self
->
serial
,
8
);
if
(
!
oname
)
return
oname
;
ASSIGN
(
oname
,
PyObject_CallFunction
(
TimeStamp
,
"O"
,
oname
));
if
(
!
oname
)
return
oname
;
ASSIGN
(
oname
,
PyObject_GetAttr
(
oname
,
py_timeTime
));
if
(
!
oname
)
return
oname
;
ASSIGN
(
oname
,
PyObject_CallObject
(
oname
,
NULL
));
return
oname
;
}
break
;
}
return
getattrf
((
PyObject
*
)
self
,
oname
);
}
if
(
!
(
name
&&
*
name
++==
'_'
&&
*
name
++==
'_'
&&
(
strcmp
(
name
,
"dict__"
)
==
0
||
strcmp
(
name
,
"class__"
)
==
0
||
strcmp
(
name
,
"of__"
)
==
0
)))
{
if
(
!
unghostify
(
self
))
return
NULL
;
accessed
(
self
);
}
return
getattrf
((
PyObject
*
)
self
,
oname
);
}
static
PyObject
*
Per_getattro
(
cPersistentObject
*
self
,
PyObject
*
name
)
{
char
*
s
=
NULL
;
PyObject
*
r
;
if
(
PyString_Check
(
name
))
UNLESS
(
s
=
PyString_AS_STRING
(
name
))
return
NULL
;
r
=
Per_getattr
(
self
,
name
,
s
,
PyExtensionClassCAPI
->
getattro
);
if
(
!
r
&&
self
->
state
!=
cPersistent_GHOST_STATE
&&
(((
PyExtensionClass
*
)(
self
->
ob_type
))
->
class_flags
&
EXTENSIONCLASS_USERGETATTR_FLAG
)
)
{
PyErr_Clear
();
r
=
PyObject_GetAttr
(
OBJECT
(
self
),
py___getattr__
);
if
(
r
)
{
ASSIGN
(
r
,
PyObject_CallFunction
(
r
,
"O"
,
name
));
}
else
PyErr_SetObject
(
PyExc_AttributeError
,
name
);
}
return
r
;
}
static
int
_setattro
(
cPersistentObject
*
self
,
PyObject
*
oname
,
PyObject
*
v
,
int
(
*
setattrf
)(
PyObject
*
,
PyObject
*
,
PyObject
*
))
{
char
*
name
=
""
;
if
(
oname
==
NULL
)
return
-
1
;
if
(
!
PyString_Check
(
oname
))
return
-
1
;
name
=
PyString_AS_STRING
(
oname
);
if
(
name
==
NULL
)
return
-
1
;
if
(
*
name
==
'_'
&&
name
[
1
]
==
'p'
&&
name
[
2
]
==
'_'
)
{
if
(
strcmp
(
name
+
3
,
"oid"
)
==
0
)
{
if
(
self
->
cache
)
{
int
result
;
if
(
v
==
NULL
)
{
PyErr_SetString
(
PyExc_ValueError
,
"can not delete oid of cached object"
);
return
-
1
;
}
if
(
PyObject_Cmp
(
self
->
oid
,
v
,
&
result
)
<
0
)
return
-
1
;
if
(
result
)
{
PyErr_SetString
(
PyExc_ValueError
,
"can not change oid of cached object"
);
return
-
1
;
}
}
Py_XINCREF
(
v
);
ASSIGN
(
self
->
oid
,
v
);
return
0
;
}
else
if
(
strcmp
(
name
+
3
,
"jar"
)
==
0
)
{
if
(
self
->
cache
&&
self
->
jar
)
{
int
result
;
if
(
v
==
NULL
)
{
PyErr_SetString
(
PyExc_ValueError
,
"can not delete jar of cached object"
);
return
-
1
;
}
if
(
PyObject_Cmp
(
self
->
jar
,
v
,
&
result
)
<
0
)
return
-
1
;
if
(
result
)
{
PyErr_SetString
(
PyExc_ValueError
,
"can not change jar of cached object"
);
return
-
1
;
}
}
Py_XINCREF
(
v
);
ASSIGN
(
self
->
jar
,
v
);
return
0
;
}
else
if
(
strcmp
(
name
+
3
,
"serial"
)
==
0
)
{
if
(
v
)
{
if
(
PyString_Check
(
v
)
&&
PyString_GET_SIZE
(
v
)
==
8
)
memcpy
(
self
->
serial
,
PyString_AS_STRING
(
v
),
8
);
else
{
PyErr_SetString
(
PyExc_ValueError
,
"_p_serial must be an 8-character string"
);
return
-
1
;
}
}
else
memset
(
self
->
serial
,
0
,
8
);
return
0
;
}
else
if
(
strcmp
(
name
+
3
,
"changed"
)
==
0
)
{
int
deactivate
=
0
;
if
(
!
v
)
{
/* delatter is used to invalidate the object
*even* if it has changed.
*/
if
(
self
->
state
!=
cPersistent_GHOST_STATE
)
self
->
state
=
cPersistent_UPTODATE_STATE
;
deactivate
=
1
;
}
else
if
(
v
==
Py_None
)
deactivate
=
1
;
if
(
deactivate
)
{
PyObject
*
res
;
PyObject
*
meth
=
PyObject_GetAttr
(
OBJECT
(
self
),
py__p_deactivate
);
if
(
meth
==
NULL
)
return
-
1
;
res
=
PyObject_CallObject
(
meth
,
NULL
);
if
(
res
)
{
Py_DECREF
(
res
);
}
else
{
/* an error occured in _p_deactivate().
It's not clear what we should do here. The code is
obviously ignoring the exception, but it shouldn't
return 0 for a getattr and set an exception. The
simplest change is to clear the exception, but that
simply masks the error.
XXX We'll print an error to stderr just like
exceptions in __del__(). It would probably be
better to log it but that would be painful from C.
*/
PyErr_WriteUnraisable
(
meth
);
}
Py_DECREF
(
meth
);
return
0
;
}
if
(
PyObject_IsTrue
(
v
))
return
changed
(
self
);
if
(
self
->
state
>=
0
)
self
->
state
=
cPersistent_UPTODATE_STATE
;
return
0
;
}
}
else
{
if
(
!
unghostify
(
self
))
return
-
1
;
accessed
(
self
);
if
((
!
(
*
name
==
'_'
&&
name
[
1
]
==
'v'
&&
name
[
2
]
==
'_'
))
&&
(
self
->
state
!=
cPersistent_CHANGED_STATE
&&
self
->
jar
)
&&
setattrf
)
if
(
changed
(
self
)
<
0
)
return
-
1
;
}
if
(
setattrf
)
return
setattrf
((
PyObject
*
)
self
,
oname
,
v
);
return
1
;
/* Ready for user setattr */
}
static
int
Per_setattro
(
cPersistentObject
*
self
,
PyObject
*
oname
,
PyObject
*
v
)
{
int
r
;
PyObject
*
m
;
if
(
v
&&
(((
PyExtensionClass
*
)
self
->
ob_type
)
->
class_flags
&
EXTENSIONCLASS_USERSETATTR_FLAG
))
{
r
=
_setattro
(
self
,
oname
,
v
,
NULL
);
if
(
r
<
1
)
return
r
;
m
=
PyObject_GetAttr
(
OBJECT
(
self
),
py___setattr__
);
if
(
m
)
{
ASSIGN
(
m
,
PyObject_CallFunction
(
m
,
"OO"
,
oname
,
v
));
}
else
PyErr_SetObject
(
PyExc_AttributeError
,
oname
);
}
else
if
(
!
v
&&
(((
PyExtensionClass
*
)
self
->
ob_type
)
->
class_flags
&
EXTENSIONCLASS_USERDELATTR_FLAG
)
)
{
r
=
_setattro
(
self
,
oname
,
v
,
NULL
);
if
(
r
<
1
)
return
r
;
m
=
PyObject_GetAttr
(
OBJECT
(
self
),
py___delattr__
);
if
(
m
)
{
ASSIGN
(
m
,
PyObject_CallFunction
(
m
,
"O"
,
oname
));
}
else
PyErr_SetObject
(
PyExc_AttributeError
,
oname
);
}
else
return
_setattro
(
self
,
oname
,
v
,
PyExtensionClassCAPI
->
setattro
);
if
(
m
)
{
Py_DECREF
(
m
);
return
0
;
}
return
-
1
;
}
static
PyExtensionClass
Pertype
=
{
PyObject_HEAD_INIT
(
NULL
)
0
,
/*ob_size*/
"Persistent"
,
/*tp_name*/
sizeof
(
cPersistentObject
),
/*tp_basicsize*/
0
,
/*tp_itemsize*/
/* methods */
(
destructor
)
Per_dealloc
,
/*tp_dealloc*/
(
printfunc
)
0
,
/*tp_print*/
(
getattrfunc
)
0
,
/*tp_getattr*/
(
setattrfunc
)
0
,
/*tp_setattr*/
(
cmpfunc
)
0
,
/*tp_compare*/
(
reprfunc
)
0
,
/*tp_repr*/
0
,
/*tp_as_number*/
0
,
/*tp_as_sequence*/
0
,
/*tp_as_mapping*/
(
hashfunc
)
0
,
/*tp_hash*/
(
ternaryfunc
)
0
,
/*tp_call*/
(
reprfunc
)
0
,
/*tp_str*/
(
getattrofunc
)
Per_getattro
,
/*tp_getattr with object key*/
(
setattrofunc
)
Per_setattro
,
/*tp_setattr with object key*/
/* Space for future expansion */
0L
,
0L
,
"Base class for objects that are stored in their own database records"
,
METHOD_CHAIN
(
Per_methods
),
PERSISTENCE_FLAGS
,
};
static
PyExtensionClass
Overridable
=
{
PyObject_HEAD_INIT
(
NULL
)
0
,
/*ob_size*/
"Overridable"
,
/*tp_name*/
sizeof
(
cPersistentObject
),
/*tp_basicsize*/
0
,
/*tp_itemsize*/
/* methods */
(
destructor
)
Per_dealloc
,
/*tp_dealloc*/
(
printfunc
)
0
,
/*tp_print*/
(
getattrfunc
)
0
,
/*tp_getattr*/
(
setattrfunc
)
0
,
/*tp_setattr*/
(
cmpfunc
)
0
,
/*tp_compare*/
(
reprfunc
)
0
,
/*tp_repr*/
0
,
/*tp_as_number*/
0
,
/*tp_as_sequence*/
0
,
/*tp_as_mapping*/
(
hashfunc
)
0
,
/*tp_hash*/
(
ternaryfunc
)
0
,
/*tp_call*/
(
reprfunc
)
0
,
/*tp_str*/
(
getattrofunc
)
Per_getattro
,
/*tp_getattr with object key*/
(
setattrofunc
)
Per_setattro
,
/*tp_setattr with object key*/
/* Space for future expansion */
0L
,
0L
,
"Hacked base class used in Zope's App.Uninstalled.BrokenClass
\n\n
"
"Not sure if this is still needed"
,
METHOD_CHAIN
(
Per_methods
),
EXTENSIONCLASS_BASICNEW_FLAG
|
PERSISTENT_TYPE_FLAG
};
/* End of code for Persistent objects */
/* -------------------------------------------------------- */
/* List of methods defined in the module */
#ifdef DEBUG_LOG
static
PyObject
*
set_debug_log
(
PyObject
*
ignored
,
PyObject
*
args
)
{
Py_INCREF
(
args
);
ASSIGN
(
debug_log
,
args
);
if
(
debug_log
)
idebug_log
=-
1
;
else
idebug_log
=
0
;
Py_INCREF
(
Py_None
);
return
Py_None
;
}
#endif
static
struct
PyMethodDef
cP_methods
[]
=
{
#ifdef DEBUG_LOG
{
"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
"
},
#endif
{
NULL
,
NULL
}
/* sentinel */
};
/* Initialization function for the module (*must* be called initcPersistence) */
typedef
int
(
*
intfunctionwithpythonarg
)(
PyObject
*
);
static
cPersistenceCAPIstruct
truecPersistenceCAPI
=
{
&
(
Pertype
.
methods
),
(
getattrofunc
)
Per_getattro
,
/*tp_getattr with object key*/
(
setattrofunc
)
Per_setattro
,
/*tp_setattr with object key*/
changed
,
accessed
,
ghostify
,
deallocated
,
(
intfunctionwithpythonarg
)
Per_setstate
,
(
pergetattr
)
Per_getattr
,
(
persetattr
)
_setattro
,
NULL
/* The percachedel slot is initialized in cPickleCache.c when
the module is loaded. It uses a function in a different
shared library. */
};
void
initcPersistence
(
void
)
{
PyObject
*
m
,
*
d
,
*
s
;
s
=
PyString_FromString
(
"ZODB.TimeStamp"
);
if
(
s
==
NULL
)
return
;
m
=
PyImport_Import
(
s
);
if
(
m
==
NULL
)
{
Py_DECREF
(
s
);
return
;
}
TimeStamp
=
PyObject_GetAttrString
(
m
,
"TimeStamp"
);
assert
(
TimeStamp
);
Py_DECREF
(
m
);
Py_DECREF
(
s
);
if
(
init_strings
()
<
0
)
return
;
m
=
Py_InitModule4
(
"cPersistence"
,
cP_methods
,
cPersistence_doc_string
,
(
PyObject
*
)
NULL
,
PYTHON_API_VERSION
);
d
=
PyModule_GetDict
(
m
);
PyExtensionClass_Export
(
d
,
"Persistent"
,
Pertype
);
PyExtensionClass_Export
(
d
,
"Overridable"
,
Overridable
);
cPersistenceCAPI
=
&
truecPersistenceCAPI
;
s
=
PyCObject_FromVoidPtr
(
cPersistenceCAPI
,
NULL
);
PyDict_SetItemString
(
d
,
"CAPI"
,
s
);
Py_XDECREF
(
s
);
}
src/persistent/cPersistence.h
deleted
100644 → 0
View file @
4d22ca2b
/*****************************************************************************
Copyright (c) 2001, 2002 Zope Corporation and Contributors.
All Rights Reserved.
This software is subject to the provisions of the Zope Public License,
Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
FOR A PARTICULAR PURPOSE
****************************************************************************/
#ifndef CPERSISTENCE_H
#define CPERSISTENCE_H
#include "ExtensionClass.h"
#include <time.h>
typedef
struct
CPersistentRing_struct
{
struct
CPersistentRing_struct
*
prev
;
struct
CPersistentRing_struct
*
next
;
}
CPersistentRing
;
#define CACHE_HEAD \
PyObject_HEAD \
CPersistentRing ring_home; \
int non_ghost_count;
struct
ccobject_head_struct
;
typedef
struct
ccobject_head_struct
PerCache
;
#define cPersistent_HEAD \
PyObject_HEAD \
PyObject *jar; \
PyObject *oid; \
PerCache *cache; \
CPersistentRing ring; \
char serial[8]; \
signed char state; \
unsigned char reserved[3];
#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
int
(
*
persetattr
)(
PyObject
*
,
PyObject
*
,
PyObject
*
,
setattrofunc
);
typedef
PyObject
*
(
*
pergetattr
)(
PyObject
*
,
PyObject
*
,
char
*
,
getattrofunc
);
typedef
int
(
*
percachedelfunc
)(
PerCache
*
,
PyObject
*
);
typedef
struct
{
PyMethodChain
*
methods
;
getattrofunc
getattro
;
setattrofunc
setattro
;
int
(
*
changed
)(
cPersistentObject
*
);
void
(
*
accessed
)(
cPersistentObject
*
);
void
(
*
ghostify
)(
cPersistentObject
*
);
void
(
*
deallocated
)(
cPersistentObject
*
);
int
(
*
setstate
)(
PyObject
*
);
pergetattr
pergetattro
;
persetattr
persetattro
;
percachedelfunc
percachedel
;
}
cPersistenceCAPIstruct
;
#ifndef DONT_USE_CPERSISTENCECAPI
static
cPersistenceCAPIstruct
*
cPersistenceCAPI
;
#endif
#define cPersistanceModuleName "cPersistence"
#define PERSISTENT_TYPE_FLAG EXTENSIONCLASS_USER_FLAG8
/* ExtensionClass class flags for persistent base classes should
include PERSISTENCE_FLAGS.
*/
#define PERSISTENCE_FLAGS EXTENSIONCLASS_BASICNEW_FLAG | PERSISTENT_TYPE_FLAG \
| EXTENSIONCLASS_PYTHONICATTR_FLAG
#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_CHANGED(O) (cPersistenceCAPI->changed((cPersistentObject*)(O)))
#define PER_GHOSTIFY(O) (cPersistenceCAPI->ghostify((cPersistentObject*)(O)))
#define PER_ALLOW_DEACTIVATION(O) ((O)->state==cPersistent_STICKY_STATE && ((O)->state=cPersistent_UPTODATE_STATE))
#define PER_PREVENT_DEACTIVATION(O) ((O)->state==cPersistent_UPTODATE_STATE && ((O)->state=cPersistent_STICKY_STATE))
#define PER_DEL(O) (cPersistenceCAPI->deallocated((cPersistentObject*)(O)))
#define PER_USE(O) \
(((O)->state != cPersistent_GHOST_STATE \
|| (cPersistenceCAPI->setstate((PyObject*)(O)) >= 0)) \
? (((O)->state==cPersistent_UPTODATE_STATE) \
? ((O)->state=cPersistent_STICKY_STATE) : 1) : 0)
#define PER_ACCESSED(O) (cPersistenceCAPI->accessed((cPersistentObject*)(O)))
#endif
src/persistent/cPickleCache.c
deleted
100644 → 0
View file @
4d22ca2b
/*****************************************************************************
Copyright (c) 2001, 2002 Zope Corporation and Contributors.
All Rights Reserved.
This software is subject to the provisions of the Zope Public License,
Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
FOR A PARTICULAR PURPOSE
****************************************************************************/
/*
Objects are stored under three different regimes:
Regime 1: Persistent Classes
Persistent Classes are part of ZClasses. They are stored in the
self->data dictionary, and are never garbage collected.
The klass_items() method returns a sequence of (oid,object) tuples for
every Persistent Class, which should make it possible to implement
garbage collection in Python if necessary.
Regime 2: Ghost Objects
There is no benefit to keeping a ghost object which has no external
references, therefore a weak reference scheme is used to ensure that
ghost objects are removed from memory as soon as possible, when the
last external reference is lost.
Ghost objects are stored in the self->data dictionary. Normally a
dictionary keeps a strong reference on its values, however this
reference count is 'stolen'.
This weak reference scheme leaves a dangling reference, in the
dictionary, when the last external reference is lost. To clean up this
dangling reference the persistent object dealloc function calls
self->cache->_oid_unreferenced(self->oid). The cache looks up the oid
in the dictionary, ensures it points to an object whose reference
count is zero, then removes it from the dictionary. Before removing
the object from the dictionary it must temporarily resurrect the
object in much the same way that class instances are resurrected
before their __del__ is called.
Since ghost objects are stored under a different regime to non-ghost
objects, an extra ghostify function in cPersistenceAPI replaces
self->state=GHOST_STATE assignments that were common in other
persistent classes (such as BTrees).
Regime 3: Non-Ghost Objects
Non-ghost objects are stored in two data structures: the dictionary
mapping oids to objects and a doubly-linked list that encodes the
order in which the objects were accessed. The dictionary reference is
borrowed, as it is for ghosts. The list reference is a new reference;
the list stores recently used objects, even if they are otherwise
unreferenced, to avoid loading the object from the database again.
The doubly-link-list nodes contain next and previous pointers linking
together the cache and all non-ghost persistent objects.
The node embedded in the cache is the home position. On every
attribute access a non-ghost object will relink itself just behind the
home position in the ring. Objects accessed least recently will
eventually find themselves positioned after the home position.
Occasionally other nodes are temporarily inserted in the ring as
position markers. The cache contains a ring_lock flag which must be
set and unset before and after doing so. Only if the flag is unset can
the cache assume that all nodes are either his own home node, or nodes
from persistent objects. This assumption is useful during the garbage
collection process.
The number of non-ghost objects is counted in self->non_ghost_count.
The garbage collection process consists of traversing the ring, and
deactivating (that is, turning into a ghost) every object until
self->non_ghost_count is down to the target size, or until it
reaches the home position again.
Note that objects in the sticky or changed states are still kept in
the ring, however they can not be deactivated. The garbage collection
process must skip such objects, rather than deactivating them.
*/
static
char
cPickleCache_doc_string
[]
=
"Defines the PickleCache used by ZODB Connection objects.
\n
"
"
\n
"
"$Id: cPickleCache.c,v 1.85 2003/05/30 19:20:55 jeremy Exp $
\n
"
;
#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 OBJECT(O) ((PyObject*)O)
#define DONT_USE_CPERSISTENCECAPI
#include "cPersistence.h"
#include <time.h>
#include <stddef.h>
#undef Py_FindMethod
static
PyObject
*
py__p_oid
,
*
py_reload
,
*
py__p_jar
,
*
py__p_changed
;
static
cPersistenceCAPIstruct
*
capi
;
/* Do we want 'engine noise'.... abstract debugging output useful for
visualizing cache behavior */
#if 0
#define ENGINE_NOISE(A) printf(A)
#else
#define ENGINE_NOISE(A) ((void)A)
#endif
/* This object is the pickle cache. The CACHE_HEAD macro guarantees
that layout of this struct is the same as the start of
ccobject_head in cPersistence.c */
typedef
struct
{
CACHE_HEAD
int
klass_count
;
/* count of persistent classes */
PyObject
*
data
;
/* oid -> object dict */
PyObject
*
jar
;
/* Connection object */
PyObject
*
setklassstate
;
/* ??? */
int
cache_size
;
/* target number of items in cache */
/* Most of the time the ring contains only:
* many nodes corresponding to persistent objects
* one 'home' node from the cache.
In some cases it is handy to temporarily add other types
of node into the ring as placeholders. 'ring_lock' is a boolean
indicating that someone has already done this. Currently this
is only used by the garbage collection code. */
int
ring_lock
;
/* 'cache_drain_resistance' controls how quickly the cache size will drop
when it is smaller than the configured size. A value of zero means it will
not drop below the configured size (suitable for most caches). Otherwise,
it will remove cache_non_ghost_count/cache_drain_resistance items from
the cache every time (suitable for rarely used caches, such as those
associated with Zope versions. */
int
cache_drain_resistance
;
}
ccobject
;
static
int
cc_ass_sub
(
ccobject
*
self
,
PyObject
*
key
,
PyObject
*
v
);
/* ---------------------------------------------------------------- */
#define OBJECT_FROM_RING(SELF, HERE, CTX) \
((cPersistentObject *)(((char *)here) - offsetof(cPersistentObject, ring)))
static
int
scan_gc_items
(
ccobject
*
self
,
int
target
)
{
/* This function must only be called with the ring lock held,
because it places a non-object placeholder in the ring.
*/
cPersistentObject
*
object
;
int
error
;
CPersistentRing
placeholder
;
CPersistentRing
*
here
=
self
->
ring_home
.
next
;
/* Scan through the ring until we either find the ring_home (i.e. start
* of the ring, or we've ghosted enough objects to reach the target
* size.
*/
while
(
1
)
{
/* back to the home position. stop looking */
if
(
here
==
&
self
->
ring_home
)
return
0
;
/* At this point we know that the ring only contains nodes
from persistent objects, plus our own home node. We know
this because the ring lock is held. We can safely assume
the current ring node is a persistent object now we know it
is not the home */
object
=
OBJECT_FROM_RING
(
self
,
here
,
"scan_gc_items"
);
if
(
!
object
)
return
-
1
;
/* we are small enough */
if
(
self
->
non_ghost_count
<=
target
)
return
0
;
else
if
(
object
->
state
==
cPersistent_UPTODATE_STATE
)
{
/* deactivate it. This is the main memory saver. */
/* Add a placeholder; a dummy node in the ring. We need
to do this to mark our position in the ring. It is
possible that the PyObject_SetAttr() call below will
invoke an __setattr__() hook in Python. If it does,
another thread might run; if that thread accesses a
persistent object and moves it to the head of the ring,
it might cause the gc scan to start working from the
head of the list.
*/
placeholder
.
next
=
here
->
next
;
placeholder
.
prev
=
here
;
here
->
next
->
prev
=
&
placeholder
;
here
->
next
=
&
placeholder
;
ENGINE_NOISE
(
"G"
);
/* In Python, "obj._p_changed = None" spells, ghostify */
error
=
PyObject_SetAttr
((
PyObject
*
)
object
,
py__p_changed
,
Py_None
);
/* unlink the placeholder */
placeholder
.
next
->
prev
=
placeholder
.
prev
;
placeholder
.
prev
->
next
=
placeholder
.
next
;
here
=
placeholder
.
next
;
if
(
error
)
return
-
1
;
/* problem */
}
else
{
ENGINE_NOISE
(
"."
);
here
=
here
->
next
;
}
}
}
static
PyObject
*
lockgc
(
ccobject
*
self
,
int
target_size
)
{
/* This is thread-safe because of the GIL, and there's nothing
* in between checking the ring_lock and acquiring it that calls back
* into Python.
*/
if
(
self
->
ring_lock
)
{
Py_INCREF
(
Py_None
);
return
Py_None
;
}
ENGINE_NOISE
(
"<"
);
self
->
ring_lock
=
1
;
if
(
scan_gc_items
(
self
,
target_size
))
{
self
->
ring_lock
=
0
;
return
NULL
;
}
self
->
ring_lock
=
0
;
ENGINE_NOISE
(
">
\n
"
);
Py_INCREF
(
Py_None
);
return
Py_None
;
}
static
PyObject
*
cc_incrgc
(
ccobject
*
self
,
PyObject
*
args
)
{
int
n
=
1
;
int
starting_size
=
self
->
non_ghost_count
;
int
target_size
=
self
->
cache_size
;
if
(
self
->
cache_drain_resistance
>=
1
)
{
/* This cache will gradually drain down to a small size. Check
a (small) number of objects proportional to the current size */
int
target_size_2
=
(
starting_size
-
1
-
starting_size
/
self
->
cache_drain_resistance
);
if
(
target_size_2
<
target_size
)
target_size
=
target_size_2
;
}
if
(
!
PyArg_ParseTuple
(
args
,
"|i:incrgc"
,
&
n
))
return
NULL
;
return
lockgc
(
self
,
target_size
);
}
static
PyObject
*
cc_full_sweep
(
ccobject
*
self
,
PyObject
*
args
)
{
int
dt
=
0
;
if
(
!
PyArg_ParseTuple
(
args
,
"|i:full_sweep"
,
&
dt
))
return
NULL
;
if
(
dt
==
0
)
return
lockgc
(
self
,
0
);
else
return
cc_incrgc
(
self
,
args
);
}
static
PyObject
*
cc_minimize
(
ccobject
*
self
,
PyObject
*
args
)
{
int
ignored
;
if
(
!
PyArg_ParseTuple
(
args
,
"|i:minimize"
,
&
ignored
))
return
NULL
;
return
lockgc
(
self
,
0
);
}
static
void
_invalidate
(
ccobject
*
self
,
PyObject
*
key
)
{
PyObject
*
v
=
PyDict_GetItem
(
self
->
data
,
key
);
if
(
!
v
)
return
;
if
(
PyExtensionClass_Check
(
v
))
{
if
(
v
->
ob_refcnt
<=
1
)
{
self
->
klass_count
--
;
if
(
PyDict_DelItem
(
self
->
data
,
key
)
<
0
)
PyErr_Clear
();
}
else
{
v
=
PyObject_CallFunction
(
self
->
setklassstate
,
"O"
,
v
);
if
(
v
)
Py_DECREF
(
v
);
else
PyErr_Clear
();
}
}
else
{
if
(
PyObject_DelAttr
(
v
,
py__p_changed
)
<
0
)
PyErr_Clear
();
}
}
static
PyObject
*
cc_invalidate
(
ccobject
*
self
,
PyObject
*
args
)
{
PyObject
*
inv
,
*
key
,
*
v
;
int
i
=
0
;
if
(
PyArg_ParseTuple
(
args
,
"O!"
,
&
PyDict_Type
,
&
inv
))
{
while
(
PyDict_Next
(
inv
,
&
i
,
&
key
,
&
v
))
_invalidate
(
self
,
key
);
PyDict_Clear
(
inv
);
}
else
{
PyErr_Clear
();
if
(
!
PyArg_ParseTuple
(
args
,
"O:invalidate"
,
&
inv
))
return
NULL
;
if
(
PyString_Check
(
inv
))
_invalidate
(
self
,
inv
);
else
{
int
l
;
PyErr_Clear
();
l
=
PyObject_Length
(
inv
);
if
(
l
<
0
)
return
NULL
;
for
(
i
=
l
;
--
i
>=
0
;
)
{
key
=
PySequence_GetItem
(
inv
,
i
);
if
(
!
key
)
return
NULL
;
_invalidate
(
self
,
key
);
Py_DECREF
(
key
);
}
/* XXX Do we really want to modify the input? */
PySequence_DelSlice
(
inv
,
0
,
l
);
}
}
Py_INCREF
(
Py_None
);
return
Py_None
;
}
static
PyObject
*
cc_get
(
ccobject
*
self
,
PyObject
*
args
)
{
PyObject
*
r
,
*
key
,
*
d
=
NULL
;
if
(
!
PyArg_ParseTuple
(
args
,
"O|O:get"
,
&
key
,
&
d
))
return
NULL
;
r
=
PyDict_GetItem
(
self
->
data
,
key
);
if
(
!
r
)
{
if
(
d
)
{
r
=
d
;
}
else
{
PyErr_SetObject
(
PyExc_KeyError
,
key
);
return
NULL
;
}
}
Py_INCREF
(
r
);
return
r
;
}
static
PyObject
*
cc_klass_items
(
ccobject
*
self
,
PyObject
*
args
)
{
PyObject
*
l
,
*
k
,
*
v
;
int
p
=
0
;
if
(
!
PyArg_ParseTuple
(
args
,
":klass_items"
))
return
NULL
;
l
=
PyList_New
(
PyDict_Size
(
self
->
data
));
if
(
l
==
NULL
)
return
NULL
;
while
(
PyDict_Next
(
self
->
data
,
&
p
,
&
k
,
&
v
))
{
if
(
PyExtensionClass_Check
(
v
))
{
v
=
Py_BuildValue
(
"OO"
,
k
,
v
);
if
(
v
==
NULL
)
{
Py_DECREF
(
l
);
return
NULL
;
}
if
(
PyList_Append
(
l
,
v
)
<
0
)
{
Py_DECREF
(
v
);
Py_DECREF
(
l
);
return
NULL
;
}
Py_DECREF
(
v
);
}
}
return
l
;
}
static
PyObject
*
cc_lru_items
(
ccobject
*
self
,
PyObject
*
args
)
{
PyObject
*
l
;
CPersistentRing
*
here
;
if
(
!
PyArg_ParseTuple
(
args
,
":lru_items"
))
return
NULL
;
if
(
self
->
ring_lock
)
{
/* When the ring lock is held, we have no way of know which
ring nodes belong to persistent objects, and which a
placeholders. */
PyErr_SetString
(
PyExc_ValueError
,
".lru_items() is unavailable during garbage collection"
);
return
NULL
;
}
l
=
PyList_New
(
0
);
if
(
l
==
NULL
)
return
NULL
;
here
=
self
->
ring_home
.
next
;
while
(
here
!=
&
self
->
ring_home
)
{
PyObject
*
v
;
cPersistentObject
*
object
=
OBJECT_FROM_RING
(
self
,
here
,
"cc_items"
);
if
(
object
==
NULL
)
{
Py_DECREF
(
l
);
return
NULL
;
}
v
=
Py_BuildValue
(
"OO"
,
object
->
oid
,
object
);
if
(
v
==
NULL
)
{
Py_DECREF
(
l
);
return
NULL
;
}
if
(
PyList_Append
(
l
,
v
)
<
0
)
{
Py_DECREF
(
v
);
Py_DECREF
(
l
);
return
NULL
;
}
Py_DECREF
(
v
);
here
=
here
->
next
;
}
return
l
;
}
/* Be very careful about calling clear().
It removes all non-ghost objects from the ring without otherwise
removing them from the cache. The method should only be called
after the cache is no longer in use.
*/
static
PyObject
*
cc_clear
(
ccobject
*
self
,
PyObject
*
args
)
{
CPersistentRing
*
here
;
if
(
!
PyArg_ParseTuple
(
args
,
":clear"
))
return
NULL
;
if
(
self
->
ring_lock
)
{
/* When the ring lock is held, we have no way of know which
ring nodes belong to persistent objects, and which a
placeholders. */
PyErr_SetString
(
PyExc_ValueError
,
".lru_items() is unavailable during garbage collection"
);
return
NULL
;
}
self
->
ring_lock
=
1
;
while
((
here
=
self
->
ring_home
.
next
)
!=
&
self
->
ring_home
)
{
cPersistentObject
*
o
=
OBJECT_FROM_RING
(
self
,
here
,
"clear"
);
self
->
non_ghost_count
--
;
o
->
ring
.
next
->
prev
=
&
self
->
ring_home
;
self
->
ring_home
.
next
=
o
->
ring
.
next
;
o
->
ring
.
next
=
NULL
;
o
->
ring
.
prev
=
NULL
;
Py_DECREF
(
o
);
}
self
->
ring_lock
=
0
;
Py_INCREF
(
Py_None
);
return
Py_None
;
}
static
int
cc_oid_unreferenced
(
ccobject
*
self
,
PyObject
*
oid
)
{
/* This is called by the persistent object deallocation function
when the reference count on a persistent object reaches
zero. We need to fix up our dictionary; its reference is now
dangling because we stole its reference count. Be careful to
not release the global interpreter lock until this is
complete. */
PyObject
*
v
;
v
=
PyDict_GetItem
(
self
->
data
,
oid
);
if
(
v
==
NULL
)
{
PyErr_SetObject
(
PyExc_KeyError
,
oid
);
return
-
1
;
}
assert
(
v
->
ob_refcnt
==
0
);
/* Need to be very hairy here because a dictionary is about
to decref an already deleted object.
*/
#ifdef Py_TRACE_REFS
/* This is called from the deallocation function after the
interpreter has untracked the reference. Track it again.
*/
_Py_NewReference
(
v
);
/* Don't increment total refcount as a result of the
shenanigans played in this function. The _Py_NewReference()
call above creates artificial references to v.
*/
_Py_RefTotal
--
;
assert
(
v
->
ob_type
);
#else
Py_INCREF
(
v
);
#endif
assert
(
v
->
ob_refcnt
==
1
);
/* Incremement the refcount again, because delitem is going to
DECREF it. If it's refcount reached zero again, we'd call back to
the dealloc function that called us.
*/
Py_INCREF
(
v
);
/* XXX Should we call _Py_ForgetReference() on error exit? */
if
(
PyDict_DelItem
(
self
->
data
,
oid
)
<
0
)
return
-
1
;
Py_DECREF
((
ccobject
*
)((
cPersistentObject
*
)
v
)
->
cache
);
if
(
v
->
ob_refcnt
!=
1
)
{
PyErr_SetString
(
PyExc_ValueError
,
"refcount is not 1 after removal from dict"
);
return
-
1
;
}
/* Undo the temporary resurrection.
Don't DECREF the object, because this function is called from
the object's dealloc function. If the refcnt reaches zero, it
will all be invoked recursively.
*/
_Py_ForgetReference
(
v
);
return
0
;
}
static
PyObject
*
cc_ringlen
(
ccobject
*
self
,
PyObject
*
args
)
{
CPersistentRing
*
here
;
int
c
=
0
;
if
(
!
PyArg_ParseTuple
(
args
,
":ringlen"
))
return
NULL
;
for
(
here
=
self
->
ring_home
.
next
;
here
!=
&
self
->
ring_home
;
here
=
here
->
next
)
c
++
;
return
PyInt_FromLong
(
c
);
}
static
struct
PyMethodDef
cc_methods
[]
=
{
{
"lru_items"
,
(
PyCFunction
)
cc_lru_items
,
METH_VARARGS
,
"List (oid, object) pairs from the lru list, as 2-tuples.
\n
"
},
{
"klass_items"
,
(
PyCFunction
)
cc_klass_items
,
METH_VARARGS
,
"List (oid, object) pairs of cached persistent classes.
\n
"
},
{
"full_sweep"
,
(
PyCFunction
)
cc_full_sweep
,
METH_VARARGS
,
"full_sweep([age]) -- Perform a full sweep of the cache
\n\n
"
"Supported for backwards compatibility. If the age argument is 0,
\n
"
"behaves like minimize(). Otherwise, behaves like incrgc()."
},
{
"minimize"
,
(
PyCFunction
)
cc_minimize
,
METH_VARARGS
,
"minimize([ignored]) -- Remove as many objects as possible
\n\n
"
"Ghostify all objects that are not modified. Takes an optional
\n
"
"argument, but ignores it."
},
{
"incrgc"
,
(
PyCFunction
)
cc_incrgc
,
METH_VARARGS
,
"incrgc([n]) -- Perform incremental garbage collection
\n\n
"
"Some other implementations support an optional parameter 'n' which
\n
"
"indicates a repetition count; this value is ignored.
\n
"
},
{
"invalidate"
,
(
PyCFunction
)
cc_invalidate
,
METH_VARARGS
,
"invalidate(oids) -- invalidate one, many, or all ids"
},
{
"get"
,
(
PyCFunction
)
cc_get
,
METH_VARARGS
,
"get(key [, default]) -- get an item, or a default"
},
{
"ringlen"
,
(
PyCFunction
)
cc_ringlen
,
METH_VARARGS
,
"ringlen() -- Returns number of non-ghost items in cache."
},
{
"clear"
,
(
PyCFunction
)
cc_clear
,
METH_VARARGS
,
"clear() -- remove all objects from the cache"
},
{
NULL
,
NULL
}
/* sentinel */
};
static
void
cc_dealloc
(
ccobject
*
self
)
{
Py_XDECREF
(
self
->
data
);
Py_XDECREF
(
self
->
jar
);
Py_XDECREF
(
self
->
setklassstate
);
PyMem_DEL
(
self
);
}
static
PyObject
*
cc_getattr
(
ccobject
*
self
,
char
*
name
)
{
if
(
*
name
==
'c'
)
{
if
(
strcmp
(
name
,
"cache_age"
)
==
0
)
return
PyInt_FromLong
(
0
);
/* this cache does not use this value */
if
(
strcmp
(
name
,
"cache_size"
)
==
0
)
return
PyInt_FromLong
(
self
->
cache_size
);
if
(
strcmp
(
name
,
"cache_drain_resistance"
)
==
0
)
return
PyInt_FromLong
(
self
->
cache_drain_resistance
);
if
(
strcmp
(
name
,
"cache_non_ghost_count"
)
==
0
)
return
PyInt_FromLong
(
self
->
non_ghost_count
);
if
(
strcmp
(
name
,
"cache_klass_count"
)
==
0
)
return
PyInt_FromLong
(
self
->
klass_count
);
if
(
strcmp
(
name
,
"cache_data"
)
==
0
)
{
/* now a copy of our data; the ring is too fragile */
return
PyDict_Copy
(
self
->
data
);
}
}
if
(
strcmp
(
name
,
"items"
)
==
0
)
return
PyObject_GetAttrString
(
self
->
data
,
name
);
return
Py_FindMethod
(
cc_methods
,
(
PyObject
*
)
self
,
name
);
}
static
int
cc_setattr
(
ccobject
*
self
,
char
*
name
,
PyObject
*
value
)
{
if
(
value
)
{
int
v
;
if
(
strcmp
(
name
,
"cache_age"
)
==
0
)
{
/* this cache doesnt use the age */
return
0
;
}
if
(
strcmp
(
name
,
"cache_size"
)
==
0
)
{
UNLESS
(
PyArg_Parse
(
value
,
"i"
,
&
v
))
return
-
1
;
self
->
cache_size
=
v
;
return
0
;
}
if
(
strcmp
(
name
,
"cache_drain_resistance"
)
==
0
)
{
UNLESS
(
PyArg_Parse
(
value
,
"i"
,
&
v
))
return
-
1
;
self
->
cache_drain_resistance
=
v
;
return
0
;
}
}
PyErr_SetString
(
PyExc_AttributeError
,
name
);
return
-
1
;
}
static
int
cc_length
(
ccobject
*
self
)
{
return
PyObject_Length
(
self
->
data
);
}
static
PyObject
*
cc_subscript
(
ccobject
*
self
,
PyObject
*
key
)
{
PyObject
*
r
;
r
=
PyDict_GetItem
(
self
->
data
,
key
);
if
(
r
==
NULL
)
{
PyErr_SetObject
(
PyExc_KeyError
,
key
);
return
NULL
;
}
Py_INCREF
(
r
);
return
r
;
}
static
int
cc_add_item
(
ccobject
*
self
,
PyObject
*
key
,
PyObject
*
v
)
{
int
result
;
PyObject
*
oid
,
*
object_again
,
*
jar
;
cPersistentObject
*
p
;
if
(
PyExtensionClass_Check
(
v
))
{
/* Its a persistent class, such as a ZClass. Thats ok. */
}
else
if
(
PyExtensionInstance_Check
(
v
)
&&
(((
PyExtensionClass
*
)(
v
->
ob_type
))
->
class_flags
&
PERSISTENT_TYPE_FLAG
)
&&
(
v
->
ob_type
->
tp_basicsize
>=
sizeof
(
cPersistentObject
))
)
{
/* Its and instance of a persistent class, (ie Python classeses that
derive from Persistence.Persistent, BTrees, etc). Thats ok. */
}
else
{
PyErr_SetString
(
PyExc_TypeError
,
"Cache values must be persistent objects."
);
return
-
1
;
}
/* Can't access v->oid directly because the object might be a
* persistent class.
*/
oid
=
PyObject_GetAttr
(
v
,
py__p_oid
);
if
(
oid
==
NULL
)
return
-
1
;
if
(
!
PyString_Check
(
oid
))
{
PyErr_Format
(
PyExc_TypeError
,
"Cached object oid must be a string, not a %s"
,
oid
->
ob_type
->
tp_name
);
return
-
1
;
}
/* we know they are both strings.
* now check if they are the same string.
*/
result
=
PyObject_Compare
(
key
,
oid
);
if
(
PyErr_Occurred
())
{
Py_DECREF
(
oid
);
return
-
1
;
}
Py_DECREF
(
oid
);
if
(
result
)
{
PyErr_SetString
(
PyExc_ValueError
,
"Cache key does not match oid"
);
return
-
1
;
}
/* useful sanity check, but not strictly an invariant of this class */
jar
=
PyObject_GetAttr
(
v
,
py__p_jar
);
if
(
jar
==
NULL
)
return
-
1
;
if
(
jar
==
Py_None
)
{
Py_DECREF
(
jar
);
PyErr_SetString
(
PyExc_ValueError
,
"Cached object jar missing"
);
return
-
1
;
}
Py_DECREF
(
jar
);
object_again
=
PyDict_GetItem
(
self
->
data
,
key
);
if
(
object_again
)
{
if
(
object_again
!=
v
)
{
PyErr_SetString
(
PyExc_ValueError
,
"Can not re-register object under a different oid"
);
return
-
1
;
}
else
{
/* re-register under the same oid - no work needed */
return
0
;
}
}
if
(
PyExtensionClass_Check
(
v
))
{
if
(
PyDict_SetItem
(
self
->
data
,
key
,
v
)
<
0
)
return
-
1
;
self
->
klass_count
++
;
return
0
;
}
else
{
PerCache
*
cache
=
((
cPersistentObject
*
)
v
)
->
cache
;
if
(
cache
)
{
if
(
cache
!=
(
PerCache
*
)
self
)
/* This object is already in a different cache. */
PyErr_SetString
(
PyExc_ValueError
,
"Cache values may only be in one cache."
);
return
-
1
;
}
/* else:
This object is already one of ours, which is ok. It
would be very strange if someone was trying to register
the same object under a different key.
*/
}
if
(
PyDict_SetItem
(
self
->
data
,
key
,
v
)
<
0
)
return
-
1
;
/* the dict should have a borrowed reference */
Py_DECREF
(
v
);
p
=
(
cPersistentObject
*
)
v
;
Py_INCREF
(
self
);
p
->
cache
=
(
PerCache
*
)
self
;
if
(
p
->
state
>=
0
)
{
/* insert this non-ghost object into the ring just
behind the home position. */
self
->
non_ghost_count
++
;
p
->
ring
.
next
=
&
self
->
ring_home
;
p
->
ring
.
prev
=
self
->
ring_home
.
prev
;
self
->
ring_home
.
prev
->
next
=
&
p
->
ring
;
self
->
ring_home
.
prev
=
&
p
->
ring
;
/* this list should have a new reference to the object */
Py_INCREF
(
v
);
}
return
0
;
}
static
int
cc_del_item
(
ccobject
*
self
,
PyObject
*
key
)
{
PyObject
*
v
;
cPersistentObject
*
p
;
/* unlink this item from the ring */
v
=
PyDict_GetItem
(
self
->
data
,
key
);
if
(
v
==
NULL
)
return
-
1
;
if
(
PyExtensionClass_Check
(
v
))
{
self
->
klass_count
--
;
}
else
{
p
=
(
cPersistentObject
*
)
v
;
if
(
p
->
state
>=
0
)
{
self
->
non_ghost_count
--
;
p
->
ring
.
next
->
prev
=
p
->
ring
.
prev
;
p
->
ring
.
prev
->
next
=
p
->
ring
.
next
;
p
->
ring
.
prev
=
NULL
;
p
->
ring
.
next
=
NULL
;
/* The DelItem below will account for the reference
held by the list. */
}
else
{
/* This is a ghost object, so we havent kept a reference
count on it. For it have stayed alive this long
someone else must be keeping a reference to
it. Therefore we need to temporarily give it back a
reference count before calling DelItem below */
Py_INCREF
(
v
);
}
Py_DECREF
((
PyObject
*
)
p
->
cache
);
p
->
cache
=
NULL
;
}
if
(
PyDict_DelItem
(
self
->
data
,
key
)
<
0
)
{
PyErr_SetString
(
PyExc_RuntimeError
,
"unexpectedly couldn't remove key in cc_ass_sub"
);
return
-
1
;
}
return
0
;
}
static
int
cc_ass_sub
(
ccobject
*
self
,
PyObject
*
key
,
PyObject
*
v
)
{
if
(
!
PyString_Check
(
key
))
{
PyErr_Format
(
PyExc_TypeError
,
"cPickleCache key must be a string, not a %s"
,
key
->
ob_type
->
tp_name
);
return
-
1
;
}
if
(
v
)
return
cc_add_item
(
self
,
key
,
v
);
else
return
cc_del_item
(
self
,
key
);
}
static
PyMappingMethods
cc_as_mapping
=
{
(
inquiry
)
cc_length
,
/*mp_length*/
(
binaryfunc
)
cc_subscript
,
/*mp_subscript*/
(
objobjargproc
)
cc_ass_sub
,
/*mp_ass_subscript*/
};
static
PyTypeObject
Cctype
=
{
PyObject_HEAD_INIT
(
NULL
)
0
,
/*ob_size*/
"cPickleCache"
,
/*tp_name*/
sizeof
(
ccobject
),
/*tp_basicsize*/
0
,
/*tp_itemsize*/
/* methods */
(
destructor
)
cc_dealloc
,
/*tp_dealloc*/
(
printfunc
)
0
,
/*tp_print*/
(
getattrfunc
)
cc_getattr
,
/*tp_getattr*/
(
setattrfunc
)
cc_setattr
,
/*tp_setattr*/
(
cmpfunc
)
0
,
/*tp_compare*/
(
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
)
0
,
/*tp_str*/
};
static
ccobject
*
newccobject
(
PyObject
*
jar
,
int
cache_size
)
{
ccobject
*
self
;
self
=
PyObject_NEW
(
ccobject
,
&
Cctype
);
if
(
self
==
NULL
)
return
NULL
;
self
->
setklassstate
=
self
->
jar
=
NULL
;
self
->
data
=
PyDict_New
();
if
(
self
->
data
==
NULL
)
{
Py_DECREF
(
self
);
return
NULL
;
}
self
->
setklassstate
=
PyObject_GetAttrString
(
jar
,
"setklassstate"
);
if
(
self
->
setklassstate
==
NULL
)
{
Py_DECREF
(
self
);
return
NULL
;
}
self
->
jar
=
jar
;
Py_INCREF
(
jar
);
self
->
cache_size
=
cache_size
;
self
->
non_ghost_count
=
0
;
self
->
klass_count
=
0
;
self
->
cache_drain_resistance
=
0
;
self
->
ring_lock
=
0
;
self
->
ring_home
.
next
=
&
self
->
ring_home
;
self
->
ring_home
.
prev
=
&
self
->
ring_home
;
return
self
;
}
static
PyObject
*
cCM_new
(
PyObject
*
self
,
PyObject
*
args
)
{
int
cache_size
=
100
;
PyObject
*
jar
;
if
(
!
PyArg_ParseTuple
(
args
,
"O|i"
,
&
jar
,
&
cache_size
))
return
NULL
;
return
(
PyObject
*
)
newccobject
(
jar
,
cache_size
);
}
static
struct
PyMethodDef
cCM_methods
[]
=
{
{
"PickleCache"
,
(
PyCFunction
)
cCM_new
,
METH_VARARGS
},
{
NULL
,
NULL
}
/* sentinel */
};
void
initcPickleCache
(
void
)
{
PyObject
*
m
,
*
d
;
Cctype
.
ob_type
=
&
PyType_Type
;
if
(
!
ExtensionClassImported
)
return
;
capi
=
(
cPersistenceCAPIstruct
*
)
PyCObject_Import
(
"cPersistence"
,
"CAPI"
);
if
(
!
capi
)
return
;
capi
->
percachedel
=
(
percachedelfunc
)
cc_oid_unreferenced
;
m
=
Py_InitModule4
(
"cPickleCache"
,
cCM_methods
,
cPickleCache_doc_string
,
(
PyObject
*
)
NULL
,
PYTHON_API_VERSION
);
py_reload
=
PyString_InternFromString
(
"reload"
);
py__p_jar
=
PyString_InternFromString
(
"_p_jar"
);
py__p_changed
=
PyString_InternFromString
(
"_p_changed"
);
py__p_oid
=
PyString_InternFromString
(
"_p_oid"
);
d
=
PyModule_GetDict
(
m
);
PyDict_SetItemString
(
d
,
"cache_variant"
,
PyString_FromString
(
"stiff/c"
));
}
src/persistent/list.py
deleted
100644 → 0
View file @
4d22ca2b
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""Python implementation of persistent list.
$Id: list.py,v 1.3 2002/08/14 22:07:09 mj Exp $"""
__version__
=
'$Revision: 1.3 $'
[
11
:
-
2
]
import
Persistence
from
UserList
import
UserList
class
PersistentList
(
UserList
,
Persistence
.
Persistent
):
__super_setitem
=
UserList
.
__setitem__
__super_delitem
=
UserList
.
__delitem__
__super_setslice
=
UserList
.
__setslice__
__super_delslice
=
UserList
.
__delslice__
__super_iadd
=
UserList
.
__iadd__
__super_imul
=
UserList
.
__imul__
__super_append
=
UserList
.
append
__super_insert
=
UserList
.
insert
__super_pop
=
UserList
.
pop
__super_remove
=
UserList
.
remove
__super_reverse
=
UserList
.
reverse
__super_sort
=
UserList
.
sort
__super_extend
=
UserList
.
extend
def
__setitem__
(
self
,
i
,
item
):
self
.
__super_setitem
(
i
,
item
)
self
.
_p_changed
=
1
def
__delitem__
(
self
,
i
):
self
.
__super_delitem
(
i
)
self
.
_p_changed
=
1
def
__setslice__
(
self
,
i
,
j
,
other
):
self
.
__super_setslice
(
i
,
j
,
other
)
self
.
_p_changed
=
1
def
__delslice__
(
self
,
i
,
j
):
self
.
__super_delslice
(
i
,
j
)
self
.
_p_changed
=
1
def
__iadd__
(
self
,
other
):
self
.
__super_iadd
(
other
)
self
.
_p_changed
=
1
def
__imul__
(
self
,
n
):
self
.
__super_imul
(
n
)
self
.
_p_changed
=
1
def
append
(
self
,
item
):
self
.
__super_append
(
item
)
self
.
_p_changed
=
1
def
insert
(
self
,
i
,
item
):
self
.
__super_insert
(
i
,
item
)
self
.
_p_changed
=
1
def
pop
(
self
,
i
=-
1
):
rtn
=
self
.
__super_pop
(
i
)
self
.
_p_changed
=
1
return
rtn
def
remove
(
self
,
item
):
self
.
__super_remove
(
item
)
self
.
_p_changed
=
1
def
reverse
(
self
):
self
.
__super_reverse
()
self
.
_p_changed
=
1
def
sort
(
self
,
*
args
):
self
.
__super_sort
(
*
args
)
self
.
_p_changed
=
1
def
extend
(
self
,
other
):
self
.
__super_extend
(
other
)
self
.
_p_changed
=
1
# This works around a bug in Python 2.1.x (up to 2.1.2 at least) where the
# __cmp__ bogusly raises a RuntimeError, and because this is an extension
# class, none of the rich comparison stuff works anyway.
def
__cmp__
(
self
,
other
):
return
cmp
(
self
.
data
,
self
.
_UserList__cast
(
other
))
src/persistent/mapping.py
deleted
100644 → 0
View file @
4d22ca2b
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""Python implementation of persistent base types
$Id: mapping.py,v 1.20 2002/08/14 22:07:09 mj Exp $"""
__version__
=
'$Revision: 1.20 $'
[
11
:
-
2
]
import
Persistence
from
UserDict
import
UserDict
class
PersistentMapping
(
UserDict
,
Persistence
.
Persistent
):
"""A persistent wrapper for mapping objects.
This class allows wrapping of mapping objects so that object
changes are registered. As a side effect, mapping objects may be
subclassed.
A subclass of PersistentMapping or any code that adds new
attributes should not create an attribute named _container. This
is reserved for backwards compatibility reasons.
"""
# UserDict provides all of the mapping behavior. The
# PersistentMapping class is responsible marking the persistent
# state as changed when a method actually changes the state. At
# the mapping API evolves, we may need to add more methods here.
__super_delitem
=
UserDict
.
__delitem__
__super_setitem
=
UserDict
.
__setitem__
__super_clear
=
UserDict
.
clear
__super_update
=
UserDict
.
update
__super_setdefault
=
UserDict
.
setdefault
def
__delitem__
(
self
,
key
):
self
.
__super_delitem
(
key
)
self
.
_p_changed
=
1
def
__setitem__
(
self
,
key
,
v
):
self
.
__super_setitem
(
key
,
v
)
self
.
_p_changed
=
1
def
clear
(
self
):
self
.
__super_clear
()
self
.
_p_changed
=
1
def
update
(
self
,
b
):
self
.
__super_update
(
b
)
self
.
_p_changed
=
1
def
setdefault
(
self
,
key
,
failobj
=
None
):
# We could inline all of UserDict's implementation into the
# method here, but I'd rather not depend at all on the
# implementation in UserDict (simple as it is).
if
not
self
.
has_key
(
key
):
self
.
_p_changed
=
1
return
self
.
__super_setdefault
(
key
,
failobj
)
try
:
__super_popitem
=
UserDict
.
popitem
except
AttributeError
:
pass
else
:
def
popitem
(
self
):
self
.
_p_changed
=
1
return
self
.
__super_popitem
()
# If the internal representation of PersistentMapping changes,
# it causes compatibility problems for pickles generated by
# different versions of the code. Compatibility works in both
# directions, because an application may want to share a database
# between applications using different versions of the code.
# Effectively, the original rep is part of the "API." To provide
# full compatibility, the getstate and setstate must read and
# right objects using the old rep.
# As a result, the PersistentMapping must save and restore the
# actual internal dictionary using the name _container.
def
__getstate__
(
self
):
state
=
{}
state
.
update
(
self
.
__dict__
)
state
[
'_container'
]
=
state
[
'data'
]
del
state
[
'data'
]
return
state
def
__setstate__
(
self
,
state
):
if
state
.
has_key
(
'_container'
):
self
.
data
=
state
[
'_container'
]
del
state
[
'_container'
]
elif
not
state
.
has_key
(
'data'
):
self
.
data
=
{}
self
.
__dict__
.
update
(
state
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment