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
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Joshua
zodb
Commits
04581d75
Commit
04581d75
authored
Jun 05, 2009
by
Jim Fulton
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fixed a compiler warning.
Cleaned up code formatting.
parent
86ec9190
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
1698 additions
and
1570 deletions
+1698
-1570
src/persistent/cPersistence.c
src/persistent/cPersistence.c
+806
-742
src/persistent/cPickleCache.c
src/persistent/cPickleCache.c
+892
-828
No files found.
src/persistent/cPersistence.c
View file @
04581d75
...
...
@@ -10,17 +10,17 @@
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$
\n
"
;
"Defines Persistent mixin class for persistent objects.
\n
"
"
\n
"
"$Id$
\n
"
;
#include "cPersistence.h"
#include "structmember.h"
struct
ccobject_head_struct
{
CACHE_HEAD
CACHE_HEAD
};
/* These two objects are initialized when the module is loaded */
...
...
@@ -37,23 +37,23 @@ static PyObject *py___getnewargs__, *py___getstate__;
static
int
init_strings
(
void
)
{
#define INIT_STRING(S) \
if (!(py_ ## S = PyString_InternFromString(#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__
);
INIT_STRING
(
__slotnames__
);
INIT_STRING
(
__getnewargs__
);
INIT_STRING
(
__getstate__
);
#define INIT_STRING(S)
\
if (!(py_ ## S = PyString_InternFromString(#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__
);
INIT_STRING
(
__slotnames__
);
INIT_STRING
(
__getnewargs__
);
INIT_STRING
(
__getstate__
);
#undef INIT_STRING
return
0
;
return
0
;
}
#ifdef Py_DEBUG
...
...
@@ -63,13 +63,13 @@ fatal_1350(cPersistentObject *self, const char *caller, const char *detail)
char
buf
[
1000
];
PyOS_snprintf
(
buf
,
sizeof
(
buf
),
"cPersistence.c %s(): object at %p with type %.200s
\n
"
"%s.
\n
"
"The only known cause is multiple threads trying to ghost and
\n
"
"unghost the object simultaneously.
\n
"
"That's not legal, but ZODB can't stop it.
\n
"
"See Collector #1350.
\n
"
,
caller
,
self
,
self
->
ob_type
->
tp_name
,
detail
);
"cPersistence.c %s(): object at %p with type %.200s
\n
"
"%s.
\n
"
"The only known cause is multiple threads trying to ghost and
\n
"
"unghost the object simultaneously.
\n
"
"That's not legal, but ZODB can't stop it.
\n
"
"See Collector #1350.
\n
"
,
caller
,
self
,
self
->
ob_type
->
tp_name
,
detail
);
Py_FatalError
(
buf
);
}
#endif
...
...
@@ -82,44 +82,48 @@ static void ghostify(cPersistentObject*);
static
int
unghostify
(
cPersistentObject
*
self
)
{
if
(
self
->
state
<
0
&&
self
->
jar
)
{
PyObject
*
r
;
/* Is it ever possible to not have a cache? */
if
(
self
->
cache
)
{
/* Create a node in the ring for this unghostified object. */
self
->
cache
->
non_ghost_count
++
;
self
->
cache
->
total_estimated_size
+=
_estimated_size_in_bytes
(
self
->
estimated_size
);
ring_add
(
&
self
->
cache
->
ring_home
,
&
self
->
ring
);
Py_INCREF
(
self
);
if
(
self
->
state
<
0
&&
self
->
jar
)
{
PyObject
*
r
;
/* Is it ever possible to not have a cache? */
if
(
self
->
cache
)
{
/* Create a node in the ring for this unghostified object. */
self
->
cache
->
non_ghost_count
++
;
self
->
cache
->
total_estimated_size
+=
_estimated_size_in_bytes
(
self
->
estimated_size
);
ring_add
(
&
self
->
cache
->
ring_home
,
&
self
->
ring
);
Py_INCREF
(
self
);
}
/* set state to CHANGED while setstate() call is in progress
to prevent a recursive call to _PyPersist_Load().
*/
self
->
state
=
cPersistent_CHANGED_STATE
;
/* Call the object's __setstate__() */
r
=
PyObject_CallMethod
(
self
->
jar
,
"setstate"
,
"O"
,
(
PyObject
*
)
self
);
if
(
r
==
NULL
)
{
ghostify
(
self
);
return
-
1
;
/* set state to CHANGED while setstate() call is in progress
to prevent a recursive call to _PyPersist_Load().
*/
self
->
state
=
cPersistent_CHANGED_STATE
;
/* Call the object's __setstate__() */
r
=
PyObject_CallMethod
(
self
->
jar
,
"setstate"
,
"O"
,
(
PyObject
*
)
self
);
if
(
r
==
NULL
)
{
ghostify
(
self
);
return
-
1
;
}
self
->
state
=
cPersistent_UPTODATE_STATE
;
Py_DECREF
(
r
);
if
(
self
->
cache
&&
self
->
ring
.
r_next
==
NULL
)
{
self
->
state
=
cPersistent_UPTODATE_STATE
;
Py_DECREF
(
r
);
if
(
self
->
cache
&&
self
->
ring
.
r_next
==
NULL
)
{
#ifdef Py_DEBUG
fatal_1350
(
self
,
"unghostify"
,
"is not in the cache despite that we just "
"unghostified it"
);
"is not in the cache despite that we just "
"unghostified it"
);
#else
PyErr_Format
(
PyExc_SystemError
,
"object at %p with type "
"%.200s not in the cache despite that we just "
"unghostified it"
,
self
,
self
->
ob_type
->
tp_name
);
return
-
1
;
PyErr_Format
(
PyExc_SystemError
,
"object at %p with type "
"%.200s not in the cache despite that we just "
"unghostified it"
,
self
,
self
->
ob_type
->
tp_name
);
return
-
1
;
#endif
}
}
}
return
1
;
return
1
;
}
/****************************************************************************/
...
...
@@ -129,72 +133,58 @@ static PyTypeObject 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
.
r_next
)
ring_move_to_head
(
&
self
->
cache
->
ring_home
,
&
self
->
ring
);
}
static
void
unlink_from_ring
(
cPersistentObject
*
self
)
{
/* If the cache has been cleared, then a non-ghost object
isn't in the ring any longer.
*/
if
(
self
->
ring
.
r_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
->
cache
->
total_estimated_size
-=
_estimated_size_in_bytes
(
self
->
estimated_size
);
ring_del
(
&
self
->
ring
);
/* Do nothing unless the object is in a cache and not a ghost. */
if
(
self
->
cache
&&
self
->
state
>=
0
&&
self
->
ring
.
r_next
)
ring_move_to_head
(
&
self
->
cache
->
ring_home
,
&
self
->
ring
);
}
static
void
ghostify
(
cPersistentObject
*
self
)
{
PyObject
**
dictptr
;
PyObject
**
dictptr
;
/* are we already a ghost? */
if
(
self
->
state
==
cPersistent_GHOST_STATE
)
return
;
/* are we already a ghost? */
if
(
self
->
state
==
cPersistent_GHOST_STATE
)
return
;
/* Is it ever possible to not have a cache? */
if
(
self
->
cache
==
NULL
)
{
self
->
state
=
cPersistent_GHOST_STATE
;
return
;
/* Is it ever possible to not have a cache? */
if
(
self
->
cache
==
NULL
)
{
self
->
state
=
cPersistent_GHOST_STATE
;
return
;
}
if
(
self
->
ring
.
r_next
==
NULL
)
{
/* There's no way to raise an error in this routine. */
if
(
self
->
ring
.
r_next
==
NULL
)
{
/* There's no way to raise an error in this routine. */
#ifdef Py_DEBUG
fatal_1350
(
self
,
"ghostify"
,
"claims to be in a cache but isn't"
);
fatal_1350
(
self
,
"ghostify"
,
"claims to be in a cache but isn't"
);
#else
return
;
return
;
#endif
}
/* 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
->
cache
->
total_estimated_size
-=
_estimated_size_in_bytes
(
self
->
estimated_size
);
ring_del
(
&
self
->
ring
);
self
->
state
=
cPersistent_GHOST_STATE
;
dictptr
=
_PyObject_GetDictPtr
((
PyObject
*
)
self
);
if
(
dictptr
&&
*
dictptr
)
{
Py_DECREF
(
*
dictptr
);
*
dictptr
=
NULL
;
/* 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
->
cache
->
total_estimated_size
-=
_estimated_size_in_bytes
(
self
->
estimated_size
);
ring_del
(
&
self
->
ring
);
self
->
state
=
cPersistent_GHOST_STATE
;
dictptr
=
_PyObject_GetDictPtr
((
PyObject
*
)
self
);
if
(
dictptr
&&
*
dictptr
)
{
Py_DECREF
(
*
dictptr
);
*
dictptr
=
NULL
;
}
/* 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
);
/* 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
int
...
...
@@ -202,31 +192,32 @@ changed(cPersistentObject *self)
{
if
((
self
->
state
==
cPersistent_UPTODATE_STATE
||
self
->
state
==
cPersistent_STICKY_STATE
)
&&
self
->
jar
)
&&
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
;
}
Py_INCREF
(
self
);
PyTuple_SET_ITEM
(
arg
,
0
,
(
PyObject
*
)
self
);
result
=
PyEval_CallObject
(
meth
,
arg
);
Py_DECREF
(
arg
);
Py_DECREF
(
meth
);
if
(
result
==
NULL
)
return
-
1
;
Py_DECREF
(
result
);
self
->
state
=
cPersistent_CHANGED_STATE
;
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
;
}
Py_INCREF
(
self
);
PyTuple_SET_ITEM
(
arg
,
0
,
(
PyObject
*
)
self
);
result
=
PyEval_CallObject
(
meth
,
arg
);
Py_DECREF
(
arg
);
Py_DECREF
(
meth
);
if
(
result
==
NULL
)
return
-
1
;
Py_DECREF
(
result
);
self
->
state
=
cPersistent_CHANGED_STATE
;
}
return
0
;
...
...
@@ -235,30 +226,32 @@ changed(cPersistentObject *self)
static
PyObject
*
Per__p_deactivate
(
cPersistentObject
*
self
)
{
if
(
self
->
state
==
cPersistent_UPTODATE_STATE
&&
self
->
jar
)
{
PyObject
**
dictptr
=
_PyObject_GetDictPtr
((
PyObject
*
)
self
);
if
(
dictptr
&&
*
dictptr
)
{
Py_DECREF
(
*
dictptr
);
*
dictptr
=
NULL
;
}
/* 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
);
if
(
self
->
state
==
cPersistent_UPTODATE_STATE
&&
self
->
jar
)
{
PyObject
**
dictptr
=
_PyObject_GetDictPtr
((
PyObject
*
)
self
);
if
(
dictptr
&&
*
dictptr
)
{
Py_DECREF
(
*
dictptr
);
*
dictptr
=
NULL
;
}
/* 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
);
}
Py_INCREF
(
Py_None
);
return
Py_None
;
Py_INCREF
(
Py_None
);
return
Py_None
;
}
static
PyObject
*
Per__p_activate
(
cPersistentObject
*
self
)
{
if
(
unghostify
(
self
)
<
0
)
return
NULL
;
if
(
unghostify
(
self
)
<
0
)
return
NULL
;
Py_INCREF
(
Py_None
);
return
Py_None
;
Py_INCREF
(
Py_None
);
return
Py_None
;
}
static
int
Per_set_changed
(
cPersistentObject
*
self
,
PyObject
*
v
);
...
...
@@ -266,278 +259,298 @@ static int Per_set_changed(cPersistentObject *self, PyObject *v);
static
PyObject
*
Per__p_invalidate
(
cPersistentObject
*
self
)
{
signed
char
old_state
=
self
->
state
;
signed
char
old_state
=
self
->
state
;
if
(
old_state
!=
cPersistent_GHOST_STATE
)
{
if
(
Per_set_changed
(
self
,
NULL
)
<
0
)
return
NULL
;
ghostify
(
self
);
if
(
old_state
!=
cPersistent_GHOST_STATE
)
{
if
(
Per_set_changed
(
self
,
NULL
)
<
0
)
return
NULL
;
ghostify
(
self
);
}
Py_INCREF
(
Py_None
);
return
Py_None
;
Py_INCREF
(
Py_None
);
return
Py_None
;
}
static
PyObject
*
pickle_slotnames
(
PyTypeObject
*
cls
)
{
PyObject
*
slotnames
;
PyObject
*
slotnames
;
slotnames
=
PyDict_GetItem
(
cls
->
tp_dict
,
py___slotnames__
);
if
(
slotnames
)
{
Py_INCREF
(
slotnames
);
return
slotnames
;
slotnames
=
PyDict_GetItem
(
cls
->
tp_dict
,
py___slotnames__
);
if
(
slotnames
)
{
Py_INCREF
(
slotnames
);
return
slotnames
;
}
slotnames
=
PyObject_CallFunctionObjArgs
(
copy_reg_slotnames
,
(
PyObject
*
)
cls
,
NULL
);
if
(
slotnames
&&
!
(
slotnames
==
Py_None
||
PyList_Check
(
slotnames
)))
{
PyErr_SetString
(
PyExc_TypeError
,
"copy_reg._slotnames didn't return a list or None"
);
Py_DECREF
(
slotnames
);
return
NULL
;
slotnames
=
PyObject_CallFunctionObjArgs
(
copy_reg_slotnames
,
(
PyObject
*
)
cls
,
NULL
);
if
(
slotnames
&&
!
(
slotnames
==
Py_None
||
PyList_Check
(
slotnames
)))
{
PyErr_SetString
(
PyExc_TypeError
,
"copy_reg._slotnames didn't return a list or None"
);
Py_DECREF
(
slotnames
);
return
NULL
;
}
return
slotnames
;
return
slotnames
;
}
static
PyObject
*
pickle_copy_dict
(
PyObject
*
state
)
{
PyObject
*
copy
,
*
key
,
*
value
;
char
*
ckey
;
Py_ssize_t
pos
=
0
;
copy
=
PyDict_New
();
if
(
!
copy
)
return
NULL
;
if
(
!
state
)
return
copy
;
while
(
PyDict_Next
(
state
,
&
pos
,
&
key
,
&
value
))
{
if
(
key
&&
PyString_Check
(
key
))
{
ckey
=
PyString_AS_STRING
(
key
);
if
(
*
ckey
==
'_'
&&
(
ckey
[
1
]
==
'v'
||
ckey
[
1
]
==
'p'
)
&&
ckey
[
2
]
==
'_'
)
/* skip volatile and persistent */
continue
;
PyObject
*
copy
,
*
key
,
*
value
;
char
*
ckey
;
Py_ssize_t
pos
=
0
;
copy
=
PyDict_New
();
if
(
!
copy
)
return
NULL
;
if
(
!
state
)
return
copy
;
while
(
PyDict_Next
(
state
,
&
pos
,
&
key
,
&
value
))
{
if
(
key
&&
PyString_Check
(
key
))
{
ckey
=
PyString_AS_STRING
(
key
);
if
(
*
ckey
==
'_'
&&
(
ckey
[
1
]
==
'v'
||
ckey
[
1
]
==
'p'
)
&&
ckey
[
2
]
==
'_'
)
/* skip volatile and persistent */
continue
;
}
if
(
PyObject_SetItem
(
copy
,
key
,
value
)
<
0
)
goto
err
;
if
(
PyObject_SetItem
(
copy
,
key
,
value
)
<
0
)
goto
err
;
}
return
copy
;
return
copy
;
err:
Py_DECREF
(
copy
);
return
NULL
;
Py_DECREF
(
copy
);
return
NULL
;
}
static
char
pickle___getstate__doc
[]
=
"Get the object serialization state
\n
"
"
\n
"
"If the object has no assigned slots and has no instance dictionary, then
\n
"
"None is returned.
\n
"
"
\n
"
"If the object has no assigned slots and has an instance dictionary, then
\n
"
"the a copy of the instance dictionary is returned. The copy has any items
\n
"
"with names starting with '_v_' or '_p_' ommitted.
\n
"
"
\n
"
"If the object has assigned slots, then a two-element tuple is returned.
\n
"
"The first element is either None or a copy of the instance dictionary,
\n
"
"as described above. The second element is a dictionary with items
\n
"
"for each of the assigned slots.
\n
"
;
"Get the object serialization state
\n
"
"
\n
"
"If the object has no assigned slots and has no instance dictionary, then
\n
"
"None is returned.
\n
"
"
\n
"
"If the object has no assigned slots and has an instance dictionary, then
\n
"
"the a copy of the instance dictionary is returned. The copy has any items
\n
"
"with names starting with '_v_' or '_p_' ommitted.
\n
"
"
\n
"
"If the object has assigned slots, then a two-element tuple is returned.
\n
"
"The first element is either None or a copy of the instance dictionary,
\n
"
"as described above. The second element is a dictionary with items
\n
"
"for each of the assigned slots.
\n
"
;
static
PyObject
*
pickle___getstate__
(
PyObject
*
self
)
{
PyObject
*
slotnames
=
NULL
,
*
slots
=
NULL
,
*
state
=
NULL
;
PyObject
**
dictp
;
int
n
=
0
;
slotnames
=
pickle_slotnames
(
self
->
ob_type
);
if
(
!
slotnames
)
return
NULL
;
dictp
=
_PyObject_GetDictPtr
(
self
);
if
(
dictp
)
state
=
pickle_copy_dict
(
*
dictp
);
else
{
state
=
Py_None
;
Py_INCREF
(
state
);
PyObject
*
slotnames
=
NULL
,
*
slots
=
NULL
,
*
state
=
NULL
;
PyObject
**
dictp
;
int
n
=
0
;
slotnames
=
pickle_slotnames
(
self
->
ob_type
);
if
(
!
slotnames
)
return
NULL
;
dictp
=
_PyObject_GetDictPtr
(
self
);
if
(
dictp
)
state
=
pickle_copy_dict
(
*
dictp
);
else
{
state
=
Py_None
;
Py_INCREF
(
state
);
}
if
(
slotnames
!=
Py_None
)
{
int
i
;
slots
=
PyDict_New
();
if
(
!
slots
)
goto
end
;
for
(
i
=
0
;
i
<
PyList_GET_SIZE
(
slotnames
);
i
++
)
{
PyObject
*
name
,
*
value
;
char
*
cname
;
name
=
PyList_GET_ITEM
(
slotnames
,
i
);
if
(
PyString_Check
(
name
))
{
cname
=
PyString_AS_STRING
(
name
);
if
(
*
cname
==
'_'
&&
(
cname
[
1
]
==
'v'
||
cname
[
1
]
==
'p'
)
&&
cname
[
2
]
==
'_'
)
/* skip volatile and persistent */
continue
;
if
(
slotnames
!=
Py_None
)
{
int
i
;
slots
=
PyDict_New
();
if
(
!
slots
)
goto
end
;
for
(
i
=
0
;
i
<
PyList_GET_SIZE
(
slotnames
);
i
++
)
{
PyObject
*
name
,
*
value
;
char
*
cname
;
name
=
PyList_GET_ITEM
(
slotnames
,
i
);
if
(
PyString_Check
(
name
))
{
cname
=
PyString_AS_STRING
(
name
);
if
(
*
cname
==
'_'
&&
(
cname
[
1
]
==
'v'
||
cname
[
1
]
==
'p'
)
&&
cname
[
2
]
==
'_'
)
/* skip volatile and persistent */
continue
;
}
/* Unclear: Will this go through our getattr hook? */
value
=
PyObject_GetAttr
(
self
,
name
);
if
(
value
==
NULL
)
PyErr_Clear
();
else
{
int
err
=
PyDict_SetItem
(
slots
,
name
,
value
);
Py_DECREF
(
value
);
if
(
err
<
0
)
goto
end
;
n
++
;
/* Unclear: Will this go through our getattr hook? */
value
=
PyObject_GetAttr
(
self
,
name
);
if
(
value
==
NULL
)
PyErr_Clear
();
else
{
int
err
=
PyDict_SetItem
(
slots
,
name
,
value
);
Py_DECREF
(
value
);
if
(
err
<
0
)
goto
end
;
n
++
;
}
}
}
if
(
n
)
state
=
Py_BuildValue
(
"(NO)"
,
state
,
slots
);
if
(
n
)
state
=
Py_BuildValue
(
"(NO)"
,
state
,
slots
);
end:
Py_XDECREF
(
slotnames
);
Py_XDECREF
(
slots
);
Py_XDECREF
(
slotnames
);
Py_XDECREF
(
slots
);
return
state
;
return
state
;
}
static
int
pickle_setattrs_from_dict
(
PyObject
*
self
,
PyObject
*
dict
)
{
PyObject
*
key
,
*
value
;
Py_ssize_t
pos
=
0
;
PyObject
*
key
,
*
value
;
Py_ssize_t
pos
=
0
;
if
(
!
PyDict_Check
(
dict
))
{
PyErr_SetString
(
PyExc_TypeError
,
"Expected dictionary"
);
return
-
1
;
if
(
!
PyDict_Check
(
dict
))
{
PyErr_SetString
(
PyExc_TypeError
,
"Expected dictionary"
);
return
-
1
;
}
while
(
PyDict_Next
(
dict
,
&
pos
,
&
key
,
&
value
))
{
if
(
PyObject_SetAttr
(
self
,
key
,
value
)
<
0
)
return
-
1
;
while
(
PyDict_Next
(
dict
,
&
pos
,
&
key
,
&
value
))
{
if
(
PyObject_SetAttr
(
self
,
key
,
value
)
<
0
)
return
-
1
;
}
return
0
;
return
0
;
}
static
char
pickle___setstate__doc
[]
=
"Set the object serialization state
\n\n
"
"The state should be in one of 3 forms:
\n\n
"
"- None
\n\n
"
" Ignored
\n\n
"
"- A dictionary
\n\n
"
" In this case, the object's instance dictionary will be cleared and
\n
"
" updated with the new state.
\n\n
"
"- A two-tuple with a string as the first element.
\n\n
"
" In this case, the method named by the string in the first element will be
\n
"
"
called with the second element.
\n\n
"
" This form supports migration of data formats.
\n\n
"
"- A two-tuple with None or a Dictionary as the first element and
\n
"
" with a dictionary as the second element.
\n\n
"
" If the first element is not None, then the object's instance dictionary
\n
"
" will be cleared and updated with the value.
\n\n
"
" The items in the second element will be assigned as attributes.
\n
"
;
"Set the object serialization state
\n\n
"
"The state should be in one of 3 forms:
\n\n
"
"- None
\n\n
"
" Ignored
\n\n
"
"- A dictionary
\n\n
"
" In this case, the object's instance dictionary will be cleared and
\n
"
" updated with the new state.
\n\n
"
"- A two-tuple with a string as the first element.
\n\n
"
" In this case, the method named by the string in the first element will
\n
"
" be
called with the second element.
\n\n
"
" This form supports migration of data formats.
\n\n
"
"- A two-tuple with None or a Dictionary as the first element and
\n
"
" with a dictionary as the second element.
\n\n
"
" If the first element is not None, then the object's instance dictionary
\n
"
" will be cleared and updated with the value.
\n\n
"
" The items in the second element will be assigned as attributes.
\n
"
;
static
PyObject
*
pickle___setstate__
(
PyObject
*
self
,
PyObject
*
state
)
{
PyObject
*
slots
=
NULL
;
PyObject
*
slots
=
NULL
;
if
(
PyTuple_Check
(
state
))
{
if
(
!
PyArg_ParseTuple
(
state
,
"OO:__setstate__"
,
&
state
,
&
slots
))
return
NULL
;
if
(
PyTuple_Check
(
state
))
{
if
(
!
PyArg_ParseTuple
(
state
,
"OO:__setstate__"
,
&
state
,
&
slots
))
return
NULL
;
}
if
(
state
!=
Py_None
)
{
PyObject
**
dict
;
dict
=
_PyObject_GetDictPtr
(
self
);
if
(
dict
)
{
if
(
!*
dict
)
{
*
dict
=
PyDict_New
();
if
(
!*
dict
)
return
NULL
;
if
(
state
!=
Py_None
)
{
PyObject
**
dict
;
dict
=
_PyObject_GetDictPtr
(
self
);
if
(
dict
)
{
if
(
!*
dict
)
{
*
dict
=
PyDict_New
();
if
(
!*
dict
)
return
NULL
;
}
}
if
(
*
dict
)
{
PyDict_Clear
(
*
dict
);
if
(
PyDict_Update
(
*
dict
,
state
)
<
0
)
return
NULL
;
if
(
*
dict
)
{
PyDict_Clear
(
*
dict
);
if
(
PyDict_Update
(
*
dict
,
state
)
<
0
)
return
NULL
;
}
else
if
(
pickle_setattrs_from_dict
(
self
,
state
)
<
0
)
return
NULL
;
else
if
(
pickle_setattrs_from_dict
(
self
,
state
)
<
0
)
return
NULL
;
}
if
(
slots
&&
pickle_setattrs_from_dict
(
self
,
slots
)
<
0
)
return
NULL
;
if
(
slots
&&
pickle_setattrs_from_dict
(
self
,
slots
)
<
0
)
return
NULL
;
Py_INCREF
(
Py_None
);
return
Py_None
;
Py_INCREF
(
Py_None
);
return
Py_None
;
}
static
char
pickle___reduce__doc
[]
=
"Reduce an object to contituent parts for serialization
\n
"
;
"Reduce an object to contituent parts for serialization
\n
"
;
static
PyObject
*
pickle___reduce__
(
PyObject
*
self
)
{
PyObject
*
args
=
NULL
,
*
bargs
=
NULL
,
*
state
=
NULL
,
*
getnewargs
=
NULL
;
int
l
,
i
;
getnewargs
=
PyObject_GetAttr
(
self
,
py___getnewargs__
);
if
(
getnewargs
)
{
bargs
=
PyObject_CallFunctionObjArgs
(
getnewargs
,
NULL
);
Py_DECREF
(
getnewargs
);
if
(
!
bargs
)
return
NULL
;
l
=
PyTuple_Size
(
bargs
);
if
(
l
<
0
)
goto
end
;
PyObject
*
args
=
NULL
,
*
bargs
=
NULL
,
*
state
=
NULL
,
*
getnewargs
=
NULL
;
int
l
,
i
;
getnewargs
=
PyObject_GetAttr
(
self
,
py___getnewargs__
);
if
(
getnewargs
)
{
bargs
=
PyObject_CallFunctionObjArgs
(
getnewargs
,
NULL
);
Py_DECREF
(
getnewargs
);
if
(
!
bargs
)
return
NULL
;
l
=
PyTuple_Size
(
bargs
);
if
(
l
<
0
)
goto
end
;
}
else
{
PyErr_Clear
();
l
=
0
;
else
{
PyErr_Clear
();
l
=
0
;
}
args
=
PyTuple_New
(
l
+
1
);
if
(
args
==
NULL
)
goto
end
;
args
=
PyTuple_New
(
l
+
1
);
if
(
args
==
NULL
)
goto
end
;
Py_INCREF
(
self
->
ob_type
);
PyTuple_SET_ITEM
(
args
,
0
,
(
PyObject
*
)(
self
->
ob_type
));
for
(
i
=
0
;
i
<
l
;
i
++
)
{
Py_INCREF
(
PyTuple_GET_ITEM
(
bargs
,
i
));
PyTuple_SET_ITEM
(
args
,
i
+
1
,
PyTuple_GET_ITEM
(
bargs
,
i
));
Py_INCREF
(
self
->
ob_type
);
PyTuple_SET_ITEM
(
args
,
0
,
(
PyObject
*
)(
self
->
ob_type
));
for
(
i
=
0
;
i
<
l
;
i
++
)
{
Py_INCREF
(
PyTuple_GET_ITEM
(
bargs
,
i
));
PyTuple_SET_ITEM
(
args
,
i
+
1
,
PyTuple_GET_ITEM
(
bargs
,
i
));
}
state
=
PyObject_CallMethodObjArgs
(
self
,
py___getstate__
,
NULL
);
if
(
!
state
)
goto
end
;
state
=
PyObject_CallMethodObjArgs
(
self
,
py___getstate__
,
NULL
);
if
(
!
state
)
goto
end
;
state
=
Py_BuildValue
(
"(OON)"
,
__newobj__
,
args
,
state
);
state
=
Py_BuildValue
(
"(OON)"
,
__newobj__
,
args
,
state
);
end:
Py_XDECREF
(
bargs
);
Py_XDECREF
(
args
);
Py_XDECREF
(
bargs
);
Py_XDECREF
(
args
);
return
state
;
return
state
;
}
...
...
@@ -554,13 +567,13 @@ pickle___reduce__(PyObject *self)
static
PyObject
*
Per__getstate__
(
cPersistentObject
*
self
)
{
/* TODO: Should it be an error to call __getstate__() on a ghost? */
if
(
unghostify
(
self
)
<
0
)
return
NULL
;
/* TODO: Should it be an error to call __getstate__() on a ghost? */
if
(
unghostify
(
self
)
<
0
)
return
NULL
;
/* TODO: should we increment stickyness? Tim doesn't understand that
question. S*/
return
pickle___getstate__
((
PyObject
*
)
self
);
/* TODO: should we increment stickyness? Tim doesn't understand that
question. S*/
return
pickle___getstate__
((
PyObject
*
)
self
);
}
/* The Persistent base type provides a traverse function, but not a
...
...
@@ -578,34 +591,48 @@ Per__getstate__(cPersistentObject *self)
static
void
Per_dealloc
(
cPersistentObject
*
self
)
{
if
(
self
->
state
>=
0
)
unlink_from_ring
(
self
);
if
(
self
->
cache
)
cPersistenceCAPI
->
percachedel
(
self
->
cache
,
self
->
oid
);
Py_XDECREF
(
self
->
cache
);
Py_XDECREF
(
self
->
jar
);
Py_XDECREF
(
self
->
oid
);
self
->
ob_type
->
tp_free
(
self
);
if
(
self
->
state
>=
0
)
{
/* If the cache has been cleared, then a non-ghost object
isn't in the ring any longer.
*/
if
(
self
->
ring
.
r_next
!=
NULL
)
{
/* 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
->
cache
->
total_estimated_size
-=
_estimated_size_in_bytes
(
self
->
estimated_size
);
ring_del
(
&
self
->
ring
);
}
}
if
(
self
->
cache
)
cPersistenceCAPI
->
percachedel
(
self
->
cache
,
self
->
oid
);
Py_XDECREF
(
self
->
cache
);
Py_XDECREF
(
self
->
jar
);
Py_XDECREF
(
self
->
oid
);
self
->
ob_type
->
tp_free
(
self
);
}
static
int
Per_traverse
(
cPersistentObject
*
self
,
visitproc
visit
,
void
*
arg
)
{
int
err
;
int
err
;
#define VISIT(SLOT) \
if (SLOT) {
\
err = visit((PyObject *)(SLOT), arg);
\
if (err)
\
return err;
\
}
#define VISIT(SLOT)
\
if (SLOT) {
\
err = visit((PyObject *)(SLOT), arg);
\
if (err)
\
return err;
\
}
VISIT
(
self
->
jar
);
VISIT
(
self
->
oid
);
VISIT
(
self
->
cache
);
VISIT
(
self
->
jar
);
VISIT
(
self
->
oid
);
VISIT
(
self
->
cache
);
#undef VISIT
return
0
;
return
0
;
}
/* convert_name() returns a new reference to a string name
...
...
@@ -616,20 +643,23 @@ static PyObject *
convert_name
(
PyObject
*
name
)
{
#ifdef Py_USING_UNICODE
/* The Unicode to string conversion is done here because the
existing tp_setattro slots expect a string object as name
and we wouldn't want to break those. */
if
(
PyUnicode_Check
(
name
))
{
name
=
PyUnicode_AsEncodedString
(
name
,
NULL
,
NULL
);
/* The Unicode to string conversion is done here because the
existing tp_setattro slots expect a string object as name
and we wouldn't want to break those. */
if
(
PyUnicode_Check
(
name
))
{
name
=
PyUnicode_AsEncodedString
(
name
,
NULL
,
NULL
);
}
else
else
#endif
if
(
!
PyString_Check
(
name
))
{
PyErr_SetString
(
PyExc_TypeError
,
"attribute name must be a string"
);
return
NULL
;
}
else
Py_INCREF
(
name
);
return
name
;
if
(
!
PyString_Check
(
name
))
{
PyErr_SetString
(
PyExc_TypeError
,
"attribute name must be a string"
);
return
NULL
;
}
else
Py_INCREF
(
name
);
return
name
;
}
/* Returns true if the object requires unghostification.
...
...
@@ -646,375 +676,400 @@ convert_name(PyObject *name)
static
int
unghost_getattr
(
const
char
*
s
)
{
if
(
*
s
++
!=
'_'
)
return
1
;
if
(
*
s
==
'p'
)
{
s
++
;
if
(
*
s
==
'_'
)
return
0
;
/* _p_ */
else
return
1
;
if
(
*
s
++
!=
'_'
)
return
1
;
if
(
*
s
==
'p'
)
{
s
++
;
if
(
*
s
==
'_'
)
return
0
;
/* _p_ */
else
return
1
;
}
else
if
(
*
s
==
'_'
)
{
s
++
;
switch
(
*
s
)
{
case
'c'
:
return
strcmp
(
s
,
"class__"
);
case
'd'
:
s
++
;
if
(
!
strcmp
(
s
,
"el__"
))
return
0
;
/* __del__ */
if
(
!
strcmp
(
s
,
"ict__"
))
return
0
;
/* __dict__ */
return
1
;
case
'o'
:
return
strcmp
(
s
,
"of__"
);
case
's'
:
return
strcmp
(
s
,
"setstate__"
);
default:
return
1
;
}
else
if
(
*
s
==
'_'
)
{
s
++
;
switch
(
*
s
)
{
case
'c'
:
return
strcmp
(
s
,
"class__"
);
case
'd'
:
s
++
;
if
(
!
strcmp
(
s
,
"el__"
))
return
0
;
/* __del__ */
if
(
!
strcmp
(
s
,
"ict__"
))
return
0
;
/* __dict__ */
return
1
;
case
'o'
:
return
strcmp
(
s
,
"of__"
);
case
's'
:
return
strcmp
(
s
,
"setstate__"
);
default:
return
1
;
}
}
return
1
;
return
1
;
}
static
PyObject
*
Per_getattro
(
cPersistentObject
*
self
,
PyObject
*
name
)
{
PyObject
*
result
=
NULL
;
/* guilty until proved innocent */
char
*
s
;
name
=
convert_name
(
name
);
if
(
!
name
)
goto
Done
;
s
=
PyString_AS_STRING
(
name
);
if
(
unghost_getattr
(
s
))
{
if
(
unghostify
(
self
)
<
0
)
goto
Done
;
accessed
(
self
);
PyObject
*
result
=
NULL
;
/* guilty until proved innocent */
char
*
s
;
name
=
convert_name
(
name
);
if
(
!
name
)
goto
Done
;
s
=
PyString_AS_STRING
(
name
);
if
(
unghost_getattr
(
s
))
{
if
(
unghostify
(
self
)
<
0
)
goto
Done
;
accessed
(
self
);
}
result
=
PyObject_GenericGetAttr
((
PyObject
*
)
self
,
name
);
result
=
PyObject_GenericGetAttr
((
PyObject
*
)
self
,
name
);
Done:
Py_XDECREF
(
name
);
return
result
;
Done:
Py_XDECREF
(
name
);
return
result
;
}
/* Exposed as _p_getattr method. Test whether base getattr should be used */
static
PyObject
*
Per__p_getattr
(
cPersistentObject
*
self
,
PyObject
*
name
)
{
PyObject
*
result
=
NULL
;
/* guilty until proved innocent */
char
*
s
;
name
=
convert_name
(
name
);
if
(
!
name
)
goto
Done
;
s
=
PyString_AS_STRING
(
name
);
if
(
*
s
!=
'_'
||
unghost_getattr
(
s
))
{
if
(
unghostify
(
self
)
<
0
)
goto
Done
;
accessed
(
self
);
result
=
Py_False
;
PyObject
*
result
=
NULL
;
/* guilty until proved innocent */
char
*
s
;
name
=
convert_name
(
name
);
if
(
!
name
)
goto
Done
;
s
=
PyString_AS_STRING
(
name
);
if
(
*
s
!=
'_'
||
unghost_getattr
(
s
))
{
if
(
unghostify
(
self
)
<
0
)
goto
Done
;
accessed
(
self
);
result
=
Py_False
;
}
else
result
=
Py_True
;
else
result
=
Py_True
;
Py_INCREF
(
result
);
Py_INCREF
(
result
);
Done:
Py_XDECREF
(
name
);
return
result
;
Done:
Py_XDECREF
(
name
);
return
result
;
}
/*
TODO: we should probably not allow assignment of __class__ and __dict__.
TODO: we should probably not allow assignment of __class__ and __dict__.
*/
static
int
Per_setattro
(
cPersistentObject
*
self
,
PyObject
*
name
,
PyObject
*
v
)
{
int
result
=
-
1
;
/* guilty until proved innocent */
char
*
s
;
name
=
convert_name
(
name
);
if
(
!
name
)
goto
Done
;
s
=
PyString_AS_STRING
(
name
);
if
(
strncmp
(
s
,
"_p_"
,
3
)
!=
0
)
{
if
(
unghostify
(
self
)
<
0
)
goto
Done
;
accessed
(
self
);
if
(
strncmp
(
s
,
"_v_"
,
3
)
!=
0
&&
self
->
state
!=
cPersistent_CHANGED_STATE
)
{
if
(
changed
(
self
)
<
0
)
goto
Done
;
}
int
result
=
-
1
;
/* guilty until proved innocent */
char
*
s
;
name
=
convert_name
(
name
);
if
(
!
name
)
goto
Done
;
s
=
PyString_AS_STRING
(
name
);
if
(
strncmp
(
s
,
"_p_"
,
3
)
!=
0
)
{
if
(
unghostify
(
self
)
<
0
)
goto
Done
;
accessed
(
self
);
if
(
strncmp
(
s
,
"_v_"
,
3
)
!=
0
&&
self
->
state
!=
cPersistent_CHANGED_STATE
)
{
if
(
changed
(
self
)
<
0
)
goto
Done
;
}
}
result
=
PyObject_GenericSetAttr
((
PyObject
*
)
self
,
name
,
v
);
result
=
PyObject_GenericSetAttr
((
PyObject
*
)
self
,
name
,
v
);
Done:
Py_XDECREF
(
name
);
return
result
;
Py_XDECREF
(
name
);
return
result
;
}
static
int
Per_p_set_or_delattro
(
cPersistentObject
*
self
,
PyObject
*
name
,
PyObject
*
v
)
{
int
result
=
-
1
;
/* guilty until proved innocent */
char
*
s
;
int
result
=
-
1
;
/* guilty until proved innocent */
char
*
s
;
name
=
convert_name
(
name
);
if
(
!
name
)
goto
Done
;
s
=
PyString_AS_STRING
(
name
);
name
=
convert_name
(
name
);
if
(
!
name
)
goto
Done
;
s
=
PyString_AS_STRING
(
name
);
if
(
strncmp
(
s
,
"_p_"
,
3
))
{
if
(
unghostify
(
self
)
<
0
)
goto
Done
;
accessed
(
self
);
if
(
strncmp
(
s
,
"_p_"
,
3
))
{
if
(
unghostify
(
self
)
<
0
)
goto
Done
;
accessed
(
self
);
result
=
0
;
result
=
0
;
}
else
{
if
(
PyObject_GenericSetAttr
((
PyObject
*
)
self
,
name
,
v
)
<
0
)
goto
Done
;
result
=
1
;
else
{
if
(
PyObject_GenericSetAttr
((
PyObject
*
)
self
,
name
,
v
)
<
0
)
goto
Done
;
result
=
1
;
}
Done:
Py_XDECREF
(
name
);
return
result
;
Py_XDECREF
(
name
);
return
result
;
}
static
PyObject
*
Per__p_setattr
(
cPersistentObject
*
self
,
PyObject
*
args
)
{
PyObject
*
name
,
*
v
,
*
result
;
int
r
;
PyObject
*
name
,
*
v
,
*
result
;
int
r
;
if
(
!
PyArg_ParseTuple
(
args
,
"OO:_p_setattr"
,
&
name
,
&
v
))
return
NULL
;
if
(
!
PyArg_ParseTuple
(
args
,
"OO:_p_setattr"
,
&
name
,
&
v
))
return
NULL
;
r
=
Per_p_set_or_delattro
(
self
,
name
,
v
);
if
(
r
<
0
)
return
NULL
;
r
=
Per_p_set_or_delattro
(
self
,
name
,
v
);
if
(
r
<
0
)
return
NULL
;
result
=
r
?
Py_True
:
Py_False
;
Py_INCREF
(
result
);
return
result
;
result
=
r
?
Py_True
:
Py_False
;
Py_INCREF
(
result
);
return
result
;
}
static
PyObject
*
Per__p_delattr
(
cPersistentObject
*
self
,
PyObject
*
name
)
{
int
r
;
PyObject
*
result
;
int
r
;
PyObject
*
result
;
r
=
Per_p_set_or_delattro
(
self
,
name
,
NULL
);
if
(
r
<
0
)
return
NULL
;
r
=
Per_p_set_or_delattro
(
self
,
name
,
NULL
);
if
(
r
<
0
)
return
NULL
;
result
=
r
?
Py_True
:
Py_False
;
Py_INCREF
(
result
);
return
result
;
result
=
r
?
Py_True
:
Py_False
;
Py_INCREF
(
result
);
return
result
;
}
static
PyObject
*
Per_get_changed
(
cPersistentObject
*
self
)
{
if
(
self
->
state
<
0
)
{
Py_INCREF
(
Py_None
);
return
Py_None
;
if
(
self
->
state
<
0
)
{
Py_INCREF
(
Py_None
);
return
Py_None
;
}
return
PyBool_FromLong
(
self
->
state
==
cPersistent_CHANGED_STATE
);
return
PyBool_FromLong
(
self
->
state
==
cPersistent_CHANGED_STATE
);
}
static
int
Per_set_changed
(
cPersistentObject
*
self
,
PyObject
*
v
)
{
int
deactivate
=
0
;
int
true
;
if
(
!
v
)
{
/* delattr is used to invalidate an object even if it has changed. */
if
(
self
->
state
!=
cPersistent_GHOST_STATE
)
self
->
state
=
cPersistent_UPTODATE_STATE
;
deactivate
=
1
;
int
deactivate
=
0
;
int
true
;
if
(
!
v
)
{
/* delattr is used to invalidate an 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
,
*
meth
;
meth
=
PyObject_GetAttr
((
PyObject
*
)
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.
This prints 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
;
else
if
(
v
==
Py_None
)
deactivate
=
1
;
if
(
deactivate
)
{
PyObject
*
res
,
*
meth
;
meth
=
PyObject_GetAttr
((
PyObject
*
)
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.
This prints 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
;
}
/* !deactivate. If passed a true argument, mark self as changed (starting
* with ZODB 3.6, that includes activating the object if it's a ghost).
* If passed a false argument, and the object isn't a ghost, set the
* state as up-to-date.
*/
true
=
PyObject_IsTrue
(
v
);
if
(
true
==
-
1
)
return
-
1
;
if
(
true
)
{
if
(
self
->
state
<
0
)
{
/* !deactivate. If passed a true argument, mark self as changed (starting
* with ZODB 3.6, that includes activating the object if it's a ghost).
* If passed a false argument, and the object isn't a ghost, set the
* state as up-to-date.
*/
true
=
PyObject_IsTrue
(
v
);
if
(
true
==
-
1
)
return
-
1
;
if
(
true
)
{
if
(
self
->
state
<
0
)
{
if
(
unghostify
(
self
)
<
0
)
return
-
1
;
}
return
changed
(
self
);
return
-
1
;
}
return
changed
(
self
);
}
/* We were passed a false, non-None argument. If we're not a ghost,
* mark self as up-to-date.
*/
if
(
self
->
state
>=
0
)
self
->
state
=
cPersistent_UPTODATE_STATE
;
return
0
;
/* We were passed a false, non-None argument. If we're not a ghost,
* mark self as up-to-date.
*/
if
(
self
->
state
>=
0
)
self
->
state
=
cPersistent_UPTODATE_STATE
;
return
0
;
}
static
PyObject
*
Per_get_oid
(
cPersistentObject
*
self
)
{
PyObject
*
oid
=
self
->
oid
?
self
->
oid
:
Py_None
;
Py_INCREF
(
oid
);
return
oid
;
PyObject
*
oid
=
self
->
oid
?
self
->
oid
:
Py_None
;
Py_INCREF
(
oid
);
return
oid
;
}
static
int
Per_set_oid
(
cPersistentObject
*
self
,
PyObject
*
v
)
{
if
(
self
->
cache
)
{
int
result
;
if
(
v
==
NULL
)
{
PyErr_SetString
(
PyExc_ValueError
,
"can't delete _p_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 _p_oid of cached object"
);
return
-
1
;
}
if
(
self
->
cache
)
{
int
result
;
if
(
v
==
NULL
)
{
PyErr_SetString
(
PyExc_ValueError
,
"can't delete _p_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 _p_oid of cached object"
);
return
-
1
;
}
}
Py_XDECREF
(
self
->
oid
);
Py_XINCREF
(
v
);
self
->
oid
=
v
;
return
0
;
Py_XDECREF
(
self
->
oid
);
Py_XINCREF
(
v
);
self
->
oid
=
v
;
return
0
;
}
static
PyObject
*
Per_get_jar
(
cPersistentObject
*
self
)
{
PyObject
*
jar
=
self
->
jar
?
self
->
jar
:
Py_None
;
Py_INCREF
(
jar
);
return
jar
;
PyObject
*
jar
=
self
->
jar
?
self
->
jar
:
Py_None
;
Py_INCREF
(
jar
);
return
jar
;
}
static
int
Per_set_jar
(
cPersistentObject
*
self
,
PyObject
*
v
)
{
if
(
self
->
cache
)
{
int
result
;
if
(
v
==
NULL
)
{
PyErr_SetString
(
PyExc_ValueError
,
"can't delete _p_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 _p_jar of cached object"
);
return
-
1
;
}
if
(
self
->
cache
)
{
int
result
;
if
(
v
==
NULL
)
{
PyErr_SetString
(
PyExc_ValueError
,
"can't delete _p_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 _p_jar of cached object"
);
return
-
1
;
}
}
Py_XDECREF
(
self
->
jar
);
Py_XINCREF
(
v
);
self
->
jar
=
v
;
return
0
;
Py_XDECREF
(
self
->
jar
);
Py_XINCREF
(
v
);
self
->
jar
=
v
;
return
0
;
}
static
PyObject
*
Per_get_serial
(
cPersistentObject
*
self
)
{
return
PyString_FromStringAndSize
(
self
->
serial
,
8
);
return
PyString_FromStringAndSize
(
self
->
serial
,
8
);
}
static
int
Per_set_serial
(
cPersistentObject
*
self
,
PyObject
*
v
)
{
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
;
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
;
}
static
PyObject
*
Per_get_mtime
(
cPersistentObject
*
self
)
{
PyObject
*
t
,
*
v
;
PyObject
*
t
,
*
v
;
if
(
unghostify
(
self
)
<
0
)
return
NULL
;
if
(
unghostify
(
self
)
<
0
)
return
NULL
;
accessed
(
self
);
accessed
(
self
);
if
(
memcmp
(
self
->
serial
,
"
\0\0\0\0\0\0\0\0
"
,
8
)
==
0
)
{
Py_INCREF
(
Py_None
);
return
Py_None
;
if
(
memcmp
(
self
->
serial
,
"
\0\0\0\0\0\0\0\0
"
,
8
)
==
0
)
{
Py_INCREF
(
Py_None
);
return
Py_None
;
}
t
=
PyObject_CallFunction
(
TimeStamp
,
"s#"
,
self
->
serial
,
8
);
if
(
!
t
)
return
NULL
;
v
=
PyObject_CallMethod
(
t
,
"timeTime"
,
""
);
Py_DECREF
(
t
);
return
v
;
t
=
PyObject_CallFunction
(
TimeStamp
,
"s#"
,
self
->
serial
,
8
);
if
(
!
t
)
return
NULL
;
v
=
PyObject_CallMethod
(
t
,
"timeTime"
,
""
);
Py_DECREF
(
t
);
return
v
;
}
static
PyObject
*
Per_get_state
(
cPersistentObject
*
self
)
{
return
PyInt_FromLong
(
self
->
state
);
return
PyInt_FromLong
(
self
->
state
);
}
static
PyObject
*
...
...
@@ -1026,37 +1081,42 @@ Per_get_estimated_size(cPersistentObject *self)
static
int
Per_set_estimated_size
(
cPersistentObject
*
self
,
PyObject
*
v
)
{
if
(
v
)
{
if
(
PyInt_Check
(
v
))
{
long
lv
=
PyInt_AS_LONG
(
v
);
if
(
lv
<
0
)
{
PyErr_SetString
(
PyExc_ValueError
,
"_p_estimated_size must not be negative"
);
return
-
1
;
}
self
->
estimated_size
=
_estimated_size_in_24_bits
(
lv
);
}
else
{
PyErr_SetString
(
PyExc_ValueError
,
"_p_estimated_size must be an integer"
);
return
-
1
;
if
(
v
)
{
if
(
PyInt_Check
(
v
))
{
long
lv
=
PyInt_AS_LONG
(
v
);
if
(
lv
<
0
)
{
PyErr_SetString
(
PyExc_ValueError
,
"_p_estimated_size must not be negative"
);
return
-
1
;
}
self
->
estimated_size
=
_estimated_size_in_24_bits
(
lv
);
}
else
{
PyErr_SetString
(
PyExc_ValueError
,
"_p_estimated_size must be an integer"
);
return
-
1
;
}
}
}
else
else
self
->
estimated_size
=
0
;
return
0
;
}
static
PyGetSetDef
Per_getsets
[]
=
{
{
"_p_changed"
,
(
getter
)
Per_get_changed
,
(
setter
)
Per_set_changed
},
{
"_p_jar"
,
(
getter
)
Per_get_jar
,
(
setter
)
Per_set_jar
},
{
"_p_mtime"
,
(
getter
)
Per_get_mtime
},
{
"_p_oid"
,
(
getter
)
Per_get_oid
,
(
setter
)
Per_set_oid
},
{
"_p_serial"
,
(
getter
)
Per_get_serial
,
(
setter
)
Per_set_serial
},
{
"_p_state"
,
(
getter
)
Per_get_state
},
{
"_p_estimated_size"
,
(
getter
)
Per_get_estimated_size
,
(
setter
)
Per_set_estimated_size
},
{
NULL
}
{
"_p_changed"
,
(
getter
)
Per_get_changed
,
(
setter
)
Per_set_changed
},
{
"_p_jar"
,
(
getter
)
Per_get_jar
,
(
setter
)
Per_set_jar
},
{
"_p_mtime"
,
(
getter
)
Per_get_mtime
},
{
"_p_oid"
,
(
getter
)
Per_get_oid
,
(
setter
)
Per_set_oid
},
{
"_p_serial"
,
(
getter
)
Per_get_serial
,
(
setter
)
Per_set_serial
},
{
"_p_state"
,
(
getter
)
Per_get_state
},
{
"_p_estimated_size"
,
(
getter
)
Per_get_estimated_size
,
(
setter
)
Per_set_estimated_size
},
{
NULL
}
};
static
struct
PyMethodDef
Per_methods
[]
=
{
...
...
@@ -1117,38 +1177,38 @@ static struct PyMethodDef Per_methods[] = {
#define DEFERRED_ADDRESS(ADDR) 0
static
PyTypeObject
Pertype
=
{
PyObject_HEAD_INIT
(
DEFERRED_ADDRESS
(
&
PyPersist_MetaType
))
0
,
/* ob_size */
"persistent.Persistent"
,
/* tp_name */
sizeof
(
cPersistentObject
),
/* tp_basicsize */
0
,
/* tp_itemsize */
(
destructor
)
Per_dealloc
,
/* tp_dealloc */
0
,
/* tp_print */
0
,
/* tp_getattr */
0
,
/* tp_setattr */
0
,
/* tp_compare */
0
,
/* tp_repr */
0
,
/* tp_as_number */
0
,
/* tp_as_sequence */
0
,
/* tp_as_mapping */
0
,
/* tp_hash */
0
,
/* tp_call */
0
,
/* tp_str */
(
getattrofunc
)
Per_getattro
,
/* tp_getattro */
(
setattrofunc
)
Per_setattro
,
/* tp_setattro */
0
,
/* tp_as_buffer */
Py_TPFLAGS_DEFAULT
|
Py_TPFLAGS_BASETYPE
|
Py_TPFLAGS_HAVE_GC
,
/* tp_flags */
0
,
/* tp_doc */
(
traverseproc
)
Per_traverse
,
/* tp_traverse */
0
,
/* tp_clear */
0
,
/* tp_richcompare */
0
,
/* tp_weaklistoffset */
0
,
/* tp_iter */
0
,
/* tp_iternext */
Per_methods
,
/* tp_methods */
0
,
/* tp_members */
Per_getsets
,
/* tp_getset */
PyObject_HEAD_INIT
(
DEFERRED_ADDRESS
(
&
PyPersist_MetaType
))
0
,
/* ob_size */
"persistent.Persistent"
,
/* tp_name */
sizeof
(
cPersistentObject
),
/* tp_basicsize */
0
,
/* tp_itemsize */
(
destructor
)
Per_dealloc
,
/* tp_dealloc */
0
,
/* tp_print */
0
,
/* tp_getattr */
0
,
/* tp_setattr */
0
,
/* tp_compare */
0
,
/* tp_repr */
0
,
/* tp_as_number */
0
,
/* tp_as_sequence */
0
,
/* tp_as_mapping */
0
,
/* tp_hash */
0
,
/* tp_call */
0
,
/* tp_str */
(
getattrofunc
)
Per_getattro
,
/* tp_getattro */
(
setattrofunc
)
Per_setattro
,
/* tp_setattro */
0
,
/* tp_as_buffer */
Py_TPFLAGS_DEFAULT
|
Py_TPFLAGS_BASETYPE
|
Py_TPFLAGS_HAVE_GC
,
/* tp_flags */
0
,
/* tp_doc */
(
traverseproc
)
Per_traverse
,
/* tp_traverse */
0
,
/* tp_clear */
0
,
/* tp_richcompare */
0
,
/* tp_weaklistoffset */
0
,
/* tp_iter */
0
,
/* tp_iternext */
Per_methods
,
/* tp_methods */
0
,
/* tp_members */
Per_getsets
,
/* tp_getset */
};
/* End of code for Persistent objects */
...
...
@@ -1160,104 +1220,108 @@ typedef int (*intfunctionwithpythonarg)(PyObject*);
static
int
Per_setstate
(
cPersistentObject
*
self
)
{
if
(
unghostify
(
self
)
<
0
)
return
-
1
;
self
->
state
=
cPersistent_STICKY_STATE
;
return
0
;
if
(
unghostify
(
self
)
<
0
)
return
-
1
;
self
->
state
=
cPersistent_STICKY_STATE
;
return
0
;
}
static
PyObject
*
simple_new
(
PyObject
*
self
,
PyObject
*
type_object
)
{
return
PyType_GenericNew
((
PyTypeObject
*
)
type_object
,
NULL
,
NULL
);
return
PyType_GenericNew
((
PyTypeObject
*
)
type_object
,
NULL
,
NULL
);
}
static
PyMethodDef
cPersistence_methods
[]
=
{
static
PyMethodDef
cPersistence_methods
[]
=
{
{
"simple_new"
,
simple_new
,
METH_O
,
"Create an object by simply calling a class's __new__ method without "
"arguments."
},
{
NULL
,
NULL
}
};
};
static
cPersistenceCAPIstruct
truecPersistenceCAPI
=
{
&
Pertype
,
(
getattrofunc
)
Per_getattro
,
/*tp_getattr with object key*/
(
setattrofunc
)
Per_setattro
,
/*tp_setattr with object key*/
changed
,
accessed
,
ghostify
,
(
intfunctionwithpythonarg
)
Per_setstate
,
NULL
/* The percachedel slot is initialized in cPickleCache.c when
the module is loaded. It uses a function in a different
shared library. */
&
Pertype
,
(
getattrofunc
)
Per_getattro
,
/*tp_getattr with object key*/
(
setattrofunc
)
Per_setattro
,
/*tp_setattr with object key*/
changed
,
accessed
,
ghostify
,
(
intfunctionwithpythonarg
)
Per_setstate
,
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
,
*
s
;
PyObject
*
copy_reg
;
PyObject
*
m
,
*
s
;
PyObject
*
copy_reg
;
if
(
init_strings
()
<
0
)
return
;
if
(
init_strings
()
<
0
)
return
;
m
=
Py_InitModule3
(
"cPersistence"
,
cPersistence_methods
,
cPersistence_doc_string
);
m
=
Py_InitModule3
(
"cPersistence"
,
cPersistence_methods
,
cPersistence_doc_string
);
Pertype
.
ob_type
=
&
PyType_Type
;
Pertype
.
tp_new
=
PyType_GenericNew
;
if
(
PyType_Ready
(
&
Pertype
)
<
0
)
return
;
if
(
PyModule_AddObject
(
m
,
"Persistent"
,
(
PyObject
*
)
&
Pertype
)
<
0
)
return
;
Pertype
.
ob_type
=
&
PyType_Type
;
Pertype
.
tp_new
=
PyType_GenericNew
;
if
(
PyType_Ready
(
&
Pertype
)
<
0
)
return
;
if
(
PyModule_AddObject
(
m
,
"Persistent"
,
(
PyObject
*
)
&
Pertype
)
<
0
)
return
;
cPersistenceCAPI
=
&
truecPersistenceCAPI
;
s
=
PyCObject_FromVoidPtr
(
cPersistenceCAPI
,
NULL
);
if
(
!
s
)
return
;
if
(
PyModule_AddObject
(
m
,
"CAPI"
,
s
)
<
0
)
return
;
cPersistenceCAPI
=
&
truecPersistenceCAPI
;
s
=
PyCObject_FromVoidPtr
(
cPersistenceCAPI
,
NULL
);
if
(
!
s
)
return
;
if
(
PyModule_AddObject
(
m
,
"CAPI"
,
s
)
<
0
)
return
;
if
(
PyModule_AddIntConstant
(
m
,
"GHOST"
,
cPersistent_GHOST_STATE
)
<
0
)
return
;
if
(
PyModule_AddIntConstant
(
m
,
"GHOST"
,
cPersistent_GHOST_STATE
)
<
0
)
return
;
if
(
PyModule_AddIntConstant
(
m
,
"UPTODATE"
,
cPersistent_UPTODATE_STATE
)
<
0
)
return
;
if
(
PyModule_AddIntConstant
(
m
,
"UPTODATE"
,
cPersistent_UPTODATE_STATE
)
<
0
)
return
;
if
(
PyModule_AddIntConstant
(
m
,
"CHANGED"
,
cPersistent_CHANGED_STATE
)
<
0
)
return
;
if
(
PyModule_AddIntConstant
(
m
,
"CHANGED"
,
cPersistent_CHANGED_STATE
)
<
0
)
return
;
if
(
PyModule_AddIntConstant
(
m
,
"STICKY"
,
cPersistent_STICKY_STATE
)
<
0
)
return
;
if
(
PyModule_AddIntConstant
(
m
,
"STICKY"
,
cPersistent_STICKY_STATE
)
<
0
)
return
;
py_simple_new
=
PyObject_GetAttrString
(
m
,
"simple_new"
);
if
(
!
py_simple_new
)
return
;
py_simple_new
=
PyObject_GetAttrString
(
m
,
"simple_new"
);
if
(
!
py_simple_new
)
return
;
copy_reg
=
PyImport_ImportModule
(
"copy_reg"
);
if
(
!
copy_reg
)
return
;
copy_reg
=
PyImport_ImportModule
(
"copy_reg"
);
if
(
!
copy_reg
)
return
;
copy_reg_slotnames
=
PyObject_GetAttrString
(
copy_reg
,
"_slotnames"
);
if
(
!
copy_reg_slotnames
)
{
Py_DECREF
(
copy_reg
);
return
;
copy_reg_slotnames
=
PyObject_GetAttrString
(
copy_reg
,
"_slotnames"
);
if
(
!
copy_reg_slotnames
)
{
Py_DECREF
(
copy_reg
);
return
;
}
__newobj__
=
PyObject_GetAttrString
(
copy_reg
,
"__newobj__"
);
if
(
!
__newobj__
)
{
Py_DECREF
(
copy_reg
);
return
;
__newobj__
=
PyObject_GetAttrString
(
copy_reg
,
"__newobj__"
);
if
(
!
__newobj__
)
{
Py_DECREF
(
copy_reg
);
return
;
}
if
(
!
TimeStamp
)
{
m
=
PyImport_ImportModule
(
"persistent.TimeStamp"
);
if
(
!
m
)
return
;
TimeStamp
=
PyObject_GetAttrString
(
m
,
"TimeStamp"
);
Py_DECREF
(
m
);
/* fall through to immediate return on error */
if
(
!
TimeStamp
)
{
m
=
PyImport_ImportModule
(
"persistent.TimeStamp"
);
if
(
!
m
)
return
;
TimeStamp
=
PyObject_GetAttrString
(
m
,
"TimeStamp"
);
Py_DECREF
(
m
);
/* fall through to immediate return on error */
}
}
src/persistent/cPickleCache.c
View file @
04581d75
/*****************************************************************************
/*****************************************************************************
Copyright (c) 2001, 2002 Zope Corporation and Contributors.
All Rights Reserved.
...
...
@@ -10,87 +10,87 @@
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.
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$
\n
"
;
"Defines the PickleCache used by ZODB Connection objects.
\n
"
"
\n
"
"$Id$
\n
"
;
#define DONT_USE_CPERSISTENCECAPI
#include "cPersistence.h"
...
...
@@ -99,6 +99,12 @@ static char cPickleCache_doc_string[] =
#include <stddef.h>
#undef Py_FindMethod
/* Python 2.4 backward compat */
#if PY_MAJOR_VERSION <= 2 && PY_MINOR_VERSION < 5
#define Py_ssize_t int
typedef
Py_ssize_t
(
*
lenfunc
)(
PyObject
*
);
#endif
/* Python string objects to speed lookups; set by module init. */
static
PyObject
*
py__p_changed
;
static
PyObject
*
py__p_deactivate
;
...
...
@@ -111,31 +117,32 @@ static cPersistenceCAPIstruct *capi;
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 */
int
cache_size
;
/* target number of items in cache */
PY_LONG_LONG
cache_size_bytes
;
/* target total estimated size 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
;
CACHE_HEAD
int
klass_count
;
/* count of persistent classes */
PyObject
*
data
;
/* oid -> object dict */
PyObject
*
jar
;
/* Connection object */
int
cache_size
;
/* target number of items in cache */
PY_LONG_LONG
cache_size_bytes
;
/* target total estimated size 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
;
...
...
@@ -143,224 +150,231 @@ static int cc_ass_sub(ccobject *self, PyObject *key, PyObject *v);
/* ---------------------------------------------------------------- */
#define OBJECT_FROM_RING(SELF, HERE) \
((cPersistentObject *)(((char *)here) - offsetof(cPersistentObject, ring)))
#define OBJECT_FROM_RING(SELF, HERE)
\
((cPersistentObject *)(((char *)here) - offsetof(cPersistentObject, ring)))
/* Insert self into the ring, following after. */
static
void
insert_after
(
CPersistentRing
*
self
,
CPersistentRing
*
after
)
{
assert
(
self
!=
NULL
);
assert
(
after
!=
NULL
);
self
->
r_prev
=
after
;
self
->
r_next
=
after
->
r_next
;
after
->
r_next
->
r_prev
=
self
;
after
->
r_next
=
self
;
assert
(
self
!=
NULL
);
assert
(
after
!=
NULL
);
self
->
r_prev
=
after
;
self
->
r_next
=
after
->
r_next
;
after
->
r_next
->
r_prev
=
self
;
after
->
r_next
=
self
;
}
/* Remove self from the ring. */
static
void
unlink_from_ring
(
CPersistentRing
*
self
)
{
assert
(
self
!=
NULL
);
self
->
r_prev
->
r_next
=
self
->
r_next
;
self
->
r_next
->
r_prev
=
self
->
r_prev
;
assert
(
self
!=
NULL
);
self
->
r_prev
->
r_next
=
self
->
r_next
;
self
->
r_next
->
r_prev
=
self
->
r_prev
;
}
static
int
scan_gc_items
(
ccobject
*
self
,
int
target
,
PY_LONG_LONG
target_bytes
)
{
/* This function must only be called with the ring lock held,
because it places non-object placeholders in the ring.
*/
cPersistentObject
*
object
;
CPersistentRing
*
here
;
CPersistentRing
before_original_home
;
int
result
=
-
1
;
/* guilty until proved innocent */
/* Scan the ring, from least to most recently used, deactivating
* up-to-date objects, until we either find the ring_home again or
* or we've ghosted enough objects to reach the target size.
* Tricky: __getattr__ and __del__ methods can do anything, and in
* particular if we ghostify an object with a __del__ method, that method
* can load the object again, putting it back into the MRU part of the
* ring. Waiting to find ring_home again can thus cause an infinite
* loop (Collector #1208). So before_original_home records the MRU
* position we start with, and we stop the scan when we reach that.
*/
insert_after
(
&
before_original_home
,
self
->
ring_home
.
r_prev
);
here
=
self
->
ring_home
.
r_next
;
/* least recently used object */
while
(
here
!=
&
before_original_home
&&
(
self
->
non_ghost_count
>
target
||
(
target_bytes
&&
self
->
total_estimated_size
>
target_bytes
)
)
)
{
assert
(
self
->
ring_lock
);
assert
(
here
!=
&
self
->
ring_home
);
/* 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
);
if
(
object
->
state
==
cPersistent_UPTODATE_STATE
)
{
CPersistentRing
placeholder
;
PyObject
*
method
;
PyObject
*
temp
;
int
error_occurred
=
0
;
/* 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_GetAttr() call below will
invoke a __getattr__() hook in Python. Also possible
that deactivation will lead to a __del__ method call.
So another thread might run, and mutate the ring as a side
effect of object accesses. There's no predicting then where
in the ring here->next will point after that. The
placeholder won't move as a side effect of calling Python
code.
*/
insert_after
(
&
placeholder
,
here
);
method
=
PyObject_GetAttr
((
PyObject
*
)
object
,
py__p_deactivate
);
if
(
method
==
NULL
)
error_occurred
=
1
;
else
{
temp
=
PyObject_CallObject
(
method
,
NULL
);
Py_DECREF
(
method
);
if
(
temp
==
NULL
)
error_occurred
=
1
;
}
here
=
placeholder
.
r_next
;
unlink_from_ring
(
&
placeholder
);
if
(
error_occurred
)
goto
Done
;
/* This function must only be called with the ring lock held,
because it places non-object placeholders in the ring.
*/
cPersistentObject
*
object
;
CPersistentRing
*
here
;
CPersistentRing
before_original_home
;
int
result
=
-
1
;
/* guilty until proved innocent */
/* Scan the ring, from least to most recently used, deactivating
* up-to-date objects, until we either find the ring_home again or
* or we've ghosted enough objects to reach the target size.
* Tricky: __getattr__ and __del__ methods can do anything, and in
* particular if we ghostify an object with a __del__ method, that method
* can load the object again, putting it back into the MRU part of the
* ring. Waiting to find ring_home again can thus cause an infinite
* loop (Collector #1208). So before_original_home records the MRU
* position we start with, and we stop the scan when we reach that.
*/
insert_after
(
&
before_original_home
,
self
->
ring_home
.
r_prev
);
here
=
self
->
ring_home
.
r_next
;
/* least recently used object */
while
(
here
!=
&
before_original_home
&&
(
self
->
non_ghost_count
>
target
||
(
target_bytes
&&
self
->
total_estimated_size
>
target_bytes
)
)
)
{
assert
(
self
->
ring_lock
);
assert
(
here
!=
&
self
->
ring_home
);
/* 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
);
if
(
object
->
state
==
cPersistent_UPTODATE_STATE
)
{
CPersistentRing
placeholder
;
PyObject
*
method
;
PyObject
*
temp
;
int
error_occurred
=
0
;
/* 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_GetAttr() call below will
invoke a __getattr__() hook in Python. Also possible
that deactivation will lead to a __del__ method call.
So another thread might run, and mutate the ring as a side
effect of object accesses. There's no predicting then where
in the ring here->next will point after that. The
placeholder won't move as a side effect of calling Python
code.
*/
insert_after
(
&
placeholder
,
here
);
method
=
PyObject_GetAttr
((
PyObject
*
)
object
,
py__p_deactivate
);
if
(
method
==
NULL
)
error_occurred
=
1
;
else
{
temp
=
PyObject_CallObject
(
method
,
NULL
);
Py_DECREF
(
method
);
if
(
temp
==
NULL
)
error_occurred
=
1
;
}
here
=
placeholder
.
r_next
;
unlink_from_ring
(
&
placeholder
);
if
(
error_occurred
)
goto
Done
;
}
else
here
=
here
->
r_next
;
else
here
=
here
->
r_next
;
}
result
=
0
;
result
=
0
;
Done:
unlink_from_ring
(
&
before_original_home
);
return
result
;
unlink_from_ring
(
&
before_original_home
);
return
result
;
}
static
PyObject
*
lockgc
(
ccobject
*
self
,
int
target_size
,
PY_LONG_LONG
target_size_bytes
)
{
/* 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
;
/* 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
;
}
self
->
ring_lock
=
1
;
if
(
scan_gc_items
(
self
,
target_size
,
target_size_bytes
)
<
0
)
{
self
->
ring_lock
=
0
;
return
NULL
;
self
->
ring_lock
=
1
;
if
(
scan_gc_items
(
self
,
target_size
,
target_size_bytes
)
<
0
)
{
self
->
ring_lock
=
0
;
return
NULL
;
}
self
->
ring_lock
=
0
;
self
->
ring_lock
=
0
;
Py_INCREF
(
Py_None
);
return
Py_None
;
Py_INCREF
(
Py_None
);
return
Py_None
;
}
static
PyObject
*
cc_incrgc
(
ccobject
*
self
,
PyObject
*
args
)
{
int
obsolete_arg
=
-
999
;
int
starting_size
=
self
->
non_ghost_count
;
int
target_size
=
self
->
cache_size
;
PY_LONG_LONG
target_size_bytes
=
self
->
cache_size_bytes
;
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
;
int
obsolete_arg
=
-
999
;
int
starting_size
=
self
->
non_ghost_count
;
int
target_size
=
self
->
cache_size
;
PY_LONG_LONG
target_size_bytes
=
self
->
cache_size_bytes
;
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"
,
&
obsolete_arg
))
return
NULL
;
if
(
!
PyArg_ParseTuple
(
args
,
"|i:incrgc"
,
&
obsolete_arg
))
return
NULL
;
if
(
obsolete_arg
!=
-
999
&&
(
PyErr_Warn
(
PyExc_DeprecationWarning
,
"No argument expected"
)
<
0
))
return
NULL
;
if
(
obsolete_arg
!=
-
999
&&
(
PyErr_Warn
(
PyExc_DeprecationWarning
,
"No argument expected"
)
<
0
))
return
NULL
;
return
lockgc
(
self
,
target_size
,
target_size_bytes
);
return
lockgc
(
self
,
target_size
,
target_size_bytes
);
}
static
PyObject
*
cc_full_sweep
(
ccobject
*
self
,
PyObject
*
args
)
{
int
dt
=
-
999
;
int
dt
=
-
999
;
/* TODO: This should be deprecated; */
/* TODO: This should be deprecated; */
if
(
!
PyArg_ParseTuple
(
args
,
"|i:full_sweep"
,
&
dt
))
return
NULL
;
if
(
dt
==
-
999
)
return
lockgc
(
self
,
0
,
0
);
else
return
cc_incrgc
(
self
,
args
);
if
(
!
PyArg_ParseTuple
(
args
,
"|i:full_sweep"
,
&
dt
))
return
NULL
;
if
(
dt
==
-
999
)
return
lockgc
(
self
,
0
,
0
);
else
return
cc_incrgc
(
self
,
args
);
}
static
PyObject
*
cc_minimize
(
ccobject
*
self
,
PyObject
*
args
)
{
int
ignored
=
-
999
;
int
ignored
=
-
999
;
if
(
!
PyArg_ParseTuple
(
args
,
"|i:minimize"
,
&
ignored
))
return
NULL
;
if
(
!
PyArg_ParseTuple
(
args
,
"|i:minimize"
,
&
ignored
))
return
NULL
;
if
(
ignored
!=
-
999
&&
(
PyErr_Warn
(
PyExc_DeprecationWarning
,
"No argument expected"
)
<
0
))
return
NULL
;
if
(
ignored
!=
-
999
&&
(
PyErr_Warn
(
PyExc_DeprecationWarning
,
"No argument expected"
)
<
0
))
return
NULL
;
return
lockgc
(
self
,
0
,
0
);
return
lockgc
(
self
,
0
,
0
);
}
static
int
_invalidate
(
ccobject
*
self
,
PyObject
*
key
)
{
static
PyObject
*
_p_invalidate
=
NULL
;
PyObject
*
meth
,
*
v
;
v
=
PyDict_GetItem
(
self
->
data
,
key
);
if
(
v
==
NULL
)
return
0
;
if
(
_p_invalidate
==
NULL
)
{
_p_invalidate
=
PyString_InternFromString
(
"_p_invalidate"
);
if
(
_p_invalidate
==
NULL
)
{
/* It doesn't make any sense to ignore this error, but
the caller ignores all errors.
TODO: and why does it do that? This should be fixed
*/
return
-
1
;
}
}
if
(
v
->
ob_refcnt
<=
1
&&
PyType_Check
(
v
))
{
static
PyObject
*
_p_invalidate
=
NULL
;
PyObject
*
meth
,
*
v
;
v
=
PyDict_GetItem
(
self
->
data
,
key
);
if
(
v
==
NULL
)
return
0
;
if
(
_p_invalidate
==
NULL
)
{
_p_invalidate
=
PyString_InternFromString
(
"_p_invalidate"
);
if
(
_p_invalidate
==
NULL
)
{
/* It doesn't make any sense to ignore this error, but
the caller ignores all errors.
TODO: and why does it do that? This should be fixed
*/
return
-
1
;
}
}
if
(
v
->
ob_refcnt
<=
1
&&
PyType_Check
(
v
))
{
/* This looks wrong, but it isn't. We use strong references to types
because they don't have the ring members.
...
...
@@ -371,13 +385,13 @@ _invalidate(ccobject *self, PyObject *key)
return
PyDict_DelItem
(
self
->
data
,
key
);
}
meth
=
PyObject_GetAttr
(
v
,
_p_invalidate
);
if
(
meth
==
NULL
)
return
-
1
;
meth
=
PyObject_GetAttr
(
v
,
_p_invalidate
);
if
(
meth
==
NULL
)
return
-
1
;
v
=
PyObject_CallObject
(
meth
,
NULL
);
Py_DECREF
(
meth
);
return
v
==
NULL
?
-
1
:
0
;
v
=
PyObject_CallObject
(
meth
,
NULL
);
Py_DECREF
(
meth
);
return
v
==
NULL
?
-
1
:
0
;
}
static
PyObject
*
...
...
@@ -390,36 +404,39 @@ cc_invalidate(ccobject *self, PyObject *inv)
{
while
(
PyDict_Next
(
inv
,
&
i
,
&
key
,
&
v
))
{
if
(
_invalidate
(
self
,
key
)
<
0
)
if
(
_invalidate
(
self
,
key
)
<
0
)
return
NULL
;
}
PyDict_Clear
(
inv
);
}
else
{
else
{
if
(
PyString_Check
(
inv
))
{
if
(
_invalidate
(
self
,
inv
)
<
0
)
if
(
_invalidate
(
self
,
inv
)
<
0
)
return
NULL
;
}
else
{
int
l
,
r
;
l
=
PyObject_Length
(
inv
);
if
(
l
<
0
)
return
NULL
;
for
(
i
=
l
;
--
i
>=
0
;
)
{
key
=
PySequence_GetItem
(
inv
,
i
);
if
(
!
key
)
return
NULL
;
r
=
_invalidate
(
self
,
key
);
Py_DECREF
(
key
);
else
{
int
l
,
r
;
l
=
PyObject_Length
(
inv
);
if
(
l
<
0
)
return
NULL
;
for
(
i
=
l
;
--
i
>=
0
;
)
{
key
=
PySequence_GetItem
(
inv
,
i
);
if
(
!
key
)
return
NULL
;
r
=
_invalidate
(
self
,
key
);
Py_DECREF
(
key
);
if
(
r
<
0
)
return
NULL
;
}
/* Dubious: modifying the input may be an unexpected side effect. */
PySequence_DelSlice
(
inv
,
0
,
l
);
}
}
}
/* Dubious: modifying the input may be an unexpected side effect. */
PySequence_DelSlice
(
inv
,
0
,
l
);
}
}
Py_INCREF
(
Py_None
);
return
Py_None
;
...
...
@@ -428,211 +445,221 @@ cc_invalidate(ccobject *self, PyObject *inv)
static
PyObject
*
cc_get
(
ccobject
*
self
,
PyObject
*
args
)
{
PyObject
*
r
,
*
key
,
*
d
=
NULL
;
PyObject
*
r
,
*
key
,
*
d
=
NULL
;
if
(
!
PyArg_ParseTuple
(
args
,
"O|O:get"
,
&
key
,
&
d
))
return
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
r
=
Py_None
;
r
=
PyDict_GetItem
(
self
->
data
,
key
);
if
(
!
r
)
{
if
(
d
)
r
=
d
;
else
r
=
Py_None
;
}
Py_INCREF
(
r
);
return
r
;
Py_INCREF
(
r
);
return
r
;
}
static
PyObject
*
cc_items
(
ccobject
*
self
)
{
return
PyObject_CallMethod
(
self
->
data
,
"items"
,
""
);
return
PyObject_CallMethod
(
self
->
data
,
"items"
,
""
);
}
static
PyObject
*
cc_klass_items
(
ccobject
*
self
)
{
PyObject
*
l
,
*
k
,
*
v
;
Py_ssize_t
p
=
0
;
l
=
PyList_New
(
0
);
if
(
l
==
NULL
)
return
NULL
;
while
(
PyDict_Next
(
self
->
data
,
&
p
,
&
k
,
&
v
))
{
if
(
PyType_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
);
PyObject
*
l
,
*
k
,
*
v
;
Py_ssize_t
p
=
0
;
l
=
PyList_New
(
0
);
if
(
l
==
NULL
)
return
NULL
;
while
(
PyDict_Next
(
self
->
data
,
&
p
,
&
k
,
&
v
))
{
if
(
PyType_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
;
return
l
;
}
static
PyObject
*
cc_debug_info
(
ccobject
*
self
)
{
PyObject
*
l
,
*
k
,
*
v
;
Py_ssize_t
p
=
0
;
PyObject
*
l
,
*
k
,
*
v
;
Py_ssize_t
p
=
0
;
l
=
PyList_New
(
0
);
if
(
l
==
NULL
)
return
NULL
;
while
(
PyDict_Next
(
self
->
data
,
&
p
,
&
k
,
&
v
))
{
if
(
v
->
ob_refcnt
<=
0
)
v
=
Py_BuildValue
(
"Oi"
,
k
,
v
->
ob_refcnt
);
else
if
(
!
PyType_Check
(
v
)
&&
(
v
->
ob_type
->
tp_basicsize
>=
sizeof
(
cPersistentObject
))
)
v
=
Py_BuildValue
(
"Oisi"
,
k
,
v
->
ob_refcnt
,
v
->
ob_type
->
tp_name
,
((
cPersistentObject
*
)
v
)
->
state
);
else
v
=
Py_BuildValue
(
"Ois"
,
k
,
v
->
ob_refcnt
,
v
->
ob_type
->
tp_name
);
if
(
v
==
NULL
)
goto
err
;
l
=
PyList_New
(
0
);
if
(
l
==
NULL
)
return
NULL
;
if
(
PyList_Append
(
l
,
v
)
<
0
)
goto
err
;
}
while
(
PyDict_Next
(
self
->
data
,
&
p
,
&
k
,
&
v
))
{
if
(
v
->
ob_refcnt
<=
0
)
v
=
Py_BuildValue
(
"Oi"
,
k
,
v
->
ob_refcnt
);
else
if
(
!
PyType_Check
(
v
)
&&
(
v
->
ob_type
->
tp_basicsize
>=
sizeof
(
cPersistentObject
))
)
v
=
Py_BuildValue
(
"Oisi"
,
k
,
v
->
ob_refcnt
,
v
->
ob_type
->
tp_name
,
((
cPersistentObject
*
)
v
)
->
state
);
else
v
=
Py_BuildValue
(
"Ois"
,
k
,
v
->
ob_refcnt
,
v
->
ob_type
->
tp_name
);
if
(
v
==
NULL
)
goto
err
;
if
(
PyList_Append
(
l
,
v
)
<
0
)
goto
err
;
}
return
l
;
return
l
;
err:
Py_DECREF
(
l
);
return
NULL
;
Py_DECREF
(
l
);
return
NULL
;
}
static
PyObject
*
cc_lru_items
(
ccobject
*
self
)
{
PyObject
*
l
;
CPersistentRing
*
here
;
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
;
PyObject
*
l
;
CPersistentRing
*
here
;
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
;
l
=
PyList_New
(
0
);
if
(
l
==
NULL
)
return
NULL
;
here
=
self
->
ring_home
.
r_next
;
while
(
here
!=
&
self
->
ring_home
)
{
PyObject
*
v
;
cPersistentObject
*
object
=
OBJECT_FROM_RING
(
self
,
here
);
here
=
self
->
ring_home
.
r_next
;
while
(
here
!=
&
self
->
ring_home
)
{
PyObject
*
v
;
cPersistentObject
*
object
=
OBJECT_FROM_RING
(
self
,
here
);
if
(
object
==
NULL
)
{
Py_DECREF
(
l
);
return
NULL
;
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
->
r_next
;
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
->
r_next
;
}
return
l
;
return
l
;
}
static
void
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
;
/* If the cache has been cleared by GC, data will be NULL. */
if
(
!
self
->
data
)
return
;
v
=
PyDict_GetItem
(
self
->
data
,
oid
);
assert
(
v
);
assert
(
v
->
ob_refcnt
==
0
);
/* Need to be very hairy here because a dictionary is about
to decref an already deleted object.
*/
/* 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
;
/* If the cache has been cleared by GC, data will be NULL. */
if
(
!
self
->
data
)
return
;
v
=
PyDict_GetItem
(
self
->
data
,
oid
);
assert
(
v
);
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
);
/* 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
);
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
);
/* TODO: Should we call _Py_ForgetReference() on error exit? */
if
(
PyDict_DelItem
(
self
->
data
,
oid
)
<
0
)
return
;
Py_DECREF
((
ccobject
*
)((
cPersistentObject
*
)
v
)
->
cache
);
((
cPersistentObject
*
)
v
)
->
cache
=
NULL
;
assert
(
v
->
ob_refcnt
==
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
);
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
);
/* TODO: Should we call _Py_ForgetReference() on error exit? */
if
(
PyDict_DelItem
(
self
->
data
,
oid
)
<
0
)
return
;
Py_DECREF
((
ccobject
*
)((
cPersistentObject
*
)
v
)
->
cache
);
((
cPersistentObject
*
)
v
)
->
cache
=
NULL
;
assert
(
v
->
ob_refcnt
==
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
);
}
static
PyObject
*
cc_ringlen
(
ccobject
*
self
)
{
CPersistentRing
*
here
;
int
c
=
0
;
CPersistentRing
*
here
;
int
c
=
0
;
for
(
here
=
self
->
ring_home
.
r_next
;
here
!=
&
self
->
ring_home
;
here
=
here
->
r_next
)
c
++
;
return
PyInt_FromLong
(
c
);
for
(
here
=
self
->
ring_home
.
r_next
;
here
!=
&
self
->
ring_home
;
here
=
here
->
r_next
)
c
++
;
return
PyInt_FromLong
(
c
);
}
static
PyObject
*
...
...
@@ -645,428 +672,464 @@ cc_update_object_size_estimation(ccobject *self, PyObject *args)
return
NULL
;
/* Note: reference borrowed */
v
=
(
cPersistentObject
*
)
PyDict_GetItem
(
self
->
data
,
oid
);
if
(
v
)
{
/* we know this object -- update our "total_size_estimation"
we must only update when the object is in the ring
*/
if
(
v
->
ring
.
r_next
)
{
self
->
total_estimated_size
+=
_estimated_size_in_bytes
(
_estimated_size_in_24_bits
(
new_size
)
-
v
->
estimated_size
);
/* we do this in "Connection" as we need it even when the
object is not in the cache (or not the ring)
if
(
v
)
{
/* we know this object -- update our "total_size_estimation"
we must only update when the object is in the ring
*/
/* v->estimated_size = new_size; */
if
(
v
->
ring
.
r_next
)
{
self
->
total_estimated_size
+=
_estimated_size_in_bytes
(
_estimated_size_in_24_bits
(
new_size
)
-
v
->
estimated_size
);
/* we do this in "Connection" as we need it even when the
object is not in the cache (or not the ring)
*/
/* v->estimated_size = new_size; */
}
}
}
Py_RETURN_NONE
;
}
static
struct
PyMethodDef
cc_methods
[]
=
{
{
"items"
,
(
PyCFunction
)
cc_items
,
METH_NOARGS
,
"Return list of oid, object pairs for all items in cache."
},
{
"lru_items"
,
(
PyCFunction
)
cc_lru_items
,
METH_NOARGS
,
"List (oid, object) pairs from the lru list, as 2-tuples."
},
{
"klass_items"
,
(
PyCFunction
)
cc_klass_items
,
METH_NOARGS
,
"List (oid, object) pairs of cached persistent classes."
},
{
"full_sweep"
,
(
PyCFunction
)
cc_full_sweep
,
METH_VARARGS
,
"full_sweep() -- Perform a full sweep of the cache."
},
{
"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() -- Perform incremental garbage collection
\n\n
"
"This method had been depricated!"
"Some other implementations support an optional parameter 'n' which
\n
"
"indicates a repetition count; this value is ignored."
},
{
"invalidate"
,
(
PyCFunction
)
cc_invalidate
,
METH_O
,
"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_NOARGS
,
"ringlen() -- Returns number of non-ghost items in cache."
},
{
"debug_info"
,
(
PyCFunction
)
cc_debug_info
,
METH_NOARGS
,
"debug_info() -- Returns debugging data about objects in the cache."
},
{
"update_object_size_estimation"
,
(
PyCFunction
)
cc_update_object_size_estimation
,
METH_VARARGS
,
"update_object_size_estimation(oid, new_size) -- update the caches size estimation for *oid* (if this is known to the cache)."
},
{
NULL
,
NULL
}
/* sentinel */
{
"items"
,
(
PyCFunction
)
cc_items
,
METH_NOARGS
,
"Return list of oid, object pairs for all items in cache."
},
{
"lru_items"
,
(
PyCFunction
)
cc_lru_items
,
METH_NOARGS
,
"List (oid, object) pairs from the lru list, as 2-tuples."
},
{
"klass_items"
,
(
PyCFunction
)
cc_klass_items
,
METH_NOARGS
,
"List (oid, object) pairs of cached persistent classes."
},
{
"full_sweep"
,
(
PyCFunction
)
cc_full_sweep
,
METH_VARARGS
,
"full_sweep() -- Perform a full sweep of the cache."
},
{
"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() -- Perform incremental garbage collection
\n\n
"
"This method had been depricated!"
"Some other implementations support an optional parameter 'n' which
\n
"
"indicates a repetition count; this value is ignored."
},
{
"invalidate"
,
(
PyCFunction
)
cc_invalidate
,
METH_O
,
"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_NOARGS
,
"ringlen() -- Returns number of non-ghost items in cache."
},
{
"debug_info"
,
(
PyCFunction
)
cc_debug_info
,
METH_NOARGS
,
"debug_info() -- Returns debugging data about objects in the cache."
},
{
"update_object_size_estimation"
,
(
PyCFunction
)
cc_update_object_size_estimation
,
METH_VARARGS
,
"update_object_size_estimation(oid, new_size) -- "
"update the caches size estimation for *oid* "
"(if this is known to the cache)."
},
{
NULL
,
NULL
}
/* sentinel */
};
static
int
cc_init
(
ccobject
*
self
,
PyObject
*
args
,
PyObject
*
kwds
)
{
int
cache_size
=
100
;
PY_LONG_LONG
cache_size_bytes
=
0
;
PyObject
*
jar
;
if
(
!
PyArg_ParseTuple
(
args
,
"O|iL"
,
&
jar
,
&
cache_size
,
&
cache_size_bytes
))
return
-
1
;
self
->
jar
=
NULL
;
self
->
data
=
PyDict_New
();
if
(
self
->
data
==
NULL
)
{
Py_DECREF
(
self
);
return
-
1
;
int
cache_size
=
100
;
PY_LONG_LONG
cache_size_bytes
=
0
;
PyObject
*
jar
;
if
(
!
PyArg_ParseTuple
(
args
,
"O|iL"
,
&
jar
,
&
cache_size
,
&
cache_size_bytes
))
return
-
1
;
self
->
jar
=
NULL
;
self
->
data
=
PyDict_New
();
if
(
self
->
data
==
NULL
)
{
Py_DECREF
(
self
);
return
-
1
;
}
/* Untrack the dict mapping oids to objects.
The dict contains uncounted references to ghost objects, so it
isn't safe for GC to visit it. If GC finds an object with more
referents that refcounts, it will die with an assertion failure.
When the cache participates in GC, it will need to traverse the
objects in the doubly-linked list, which will account for all the
non-ghost objects.
*/
PyObject_GC_UnTrack
((
void
*
)
self
->
data
);
self
->
jar
=
jar
;
Py_INCREF
(
jar
);
self
->
cache_size
=
cache_size
;
self
->
cache_size_bytes
=
cache_size_bytes
;
self
->
non_ghost_count
=
0
;
self
->
total_estimated_size
=
0
;
self
->
klass_count
=
0
;
self
->
cache_drain_resistance
=
0
;
self
->
ring_lock
=
0
;
self
->
ring_home
.
r_next
=
&
self
->
ring_home
;
self
->
ring_home
.
r_prev
=
&
self
->
ring_home
;
return
0
;
/* Untrack the dict mapping oids to objects.
The dict contains uncounted references to ghost objects, so it
isn't safe for GC to visit it. If GC finds an object with more
referents that refcounts, it will die with an assertion failure.
When the cache participates in GC, it will need to traverse the
objects in the doubly-linked list, which will account for all the
non-ghost objects.
*/
PyObject_GC_UnTrack
((
void
*
)
self
->
data
);
self
->
jar
=
jar
;
Py_INCREF
(
jar
);
self
->
cache_size
=
cache_size
;
self
->
cache_size_bytes
=
cache_size_bytes
;
self
->
non_ghost_count
=
0
;
self
->
total_estimated_size
=
0
;
self
->
klass_count
=
0
;
self
->
cache_drain_resistance
=
0
;
self
->
ring_lock
=
0
;
self
->
ring_home
.
r_next
=
&
self
->
ring_home
;
self
->
ring_home
.
r_prev
=
&
self
->
ring_home
;
return
0
;
}
static
void
cc_dealloc
(
ccobject
*
self
)
{
Py_XDECREF
(
self
->
data
);
Py_XDECREF
(
self
->
jar
);
PyObject_GC_Del
(
self
);
Py_XDECREF
(
self
->
data
);
Py_XDECREF
(
self
->
jar
);
PyObject_GC_Del
(
self
);
}
static
int
cc_clear
(
ccobject
*
self
)
{
Py_ssize_t
pos
=
0
;
PyObject
*
k
,
*
v
;
/* Clearing the cache is delicate.
A non-ghost object will show up in the ring and in the dict. If
we deallocating the dict before clearing the ring, the GC will
decref each object in the dict. Since the dict references are
uncounted, this will lead to objects having negative refcounts.
Freeing the non-ghost objects should eliminate many objects from
the cache, but there may still be ghost objects left. It's
not safe to decref the dict until it's empty, so we need to manually
clear those out of the dict, too. We accomplish that by replacing
all the ghost objects with None.
*/
/* We don't need to lock the ring, because the cache is unreachable.
It should be impossible for anyone to be modifying the cache.
*/
assert
(
!
self
->
ring_lock
);
while
(
self
->
ring_home
.
r_next
!=
&
self
->
ring_home
)
{
CPersistentRing
*
here
=
self
->
ring_home
.
r_next
;
cPersistentObject
*
o
=
OBJECT_FROM_RING
(
self
,
here
);
if
(
o
->
cache
)
{
Py_INCREF
(
o
);
/* account for uncounted reference */
if
(
PyDict_DelItem
(
self
->
data
,
o
->
oid
)
<
0
)
return
-
1
;
}
o
->
cache
=
NULL
;
Py_DECREF
(
self
);
self
->
ring_home
.
r_next
=
here
->
r_next
;
o
->
ring
.
r_prev
=
NULL
;
o
->
ring
.
r_next
=
NULL
;
Py_DECREF
(
o
);
here
=
here
->
r_next
;
Py_ssize_t
pos
=
0
;
PyObject
*
k
,
*
v
;
/* Clearing the cache is delicate.
A non-ghost object will show up in the ring and in the dict. If
we deallocating the dict before clearing the ring, the GC will
decref each object in the dict. Since the dict references are
uncounted, this will lead to objects having negative refcounts.
Freeing the non-ghost objects should eliminate many objects from
the cache, but there may still be ghost objects left. It's
not safe to decref the dict until it's empty, so we need to manually
clear those out of the dict, too. We accomplish that by replacing
all the ghost objects with None.
*/
/* We don't need to lock the ring, because the cache is unreachable.
It should be impossible for anyone to be modifying the cache.
*/
assert
(
!
self
->
ring_lock
);
while
(
self
->
ring_home
.
r_next
!=
&
self
->
ring_home
)
{
CPersistentRing
*
here
=
self
->
ring_home
.
r_next
;
cPersistentObject
*
o
=
OBJECT_FROM_RING
(
self
,
here
);
if
(
o
->
cache
)
{
Py_INCREF
(
o
);
/* account for uncounted reference */
if
(
PyDict_DelItem
(
self
->
data
,
o
->
oid
)
<
0
)
return
-
1
;
}
o
->
cache
=
NULL
;
Py_DECREF
(
self
);
self
->
ring_home
.
r_next
=
here
->
r_next
;
o
->
ring
.
r_prev
=
NULL
;
o
->
ring
.
r_next
=
NULL
;
Py_DECREF
(
o
);
here
=
here
->
r_next
;
}
Py_XDECREF
(
self
->
jar
);
Py_XDECREF
(
self
->
jar
);
while
(
PyDict_Next
(
self
->
data
,
&
pos
,
&
k
,
&
v
))
{
Py_INCREF
(
v
);
if
(
PyDict_SetItem
(
self
->
data
,
k
,
Py_None
)
<
0
)
return
-
1
;
while
(
PyDict_Next
(
self
->
data
,
&
pos
,
&
k
,
&
v
))
{
Py_INCREF
(
v
);
if
(
PyDict_SetItem
(
self
->
data
,
k
,
Py_None
)
<
0
)
return
-
1
;
}
Py_XDECREF
(
self
->
data
);
self
->
data
=
NULL
;
self
->
jar
=
NULL
;
return
0
;
Py_XDECREF
(
self
->
data
);
self
->
data
=
NULL
;
self
->
jar
=
NULL
;
return
0
;
}
static
int
cc_traverse
(
ccobject
*
self
,
visitproc
visit
,
void
*
arg
)
{
int
err
;
CPersistentRing
*
here
;
/* If we're in the midst of cleaning up old objects, the ring contains
* assorted junk we must not pass on to the visit() callback. This
* should be rare (our cleanup code would need to have called back
* into Python, which in turn triggered Python's gc). When it happens,
* simply don't chase any pointers. The cache will appear to be a
* source of external references then, and at worst we miss cleaning
* up a dead cycle until the next time Python's gc runs.
*/
if
(
self
->
ring_lock
)
return
0
;
#define VISIT(SLOT) \
if (SLOT) { \
err = visit((PyObject *)(SLOT), arg); \
if (err) \
return err; \
}
int
err
;
CPersistentRing
*
here
;
/* If we're in the midst of cleaning up old objects, the ring contains
* assorted junk we must not pass on to the visit() callback. This
* should be rare (our cleanup code would need to have called back
* into Python, which in turn triggered Python's gc). When it happens,
* simply don't chase any pointers. The cache will appear to be a
* source of external references then, and at worst we miss cleaning
* up a dead cycle until the next time Python's gc runs.
*/
if
(
self
->
ring_lock
)
return
0
;
VISIT
(
self
->
jar
);
#define VISIT(SLOT) \
if (SLOT) { \
err = visit((PyObject *)(SLOT), arg); \
if (err) \
return err; \
}
here
=
self
->
ring_home
.
r_next
;
VISIT
(
self
->
jar
)
;
/* It is possible that an object is traversed after it is cleared.
In that case, there is no ring.
*/
if
(
!
here
)
return
0
;
here
=
self
->
ring_home
.
r_next
;
while
(
here
!=
&
self
->
ring_home
)
{
cPersistentObject
*
o
=
OBJECT_FROM_RING
(
self
,
here
);
VISIT
(
o
);
here
=
here
->
r_next
;
/* It is possible that an object is traversed after it is cleared.
In that case, there is no ring.
*/
if
(
!
here
)
return
0
;
while
(
here
!=
&
self
->
ring_home
)
{
cPersistentObject
*
o
=
OBJECT_FROM_RING
(
self
,
here
);
VISIT
(
o
);
here
=
here
->
r_next
;
}
#undef VISIT
return
0
;
return
0
;
}
static
in
t
static
Py_ssize_
t
cc_length
(
ccobject
*
self
)
{
return
PyObject_Length
(
self
->
data
);
return
PyObject_Length
(
self
->
data
);
}
static
PyObject
*
cc_subscript
(
ccobject
*
self
,
PyObject
*
key
)
{
PyObject
*
r
;
PyObject
*
r
;
r
=
PyDict_GetItem
(
self
->
data
,
key
);
if
(
r
==
NULL
)
{
PyErr_SetObject
(
PyExc_KeyError
,
key
);
return
NULL
;
r
=
PyDict_GetItem
(
self
->
data
,
key
);
if
(
r
==
NULL
)
{
PyErr_SetObject
(
PyExc_KeyError
,
key
);
return
NULL
;
}
Py_INCREF
(
r
);
Py_INCREF
(
r
);
return
r
;
return
r
;
}
static
int
cc_add_item
(
ccobject
*
self
,
PyObject
*
key
,
PyObject
*
v
)
{
int
result
;
PyObject
*
oid
,
*
object_again
,
*
jar
;
cPersistentObject
*
p
;
int
result
;
PyObject
*
oid
,
*
object_again
,
*
jar
;
cPersistentObject
*
p
;
/* Sanity check the value given to make sure it is allowed in the cache */
if
(
PyType_Check
(
v
))
{
/* Its a persistent class, such as a ZClass. Thats ok. */
/* Sanity check the value given to make sure it is allowed in the cache */
if
(
PyType_Check
(
v
))
{
/* Its a persistent class, such as a ZClass. Thats ok. */
}
else
if
(
v
->
ob_type
->
tp_basicsize
<
sizeof
(
cPersistentObject
))
{
/* If it's not an instance of a persistent class, (ie Python
classes that derive from persistent.Persistent, BTrees,
etc), report an error.
TODO: checking sizeof() seems a poor test.
*/
PyErr_SetString
(
PyExc_TypeError
,
"Cache values must be persistent objects."
);
return
-
1
;
else
if
(
v
->
ob_type
->
tp_basicsize
<
sizeof
(
cPersistentObject
))
{
/* If it's not an instance of a persistent class, (ie Python
classes that derive from persistent.Persistent, BTrees,
etc), report an error.
TODO: checking sizeof() seems a poor test.
*/
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
;
/* 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
;
/* 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
;
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
;
/* 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
,
"A different object already has the same oid"
);
return
-
1
;
}
else
{
/* re-register under the same oid - no work needed */
return
0
;
}
Py_DECREF
(
jar
);
object_again
=
PyDict_GetItem
(
self
->
data
,
key
);
if
(
object_again
)
{
if
(
object_again
!=
v
)
{
PyErr_SetString
(
PyExc_ValueError
,
"A different object already has the same oid"
);
return
-
1
;
}
else
{
/* re-register under the same oid - no work needed */
return
0
;
}
}
if
(
PyType_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
(
PyType_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
++
;
ring_add
(
&
self
->
ring_home
,
&
p
->
ring
);
/* this list should have a new reference to the object */
Py_INCREF
(
v
);
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
++
;
ring_add
(
&
self
->
ring_home
,
&
p
->
ring
);
/* this list should have a new reference to the object */
Py_INCREF
(
v
);
}
return
0
;
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
)
{
PyErr_SetObject
(
PyExc_KeyError
,
key
);
return
-
1
;
PyObject
*
v
;
cPersistentObject
*
p
;
/* unlink this item from the ring */
v
=
PyDict_GetItem
(
self
->
data
,
key
);
if
(
v
==
NULL
)
{
PyErr_SetObject
(
PyExc_KeyError
,
key
);
return
-
1
;
}
if
(
PyType_Check
(
v
))
{
self
->
klass_count
--
;
}
else
{
p
=
(
cPersistentObject
*
)
v
;
if
(
p
->
state
>=
0
)
{
self
->
non_ghost_count
--
;
ring_del
(
&
p
->
ring
);
/* The DelItem below will account for the reference
held by the list. */
}
else
{
/* This is a ghost object, so we haven't 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
(
PyType_Check
(
v
))
{
self
->
klass_count
--
;
}
else
{
p
=
(
cPersistentObject
*
)
v
;
if
(
p
->
state
>=
0
)
{
self
->
non_ghost_count
--
;
ring_del
(
&
p
->
ring
);
/* The DelItem below will account for the reference
held by the list. */
}
else
{
/* This is a ghost object, so we haven't 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
);
}
if
(
PyDict_DelItem
(
self
->
data
,
key
)
<
0
)
{
PyErr_SetString
(
PyExc_RuntimeError
,
"unexpectedly couldn't remove key in cc_ass_sub"
);
return
-
1
;
Py_DECREF
((
PyObject
*
)
p
->
cache
);
p
->
cache
=
NULL
;
}
return
0
;
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
(
!
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
);
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
PyMappingMethods
cc_as_mapping
=
{
(
lenfunc
)
cc_length
,
/*mp_length*/
(
binaryfunc
)
cc_subscript
,
/*mp_subscript*/
(
objobjargproc
)
cc_ass_sub
,
/*mp_ass_subscript*/
};
static
PyObject
*
cc_cache_data
(
ccobject
*
self
,
void
*
context
)
{
return
PyDict_Copy
(
self
->
data
);
return
PyDict_Copy
(
self
->
data
);
}
static
PyGetSetDef
cc_getsets
[]
=
{
static
PyGetSetDef
cc_getsets
[]
=
{
{
"cache_data"
,
(
getter
)
cc_cache_data
},
{
NULL
}
};
};
static
PyMemberDef
cc_members
[]
=
{
{
"cache_size"
,
T_INT
,
offsetof
(
ccobject
,
cache_size
)},
{
"cache_size_bytes"
,
T_LONG
,
offsetof
(
ccobject
,
cache_size_bytes
)},
{
"total_estimated_size"
,
T_LONG
,
offsetof
(
ccobject
,
total_estimated_size
),
RO
},
{
"cache_drain_resistance"
,
T_INT
,
offsetof
(
ccobject
,
cache_drain_resistance
)},
{
"cache_non_ghost_count"
,
T_INT
,
offsetof
(
ccobject
,
non_ghost_count
),
RO
},
{
"cache_klass_count"
,
T_INT
,
offsetof
(
ccobject
,
klass_count
),
RO
},
{
NULL
}
{
"cache_size"
,
T_INT
,
offsetof
(
ccobject
,
cache_size
)},
{
"cache_size_bytes"
,
T_LONG
,
offsetof
(
ccobject
,
cache_size_bytes
)},
{
"total_estimated_size"
,
T_LONG
,
offsetof
(
ccobject
,
total_estimated_size
),
RO
},
{
"cache_drain_resistance"
,
T_INT
,
offsetof
(
ccobject
,
cache_drain_resistance
)},
{
"cache_non_ghost_count"
,
T_INT
,
offsetof
(
ccobject
,
non_ghost_count
),
RO
},
{
"cache_klass_count"
,
T_INT
,
offsetof
(
ccobject
,
klass_count
),
RO
},
{
NULL
}
};
/* This module is compiled as a shared library. Some compilers don't
...
...
@@ -1079,82 +1142,83 @@ static PyMemberDef cc_members[] = {
#define DEFERRED_ADDRESS(ADDR) 0
static
PyTypeObject
Cctype
=
{
PyObject_HEAD_INIT
(
DEFERRED_ADDRESS
(
&
PyType_Type
))
0
,
/* ob_size */
"persistent.PickleCache"
,
/* tp_name */
sizeof
(
ccobject
),
/* tp_basicsize */
0
,
/* tp_itemsize */
(
destructor
)
cc_dealloc
,
/* tp_dealloc */
0
,
/* tp_print */
0
,
/* tp_getattr */
0
,
/* tp_setattr */
0
,
/* tp_compare */
0
,
/* tp_repr */
0
,
/* tp_as_number */
0
,
/* tp_as_sequence */
&
cc_as_mapping
,
/* tp_as_mapping */
0
,
/* tp_hash */
0
,
/* tp_call */
0
,
/* tp_str */
0
,
/* tp_getattro */
0
,
/* tp_setattro */
0
,
/* tp_as_buffer */
Py_TPFLAGS_DEFAULT
|
Py_TPFLAGS_BASETYPE
|
Py_TPFLAGS_HAVE_GC
,
/* tp_flags */
0
,
/* tp_doc */
(
traverseproc
)
cc_traverse
,
/* tp_traverse */
(
inquiry
)
cc_clear
,
/* tp_clear */
0
,
/* tp_richcompare */
0
,
/* tp_weaklistoffset */
0
,
/* tp_iter */
0
,
/* tp_iternext */
cc_methods
,
/* tp_methods */
cc_members
,
/* tp_members */
cc_getsets
,
/* tp_getset */
0
,
/* tp_base */
0
,
/* tp_dict */
0
,
/* tp_descr_get */
0
,
/* tp_descr_set */
0
,
/* tp_dictoffset */
(
initproc
)
cc_init
,
/* tp_init */
PyObject_HEAD_INIT
(
DEFERRED_ADDRESS
(
&
PyType_Type
))
0
,
/* ob_size */
"persistent.PickleCache"
,
/* tp_name */
sizeof
(
ccobject
),
/* tp_basicsize */
0
,
/* tp_itemsize */
(
destructor
)
cc_dealloc
,
/* tp_dealloc */
0
,
/* tp_print */
0
,
/* tp_getattr */
0
,
/* tp_setattr */
0
,
/* tp_compare */
0
,
/* tp_repr */
0
,
/* tp_as_number */
0
,
/* tp_as_sequence */
&
cc_as_mapping
,
/* tp_as_mapping */
0
,
/* tp_hash */
0
,
/* tp_call */
0
,
/* tp_str */
0
,
/* tp_getattro */
0
,
/* tp_setattro */
0
,
/* tp_as_buffer */
Py_TPFLAGS_DEFAULT
|
Py_TPFLAGS_BASETYPE
|
Py_TPFLAGS_HAVE_GC
,
/* tp_flags */
0
,
/* tp_doc */
(
traverseproc
)
cc_traverse
,
/* tp_traverse */
(
inquiry
)
cc_clear
,
/* tp_clear */
0
,
/* tp_richcompare */
0
,
/* tp_weaklistoffset */
0
,
/* tp_iter */
0
,
/* tp_iternext */
cc_methods
,
/* tp_methods */
cc_members
,
/* tp_members */
cc_getsets
,
/* tp_getset */
0
,
/* tp_base */
0
,
/* tp_dict */
0
,
/* tp_descr_get */
0
,
/* tp_descr_set */
0
,
/* tp_dictoffset */
(
initproc
)
cc_init
,
/* tp_init */
};
void
initcPickleCache
(
void
)
{
PyObject
*
m
;
PyObject
*
m
;
Cctype
.
ob_type
=
&
PyType_Type
;
Cctype
.
tp_new
=
&
PyType_GenericNew
;
if
(
PyType_Ready
(
&
Cctype
)
<
0
)
{
return
;
Cctype
.
ob_type
=
&
PyType_Type
;
Cctype
.
tp_new
=
&
PyType_GenericNew
;
if
(
PyType_Ready
(
&
Cctype
)
<
0
)
{
return
;
}
m
=
Py_InitModule3
(
"cPickleCache"
,
NULL
,
cPickleCache_doc_string
);
capi
=
(
cPersistenceCAPIstruct
*
)
PyCObject_Import
(
"persistent.cPersistence"
,
"CAPI"
);
if
(
!
capi
)
return
;
capi
->
percachedel
=
(
percachedelfunc
)
cc_oid_unreferenced
;
py__p_changed
=
PyString_InternFromString
(
"_p_changed"
);
if
(
!
py__p_changed
)
return
;
py__p_deactivate
=
PyString_InternFromString
(
"_p_deactivate"
);
if
(
!
py__p_deactivate
)
return
;
py__p_jar
=
PyString_InternFromString
(
"_p_jar"
);
if
(
!
py__p_jar
)
return
;
py__p_oid
=
PyString_InternFromString
(
"_p_oid"
);
if
(
!
py__p_oid
)
return
;
if
(
PyModule_AddStringConstant
(
m
,
"cache_variant"
,
"stiff/c"
)
<
0
)
return
;
/* This leaks a reference to Cctype, but it doesn't matter. */
if
(
PyModule_AddObject
(
m
,
"PickleCache"
,
(
PyObject
*
)
&
Cctype
)
<
0
)
return
;
m
=
Py_InitModule3
(
"cPickleCache"
,
NULL
,
cPickleCache_doc_string
);
capi
=
(
cPersistenceCAPIstruct
*
)
PyCObject_Import
(
"persistent.cPersistence"
,
"CAPI"
);
if
(
!
capi
)
return
;
capi
->
percachedel
=
(
percachedelfunc
)
cc_oid_unreferenced
;
py__p_changed
=
PyString_InternFromString
(
"_p_changed"
);
if
(
!
py__p_changed
)
return
;
py__p_deactivate
=
PyString_InternFromString
(
"_p_deactivate"
);
if
(
!
py__p_deactivate
)
return
;
py__p_jar
=
PyString_InternFromString
(
"_p_jar"
);
if
(
!
py__p_jar
)
return
;
py__p_oid
=
PyString_InternFromString
(
"_p_oid"
);
if
(
!
py__p_oid
)
return
;
if
(
PyModule_AddStringConstant
(
m
,
"cache_variant"
,
"stiff/c"
)
<
0
)
return
;
/* This leaks a reference to Cctype, but it doesn't matter. */
if
(
PyModule_AddObject
(
m
,
"PickleCache"
,
(
PyObject
*
)
&
Cctype
)
<
0
)
return
;
}
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