Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Z
ZEO
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
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
nexedi
ZEO
Commits
100a2556
Commit
100a2556
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
4f579850
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 @
100a2556
...
@@ -10,17 +10,17 @@
...
@@ -10,17 +10,17 @@
WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
FOR A PARTICULAR PURPOSE
FOR A PARTICULAR PURPOSE
****************************************************************************/
****************************************************************************/
static
char
cPersistence_doc_string
[]
=
static
char
cPersistence_doc_string
[]
=
"Defines Persistent mixin class for persistent objects.
\n
"
"Defines Persistent mixin class for persistent objects.
\n
"
"
\n
"
"
\n
"
"$Id$
\n
"
;
"$Id$
\n
"
;
#include "cPersistence.h"
#include "cPersistence.h"
#include "structmember.h"
#include "structmember.h"
struct
ccobject_head_struct
{
struct
ccobject_head_struct
{
CACHE_HEAD
CACHE_HEAD
};
};
/* These two objects are initialized when the module is loaded */
/* These two objects are initialized when the module is loaded */
...
@@ -37,23 +37,23 @@ static PyObject *py___getnewargs__, *py___getstate__;
...
@@ -37,23 +37,23 @@ static PyObject *py___getnewargs__, *py___getstate__;
static
int
static
int
init_strings
(
void
)
init_strings
(
void
)
{
{
#define INIT_STRING(S) \
#define INIT_STRING(S)
\
if (!(py_ ## S = PyString_InternFromString(#S)))
\
if (!(py_ ## S = PyString_InternFromString(#S)))
\
return -1;
return -1;
INIT_STRING
(
keys
);
INIT_STRING
(
keys
);
INIT_STRING
(
setstate
);
INIT_STRING
(
setstate
);
INIT_STRING
(
timeTime
);
INIT_STRING
(
timeTime
);
INIT_STRING
(
__dict__
);
INIT_STRING
(
__dict__
);
INIT_STRING
(
_p_changed
);
INIT_STRING
(
_p_changed
);
INIT_STRING
(
_p_deactivate
);
INIT_STRING
(
_p_deactivate
);
INIT_STRING
(
__getattr__
);
INIT_STRING
(
__getattr__
);
INIT_STRING
(
__setattr__
);
INIT_STRING
(
__setattr__
);
INIT_STRING
(
__delattr__
);
INIT_STRING
(
__delattr__
);
INIT_STRING
(
__slotnames__
);
INIT_STRING
(
__slotnames__
);
INIT_STRING
(
__getnewargs__
);
INIT_STRING
(
__getnewargs__
);
INIT_STRING
(
__getstate__
);
INIT_STRING
(
__getstate__
);
#undef INIT_STRING
#undef INIT_STRING
return
0
;
return
0
;
}
}
#ifdef Py_DEBUG
#ifdef Py_DEBUG
...
@@ -63,13 +63,13 @@ fatal_1350(cPersistentObject *self, const char *caller, const char *detail)
...
@@ -63,13 +63,13 @@ fatal_1350(cPersistentObject *self, const char *caller, const char *detail)
char
buf
[
1000
];
char
buf
[
1000
];
PyOS_snprintf
(
buf
,
sizeof
(
buf
),
PyOS_snprintf
(
buf
,
sizeof
(
buf
),
"cPersistence.c %s(): object at %p with type %.200s
\n
"
"cPersistence.c %s(): object at %p with type %.200s
\n
"
"%s.
\n
"
"%s.
\n
"
"The only known cause is multiple threads trying to ghost and
\n
"
"The only known cause is multiple threads trying to ghost and
\n
"
"unghost the object simultaneously.
\n
"
"unghost the object simultaneously.
\n
"
"That's not legal, but ZODB can't stop it.
\n
"
"That's not legal, but ZODB can't stop it.
\n
"
"See Collector #1350.
\n
"
,
"See Collector #1350.
\n
"
,
caller
,
self
,
self
->
ob_type
->
tp_name
,
detail
);
caller
,
self
,
self
->
ob_type
->
tp_name
,
detail
);
Py_FatalError
(
buf
);
Py_FatalError
(
buf
);
}
}
#endif
#endif
...
@@ -82,44 +82,48 @@ static void ghostify(cPersistentObject*);
...
@@ -82,44 +82,48 @@ static void ghostify(cPersistentObject*);
static
int
static
int
unghostify
(
cPersistentObject
*
self
)
unghostify
(
cPersistentObject
*
self
)
{
{
if
(
self
->
state
<
0
&&
self
->
jar
)
{
if
(
self
->
state
<
0
&&
self
->
jar
)
PyObject
*
r
;
{
PyObject
*
r
;
/* Is it ever possible to not have a cache? */
if
(
self
->
cache
)
{
/* Is it ever possible to not have a cache? */
/* Create a node in the ring for this unghostified object. */
if
(
self
->
cache
)
self
->
cache
->
non_ghost_count
++
;
{
self
->
cache
->
total_estimated_size
+=
/* Create a node in the ring for this unghostified object. */
_estimated_size_in_bytes
(
self
->
estimated_size
);
self
->
cache
->
non_ghost_count
++
;
ring_add
(
&
self
->
cache
->
ring_home
,
&
self
->
ring
);
self
->
cache
->
total_estimated_size
+=
Py_INCREF
(
self
);
_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
/* set state to CHANGED while setstate() call is in progress
to prevent a recursive call to _PyPersist_Load().
to prevent a recursive call to _PyPersist_Load().
*/
*/
self
->
state
=
cPersistent_CHANGED_STATE
;
self
->
state
=
cPersistent_CHANGED_STATE
;
/* Call the object's __setstate__() */
/* Call the object's __setstate__() */
r
=
PyObject_CallMethod
(
self
->
jar
,
"setstate"
,
"O"
,
(
PyObject
*
)
self
);
r
=
PyObject_CallMethod
(
self
->
jar
,
"setstate"
,
"O"
,
(
PyObject
*
)
self
);
if
(
r
==
NULL
)
{
if
(
r
==
NULL
)
ghostify
(
self
);
{
return
-
1
;
ghostify
(
self
);
return
-
1
;
}
}
self
->
state
=
cPersistent_UPTODATE_STATE
;
self
->
state
=
cPersistent_UPTODATE_STATE
;
Py_DECREF
(
r
);
Py_DECREF
(
r
);
if
(
self
->
cache
&&
self
->
ring
.
r_next
==
NULL
)
{
if
(
self
->
cache
&&
self
->
ring
.
r_next
==
NULL
)
{
#ifdef Py_DEBUG
#ifdef Py_DEBUG
fatal_1350
(
self
,
"unghostify"
,
fatal_1350
(
self
,
"unghostify"
,
"is not in the cache despite that we just "
"is not in the cache despite that we just "
"unghostified it"
);
"unghostified it"
);
#else
#else
PyErr_Format
(
PyExc_SystemError
,
"object at %p with type "
PyErr_Format
(
PyExc_SystemError
,
"object at %p with type "
"%.200s not in the cache despite that we just "
"%.200s not in the cache despite that we just "
"unghostified it"
,
self
,
self
->
ob_type
->
tp_name
);
"unghostified it"
,
self
,
self
->
ob_type
->
tp_name
);
return
-
1
;
return
-
1
;
#endif
#endif
}
}
}
}
return
1
;
return
1
;
}
}
/****************************************************************************/
/****************************************************************************/
...
@@ -129,72 +133,58 @@ static PyTypeObject Pertype;
...
@@ -129,72 +133,58 @@ static PyTypeObject Pertype;
static
void
static
void
accessed
(
cPersistentObject
*
self
)
accessed
(
cPersistentObject
*
self
)
{
{
/* Do nothing unless the object is in a cache and not a ghost. */
/* Do nothing unless the object is in a cache and not a ghost. */
if
(
self
->
cache
&&
self
->
state
>=
0
&&
self
->
ring
.
r_next
)
if
(
self
->
cache
&&
self
->
state
>=
0
&&
self
->
ring
.
r_next
)
ring_move_to_head
(
&
self
->
cache
->
ring_home
,
&
self
->
ring
);
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
);
}
}
static
void
static
void
ghostify
(
cPersistentObject
*
self
)
ghostify
(
cPersistentObject
*
self
)
{
{
PyObject
**
dictptr
;
PyObject
**
dictptr
;
/* are we already a ghost? */
/* are we already a ghost? */
if
(
self
->
state
==
cPersistent_GHOST_STATE
)
if
(
self
->
state
==
cPersistent_GHOST_STATE
)
return
;
return
;
/* Is it ever possible to not have a cache? */
/* Is it ever possible to not have a cache? */
if
(
self
->
cache
==
NULL
)
{
if
(
self
->
cache
==
NULL
)
self
->
state
=
cPersistent_GHOST_STATE
;
{
return
;
self
->
state
=
cPersistent_GHOST_STATE
;
return
;
}
}
if
(
self
->
ring
.
r_next
==
NULL
)
{
if
(
self
->
ring
.
r_next
==
NULL
)
/* There's no way to raise an error in this routine. */
{
/* There's no way to raise an error in this routine. */
#ifdef Py_DEBUG
#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
#else
return
;
return
;
#endif
#endif
}
}
/* If we're ghostifying an object, we better have some non-ghosts. */
/* If we're ghostifying an object, we better have some non-ghosts. */
assert
(
self
->
cache
->
non_ghost_count
>
0
);
assert
(
self
->
cache
->
non_ghost_count
>
0
);
self
->
cache
->
non_ghost_count
--
;
self
->
cache
->
non_ghost_count
--
;
self
->
cache
->
total_estimated_size
-=
self
->
cache
->
total_estimated_size
-=
_estimated_size_in_bytes
(
self
->
estimated_size
);
_estimated_size_in_bytes
(
self
->
estimated_size
);
ring_del
(
&
self
->
ring
);
ring_del
(
&
self
->
ring
);
self
->
state
=
cPersistent_GHOST_STATE
;
self
->
state
=
cPersistent_GHOST_STATE
;
dictptr
=
_PyObject_GetDictPtr
((
PyObject
*
)
self
);
dictptr
=
_PyObject_GetDictPtr
((
PyObject
*
)
self
);
if
(
dictptr
&&
*
dictptr
)
{
if
(
dictptr
&&
*
dictptr
)
Py_DECREF
(
*
dictptr
);
{
*
dictptr
=
NULL
;
Py_DECREF
(
*
dictptr
);
*
dictptr
=
NULL
;
}
}
/* We remove the reference to the just ghosted object that the ring
/* We remove the reference to the just ghosted object that the ring
* holds. Note that the dictionary of oids->objects has an uncounted
* holds. Note that the dictionary of oids->objects has an uncounted
* reference, so if the ring's reference was the only one, this frees
* 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
* the ghost object. Note further that the object's dealloc knows to
* inform the dictionary that it is going away.
* inform the dictionary that it is going away.
*/
*/
Py_DECREF
(
self
);
Py_DECREF
(
self
);
}
}
static
int
static
int
...
@@ -202,31 +192,32 @@ changed(cPersistentObject *self)
...
@@ -202,31 +192,32 @@ changed(cPersistentObject *self)
{
{
if
((
self
->
state
==
cPersistent_UPTODATE_STATE
||
if
((
self
->
state
==
cPersistent_UPTODATE_STATE
||
self
->
state
==
cPersistent_STICKY_STATE
)
self
->
state
==
cPersistent_STICKY_STATE
)
&&
self
->
jar
)
&&
self
->
jar
)
{
{
PyObject
*
meth
,
*
arg
,
*
result
;
PyObject
*
meth
,
*
arg
,
*
result
;
static
PyObject
*
s_register
;
static
PyObject
*
s_register
;
if
(
s_register
==
NULL
)
if
(
s_register
==
NULL
)
s_register
=
PyString_InternFromString
(
"register"
);
s_register
=
PyString_InternFromString
(
"register"
);
meth
=
PyObject_GetAttr
((
PyObject
*
)
self
->
jar
,
s_register
);
meth
=
PyObject_GetAttr
((
PyObject
*
)
self
->
jar
,
s_register
);
if
(
meth
==
NULL
)
if
(
meth
==
NULL
)
return
-
1
;
return
-
1
;
arg
=
PyTuple_New
(
1
);
arg
=
PyTuple_New
(
1
);
if
(
arg
==
NULL
)
{
if
(
arg
==
NULL
)
Py_DECREF
(
meth
);
{
return
-
1
;
Py_DECREF
(
meth
);
}
return
-
1
;
Py_INCREF
(
self
);
}
PyTuple_SET_ITEM
(
arg
,
0
,
(
PyObject
*
)
self
);
Py_INCREF
(
self
);
result
=
PyEval_CallObject
(
meth
,
arg
);
PyTuple_SET_ITEM
(
arg
,
0
,
(
PyObject
*
)
self
);
Py_DECREF
(
arg
);
result
=
PyEval_CallObject
(
meth
,
arg
);
Py_DECREF
(
meth
);
Py_DECREF
(
arg
);
if
(
result
==
NULL
)
Py_DECREF
(
meth
);
return
-
1
;
if
(
result
==
NULL
)
Py_DECREF
(
result
);
return
-
1
;
Py_DECREF
(
result
);
self
->
state
=
cPersistent_CHANGED_STATE
;
self
->
state
=
cPersistent_CHANGED_STATE
;
}
}
return
0
;
return
0
;
...
@@ -235,30 +226,32 @@ changed(cPersistentObject *self)
...
@@ -235,30 +226,32 @@ changed(cPersistentObject *self)
static
PyObject
*
static
PyObject
*
Per__p_deactivate
(
cPersistentObject
*
self
)
Per__p_deactivate
(
cPersistentObject
*
self
)
{
{
if
(
self
->
state
==
cPersistent_UPTODATE_STATE
&&
self
->
jar
)
{
if
(
self
->
state
==
cPersistent_UPTODATE_STATE
&&
self
->
jar
)
PyObject
**
dictptr
=
_PyObject_GetDictPtr
((
PyObject
*
)
self
);
{
if
(
dictptr
&&
*
dictptr
)
{
PyObject
**
dictptr
=
_PyObject_GetDictPtr
((
PyObject
*
)
self
);
Py_DECREF
(
*
dictptr
);
if
(
dictptr
&&
*
dictptr
)
*
dictptr
=
NULL
;
{
}
Py_DECREF
(
*
dictptr
);
/* Note that we need to set to ghost state unless we are
*
dictptr
=
NULL
;
called directly. Methods that override this need to
}
do the same! */
/* Note that we need to set to ghost state unless we are
ghostify
(
self
);
called directly. Methods that override this need to
do the same! */
ghostify
(
self
);
}
}
Py_INCREF
(
Py_None
);
Py_INCREF
(
Py_None
);
return
Py_None
;
return
Py_None
;
}
}
static
PyObject
*
static
PyObject
*
Per__p_activate
(
cPersistentObject
*
self
)
Per__p_activate
(
cPersistentObject
*
self
)
{
{
if
(
unghostify
(
self
)
<
0
)
if
(
unghostify
(
self
)
<
0
)
return
NULL
;
return
NULL
;
Py_INCREF
(
Py_None
);
Py_INCREF
(
Py_None
);
return
Py_None
;
return
Py_None
;
}
}
static
int
Per_set_changed
(
cPersistentObject
*
self
,
PyObject
*
v
);
static
int
Per_set_changed
(
cPersistentObject
*
self
,
PyObject
*
v
);
...
@@ -266,278 +259,298 @@ static int Per_set_changed(cPersistentObject *self, PyObject *v);
...
@@ -266,278 +259,298 @@ static int Per_set_changed(cPersistentObject *self, PyObject *v);
static
PyObject
*
static
PyObject
*
Per__p_invalidate
(
cPersistentObject
*
self
)
Per__p_invalidate
(
cPersistentObject
*
self
)
{
{
signed
char
old_state
=
self
->
state
;
signed
char
old_state
=
self
->
state
;
if
(
old_state
!=
cPersistent_GHOST_STATE
)
{
if
(
old_state
!=
cPersistent_GHOST_STATE
)
if
(
Per_set_changed
(
self
,
NULL
)
<
0
)
{
return
NULL
;
if
(
Per_set_changed
(
self
,
NULL
)
<
0
)
ghostify
(
self
);
return
NULL
;
ghostify
(
self
);
}
}
Py_INCREF
(
Py_None
);
Py_INCREF
(
Py_None
);
return
Py_None
;
return
Py_None
;
}
}
static
PyObject
*
static
PyObject
*
pickle_slotnames
(
PyTypeObject
*
cls
)
pickle_slotnames
(
PyTypeObject
*
cls
)
{
{
PyObject
*
slotnames
;
PyObject
*
slotnames
;
slotnames
=
PyDict_GetItem
(
cls
->
tp_dict
,
py___slotnames__
);
slotnames
=
PyDict_GetItem
(
cls
->
tp_dict
,
py___slotnames__
);
if
(
slotnames
)
{
if
(
slotnames
)
Py_INCREF
(
slotnames
);
{
return
slotnames
;
Py_INCREF
(
slotnames
);
return
slotnames
;
}
}
slotnames
=
PyObject_CallFunctionObjArgs
(
copy_reg_slotnames
,
slotnames
=
PyObject_CallFunctionObjArgs
(
copy_reg_slotnames
,
(
PyObject
*
)
cls
,
NULL
);
(
PyObject
*
)
cls
,
NULL
);
if
(
slotnames
&&
!
(
slotnames
==
Py_None
||
PyList_Check
(
slotnames
)))
{
if
(
slotnames
&&
!
(
slotnames
==
Py_None
||
PyList_Check
(
slotnames
)))
PyErr_SetString
(
PyExc_TypeError
,
{
"copy_reg._slotnames didn't return a list or None"
);
PyErr_SetString
(
PyExc_TypeError
,
Py_DECREF
(
slotnames
);
"copy_reg._slotnames didn't return a list or None"
);
return
NULL
;
Py_DECREF
(
slotnames
);
return
NULL
;
}
}
return
slotnames
;
return
slotnames
;
}
}
static
PyObject
*
static
PyObject
*
pickle_copy_dict
(
PyObject
*
state
)
pickle_copy_dict
(
PyObject
*
state
)
{
{
PyObject
*
copy
,
*
key
,
*
value
;
PyObject
*
copy
,
*
key
,
*
value
;
char
*
ckey
;
char
*
ckey
;
Py_ssize_t
pos
=
0
;
Py_ssize_t
pos
=
0
;
copy
=
PyDict_New
();
copy
=
PyDict_New
();
if
(
!
copy
)
if
(
!
copy
)
return
NULL
;
return
NULL
;
if
(
!
state
)
if
(
!
state
)
return
copy
;
return
copy
;
while
(
PyDict_Next
(
state
,
&
pos
,
&
key
,
&
value
))
{
while
(
PyDict_Next
(
state
,
&
pos
,
&
key
,
&
value
))
if
(
key
&&
PyString_Check
(
key
))
{
{
ckey
=
PyString_AS_STRING
(
key
);
if
(
key
&&
PyString_Check
(
key
))
if
(
*
ckey
==
'_'
&&
{
(
ckey
[
1
]
==
'v'
||
ckey
[
1
]
==
'p'
)
&&
ckey
=
PyString_AS_STRING
(
key
);
ckey
[
2
]
==
'_'
)
if
(
*
ckey
==
'_'
&&
/* skip volatile and persistent */
(
ckey
[
1
]
==
'v'
||
ckey
[
1
]
==
'p'
)
&&
continue
;
ckey
[
2
]
==
'_'
)
/* skip volatile and persistent */
continue
;
}
}
if
(
PyObject_SetItem
(
copy
,
key
,
value
)
<
0
)
if
(
PyObject_SetItem
(
copy
,
key
,
value
)
<
0
)
goto
err
;
goto
err
;
}
}
return
copy
;
return
copy
;
err:
err:
Py_DECREF
(
copy
);
Py_DECREF
(
copy
);
return
NULL
;
return
NULL
;
}
}
static
char
pickle___getstate__doc
[]
=
static
char
pickle___getstate__doc
[]
=
"Get the object serialization state
\n
"
"Get the object serialization state
\n
"
"
\n
"
"
\n
"
"If the object has no assigned slots and has no instance dictionary, then
\n
"
"If the object has no assigned slots and has no instance dictionary, then
\n
"
"None is returned.
\n
"
"None is returned.
\n
"
"
\n
"
"
\n
"
"If the object has no assigned slots and has an instance dictionary, then
\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
"
"the a copy of the instance dictionary is returned. The copy has any items
\n
"
"with names starting with '_v_' or '_p_' ommitted.
\n
"
"with names starting with '_v_' or '_p_' ommitted.
\n
"
"
\n
"
"
\n
"
"If the object has assigned slots, then a two-element tuple is returned.
\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
"
"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
"
"as described above. The second element is a dictionary with items
\n
"
"for each of the assigned slots.
\n
"
"for each of the assigned slots.
\n
"
;
;
static
PyObject
*
static
PyObject
*
pickle___getstate__
(
PyObject
*
self
)
pickle___getstate__
(
PyObject
*
self
)
{
{
PyObject
*
slotnames
=
NULL
,
*
slots
=
NULL
,
*
state
=
NULL
;
PyObject
*
slotnames
=
NULL
,
*
slots
=
NULL
,
*
state
=
NULL
;
PyObject
**
dictp
;
PyObject
**
dictp
;
int
n
=
0
;
int
n
=
0
;
slotnames
=
pickle_slotnames
(
self
->
ob_type
);
slotnames
=
pickle_slotnames
(
self
->
ob_type
);
if
(
!
slotnames
)
if
(
!
slotnames
)
return
NULL
;
return
NULL
;
dictp
=
_PyObject_GetDictPtr
(
self
);
dictp
=
_PyObject_GetDictPtr
(
self
);
if
(
dictp
)
if
(
dictp
)
state
=
pickle_copy_dict
(
*
dictp
);
state
=
pickle_copy_dict
(
*
dictp
);
else
{
else
state
=
Py_None
;
{
Py_INCREF
(
state
);
state
=
Py_None
;
Py_INCREF
(
state
);
}
}
if
(
slotnames
!=
Py_None
)
{
if
(
slotnames
!=
Py_None
)
int
i
;
{
int
i
;
slots
=
PyDict_New
();
if
(
!
slots
)
slots
=
PyDict_New
();
goto
end
;
if
(
!
slots
)
goto
end
;
for
(
i
=
0
;
i
<
PyList_GET_SIZE
(
slotnames
);
i
++
)
{
PyObject
*
name
,
*
value
;
for
(
i
=
0
;
i
<
PyList_GET_SIZE
(
slotnames
);
i
++
)
char
*
cname
;
{
PyObject
*
name
,
*
value
;
name
=
PyList_GET_ITEM
(
slotnames
,
i
);
char
*
cname
;
if
(
PyString_Check
(
name
))
{
cname
=
PyString_AS_STRING
(
name
);
name
=
PyList_GET_ITEM
(
slotnames
,
i
);
if
(
*
cname
==
'_'
&&
if
(
PyString_Check
(
name
))
(
cname
[
1
]
==
'v'
||
cname
[
1
]
==
'p'
)
&&
{
cname
[
2
]
==
'_'
)
cname
=
PyString_AS_STRING
(
name
);
/* skip volatile and persistent */
if
(
*
cname
==
'_'
&&
continue
;
(
cname
[
1
]
==
'v'
||
cname
[
1
]
==
'p'
)
&&
cname
[
2
]
==
'_'
)
/* skip volatile and persistent */
continue
;
}
}
/* Unclear: Will this go through our getattr hook? */
/* Unclear: Will this go through our getattr hook? */
value
=
PyObject_GetAttr
(
self
,
name
);
value
=
PyObject_GetAttr
(
self
,
name
);
if
(
value
==
NULL
)
if
(
value
==
NULL
)
PyErr_Clear
();
PyErr_Clear
();
else
{
else
int
err
=
PyDict_SetItem
(
slots
,
name
,
value
);
{
Py_DECREF
(
value
);
int
err
=
PyDict_SetItem
(
slots
,
name
,
value
);
if
(
err
<
0
)
Py_DECREF
(
value
);
goto
end
;
if
(
err
<
0
)
n
++
;
goto
end
;
n
++
;
}
}
}
}
}
}
if
(
n
)
if
(
n
)
state
=
Py_BuildValue
(
"(NO)"
,
state
,
slots
);
state
=
Py_BuildValue
(
"(NO)"
,
state
,
slots
);
end:
end:
Py_XDECREF
(
slotnames
);
Py_XDECREF
(
slotnames
);
Py_XDECREF
(
slots
);
Py_XDECREF
(
slots
);
return
state
;
return
state
;
}
}
static
int
static
int
pickle_setattrs_from_dict
(
PyObject
*
self
,
PyObject
*
dict
)
pickle_setattrs_from_dict
(
PyObject
*
self
,
PyObject
*
dict
)
{
{
PyObject
*
key
,
*
value
;
PyObject
*
key
,
*
value
;
Py_ssize_t
pos
=
0
;
Py_ssize_t
pos
=
0
;
if
(
!
PyDict_Check
(
dict
))
{
if
(
!
PyDict_Check
(
dict
))
PyErr_SetString
(
PyExc_TypeError
,
"Expected dictionary"
);
{
return
-
1
;
PyErr_SetString
(
PyExc_TypeError
,
"Expected dictionary"
);
return
-
1
;
}
}
while
(
PyDict_Next
(
dict
,
&
pos
,
&
key
,
&
value
))
{
while
(
PyDict_Next
(
dict
,
&
pos
,
&
key
,
&
value
))
if
(
PyObject_SetAttr
(
self
,
key
,
value
)
<
0
)
{
return
-
1
;
if
(
PyObject_SetAttr
(
self
,
key
,
value
)
<
0
)
return
-
1
;
}
}
return
0
;
return
0
;
}
}
static
char
pickle___setstate__doc
[]
=
static
char
pickle___setstate__doc
[]
=
"Set the object serialization state
\n\n
"
"Set the object serialization state
\n\n
"
"The state should be in one of 3 forms:
\n\n
"
"The state should be in one of 3 forms:
\n\n
"
"- None
\n\n
"
"- None
\n\n
"
" Ignored
\n\n
"
" Ignored
\n\n
"
"- A dictionary
\n\n
"
"- A dictionary
\n\n
"
" In this case, the object's instance dictionary will be cleared and
\n
"
" In this case, the object's instance dictionary will be cleared and
\n
"
" updated with the new state.
\n\n
"
" updated with the new state.
\n\n
"
"- A two-tuple with a string as the first element.
\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
"
" In this case, the method named by the string in the first element will
\n
"
"
called with the second element.
\n\n
"
" be
called with the second element.
\n\n
"
" This form supports migration of data formats.
\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
"
"- A two-tuple with None or a Dictionary as the first element and
\n
"
" with a dictionary as the second element.
\n\n
"
" with a dictionary as the second element.
\n\n
"
" If the first element is not None, then the object's instance dictionary
\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
"
" will be cleared and updated with the value.
\n\n
"
" The items in the second element will be assigned as attributes.
\n
"
" The items in the second element will be assigned as attributes.
\n
"
;
;
static
PyObject
*
static
PyObject
*
pickle___setstate__
(
PyObject
*
self
,
PyObject
*
state
)
pickle___setstate__
(
PyObject
*
self
,
PyObject
*
state
)
{
{
PyObject
*
slots
=
NULL
;
PyObject
*
slots
=
NULL
;
if
(
PyTuple_Check
(
state
))
{
if
(
PyTuple_Check
(
state
))
if
(
!
PyArg_ParseTuple
(
state
,
"OO:__setstate__"
,
&
state
,
&
slots
))
{
return
NULL
;
if
(
!
PyArg_ParseTuple
(
state
,
"OO:__setstate__"
,
&
state
,
&
slots
))
return
NULL
;
}
}
if
(
state
!=
Py_None
)
{
if
(
state
!=
Py_None
)
PyObject
**
dict
;
{
PyObject
**
dict
;
dict
=
_PyObject_GetDictPtr
(
self
);
if
(
dict
)
{
dict
=
_PyObject_GetDictPtr
(
self
);
if
(
!*
dict
)
{
if
(
dict
)
*
dict
=
PyDict_New
();
{
if
(
!*
dict
)
if
(
!*
dict
)
return
NULL
;
{
*
dict
=
PyDict_New
();
if
(
!*
dict
)
return
NULL
;
}
}
}
}
if
(
*
dict
)
{
if
(
*
dict
)
PyDict_Clear
(
*
dict
);
{
if
(
PyDict_Update
(
*
dict
,
state
)
<
0
)
PyDict_Clear
(
*
dict
);
return
NULL
;
if
(
PyDict_Update
(
*
dict
,
state
)
<
0
)
return
NULL
;
}
}
else
if
(
pickle_setattrs_from_dict
(
self
,
state
)
<
0
)
else
if
(
pickle_setattrs_from_dict
(
self
,
state
)
<
0
)
return
NULL
;
return
NULL
;
}
}
if
(
slots
&&
pickle_setattrs_from_dict
(
self
,
slots
)
<
0
)
if
(
slots
&&
pickle_setattrs_from_dict
(
self
,
slots
)
<
0
)
return
NULL
;
return
NULL
;
Py_INCREF
(
Py_None
);
Py_INCREF
(
Py_None
);
return
Py_None
;
return
Py_None
;
}
}
static
char
pickle___reduce__doc
[]
=
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
*
static
PyObject
*
pickle___reduce__
(
PyObject
*
self
)
pickle___reduce__
(
PyObject
*
self
)
{
{
PyObject
*
args
=
NULL
,
*
bargs
=
NULL
,
*
state
=
NULL
,
*
getnewargs
=
NULL
;
PyObject
*
args
=
NULL
,
*
bargs
=
NULL
,
*
state
=
NULL
,
*
getnewargs
=
NULL
;
int
l
,
i
;
int
l
,
i
;
getnewargs
=
PyObject_GetAttr
(
self
,
py___getnewargs__
);
getnewargs
=
PyObject_GetAttr
(
self
,
py___getnewargs__
);
if
(
getnewargs
)
{
if
(
getnewargs
)
bargs
=
PyObject_CallFunctionObjArgs
(
getnewargs
,
NULL
);
{
Py_DECREF
(
getnewargs
);
bargs
=
PyObject_CallFunctionObjArgs
(
getnewargs
,
NULL
);
if
(
!
bargs
)
Py_DECREF
(
getnewargs
);
return
NULL
;
if
(
!
bargs
)
l
=
PyTuple_Size
(
bargs
);
return
NULL
;
if
(
l
<
0
)
l
=
PyTuple_Size
(
bargs
);
goto
end
;
if
(
l
<
0
)
goto
end
;
}
}
else
{
else
PyErr_Clear
();
{
l
=
0
;
PyErr_Clear
();
l
=
0
;
}
}
args
=
PyTuple_New
(
l
+
1
);
args
=
PyTuple_New
(
l
+
1
);
if
(
args
==
NULL
)
if
(
args
==
NULL
)
goto
end
;
goto
end
;
Py_INCREF
(
self
->
ob_type
);
Py_INCREF
(
self
->
ob_type
);
PyTuple_SET_ITEM
(
args
,
0
,
(
PyObject
*
)(
self
->
ob_type
));
PyTuple_SET_ITEM
(
args
,
0
,
(
PyObject
*
)(
self
->
ob_type
));
for
(
i
=
0
;
i
<
l
;
i
++
)
{
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
(
PyTuple_GET_ITEM
(
bargs
,
i
));
PyTuple_SET_ITEM
(
args
,
i
+
1
,
PyTuple_GET_ITEM
(
bargs
,
i
));
}
}
state
=
PyObject_CallMethodObjArgs
(
self
,
py___getstate__
,
NULL
);
state
=
PyObject_CallMethodObjArgs
(
self
,
py___getstate__
,
NULL
);
if
(
!
state
)
if
(
!
state
)
goto
end
;
goto
end
;
state
=
Py_BuildValue
(
"(OON)"
,
__newobj__
,
args
,
state
);
state
=
Py_BuildValue
(
"(OON)"
,
__newobj__
,
args
,
state
);
end:
end:
Py_XDECREF
(
bargs
);
Py_XDECREF
(
bargs
);
Py_XDECREF
(
args
);
Py_XDECREF
(
args
);
return
state
;
return
state
;
}
}
...
@@ -554,13 +567,13 @@ pickle___reduce__(PyObject *self)
...
@@ -554,13 +567,13 @@ pickle___reduce__(PyObject *self)
static
PyObject
*
static
PyObject
*
Per__getstate__
(
cPersistentObject
*
self
)
Per__getstate__
(
cPersistentObject
*
self
)
{
{
/* TODO: Should it be an error to call __getstate__() on a ghost? */
/* TODO: Should it be an error to call __getstate__() on a ghost? */
if
(
unghostify
(
self
)
<
0
)
if
(
unghostify
(
self
)
<
0
)
return
NULL
;
return
NULL
;
/* TODO: should we increment stickyness? Tim doesn't understand that
/* TODO: should we increment stickyness? Tim doesn't understand that
question. S*/
question. S*/
return
pickle___getstate__
((
PyObject
*
)
self
);
return
pickle___getstate__
((
PyObject
*
)
self
);
}
}
/* The Persistent base type provides a traverse function, but not a
/* The Persistent base type provides a traverse function, but not a
...
@@ -578,34 +591,48 @@ Per__getstate__(cPersistentObject *self)
...
@@ -578,34 +591,48 @@ Per__getstate__(cPersistentObject *self)
static
void
static
void
Per_dealloc
(
cPersistentObject
*
self
)
Per_dealloc
(
cPersistentObject
*
self
)
{
{
if
(
self
->
state
>=
0
)
if
(
self
->
state
>=
0
)
unlink_from_ring
(
self
);
{
if
(
self
->
cache
)
/* If the cache has been cleared, then a non-ghost object
cPersistenceCAPI
->
percachedel
(
self
->
cache
,
self
->
oid
);
isn't in the ring any longer.
Py_XDECREF
(
self
->
cache
);
*/
Py_XDECREF
(
self
->
jar
);
if
(
self
->
ring
.
r_next
!=
NULL
)
Py_XDECREF
(
self
->
oid
);
{
self
->
ob_type
->
tp_free
(
self
);
/* 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
static
int
Per_traverse
(
cPersistentObject
*
self
,
visitproc
visit
,
void
*
arg
)
Per_traverse
(
cPersistentObject
*
self
,
visitproc
visit
,
void
*
arg
)
{
{
int
err
;
int
err
;
#define VISIT(SLOT) \
#define VISIT(SLOT)
\
if (SLOT) {
\
if (SLOT) {
\
err = visit((PyObject *)(SLOT), arg);
\
err = visit((PyObject *)(SLOT), arg);
\
if (err)
\
if (err)
\
return err;
\
return err;
\
}
}
VISIT
(
self
->
jar
);
VISIT
(
self
->
jar
);
VISIT
(
self
->
oid
);
VISIT
(
self
->
oid
);
VISIT
(
self
->
cache
);
VISIT
(
self
->
cache
);
#undef VISIT
#undef VISIT
return
0
;
return
0
;
}
}
/* convert_name() returns a new reference to a string name
/* convert_name() returns a new reference to a string name
...
@@ -616,20 +643,23 @@ static PyObject *
...
@@ -616,20 +643,23 @@ static PyObject *
convert_name
(
PyObject
*
name
)
convert_name
(
PyObject
*
name
)
{
{
#ifdef Py_USING_UNICODE
#ifdef Py_USING_UNICODE
/* The Unicode to string conversion is done here because the
/* The Unicode to string conversion is done here because the
existing tp_setattro slots expect a string object as name
existing tp_setattro slots expect a string object as name
and we wouldn't want to break those. */
and we wouldn't want to break those. */
if
(
PyUnicode_Check
(
name
))
{
if
(
PyUnicode_Check
(
name
))
name
=
PyUnicode_AsEncodedString
(
name
,
NULL
,
NULL
);
{
name
=
PyUnicode_AsEncodedString
(
name
,
NULL
,
NULL
);
}
}
else
else
#endif
#endif
if
(
!
PyString_Check
(
name
))
{
if
(
!
PyString_Check
(
name
))
PyErr_SetString
(
PyExc_TypeError
,
"attribute name must be a string"
);
{
return
NULL
;
PyErr_SetString
(
PyExc_TypeError
,
"attribute name must be a string"
);
}
else
return
NULL
;
Py_INCREF
(
name
);
}
return
name
;
else
Py_INCREF
(
name
);
return
name
;
}
}
/* Returns true if the object requires unghostification.
/* Returns true if the object requires unghostification.
...
@@ -646,375 +676,400 @@ convert_name(PyObject *name)
...
@@ -646,375 +676,400 @@ convert_name(PyObject *name)
static
int
static
int
unghost_getattr
(
const
char
*
s
)
unghost_getattr
(
const
char
*
s
)
{
{
if
(
*
s
++
!=
'_'
)
if
(
*
s
++
!=
'_'
)
return
1
;
return
1
;
if
(
*
s
==
'p'
)
{
if
(
*
s
==
'p'
)
s
++
;
{
if
(
*
s
==
'_'
)
s
++
;
return
0
;
/* _p_ */
if
(
*
s
==
'_'
)
else
return
0
;
/* _p_ */
return
1
;
else
return
1
;
}
}
else
if
(
*
s
==
'_'
)
{
else
if
(
*
s
==
'_'
)
s
++
;
{
switch
(
*
s
)
{
s
++
;
case
'c'
:
switch
(
*
s
)
return
strcmp
(
s
,
"class__"
);
{
case
'd'
:
case
'c'
:
s
++
;
return
strcmp
(
s
,
"class__"
);
if
(
!
strcmp
(
s
,
"el__"
))
case
'd'
:
return
0
;
/* __del__ */
s
++
;
if
(
!
strcmp
(
s
,
"ict__"
))
if
(
!
strcmp
(
s
,
"el__"
))
return
0
;
/* __dict__ */
return
0
;
/* __del__ */
return
1
;
if
(
!
strcmp
(
s
,
"ict__"
))
case
'o'
:
return
0
;
/* __dict__ */
return
strcmp
(
s
,
"of__"
);
return
1
;
case
's'
:
case
'o'
:
return
strcmp
(
s
,
"setstate__"
);
return
strcmp
(
s
,
"of__"
);
default:
case
's'
:
return
1
;
return
strcmp
(
s
,
"setstate__"
);
}
default:
return
1
;
}
}
}
return
1
;
return
1
;
}
}
static
PyObject
*
static
PyObject
*
Per_getattro
(
cPersistentObject
*
self
,
PyObject
*
name
)
Per_getattro
(
cPersistentObject
*
self
,
PyObject
*
name
)
{
{
PyObject
*
result
=
NULL
;
/* guilty until proved innocent */
PyObject
*
result
=
NULL
;
/* guilty until proved innocent */
char
*
s
;
char
*
s
;
name
=
convert_name
(
name
);
name
=
convert_name
(
name
);
if
(
!
name
)
if
(
!
name
)
goto
Done
;
goto
Done
;
s
=
PyString_AS_STRING
(
name
);
s
=
PyString_AS_STRING
(
name
);
if
(
unghost_getattr
(
s
))
{
if
(
unghost_getattr
(
s
))
if
(
unghostify
(
self
)
<
0
)
{
goto
Done
;
if
(
unghostify
(
self
)
<
0
)
accessed
(
self
);
goto
Done
;
accessed
(
self
);
}
}
result
=
PyObject_GenericGetAttr
((
PyObject
*
)
self
,
name
);
result
=
PyObject_GenericGetAttr
((
PyObject
*
)
self
,
name
);
Done:
Done:
Py_XDECREF
(
name
);
Py_XDECREF
(
name
);
return
result
;
return
result
;
}
}
/* Exposed as _p_getattr method. Test whether base getattr should be used */
/* Exposed as _p_getattr method. Test whether base getattr should be used */
static
PyObject
*
static
PyObject
*
Per__p_getattr
(
cPersistentObject
*
self
,
PyObject
*
name
)
Per__p_getattr
(
cPersistentObject
*
self
,
PyObject
*
name
)
{
{
PyObject
*
result
=
NULL
;
/* guilty until proved innocent */
PyObject
*
result
=
NULL
;
/* guilty until proved innocent */
char
*
s
;
char
*
s
;
name
=
convert_name
(
name
);
name
=
convert_name
(
name
);
if
(
!
name
)
if
(
!
name
)
goto
Done
;
goto
Done
;
s
=
PyString_AS_STRING
(
name
);
s
=
PyString_AS_STRING
(
name
);
if
(
*
s
!=
'_'
||
unghost_getattr
(
s
))
{
if
(
*
s
!=
'_'
||
unghost_getattr
(
s
))
if
(
unghostify
(
self
)
<
0
)
{
goto
Done
;
if
(
unghostify
(
self
)
<
0
)
accessed
(
self
);
goto
Done
;
result
=
Py_False
;
accessed
(
self
);
result
=
Py_False
;
}
}
else
else
result
=
Py_True
;
result
=
Py_True
;
Py_INCREF
(
result
);
Py_INCREF
(
result
);
Done:
Done:
Py_XDECREF
(
name
);
Py_XDECREF
(
name
);
return
result
;
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
static
int
Per_setattro
(
cPersistentObject
*
self
,
PyObject
*
name
,
PyObject
*
v
)
Per_setattro
(
cPersistentObject
*
self
,
PyObject
*
name
,
PyObject
*
v
)
{
{
int
result
=
-
1
;
/* guilty until proved innocent */
int
result
=
-
1
;
/* guilty until proved innocent */
char
*
s
;
char
*
s
;
name
=
convert_name
(
name
);
name
=
convert_name
(
name
);
if
(
!
name
)
if
(
!
name
)
goto
Done
;
goto
Done
;
s
=
PyString_AS_STRING
(
name
);
s
=
PyString_AS_STRING
(
name
);
if
(
strncmp
(
s
,
"_p_"
,
3
)
!=
0
)
{
if
(
strncmp
(
s
,
"_p_"
,
3
)
!=
0
)
if
(
unghostify
(
self
)
<
0
)
{
goto
Done
;
if
(
unghostify
(
self
)
<
0
)
accessed
(
self
);
goto
Done
;
if
(
strncmp
(
s
,
"_v_"
,
3
)
!=
0
accessed
(
self
);
&&
self
->
state
!=
cPersistent_CHANGED_STATE
)
{
if
(
strncmp
(
s
,
"_v_"
,
3
)
!=
0
if
(
changed
(
self
)
<
0
)
&&
self
->
state
!=
cPersistent_CHANGED_STATE
)
goto
Done
;
{
}
if
(
changed
(
self
)
<
0
)
goto
Done
;
}
}
}
result
=
PyObject_GenericSetAttr
((
PyObject
*
)
self
,
name
,
v
);
result
=
PyObject_GenericSetAttr
((
PyObject
*
)
self
,
name
,
v
);
Done:
Done:
Py_XDECREF
(
name
);
Py_XDECREF
(
name
);
return
result
;
return
result
;
}
}
static
int
static
int
Per_p_set_or_delattro
(
cPersistentObject
*
self
,
PyObject
*
name
,
PyObject
*
v
)
Per_p_set_or_delattro
(
cPersistentObject
*
self
,
PyObject
*
name
,
PyObject
*
v
)
{
{
int
result
=
-
1
;
/* guilty until proved innocent */
int
result
=
-
1
;
/* guilty until proved innocent */
char
*
s
;
char
*
s
;
name
=
convert_name
(
name
);
name
=
convert_name
(
name
);
if
(
!
name
)
if
(
!
name
)
goto
Done
;
goto
Done
;
s
=
PyString_AS_STRING
(
name
);
s
=
PyString_AS_STRING
(
name
);
if
(
strncmp
(
s
,
"_p_"
,
3
))
{
if
(
strncmp
(
s
,
"_p_"
,
3
))
if
(
unghostify
(
self
)
<
0
)
{
goto
Done
;
if
(
unghostify
(
self
)
<
0
)
accessed
(
self
);
goto
Done
;
accessed
(
self
);
result
=
0
;
result
=
0
;
}
}
else
{
else
if
(
PyObject_GenericSetAttr
((
PyObject
*
)
self
,
name
,
v
)
<
0
)
{
goto
Done
;
if
(
PyObject_GenericSetAttr
((
PyObject
*
)
self
,
name
,
v
)
<
0
)
result
=
1
;
goto
Done
;
result
=
1
;
}
}
Done:
Done:
Py_XDECREF
(
name
);
Py_XDECREF
(
name
);
return
result
;
return
result
;
}
}
static
PyObject
*
static
PyObject
*
Per__p_setattr
(
cPersistentObject
*
self
,
PyObject
*
args
)
Per__p_setattr
(
cPersistentObject
*
self
,
PyObject
*
args
)
{
{
PyObject
*
name
,
*
v
,
*
result
;
PyObject
*
name
,
*
v
,
*
result
;
int
r
;
int
r
;
if
(
!
PyArg_ParseTuple
(
args
,
"OO:_p_setattr"
,
&
name
,
&
v
))
if
(
!
PyArg_ParseTuple
(
args
,
"OO:_p_setattr"
,
&
name
,
&
v
))
return
NULL
;
return
NULL
;
r
=
Per_p_set_or_delattro
(
self
,
name
,
v
);
r
=
Per_p_set_or_delattro
(
self
,
name
,
v
);
if
(
r
<
0
)
if
(
r
<
0
)
return
NULL
;
return
NULL
;
result
=
r
?
Py_True
:
Py_False
;
result
=
r
?
Py_True
:
Py_False
;
Py_INCREF
(
result
);
Py_INCREF
(
result
);
return
result
;
return
result
;
}
}
static
PyObject
*
static
PyObject
*
Per__p_delattr
(
cPersistentObject
*
self
,
PyObject
*
name
)
Per__p_delattr
(
cPersistentObject
*
self
,
PyObject
*
name
)
{
{
int
r
;
int
r
;
PyObject
*
result
;
PyObject
*
result
;
r
=
Per_p_set_or_delattro
(
self
,
name
,
NULL
);
r
=
Per_p_set_or_delattro
(
self
,
name
,
NULL
);
if
(
r
<
0
)
if
(
r
<
0
)
return
NULL
;
return
NULL
;
result
=
r
?
Py_True
:
Py_False
;
result
=
r
?
Py_True
:
Py_False
;
Py_INCREF
(
result
);
Py_INCREF
(
result
);
return
result
;
return
result
;
}
}
static
PyObject
*
static
PyObject
*
Per_get_changed
(
cPersistentObject
*
self
)
Per_get_changed
(
cPersistentObject
*
self
)
{
{
if
(
self
->
state
<
0
)
{
if
(
self
->
state
<
0
)
Py_INCREF
(
Py_None
);
{
return
Py_None
;
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
static
int
Per_set_changed
(
cPersistentObject
*
self
,
PyObject
*
v
)
Per_set_changed
(
cPersistentObject
*
self
,
PyObject
*
v
)
{
{
int
deactivate
=
0
;
int
deactivate
=
0
;
int
true
;
int
true
;
if
(
!
v
)
{
if
(
!
v
)
/* delattr is used to invalidate an object even if it has changed. */
{
if
(
self
->
state
!=
cPersistent_GHOST_STATE
)
/* delattr is used to invalidate an object even if it has changed. */
self
->
state
=
cPersistent_UPTODATE_STATE
;
if
(
self
->
state
!=
cPersistent_GHOST_STATE
)
deactivate
=
1
;
self
->
state
=
cPersistent_UPTODATE_STATE
;
deactivate
=
1
;
}
}
else
if
(
v
==
Py_None
)
else
if
(
v
==
Py_None
)
deactivate
=
1
;
deactivate
=
1
;
if
(
deactivate
)
{
if
(
deactivate
)
PyObject
*
res
,
*
meth
;
{
meth
=
PyObject_GetAttr
((
PyObject
*
)
self
,
py__p_deactivate
);
PyObject
*
res
,
*
meth
;
if
(
meth
==
NULL
)
meth
=
PyObject_GetAttr
((
PyObject
*
)
self
,
py__p_deactivate
);
return
-
1
;
if
(
meth
==
NULL
)
res
=
PyObject_CallObject
(
meth
,
NULL
);
return
-
1
;
if
(
res
)
res
=
PyObject_CallObject
(
meth
,
NULL
);
Py_DECREF
(
res
);
if
(
res
)
else
{
Py_DECREF
(
res
);
/* an error occured in _p_deactivate().
else
{
It's not clear what we should do here. The code is
/* an error occured in _p_deactivate().
obviously ignoring the exception, but it shouldn't return
0 for a getattr and set an exception. The simplest change
It's not clear what we should do here. The code is
is to clear the exception, but that simply masks the
obviously ignoring the exception, but it shouldn't return
error.
0 for a getattr and set an exception. The simplest change
is to clear the exception, but that simply masks the
This prints an error to stderr just like exceptions in
error.
__del__(). It would probably be better to log it but that
would be painful from C.
This prints an error to stderr just like exceptions in
*/
__del__(). It would probably be better to log it but that
PyErr_WriteUnraisable
(
meth
);
would be painful from C.
}
*/
Py_DECREF
(
meth
);
PyErr_WriteUnraisable
(
meth
);
return
0
;
}
Py_DECREF
(
meth
);
return
0
;
}
}
/* !deactivate. If passed a true argument, mark self as changed (starting
/* !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).
* 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
* If passed a false argument, and the object isn't a ghost, set the
* state as up-to-date.
* state as up-to-date.
*/
*/
true
=
PyObject_IsTrue
(
v
);
true
=
PyObject_IsTrue
(
v
);
if
(
true
==
-
1
)
if
(
true
==
-
1
)
return
-
1
;
return
-
1
;
if
(
true
)
{
if
(
true
)
if
(
self
->
state
<
0
)
{
{
if
(
self
->
state
<
0
)
{
if
(
unghostify
(
self
)
<
0
)
if
(
unghostify
(
self
)
<
0
)
return
-
1
;
return
-
1
;
}
}
return
changed
(
self
);
return
changed
(
self
);
}
}
/* We were passed a false, non-None argument. If we're not a ghost,
/* We were passed a false, non-None argument. If we're not a ghost,
* mark self as up-to-date.
* mark self as up-to-date.
*/
*/
if
(
self
->
state
>=
0
)
if
(
self
->
state
>=
0
)
self
->
state
=
cPersistent_UPTODATE_STATE
;
self
->
state
=
cPersistent_UPTODATE_STATE
;
return
0
;
return
0
;
}
}
static
PyObject
*
static
PyObject
*
Per_get_oid
(
cPersistentObject
*
self
)
Per_get_oid
(
cPersistentObject
*
self
)
{
{
PyObject
*
oid
=
self
->
oid
?
self
->
oid
:
Py_None
;
PyObject
*
oid
=
self
->
oid
?
self
->
oid
:
Py_None
;
Py_INCREF
(
oid
);
Py_INCREF
(
oid
);
return
oid
;
return
oid
;
}
}
static
int
static
int
Per_set_oid
(
cPersistentObject
*
self
,
PyObject
*
v
)
Per_set_oid
(
cPersistentObject
*
self
,
PyObject
*
v
)
{
{
if
(
self
->
cache
)
{
if
(
self
->
cache
)
int
result
;
{
int
result
;
if
(
v
==
NULL
)
{
PyErr_SetString
(
PyExc_ValueError
,
if
(
v
==
NULL
)
"can't delete _p_oid of cached object"
);
{
return
-
1
;
PyErr_SetString
(
PyExc_ValueError
,
}
"can't delete _p_oid of cached object"
);
if
(
PyObject_Cmp
(
self
->
oid
,
v
,
&
result
)
<
0
)
return
-
1
;
return
-
1
;
}
if
(
result
)
{
if
(
PyObject_Cmp
(
self
->
oid
,
v
,
&
result
)
<
0
)
PyErr_SetString
(
PyExc_ValueError
,
return
-
1
;
"can not change _p_oid of cached object"
);
if
(
result
)
return
-
1
;
{
}
PyErr_SetString
(
PyExc_ValueError
,
"can not change _p_oid of cached object"
);
return
-
1
;
}
}
}
Py_XDECREF
(
self
->
oid
);
Py_XDECREF
(
self
->
oid
);
Py_XINCREF
(
v
);
Py_XINCREF
(
v
);
self
->
oid
=
v
;
self
->
oid
=
v
;
return
0
;
return
0
;
}
}
static
PyObject
*
static
PyObject
*
Per_get_jar
(
cPersistentObject
*
self
)
Per_get_jar
(
cPersistentObject
*
self
)
{
{
PyObject
*
jar
=
self
->
jar
?
self
->
jar
:
Py_None
;
PyObject
*
jar
=
self
->
jar
?
self
->
jar
:
Py_None
;
Py_INCREF
(
jar
);
Py_INCREF
(
jar
);
return
jar
;
return
jar
;
}
}
static
int
static
int
Per_set_jar
(
cPersistentObject
*
self
,
PyObject
*
v
)
Per_set_jar
(
cPersistentObject
*
self
,
PyObject
*
v
)
{
{
if
(
self
->
cache
)
{
if
(
self
->
cache
)
int
result
;
{
int
result
;
if
(
v
==
NULL
)
{
PyErr_SetString
(
PyExc_ValueError
,
if
(
v
==
NULL
)
"can't delete _p_jar of cached object"
);
{
return
-
1
;
PyErr_SetString
(
PyExc_ValueError
,
}
"can't delete _p_jar of cached object"
);
if
(
PyObject_Cmp
(
self
->
jar
,
v
,
&
result
)
<
0
)
return
-
1
;
return
-
1
;
}
if
(
result
)
{
if
(
PyObject_Cmp
(
self
->
jar
,
v
,
&
result
)
<
0
)
PyErr_SetString
(
PyExc_ValueError
,
return
-
1
;
"can not change _p_jar of cached object"
);
if
(
result
)
return
-
1
;
{
}
PyErr_SetString
(
PyExc_ValueError
,
"can not change _p_jar of cached object"
);
return
-
1
;
}
}
}
Py_XDECREF
(
self
->
jar
);
Py_XDECREF
(
self
->
jar
);
Py_XINCREF
(
v
);
Py_XINCREF
(
v
);
self
->
jar
=
v
;
self
->
jar
=
v
;
return
0
;
return
0
;
}
}
static
PyObject
*
static
PyObject
*
Per_get_serial
(
cPersistentObject
*
self
)
Per_get_serial
(
cPersistentObject
*
self
)
{
{
return
PyString_FromStringAndSize
(
self
->
serial
,
8
);
return
PyString_FromStringAndSize
(
self
->
serial
,
8
);
}
}
static
int
static
int
Per_set_serial
(
cPersistentObject
*
self
,
PyObject
*
v
)
Per_set_serial
(
cPersistentObject
*
self
,
PyObject
*
v
)
{
{
if
(
v
)
{
if
(
v
)
if
(
PyString_Check
(
v
)
&&
PyString_GET_SIZE
(
v
)
==
8
)
{
memcpy
(
self
->
serial
,
PyString_AS_STRING
(
v
),
8
);
if
(
PyString_Check
(
v
)
&&
PyString_GET_SIZE
(
v
)
==
8
)
else
{
memcpy
(
self
->
serial
,
PyString_AS_STRING
(
v
),
8
);
PyErr_SetString
(
PyExc_ValueError
,
else
"_p_serial must be an 8-character string"
);
{
return
-
1
;
PyErr_SetString
(
PyExc_ValueError
,
}
"_p_serial must be an 8-character string"
);
}
else
return
-
1
;
memset
(
self
->
serial
,
0
,
8
);
}
return
0
;
}
else
memset
(
self
->
serial
,
0
,
8
);
return
0
;
}
}
static
PyObject
*
static
PyObject
*
Per_get_mtime
(
cPersistentObject
*
self
)
Per_get_mtime
(
cPersistentObject
*
self
)
{
{
PyObject
*
t
,
*
v
;
PyObject
*
t
,
*
v
;
if
(
unghostify
(
self
)
<
0
)
if
(
unghostify
(
self
)
<
0
)
return
NULL
;
return
NULL
;
accessed
(
self
);
accessed
(
self
);
if
(
memcmp
(
self
->
serial
,
"
\0\0\0\0\0\0\0\0
"
,
8
)
==
0
)
{
if
(
memcmp
(
self
->
serial
,
"
\0\0\0\0\0\0\0\0
"
,
8
)
==
0
)
Py_INCREF
(
Py_None
);
{
return
Py_None
;
Py_INCREF
(
Py_None
);
return
Py_None
;
}
}
t
=
PyObject_CallFunction
(
TimeStamp
,
"s#"
,
self
->
serial
,
8
);
t
=
PyObject_CallFunction
(
TimeStamp
,
"s#"
,
self
->
serial
,
8
);
if
(
!
t
)
if
(
!
t
)
return
NULL
;
return
NULL
;
v
=
PyObject_CallMethod
(
t
,
"timeTime"
,
""
);
v
=
PyObject_CallMethod
(
t
,
"timeTime"
,
""
);
Py_DECREF
(
t
);
Py_DECREF
(
t
);
return
v
;
return
v
;
}
}
static
PyObject
*
static
PyObject
*
Per_get_state
(
cPersistentObject
*
self
)
Per_get_state
(
cPersistentObject
*
self
)
{
{
return
PyInt_FromLong
(
self
->
state
);
return
PyInt_FromLong
(
self
->
state
);
}
}
static
PyObject
*
static
PyObject
*
...
@@ -1026,37 +1081,42 @@ Per_get_estimated_size(cPersistentObject *self)
...
@@ -1026,37 +1081,42 @@ Per_get_estimated_size(cPersistentObject *self)
static
int
static
int
Per_set_estimated_size
(
cPersistentObject
*
self
,
PyObject
*
v
)
Per_set_estimated_size
(
cPersistentObject
*
self
,
PyObject
*
v
)
{
{
if
(
v
)
{
if
(
v
)
if
(
PyInt_Check
(
v
))
{
{
long
lv
=
PyInt_AS_LONG
(
v
);
if
(
PyInt_Check
(
v
))
if
(
lv
<
0
)
{
{
PyErr_SetString
(
PyExc_ValueError
,
long
lv
=
PyInt_AS_LONG
(
v
);
"_p_estimated_size must not be negative"
);
if
(
lv
<
0
)
return
-
1
;
{
}
PyErr_SetString
(
PyExc_ValueError
,
self
->
estimated_size
=
_estimated_size_in_24_bits
(
lv
);
"_p_estimated_size must not be negative"
);
}
return
-
1
;
else
{
}
PyErr_SetString
(
PyExc_ValueError
,
self
->
estimated_size
=
_estimated_size_in_24_bits
(
lv
);
"_p_estimated_size must be an integer"
);
}
return
-
1
;
else
{
PyErr_SetString
(
PyExc_ValueError
,
"_p_estimated_size must be an integer"
);
return
-
1
;
}
}
}
}
else
else
self
->
estimated_size
=
0
;
self
->
estimated_size
=
0
;
return
0
;
return
0
;
}
}
static
PyGetSetDef
Per_getsets
[]
=
{
static
PyGetSetDef
Per_getsets
[]
=
{
{
"_p_changed"
,
(
getter
)
Per_get_changed
,
(
setter
)
Per_set_changed
},
{
"_p_changed"
,
(
getter
)
Per_get_changed
,
(
setter
)
Per_set_changed
},
{
"_p_jar"
,
(
getter
)
Per_get_jar
,
(
setter
)
Per_set_jar
},
{
"_p_jar"
,
(
getter
)
Per_get_jar
,
(
setter
)
Per_set_jar
},
{
"_p_mtime"
,
(
getter
)
Per_get_mtime
},
{
"_p_mtime"
,
(
getter
)
Per_get_mtime
},
{
"_p_oid"
,
(
getter
)
Per_get_oid
,
(
setter
)
Per_set_oid
},
{
"_p_oid"
,
(
getter
)
Per_get_oid
,
(
setter
)
Per_set_oid
},
{
"_p_serial"
,
(
getter
)
Per_get_serial
,
(
setter
)
Per_set_serial
},
{
"_p_serial"
,
(
getter
)
Per_get_serial
,
(
setter
)
Per_set_serial
},
{
"_p_state"
,
(
getter
)
Per_get_state
},
{
"_p_state"
,
(
getter
)
Per_get_state
},
{
"_p_estimated_size"
,
{
"_p_estimated_size"
,
(
getter
)
Per_get_estimated_size
,
(
setter
)
Per_set_estimated_size
(
getter
)
Per_get_estimated_size
,
(
setter
)
Per_set_estimated_size
},
},
{
NULL
}
{
NULL
}
};
};
static
struct
PyMethodDef
Per_methods
[]
=
{
static
struct
PyMethodDef
Per_methods
[]
=
{
...
@@ -1117,38 +1177,38 @@ static struct PyMethodDef Per_methods[] = {
...
@@ -1117,38 +1177,38 @@ static struct PyMethodDef Per_methods[] = {
#define DEFERRED_ADDRESS(ADDR) 0
#define DEFERRED_ADDRESS(ADDR) 0
static
PyTypeObject
Pertype
=
{
static
PyTypeObject
Pertype
=
{
PyObject_HEAD_INIT
(
DEFERRED_ADDRESS
(
&
PyPersist_MetaType
))
PyObject_HEAD_INIT
(
DEFERRED_ADDRESS
(
&
PyPersist_MetaType
))
0
,
/* ob_size */
0
,
/* ob_size */
"persistent.Persistent"
,
/* tp_name */
"persistent.Persistent"
,
/* tp_name */
sizeof
(
cPersistentObject
),
/* tp_basicsize */
sizeof
(
cPersistentObject
),
/* tp_basicsize */
0
,
/* tp_itemsize */
0
,
/* tp_itemsize */
(
destructor
)
Per_dealloc
,
/* tp_dealloc */
(
destructor
)
Per_dealloc
,
/* tp_dealloc */
0
,
/* tp_print */
0
,
/* tp_print */
0
,
/* tp_getattr */
0
,
/* tp_getattr */
0
,
/* tp_setattr */
0
,
/* tp_setattr */
0
,
/* tp_compare */
0
,
/* tp_compare */
0
,
/* tp_repr */
0
,
/* tp_repr */
0
,
/* tp_as_number */
0
,
/* tp_as_number */
0
,
/* tp_as_sequence */
0
,
/* tp_as_sequence */
0
,
/* tp_as_mapping */
0
,
/* tp_as_mapping */
0
,
/* tp_hash */
0
,
/* tp_hash */
0
,
/* tp_call */
0
,
/* tp_call */
0
,
/* tp_str */
0
,
/* tp_str */
(
getattrofunc
)
Per_getattro
,
/* tp_getattro */
(
getattrofunc
)
Per_getattro
,
/* tp_getattro */
(
setattrofunc
)
Per_setattro
,
/* tp_setattro */
(
setattrofunc
)
Per_setattro
,
/* tp_setattro */
0
,
/* tp_as_buffer */
0
,
/* tp_as_buffer */
Py_TPFLAGS_DEFAULT
|
Py_TPFLAGS_BASETYPE
|
Py_TPFLAGS_HAVE_GC
,
Py_TPFLAGS_DEFAULT
|
Py_TPFLAGS_BASETYPE
|
Py_TPFLAGS_HAVE_GC
,
/* tp_flags */
/* tp_flags */
0
,
/* tp_doc */
0
,
/* tp_doc */
(
traverseproc
)
Per_traverse
,
/* tp_traverse */
(
traverseproc
)
Per_traverse
,
/* tp_traverse */
0
,
/* tp_clear */
0
,
/* tp_clear */
0
,
/* tp_richcompare */
0
,
/* tp_richcompare */
0
,
/* tp_weaklistoffset */
0
,
/* tp_weaklistoffset */
0
,
/* tp_iter */
0
,
/* tp_iter */
0
,
/* tp_iternext */
0
,
/* tp_iternext */
Per_methods
,
/* tp_methods */
Per_methods
,
/* tp_methods */
0
,
/* tp_members */
0
,
/* tp_members */
Per_getsets
,
/* tp_getset */
Per_getsets
,
/* tp_getset */
};
};
/* End of code for Persistent objects */
/* End of code for Persistent objects */
...
@@ -1160,104 +1220,108 @@ typedef int (*intfunctionwithpythonarg)(PyObject*);
...
@@ -1160,104 +1220,108 @@ typedef int (*intfunctionwithpythonarg)(PyObject*);
static
int
static
int
Per_setstate
(
cPersistentObject
*
self
)
Per_setstate
(
cPersistentObject
*
self
)
{
{
if
(
unghostify
(
self
)
<
0
)
if
(
unghostify
(
self
)
<
0
)
return
-
1
;
return
-
1
;
self
->
state
=
cPersistent_STICKY_STATE
;
self
->
state
=
cPersistent_STICKY_STATE
;
return
0
;
return
0
;
}
}
static
PyObject
*
static
PyObject
*
simple_new
(
PyObject
*
self
,
PyObject
*
type_object
)
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
,
{
"simple_new"
,
simple_new
,
METH_O
,
"Create an object by simply calling a class's __new__ method without "
"Create an object by simply calling a class's __new__ method without "
"arguments."
},
"arguments."
},
{
NULL
,
NULL
}
{
NULL
,
NULL
}
};
};
static
cPersistenceCAPIstruct
static
cPersistenceCAPIstruct
truecPersistenceCAPI
=
{
truecPersistenceCAPI
=
{
&
Pertype
,
&
Pertype
,
(
getattrofunc
)
Per_getattro
,
/*tp_getattr with object key*/
(
getattrofunc
)
Per_getattro
,
/*tp_getattr with object key*/
(
setattrofunc
)
Per_setattro
,
/*tp_setattr with object key*/
(
setattrofunc
)
Per_setattro
,
/*tp_setattr with object key*/
changed
,
changed
,
accessed
,
accessed
,
ghostify
,
ghostify
,
(
intfunctionwithpythonarg
)
Per_setstate
,
(
intfunctionwithpythonarg
)
Per_setstate
,
NULL
/* The percachedel slot is initialized in cPickleCache.c when
NULL
/* The percachedel slot is initialized in cPickleCache.c when
the module is loaded. It uses a function in a different
the module is loaded. It uses a function in a different
shared library. */
shared library. */
};
};
void
void
initcPersistence
(
void
)
initcPersistence
(
void
)
{
{
PyObject
*
m
,
*
s
;
PyObject
*
m
,
*
s
;
PyObject
*
copy_reg
;
PyObject
*
copy_reg
;
if
(
init_strings
()
<
0
)
if
(
init_strings
()
<
0
)
return
;
return
;
m
=
Py_InitModule3
(
"cPersistence"
,
cPersistence_methods
,
m
=
Py_InitModule3
(
"cPersistence"
,
cPersistence_methods
,
cPersistence_doc_string
);
cPersistence_doc_string
);
Pertype
.
ob_type
=
&
PyType_Type
;
Pertype
.
ob_type
=
&
PyType_Type
;
Pertype
.
tp_new
=
PyType_GenericNew
;
Pertype
.
tp_new
=
PyType_GenericNew
;
if
(
PyType_Ready
(
&
Pertype
)
<
0
)
if
(
PyType_Ready
(
&
Pertype
)
<
0
)
return
;
return
;
if
(
PyModule_AddObject
(
m
,
"Persistent"
,
(
PyObject
*
)
&
Pertype
)
<
0
)
if
(
PyModule_AddObject
(
m
,
"Persistent"
,
(
PyObject
*
)
&
Pertype
)
<
0
)
return
;
return
;
cPersistenceCAPI
=
&
truecPersistenceCAPI
;
cPersistenceCAPI
=
&
truecPersistenceCAPI
;
s
=
PyCObject_FromVoidPtr
(
cPersistenceCAPI
,
NULL
);
s
=
PyCObject_FromVoidPtr
(
cPersistenceCAPI
,
NULL
);
if
(
!
s
)
if
(
!
s
)
return
;
return
;
if
(
PyModule_AddObject
(
m
,
"CAPI"
,
s
)
<
0
)
if
(
PyModule_AddObject
(
m
,
"CAPI"
,
s
)
<
0
)
return
;
return
;
if
(
PyModule_AddIntConstant
(
m
,
"GHOST"
,
cPersistent_GHOST_STATE
)
<
0
)
if
(
PyModule_AddIntConstant
(
m
,
"GHOST"
,
cPersistent_GHOST_STATE
)
<
0
)
return
;
return
;
if
(
PyModule_AddIntConstant
(
m
,
"UPTODATE"
,
cPersistent_UPTODATE_STATE
)
<
0
)
if
(
PyModule_AddIntConstant
(
m
,
"UPTODATE"
,
cPersistent_UPTODATE_STATE
)
<
0
)
return
;
return
;
if
(
PyModule_AddIntConstant
(
m
,
"CHANGED"
,
cPersistent_CHANGED_STATE
)
<
0
)
if
(
PyModule_AddIntConstant
(
m
,
"CHANGED"
,
cPersistent_CHANGED_STATE
)
<
0
)
return
;
return
;
if
(
PyModule_AddIntConstant
(
m
,
"STICKY"
,
cPersistent_STICKY_STATE
)
<
0
)
if
(
PyModule_AddIntConstant
(
m
,
"STICKY"
,
cPersistent_STICKY_STATE
)
<
0
)
return
;
return
;
py_simple_new
=
PyObject_GetAttrString
(
m
,
"simple_new"
);
py_simple_new
=
PyObject_GetAttrString
(
m
,
"simple_new"
);
if
(
!
py_simple_new
)
if
(
!
py_simple_new
)
return
;
return
;
copy_reg
=
PyImport_ImportModule
(
"copy_reg"
);
copy_reg
=
PyImport_ImportModule
(
"copy_reg"
);
if
(
!
copy_reg
)
if
(
!
copy_reg
)
return
;
return
;
copy_reg_slotnames
=
PyObject_GetAttrString
(
copy_reg
,
"_slotnames"
);
copy_reg_slotnames
=
PyObject_GetAttrString
(
copy_reg
,
"_slotnames"
);
if
(
!
copy_reg_slotnames
)
{
if
(
!
copy_reg_slotnames
)
Py_DECREF
(
copy_reg
);
{
return
;
Py_DECREF
(
copy_reg
);
return
;
}
}
__newobj__
=
PyObject_GetAttrString
(
copy_reg
,
"__newobj__"
);
__newobj__
=
PyObject_GetAttrString
(
copy_reg
,
"__newobj__"
);
if
(
!
__newobj__
)
{
if
(
!
__newobj__
)
Py_DECREF
(
copy_reg
);
{
return
;
Py_DECREF
(
copy_reg
);
return
;
}
}
if
(
!
TimeStamp
)
{
if
(
!
TimeStamp
)
m
=
PyImport_ImportModule
(
"persistent.TimeStamp"
);
{
if
(
!
m
)
m
=
PyImport_ImportModule
(
"persistent.TimeStamp"
);
return
;
if
(
!
m
)
TimeStamp
=
PyObject_GetAttrString
(
m
,
"TimeStamp"
);
return
;
Py_DECREF
(
m
);
TimeStamp
=
PyObject_GetAttrString
(
m
,
"TimeStamp"
);
/* fall through to immediate return on error */
Py_DECREF
(
m
);
/* fall through to immediate return on error */
}
}
}
}
src/persistent/cPickleCache.c
View file @
100a2556
/*****************************************************************************
/*****************************************************************************
Copyright (c) 2001, 2002 Zope Corporation and Contributors.
Copyright (c) 2001, 2002 Zope Corporation and Contributors.
All Rights Reserved.
All Rights Reserved.
...
@@ -10,87 +10,87 @@
...
@@ -10,87 +10,87 @@
WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
FOR A PARTICULAR PURPOSE
FOR A PARTICULAR PURPOSE
****************************************************************************/
****************************************************************************/
/*
/*
Objects are stored under three different regimes:
Objects are stored under three different regimes:
Regime 1: Persistent Classes
Regime 1: Persistent Classes
Persistent Classes are part of ZClasses. They are stored in the
Persistent Classes are part of ZClasses. They are stored in the
self->data dictionary, and are never garbage collected.
self->data dictionary, and are never garbage collected.
The klass_items() method returns a sequence of (oid,object) tuples for
The klass_items() method returns a sequence of (oid,object) tuples for
every Persistent Class, which should make it possible to implement
every Persistent Class, which should make it possible to implement
garbage collection in Python if necessary.
garbage collection in Python if necessary.
Regime 2: Ghost Objects
Regime 2: Ghost Objects
There is no benefit to keeping a ghost object which has no external
There is no benefit to keeping a ghost object which has no external
references, therefore a weak reference scheme is used to ensure that
references, therefore a weak reference scheme is used to ensure that
ghost objects are removed from memory as soon as possible, when the
ghost objects are removed from memory as soon as possible, when the
last external reference is lost.
last external reference is lost.
Ghost objects are stored in the self->data dictionary. Normally a
Ghost objects are stored in the self->data dictionary. Normally a
dictionary keeps a strong reference on its values, however this
dictionary keeps a strong reference on its values, however this
reference count is 'stolen'.
reference count is 'stolen'.
This weak reference scheme leaves a dangling reference, in the
This weak reference scheme leaves a dangling reference, in the
dictionary, when the last external reference is lost. To clean up this
dictionary, when the last external reference is lost. To clean up this
dangling reference the persistent object dealloc function calls
dangling reference the persistent object dealloc function calls
self->cache->_oid_unreferenced(self->oid). The cache looks up the oid
self->cache->_oid_unreferenced(self->oid). The cache looks up the oid
in the dictionary, ensures it points to an object whose reference
in the dictionary, ensures it points to an object whose reference
count is zero, then removes it from the dictionary. Before removing
count is zero, then removes it from the dictionary. Before removing
the object from the dictionary it must temporarily resurrect the
the object from the dictionary it must temporarily resurrect the
object in much the same way that class instances are resurrected
object in much the same way that class instances are resurrected
before their __del__ is called.
before their __del__ is called.
Since ghost objects are stored under a different regime to non-ghost
Since ghost objects are stored under a different regime to non-ghost
objects, an extra ghostify function in cPersistenceAPI replaces
objects, an extra ghostify function in cPersistenceAPI replaces
self->state=GHOST_STATE assignments that were common in other
self->state=GHOST_STATE assignments that were common in other
persistent classes (such as BTrees).
persistent classes (such as BTrees).
Regime 3: Non-Ghost Objects
Regime 3: Non-Ghost Objects
Non-ghost objects are stored in two data structures: the dictionary
Non-ghost objects are stored in two data structures: the dictionary
mapping oids to objects and a doubly-linked list that encodes the
mapping oids to objects and a doubly-linked list that encodes the
order in which the objects were accessed. The dictionary reference is
order in which the objects were accessed. The dictionary reference is
borrowed, as it is for ghosts. The list reference is a new reference;
borrowed, as it is for ghosts. The list reference is a new reference;
the list stores recently used objects, even if they are otherwise
the list stores recently used objects, even if they are otherwise
unreferenced, to avoid loading the object from the database again.
unreferenced, to avoid loading the object from the database again.
The doubly-link-list nodes contain next and previous pointers linking
The doubly-link-list nodes contain next and previous pointers linking
together the cache and all non-ghost persistent objects.
together the cache and all non-ghost persistent objects.
The node embedded in the cache is the home position. On every
The node embedded in the cache is the home position. On every
attribute access a non-ghost object will relink itself just behind the
attribute access a non-ghost object will relink itself just behind the
home position in the ring. Objects accessed least recently will
home position in the ring. Objects accessed least recently will
eventually find themselves positioned after the home position.
eventually find themselves positioned after the home position.
Occasionally other nodes are temporarily inserted in the ring as
Occasionally other nodes are temporarily inserted in the ring as
position markers. The cache contains a ring_lock flag which must be
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
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
the cache assume that all nodes are either his own home node, or nodes
from persistent objects. This assumption is useful during the garbage
from persistent objects. This assumption is useful during the garbage
collection process.
collection process.
The number of non-ghost objects is counted in self->non_ghost_count.
The number of non-ghost objects is counted in self->non_ghost_count.
The garbage collection process consists of traversing the ring, and
The garbage collection process consists of traversing the ring, and
deactivating (that is, turning into a ghost) every object until
deactivating (that is, turning into a ghost) every object until
self->non_ghost_count is down to the target size, or until it
self->non_ghost_count is down to the target size, or until it
reaches the home position again.
reaches the home position again.
Note that objects in the sticky or changed states are still kept in
Note that objects in the sticky or changed states are still kept in
the ring, however they can not be deactivated. The garbage collection
the ring, however they can not be deactivated. The garbage collection
process must skip such objects, rather than deactivating them.
process must skip such objects, rather than deactivating them.
*/
*/
static
char
cPickleCache_doc_string
[]
=
static
char
cPickleCache_doc_string
[]
=
"Defines the PickleCache used by ZODB Connection objects.
\n
"
"Defines the PickleCache used by ZODB Connection objects.
\n
"
"
\n
"
"
\n
"
"$Id$
\n
"
;
"$Id$
\n
"
;
#define DONT_USE_CPERSISTENCECAPI
#define DONT_USE_CPERSISTENCECAPI
#include "cPersistence.h"
#include "cPersistence.h"
...
@@ -99,6 +99,12 @@ static char cPickleCache_doc_string[] =
...
@@ -99,6 +99,12 @@ static char cPickleCache_doc_string[] =
#include <stddef.h>
#include <stddef.h>
#undef Py_FindMethod
#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. */
/* Python string objects to speed lookups; set by module init. */
static
PyObject
*
py__p_changed
;
static
PyObject
*
py__p_changed
;
static
PyObject
*
py__p_deactivate
;
static
PyObject
*
py__p_deactivate
;
...
@@ -111,31 +117,32 @@ static cPersistenceCAPIstruct *capi;
...
@@ -111,31 +117,32 @@ static cPersistenceCAPIstruct *capi;
that layout of this struct is the same as the start of
that layout of this struct is the same as the start of
ccobject_head in cPersistence.c */
ccobject_head in cPersistence.c */
typedef
struct
{
typedef
struct
{
CACHE_HEAD
CACHE_HEAD
int
klass_count
;
/* count of persistent classes */
int
klass_count
;
/* count of persistent classes */
PyObject
*
data
;
/* oid -> object dict */
PyObject
*
data
;
/* oid -> object dict */
PyObject
*
jar
;
/* Connection object */
PyObject
*
jar
;
/* Connection object */
int
cache_size
;
/* target number of items in cache */
int
cache_size
;
/* target number of items in cache */
PY_LONG_LONG
cache_size_bytes
;
/* target total estimated size 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
/* Most of the time the ring contains only:
* one 'home' node from the cache.
* many nodes corresponding to persistent objects
In some cases it is handy to temporarily add other types
* one 'home' node from the cache.
of node into the ring as placeholders. 'ring_lock' is a boolean
In some cases it is handy to temporarily add other types
indicating that someone has already done this. Currently this
of node into the ring as placeholders. 'ring_lock' is a boolean
is only used by the garbage collection code. */
indicating that someone has already done this. Currently this
is only used by the garbage collection code. */
int
ring_lock
;
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
/* 'cache_drain_resistance' controls how quickly the cache size will drop
not drop below the configured size (suitable for most caches). Otherwise,
when it is smaller than the configured size. A value of zero means it will
it will remove cache_non_ghost_count/cache_drain_resistance items from
not drop below the configured size (suitable for most caches). Otherwise,
the cache every time (suitable for rarely used caches, such as those
it will remove cache_non_ghost_count/cache_drain_resistance items from
associated with Zope versions. */
the cache every time (suitable for rarely used caches, such as those
associated with Zope versions. */
int
cache_drain_resistance
;
int
cache_drain_resistance
;
}
ccobject
;
}
ccobject
;
...
@@ -143,224 +150,231 @@ static int cc_ass_sub(ccobject *self, PyObject *key, PyObject *v);
...
@@ -143,224 +150,231 @@ static int cc_ass_sub(ccobject *self, PyObject *key, PyObject *v);
/* ---------------------------------------------------------------- */
/* ---------------------------------------------------------------- */
#define OBJECT_FROM_RING(SELF, HERE) \
#define OBJECT_FROM_RING(SELF, HERE)
\
((cPersistentObject *)(((char *)here) - offsetof(cPersistentObject, ring)))
((cPersistentObject *)(((char *)here) - offsetof(cPersistentObject, ring)))
/* Insert self into the ring, following after. */
/* Insert self into the ring, following after. */
static
void
static
void
insert_after
(
CPersistentRing
*
self
,
CPersistentRing
*
after
)
insert_after
(
CPersistentRing
*
self
,
CPersistentRing
*
after
)
{
{
assert
(
self
!=
NULL
);
assert
(
self
!=
NULL
);
assert
(
after
!=
NULL
);
assert
(
after
!=
NULL
);
self
->
r_prev
=
after
;
self
->
r_prev
=
after
;
self
->
r_next
=
after
->
r_next
;
self
->
r_next
=
after
->
r_next
;
after
->
r_next
->
r_prev
=
self
;
after
->
r_next
->
r_prev
=
self
;
after
->
r_next
=
self
;
after
->
r_next
=
self
;
}
}
/* Remove self from the ring. */
/* Remove self from the ring. */
static
void
static
void
unlink_from_ring
(
CPersistentRing
*
self
)
unlink_from_ring
(
CPersistentRing
*
self
)
{
{
assert
(
self
!=
NULL
);
assert
(
self
!=
NULL
);
self
->
r_prev
->
r_next
=
self
->
r_next
;
self
->
r_prev
->
r_next
=
self
->
r_next
;
self
->
r_next
->
r_prev
=
self
->
r_prev
;
self
->
r_next
->
r_prev
=
self
->
r_prev
;
}
}
static
int
static
int
scan_gc_items
(
ccobject
*
self
,
int
target
,
PY_LONG_LONG
target_bytes
)
scan_gc_items
(
ccobject
*
self
,
int
target
,
PY_LONG_LONG
target_bytes
)
{
{
/* This function must only be called with the ring lock held,
/* This function must only be called with the ring lock held,
because it places non-object placeholders in the ring.
because it places non-object placeholders in the ring.
*/
*/
cPersistentObject
*
object
;
cPersistentObject
*
object
;
CPersistentRing
*
here
;
CPersistentRing
*
here
;
CPersistentRing
before_original_home
;
CPersistentRing
before_original_home
;
int
result
=
-
1
;
/* guilty until proved innocent */
int
result
=
-
1
;
/* guilty until proved innocent */
/* Scan the ring, from least to most recently used, deactivating
/* Scan the ring, from least to most recently used, deactivating
* up-to-date objects, until we either find the ring_home again or
* up-to-date objects, until we either find the ring_home again or
* or we've ghosted enough objects to reach the target size.
* or we've ghosted enough objects to reach the target size.
* Tricky: __getattr__ and __del__ methods can do anything, and in
* Tricky: __getattr__ and __del__ methods can do anything, and in
* particular if we ghostify an object with a __del__ method, that method
* 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
* 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
* ring. Waiting to find ring_home again can thus cause an infinite
* loop (Collector #1208). So before_original_home records the MRU
* loop (Collector #1208). So before_original_home records the MRU
* position we start with, and we stop the scan when we reach that.
* position we start with, and we stop the scan when we reach that.
*/
*/
insert_after
(
&
before_original_home
,
self
->
ring_home
.
r_prev
);
insert_after
(
&
before_original_home
,
self
->
ring_home
.
r_prev
);
here
=
self
->
ring_home
.
r_next
;
/* least recently used object */
here
=
self
->
ring_home
.
r_next
;
/* least recently used object */
while
(
here
!=
&
before_original_home
&&
while
(
here
!=
&
before_original_home
&&
(
self
->
non_ghost_count
>
target
(
self
->
non_ghost_count
>
target
||
(
target_bytes
&&
self
->
total_estimated_size
>
target_bytes
)
||
(
target_bytes
&&
self
->
total_estimated_size
>
target_bytes
)
)
)
)
{
)
assert
(
self
->
ring_lock
);
{
assert
(
here
!=
&
self
->
ring_home
);
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
/* At this point we know that the ring only contains nodes
this because the ring lock is held. We can safely assume
from persistent objects, plus our own home node. We know
the current ring node is a persistent object now we know it
this because the ring lock is held. We can safely assume
is not the home */
the current ring node is a persistent object now we know it
object
=
OBJECT_FROM_RING
(
self
,
here
);
is not the home */
object
=
OBJECT_FROM_RING
(
self
,
here
);
if
(
object
->
state
==
cPersistent_UPTODATE_STATE
)
{
CPersistentRing
placeholder
;
if
(
object
->
state
==
cPersistent_UPTODATE_STATE
)
PyObject
*
method
;
{
PyObject
*
temp
;
CPersistentRing
placeholder
;
int
error_occurred
=
0
;
PyObject
*
method
;
/* deactivate it. This is the main memory saver. */
PyObject
*
temp
;
int
error_occurred
=
0
;
/* Add a placeholder, a dummy node in the ring. We need
/* deactivate it. This is the main memory saver. */
to do this to mark our position in the ring. It is
possible that the PyObject_GetAttr() call below will
/* Add a placeholder, a dummy node in the ring. We need
invoke a __getattr__() hook in Python. Also possible
to do this to mark our position in the ring. It is
that deactivation will lead to a __del__ method call.
possible that the PyObject_GetAttr() call below will
So another thread might run, and mutate the ring as a side
invoke a __getattr__() hook in Python. Also possible
effect of object accesses. There's no predicting then where
that deactivation will lead to a __del__ method call.
in the ring here->next will point after that. The
So another thread might run, and mutate the ring as a side
placeholder won't move as a side effect of calling Python
effect of object accesses. There's no predicting then where
code.
in the ring here->next will point after that. The
*/
placeholder won't move as a side effect of calling Python
insert_after
(
&
placeholder
,
here
);
code.
method
=
PyObject_GetAttr
((
PyObject
*
)
object
,
py__p_deactivate
);
*/
if
(
method
==
NULL
)
insert_after
(
&
placeholder
,
here
);
error_occurred
=
1
;
method
=
PyObject_GetAttr
((
PyObject
*
)
object
,
py__p_deactivate
);
else
{
if
(
method
==
NULL
)
temp
=
PyObject_CallObject
(
method
,
NULL
);
error_occurred
=
1
;
Py_DECREF
(
method
);
else
if
(
temp
==
NULL
)
{
error_occurred
=
1
;
temp
=
PyObject_CallObject
(
method
,
NULL
);
}
Py_DECREF
(
method
);
if
(
temp
==
NULL
)
here
=
placeholder
.
r_next
;
error_occurred
=
1
;
unlink_from_ring
(
&
placeholder
);
}
if
(
error_occurred
)
goto
Done
;
here
=
placeholder
.
r_next
;
unlink_from_ring
(
&
placeholder
);
if
(
error_occurred
)
goto
Done
;
}
}
else
else
here
=
here
->
r_next
;
here
=
here
->
r_next
;
}
}
result
=
0
;
result
=
0
;
Done:
Done:
unlink_from_ring
(
&
before_original_home
);
unlink_from_ring
(
&
before_original_home
);
return
result
;
return
result
;
}
}
static
PyObject
*
static
PyObject
*
lockgc
(
ccobject
*
self
,
int
target_size
,
PY_LONG_LONG
target_size_bytes
)
lockgc
(
ccobject
*
self
,
int
target_size
,
PY_LONG_LONG
target_size_bytes
)
{
{
/* This is thread-safe because of the GIL, and there's nothing
/* This is thread-safe because of the GIL, and there's nothing
* in between checking the ring_lock and acquiring it that calls back
* in between checking the ring_lock and acquiring it that calls back
* into Python.
* into Python.
*/
*/
if
(
self
->
ring_lock
)
{
if
(
self
->
ring_lock
)
Py_INCREF
(
Py_None
);
{
return
Py_None
;
Py_INCREF
(
Py_None
);
return
Py_None
;
}
}
self
->
ring_lock
=
1
;
self
->
ring_lock
=
1
;
if
(
scan_gc_items
(
self
,
target_size
,
target_size_bytes
)
<
0
)
{
if
(
scan_gc_items
(
self
,
target_size
,
target_size_bytes
)
<
0
)
self
->
ring_lock
=
0
;
{
return
NULL
;
self
->
ring_lock
=
0
;
return
NULL
;
}
}
self
->
ring_lock
=
0
;
self
->
ring_lock
=
0
;
Py_INCREF
(
Py_None
);
Py_INCREF
(
Py_None
);
return
Py_None
;
return
Py_None
;
}
}
static
PyObject
*
static
PyObject
*
cc_incrgc
(
ccobject
*
self
,
PyObject
*
args
)
cc_incrgc
(
ccobject
*
self
,
PyObject
*
args
)
{
{
int
obsolete_arg
=
-
999
;
int
obsolete_arg
=
-
999
;
int
starting_size
=
self
->
non_ghost_count
;
int
starting_size
=
self
->
non_ghost_count
;
int
target_size
=
self
->
cache_size
;
int
target_size
=
self
->
cache_size
;
PY_LONG_LONG
target_size_bytes
=
self
->
cache_size_bytes
;
PY_LONG_LONG
target_size_bytes
=
self
->
cache_size_bytes
;
if
(
self
->
cache_drain_resistance
>=
1
)
{
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 */
/* 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
);
int
target_size_2
=
(
starting_size
-
1
if
(
target_size_2
<
target_size
)
-
starting_size
/
self
->
cache_drain_resistance
);
target_size
=
target_size_2
;
if
(
target_size_2
<
target_size
)
target_size
=
target_size_2
;
}
}
if
(
!
PyArg_ParseTuple
(
args
,
"|i:incrgc"
,
&
obsolete_arg
))
if
(
!
PyArg_ParseTuple
(
args
,
"|i:incrgc"
,
&
obsolete_arg
))
return
NULL
;
return
NULL
;
if
(
obsolete_arg
!=
-
999
if
(
obsolete_arg
!=
-
999
&&
&&
(
PyErr_Warn
(
PyExc_DeprecationWarning
,
(
PyErr_Warn
(
PyExc_DeprecationWarning
,
"No argument expected"
)
"No argument expected"
)
<
0
))
<
0
))
return
NULL
;
return
NULL
;
return
lockgc
(
self
,
target_size
,
target_size_bytes
);
return
lockgc
(
self
,
target_size
,
target_size_bytes
);
}
}
static
PyObject
*
static
PyObject
*
cc_full_sweep
(
ccobject
*
self
,
PyObject
*
args
)
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
))
if
(
!
PyArg_ParseTuple
(
args
,
"|i:full_sweep"
,
&
dt
))
return
NULL
;
return
NULL
;
if
(
dt
==
-
999
)
if
(
dt
==
-
999
)
return
lockgc
(
self
,
0
,
0
);
return
lockgc
(
self
,
0
,
0
);
else
else
return
cc_incrgc
(
self
,
args
);
return
cc_incrgc
(
self
,
args
);
}
}
static
PyObject
*
static
PyObject
*
cc_minimize
(
ccobject
*
self
,
PyObject
*
args
)
cc_minimize
(
ccobject
*
self
,
PyObject
*
args
)
{
{
int
ignored
=
-
999
;
int
ignored
=
-
999
;
if
(
!
PyArg_ParseTuple
(
args
,
"|i:minimize"
,
&
ignored
))
if
(
!
PyArg_ParseTuple
(
args
,
"|i:minimize"
,
&
ignored
))
return
NULL
;
return
NULL
;
if
(
ignored
!=
-
999
if
(
ignored
!=
-
999
&&
&&
(
PyErr_Warn
(
PyExc_DeprecationWarning
,
(
PyErr_Warn
(
PyExc_DeprecationWarning
,
"No argument expected"
)
"No argument expected"
)
<
0
))
<
0
))
return
NULL
;
return
NULL
;
return
lockgc
(
self
,
0
,
0
);
return
lockgc
(
self
,
0
,
0
);
}
}
static
int
static
int
_invalidate
(
ccobject
*
self
,
PyObject
*
key
)
_invalidate
(
ccobject
*
self
,
PyObject
*
key
)
{
{
static
PyObject
*
_p_invalidate
=
NULL
;
static
PyObject
*
_p_invalidate
=
NULL
;
PyObject
*
meth
,
*
v
;
PyObject
*
meth
,
*
v
;
v
=
PyDict_GetItem
(
self
->
data
,
key
);
v
=
PyDict_GetItem
(
self
->
data
,
key
);
if
(
v
==
NULL
)
if
(
v
==
NULL
)
return
0
;
return
0
;
if
(
_p_invalidate
==
NULL
)
if
(
_p_invalidate
==
NULL
)
{
{
_p_invalidate
=
PyString_InternFromString
(
"_p_invalidate"
);
_p_invalidate
=
PyString_InternFromString
(
"_p_invalidate"
);
if
(
_p_invalidate
==
NULL
)
if
(
_p_invalidate
==
NULL
)
{
{
/* It doesn't make any sense to ignore this error, but
/* It doesn't make any sense to ignore this error, but
the caller ignores all errors.
the caller ignores all errors.
TODO: and why does it do that? This should be fixed
TODO: and why does it do that? This should be fixed
*/
*/
return
-
1
;
return
-
1
;
}
}
}
}
if
(
v
->
ob_refcnt
<=
1
&&
PyType_Check
(
v
))
{
if
(
v
->
ob_refcnt
<=
1
&&
PyType_Check
(
v
))
{
/* This looks wrong, but it isn't. We use strong references to types
/* This looks wrong, but it isn't. We use strong references to types
because they don't have the ring members.
because they don't have the ring members.
...
@@ -371,13 +385,13 @@ _invalidate(ccobject *self, PyObject *key)
...
@@ -371,13 +385,13 @@ _invalidate(ccobject *self, PyObject *key)
return
PyDict_DelItem
(
self
->
data
,
key
);
return
PyDict_DelItem
(
self
->
data
,
key
);
}
}
meth
=
PyObject_GetAttr
(
v
,
_p_invalidate
);
meth
=
PyObject_GetAttr
(
v
,
_p_invalidate
);
if
(
meth
==
NULL
)
if
(
meth
==
NULL
)
return
-
1
;
return
-
1
;
v
=
PyObject_CallObject
(
meth
,
NULL
);
v
=
PyObject_CallObject
(
meth
,
NULL
);
Py_DECREF
(
meth
);
Py_DECREF
(
meth
);
return
v
==
NULL
?
-
1
:
0
;
return
v
==
NULL
?
-
1
:
0
;
}
}
static
PyObject
*
static
PyObject
*
...
@@ -390,36 +404,39 @@ cc_invalidate(ccobject *self, PyObject *inv)
...
@@ -390,36 +404,39 @@ cc_invalidate(ccobject *self, PyObject *inv)
{
{
while
(
PyDict_Next
(
inv
,
&
i
,
&
key
,
&
v
))
while
(
PyDict_Next
(
inv
,
&
i
,
&
key
,
&
v
))
{
{
if
(
_invalidate
(
self
,
key
)
<
0
)
if
(
_invalidate
(
self
,
key
)
<
0
)
return
NULL
;
return
NULL
;
}
}
PyDict_Clear
(
inv
);
PyDict_Clear
(
inv
);
}
}
else
{
else
{
if
(
PyString_Check
(
inv
))
if
(
PyString_Check
(
inv
))
{
{
if
(
_invalidate
(
self
,
inv
)
<
0
)
if
(
_invalidate
(
self
,
inv
)
<
0
)
return
NULL
;
return
NULL
;
}
}
else
{
else
int
l
,
r
;
{
int
l
,
r
;
l
=
PyObject_Length
(
inv
);
if
(
l
<
0
)
l
=
PyObject_Length
(
inv
);
return
NULL
;
if
(
l
<
0
)
for
(
i
=
l
;
--
i
>=
0
;
)
{
return
NULL
;
key
=
PySequence_GetItem
(
inv
,
i
);
for
(
i
=
l
;
--
i
>=
0
;
)
if
(
!
key
)
{
return
NULL
;
key
=
PySequence_GetItem
(
inv
,
i
);
r
=
_invalidate
(
self
,
key
);
if
(
!
key
)
Py_DECREF
(
key
);
return
NULL
;
r
=
_invalidate
(
self
,
key
);
Py_DECREF
(
key
);
if
(
r
<
0
)
if
(
r
<
0
)
return
NULL
;
return
NULL
;
}
}
/* Dubious: modifying the input may be an unexpected side effect. */
/* Dubious: modifying the input may be an unexpected side effect. */
PySequence_DelSlice
(
inv
,
0
,
l
);
PySequence_DelSlice
(
inv
,
0
,
l
);
}
}
}
}
Py_INCREF
(
Py_None
);
Py_INCREF
(
Py_None
);
return
Py_None
;
return
Py_None
;
...
@@ -428,211 +445,221 @@ cc_invalidate(ccobject *self, PyObject *inv)
...
@@ -428,211 +445,221 @@ cc_invalidate(ccobject *self, PyObject *inv)
static
PyObject
*
static
PyObject
*
cc_get
(
ccobject
*
self
,
PyObject
*
args
)
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
))
if
(
!
PyArg_ParseTuple
(
args
,
"O|O:get"
,
&
key
,
&
d
))
return
NULL
;
return
NULL
;
r
=
PyDict_GetItem
(
self
->
data
,
key
);
r
=
PyDict_GetItem
(
self
->
data
,
key
);
if
(
!
r
)
{
if
(
!
r
)
if
(
d
)
{
r
=
d
;
if
(
d
)
else
r
=
d
;
r
=
Py_None
;
else
r
=
Py_None
;
}
}
Py_INCREF
(
r
);
Py_INCREF
(
r
);
return
r
;
return
r
;
}
}
static
PyObject
*
static
PyObject
*
cc_items
(
ccobject
*
self
)
cc_items
(
ccobject
*
self
)
{
{
return
PyObject_CallMethod
(
self
->
data
,
"items"
,
""
);
return
PyObject_CallMethod
(
self
->
data
,
"items"
,
""
);
}
}
static
PyObject
*
static
PyObject
*
cc_klass_items
(
ccobject
*
self
)
cc_klass_items
(
ccobject
*
self
)
{
{
PyObject
*
l
,
*
k
,
*
v
;
PyObject
*
l
,
*
k
,
*
v
;
Py_ssize_t
p
=
0
;
Py_ssize_t
p
=
0
;
l
=
PyList_New
(
0
);
l
=
PyList_New
(
0
);
if
(
l
==
NULL
)
if
(
l
==
NULL
)
return
NULL
;
return
NULL
;
while
(
PyDict_Next
(
self
->
data
,
&
p
,
&
k
,
&
v
))
{
while
(
PyDict_Next
(
self
->
data
,
&
p
,
&
k
,
&
v
))
if
(
PyType_Check
(
v
))
{
{
v
=
Py_BuildValue
(
"OO"
,
k
,
v
);
if
(
PyType_Check
(
v
))
if
(
v
==
NULL
)
{
{
Py_DECREF
(
l
);
v
=
Py_BuildValue
(
"OO"
,
k
,
v
);
return
NULL
;
if
(
v
==
NULL
)
}
{
if
(
PyList_Append
(
l
,
v
)
<
0
)
{
Py_DECREF
(
l
);
Py_DECREF
(
v
);
return
NULL
;
Py_DECREF
(
l
);
}
return
NULL
;
if
(
PyList_Append
(
l
,
v
)
<
0
)
}
{
Py_DECREF
(
v
);
Py_DECREF
(
v
);
Py_DECREF
(
l
);
return
NULL
;
}
Py_DECREF
(
v
);
}
}
}
}
return
l
;
return
l
;
}
}
static
PyObject
*
static
PyObject
*
cc_debug_info
(
ccobject
*
self
)
cc_debug_info
(
ccobject
*
self
)
{
{
PyObject
*
l
,
*
k
,
*
v
;
PyObject
*
l
,
*
k
,
*
v
;
Py_ssize_t
p
=
0
;
Py_ssize_t
p
=
0
;
l
=
PyList_New
(
0
);
l
=
PyList_New
(
0
);
if
(
l
==
NULL
)
if
(
l
==
NULL
)
return
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
;
if
(
PyList_Append
(
l
,
v
)
<
0
)
while
(
PyDict_Next
(
self
->
data
,
&
p
,
&
k
,
&
v
))
goto
err
;
{
}
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:
err:
Py_DECREF
(
l
);
Py_DECREF
(
l
);
return
NULL
;
return
NULL
;
}
}
static
PyObject
*
static
PyObject
*
cc_lru_items
(
ccobject
*
self
)
cc_lru_items
(
ccobject
*
self
)
{
{
PyObject
*
l
;
PyObject
*
l
;
CPersistentRing
*
here
;
CPersistentRing
*
here
;
if
(
self
->
ring_lock
)
{
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
/* When the ring lock is held, we have no way of know which
placeholders. */
ring nodes belong to persistent objects, and which a
PyErr_SetString
(
PyExc_ValueError
,
placeholders. */
".lru_items() is unavailable during garbage collection"
);
PyErr_SetString
(
PyExc_ValueError
,
return
NULL
;
".lru_items() is unavailable during garbage collection"
);
return
NULL
;
}
}
l
=
PyList_New
(
0
);
l
=
PyList_New
(
0
);
if
(
l
==
NULL
)
if
(
l
==
NULL
)
return
NULL
;
return
NULL
;
here
=
self
->
ring_home
.
r_next
;
here
=
self
->
ring_home
.
r_next
;
while
(
here
!=
&
self
->
ring_home
)
{
while
(
here
!=
&
self
->
ring_home
)
PyObject
*
v
;
{
cPersistentObject
*
object
=
OBJECT_FROM_RING
(
self
,
here
);
PyObject
*
v
;
cPersistentObject
*
object
=
OBJECT_FROM_RING
(
self
,
here
);
if
(
object
==
NULL
)
{
if
(
object
==
NULL
)
Py_DECREF
(
l
);
{
return
NULL
;
Py_DECREF
(
l
);
return
NULL
;
}
}
v
=
Py_BuildValue
(
"OO"
,
object
->
oid
,
object
);
v
=
Py_BuildValue
(
"OO"
,
object
->
oid
,
object
);
if
(
v
==
NULL
)
{
if
(
v
==
NULL
)
Py_DECREF
(
l
);
{
return
NULL
;
Py_DECREF
(
l
);
}
return
NULL
;
if
(
PyList_Append
(
l
,
v
)
<
0
)
{
}
Py_DECREF
(
v
);
if
(
PyList_Append
(
l
,
v
)
<
0
)
Py_DECREF
(
l
);
{
return
NULL
;
Py_DECREF
(
v
);
}
Py_DECREF
(
l
);
Py_DECREF
(
v
);
return
NULL
;
here
=
here
->
r_next
;
}
Py_DECREF
(
v
);
here
=
here
->
r_next
;
}
}
return
l
;
return
l
;
}
}
static
void
static
void
cc_oid_unreferenced
(
ccobject
*
self
,
PyObject
*
oid
)
cc_oid_unreferenced
(
ccobject
*
self
,
PyObject
*
oid
)
{
{
/* This is called by the persistent object deallocation function
/* This is called by the persistent object deallocation function
when the reference count on a persistent object reaches
when the reference count on a persistent object reaches
zero. We need to fix up our dictionary; its reference is now
zero. We need to fix up our dictionary; its reference is now
dangling because we stole its reference count. Be careful to
dangling because we stole its reference count. Be careful to
not release the global interpreter lock until this is
not release the global interpreter lock until this is
complete. */
complete. */
PyObject
*
v
;
PyObject
*
v
;
/* If the cache has been cleared by GC, data will be NULL. */
/* If the cache has been cleared by GC, data will be NULL. */
if
(
!
self
->
data
)
if
(
!
self
->
data
)
return
;
return
;
v
=
PyDict_GetItem
(
self
->
data
,
oid
);
v
=
PyDict_GetItem
(
self
->
data
,
oid
);
assert
(
v
);
assert
(
v
);
assert
(
v
->
ob_refcnt
==
0
);
assert
(
v
->
ob_refcnt
==
0
);
/* Need to be very hairy here because a dictionary is about
/* Need to be very hairy here because a dictionary is about
to decref an already deleted object.
to decref an already deleted object.
*/
*/
#ifdef Py_TRACE_REFS
#ifdef Py_TRACE_REFS
/* This is called from the deallocation function after the
/* This is called from the deallocation function after the
interpreter has untracked the reference. Track it again.
interpreter has untracked the reference. Track it again.
*/
*/
_Py_NewReference
(
v
);
_Py_NewReference
(
v
);
/* Don't increment total refcount as a result of the
/* Don't increment total refcount as a result of the
shenanigans played in this function. The _Py_NewReference()
shenanigans played in this function. The _Py_NewReference()
call above creates artificial references to v.
call above creates artificial references to v.
*/
*/
_Py_RefTotal
--
;
_Py_RefTotal
--
;
assert
(
v
->
ob_type
);
assert
(
v
->
ob_type
);
#else
#else
Py_INCREF
(
v
);
Py_INCREF
(
v
);
#endif
#endif
assert
(
v
->
ob_refcnt
==
1
);
assert
(
v
->
ob_refcnt
==
1
);
/* Incremement the refcount again, because delitem is going to
/* Incremement the refcount again, because delitem is going to
DECREF it. If it's refcount reached zero again, we'd call back to
DECREF it. If it's refcount reached zero again, we'd call back to
the dealloc function that called us.
the dealloc function that called us.
*/
*/
Py_INCREF
(
v
);
Py_INCREF
(
v
);
/* TODO: Should we call _Py_ForgetReference() on error exit? */
/* TODO: Should we call _Py_ForgetReference() on error exit? */
if
(
PyDict_DelItem
(
self
->
data
,
oid
)
<
0
)
if
(
PyDict_DelItem
(
self
->
data
,
oid
)
<
0
)
return
;
return
;
Py_DECREF
((
ccobject
*
)((
cPersistentObject
*
)
v
)
->
cache
);
Py_DECREF
((
ccobject
*
)((
cPersistentObject
*
)
v
)
->
cache
);
((
cPersistentObject
*
)
v
)
->
cache
=
NULL
;
((
cPersistentObject
*
)
v
)
->
cache
=
NULL
;
assert
(
v
->
ob_refcnt
==
1
);
assert
(
v
->
ob_refcnt
==
1
);
/* Undo the temporary resurrection.
/* Undo the temporary resurrection.
Don't DECREF the object, because this function is called from
Don't DECREF the object, because this function is called from
the object's dealloc function. If the refcnt reaches zero, it
the object's dealloc function. If the refcnt reaches zero, it
will all be invoked recursively.
will all be invoked recursively.
*/
*/
_Py_ForgetReference
(
v
);
_Py_ForgetReference
(
v
);
}
}
static
PyObject
*
static
PyObject
*
cc_ringlen
(
ccobject
*
self
)
cc_ringlen
(
ccobject
*
self
)
{
{
CPersistentRing
*
here
;
CPersistentRing
*
here
;
int
c
=
0
;
int
c
=
0
;
for
(
here
=
self
->
ring_home
.
r_next
;
here
!=
&
self
->
ring_home
;
for
(
here
=
self
->
ring_home
.
r_next
;
here
!=
&
self
->
ring_home
;
here
=
here
->
r_next
)
here
=
here
->
r_next
)
c
++
;
c
++
;
return
PyInt_FromLong
(
c
);
return
PyInt_FromLong
(
c
);
}
}
static
PyObject
*
static
PyObject
*
...
@@ -645,428 +672,464 @@ cc_update_object_size_estimation(ccobject *self, PyObject *args)
...
@@ -645,428 +672,464 @@ cc_update_object_size_estimation(ccobject *self, PyObject *args)
return
NULL
;
return
NULL
;
/* Note: reference borrowed */
/* Note: reference borrowed */
v
=
(
cPersistentObject
*
)
PyDict_GetItem
(
self
->
data
,
oid
);
v
=
(
cPersistentObject
*
)
PyDict_GetItem
(
self
->
data
,
oid
);
if
(
v
)
{
if
(
v
)
/* we know this object -- update our "total_size_estimation"
{
we must only update when the object is in the ring
/* 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)
*/
*/
/* 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
;
Py_RETURN_NONE
;
}
}
static
struct
PyMethodDef
cc_methods
[]
=
{
static
struct
PyMethodDef
cc_methods
[]
=
{
{
"items"
,
(
PyCFunction
)
cc_items
,
METH_NOARGS
,
{
"items"
,
(
PyCFunction
)
cc_items
,
METH_NOARGS
,
"Return list of oid, object pairs for all items in cache."
},
"Return list of oid, object pairs for all items in cache."
},
{
"lru_items"
,
(
PyCFunction
)
cc_lru_items
,
METH_NOARGS
,
{
"lru_items"
,
(
PyCFunction
)
cc_lru_items
,
METH_NOARGS
,
"List (oid, object) pairs from the lru list, as 2-tuples."
},
"List (oid, object) pairs from the lru list, as 2-tuples."
},
{
"klass_items"
,
(
PyCFunction
)
cc_klass_items
,
METH_NOARGS
,
{
"klass_items"
,
(
PyCFunction
)
cc_klass_items
,
METH_NOARGS
,
"List (oid, object) pairs of cached persistent classes."
},
"List (oid, object) pairs of cached persistent classes."
},
{
"full_sweep"
,
(
PyCFunction
)
cc_full_sweep
,
METH_VARARGS
,
{
"full_sweep"
,
(
PyCFunction
)
cc_full_sweep
,
METH_VARARGS
,
"full_sweep() -- Perform a full sweep of the cache."
},
"full_sweep() -- Perform a full sweep of the cache."
},
{
"minimize"
,
(
PyCFunction
)
cc_minimize
,
METH_VARARGS
,
{
"minimize"
,
(
PyCFunction
)
cc_minimize
,
METH_VARARGS
,
"minimize([ignored]) -- Remove as many objects as possible
\n\n
"
"minimize([ignored]) -- Remove as many objects as possible
\n\n
"
"Ghostify all objects that are not modified. Takes an optional
\n
"
"Ghostify all objects that are not modified. Takes an optional
\n
"
"argument, but ignores it."
},
"argument, but ignores it."
},
{
"incrgc"
,
(
PyCFunction
)
cc_incrgc
,
METH_VARARGS
,
{
"incrgc"
,
(
PyCFunction
)
cc_incrgc
,
METH_VARARGS
,
"incrgc() -- Perform incremental garbage collection
\n\n
"
"incrgc() -- Perform incremental garbage collection
\n\n
"
"This method had been depricated!"
"This method had been depricated!"
"Some other implementations support an optional parameter 'n' which
\n
"
"Some other implementations support an optional parameter 'n' which
\n
"
"indicates a repetition count; this value is ignored."
},
"indicates a repetition count; this value is ignored."
},
{
"invalidate"
,
(
PyCFunction
)
cc_invalidate
,
METH_O
,
{
"invalidate"
,
(
PyCFunction
)
cc_invalidate
,
METH_O
,
"invalidate(oids) -- invalidate one, many, or all ids"
},
"invalidate(oids) -- invalidate one, many, or all ids"
},
{
"get"
,
(
PyCFunction
)
cc_get
,
METH_VARARGS
,
{
"get"
,
(
PyCFunction
)
cc_get
,
METH_VARARGS
,
"get(key [, default]) -- get an item, or a default"
},
"get(key [, default]) -- get an item, or a default"
},
{
"ringlen"
,
(
PyCFunction
)
cc_ringlen
,
METH_NOARGS
,
{
"ringlen"
,
(
PyCFunction
)
cc_ringlen
,
METH_NOARGS
,
"ringlen() -- Returns number of non-ghost items in cache."
},
"ringlen() -- Returns number of non-ghost items in cache."
},
{
"debug_info"
,
(
PyCFunction
)
cc_debug_info
,
METH_NOARGS
,
{
"debug_info"
,
(
PyCFunction
)
cc_debug_info
,
METH_NOARGS
,
"debug_info() -- Returns debugging data about objects in the cache."
},
"debug_info() -- Returns debugging data about objects in the cache."
},
{
"update_object_size_estimation"
,
{
"update_object_size_estimation"
,
(
PyCFunction
)
cc_update_object_size_estimation
,
(
PyCFunction
)
cc_update_object_size_estimation
,
METH_VARARGS
,
METH_VARARGS
,
"update_object_size_estimation(oid, new_size) -- update the caches size estimation for *oid* (if this is known to the cache)."
},
"update_object_size_estimation(oid, new_size) -- "
{
NULL
,
NULL
}
/* sentinel */
"update the caches size estimation for *oid* "
"(if this is known to the cache)."
},
{
NULL
,
NULL
}
/* sentinel */
};
};
static
int
static
int
cc_init
(
ccobject
*
self
,
PyObject
*
args
,
PyObject
*
kwds
)
cc_init
(
ccobject
*
self
,
PyObject
*
args
,
PyObject
*
kwds
)
{
{
int
cache_size
=
100
;
int
cache_size
=
100
;
PY_LONG_LONG
cache_size_bytes
=
0
;
PY_LONG_LONG
cache_size_bytes
=
0
;
PyObject
*
jar
;
PyObject
*
jar
;
if
(
!
PyArg_ParseTuple
(
args
,
"O|iL"
,
&
jar
,
&
cache_size
,
&
cache_size_bytes
))
if
(
!
PyArg_ParseTuple
(
args
,
"O|iL"
,
&
jar
,
&
cache_size
,
&
cache_size_bytes
))
return
-
1
;
return
-
1
;
self
->
jar
=
NULL
;
self
->
jar
=
NULL
;
self
->
data
=
PyDict_New
();
self
->
data
=
PyDict_New
();
if
(
self
->
data
==
NULL
)
{
if
(
self
->
data
==
NULL
)
Py_DECREF
(
self
);
{
return
-
1
;
Py_DECREF
(
self
);
return
-
1
;
}
}
/* Untrack the dict mapping oids to objects.
/* Untrack the dict mapping oids to objects.
The dict contains uncounted references to ghost objects, so it
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
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.
referents that refcounts, it will die with an assertion failure.
When the cache participates in GC, it will need to traverse the
When the cache participates in GC, it will need to traverse the
objects in the doubly-linked list, which will account for all the
objects in the doubly-linked list, which will account for all the
non-ghost objects.
non-ghost objects.
*/
*/
PyObject_GC_UnTrack
((
void
*
)
self
->
data
);
PyObject_GC_UnTrack
((
void
*
)
self
->
data
);
self
->
jar
=
jar
;
self
->
jar
=
jar
;
Py_INCREF
(
jar
);
Py_INCREF
(
jar
);
self
->
cache_size
=
cache_size
;
self
->
cache_size
=
cache_size
;
self
->
cache_size_bytes
=
cache_size_bytes
;
self
->
cache_size_bytes
=
cache_size_bytes
;
self
->
non_ghost_count
=
0
;
self
->
non_ghost_count
=
0
;
self
->
total_estimated_size
=
0
;
self
->
total_estimated_size
=
0
;
self
->
klass_count
=
0
;
self
->
klass_count
=
0
;
self
->
cache_drain_resistance
=
0
;
self
->
cache_drain_resistance
=
0
;
self
->
ring_lock
=
0
;
self
->
ring_lock
=
0
;
self
->
ring_home
.
r_next
=
&
self
->
ring_home
;
self
->
ring_home
.
r_next
=
&
self
->
ring_home
;
self
->
ring_home
.
r_prev
=
&
self
->
ring_home
;
self
->
ring_home
.
r_prev
=
&
self
->
ring_home
;
return
0
;
return
0
;
}
}
static
void
static
void
cc_dealloc
(
ccobject
*
self
)
cc_dealloc
(
ccobject
*
self
)
{
{
Py_XDECREF
(
self
->
data
);
Py_XDECREF
(
self
->
data
);
Py_XDECREF
(
self
->
jar
);
Py_XDECREF
(
self
->
jar
);
PyObject_GC_Del
(
self
);
PyObject_GC_Del
(
self
);
}
}
static
int
static
int
cc_clear
(
ccobject
*
self
)
cc_clear
(
ccobject
*
self
)
{
{
Py_ssize_t
pos
=
0
;
Py_ssize_t
pos
=
0
;
PyObject
*
k
,
*
v
;
PyObject
*
k
,
*
v
;
/* Clearing the cache is delicate.
/* Clearing the cache is delicate.
A non-ghost object will show up in the ring and in the dict. If
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
we deallocating the dict before clearing the ring, the GC will
decref each object in the dict. Since the dict references are
decref each object in the dict. Since the dict references are
uncounted, this will lead to objects having negative refcounts.
uncounted, this will lead to objects having negative refcounts.
Freeing the non-ghost objects should eliminate many objects from
Freeing the non-ghost objects should eliminate many objects from
the cache, but there may still be ghost objects left. It's
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
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
clear those out of the dict, too. We accomplish that by replacing
all the ghost objects with None.
all the ghost objects with None.
*/
*/
/* We don't need to lock the ring, because the cache is unreachable.
/* We don't need to lock the ring, because the cache is unreachable.
It should be impossible for anyone to be modifying the cache.
It should be impossible for anyone to be modifying the cache.
*/
*/
assert
(
!
self
->
ring_lock
);
assert
(
!
self
->
ring_lock
);
while
(
self
->
ring_home
.
r_next
!=
&
self
->
ring_home
)
{
while
(
self
->
ring_home
.
r_next
!=
&
self
->
ring_home
)
CPersistentRing
*
here
=
self
->
ring_home
.
r_next
;
{
cPersistentObject
*
o
=
OBJECT_FROM_RING
(
self
,
here
);
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
(
o
->
cache
)
if
(
PyDict_DelItem
(
self
->
data
,
o
->
oid
)
<
0
)
{
return
-
1
;
Py_INCREF
(
o
);
/* account for uncounted reference */
}
if
(
PyDict_DelItem
(
self
->
data
,
o
->
oid
)
<
0
)
o
->
cache
=
NULL
;
return
-
1
;
Py_DECREF
(
self
);
}
self
->
ring_home
.
r_next
=
here
->
r_next
;
o
->
cache
=
NULL
;
o
->
ring
.
r_prev
=
NULL
;
Py_DECREF
(
self
);
o
->
ring
.
r_next
=
NULL
;
self
->
ring_home
.
r_next
=
here
->
r_next
;
Py_DECREF
(
o
);
o
->
ring
.
r_prev
=
NULL
;
here
=
here
->
r_next
;
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
))
{
while
(
PyDict_Next
(
self
->
data
,
&
pos
,
&
k
,
&
v
))
Py_INCREF
(
v
);
{
if
(
PyDict_SetItem
(
self
->
data
,
k
,
Py_None
)
<
0
)
Py_INCREF
(
v
);
return
-
1
;
if
(
PyDict_SetItem
(
self
->
data
,
k
,
Py_None
)
<
0
)
return
-
1
;
}
}
Py_XDECREF
(
self
->
data
);
Py_XDECREF
(
self
->
data
);
self
->
data
=
NULL
;
self
->
data
=
NULL
;
self
->
jar
=
NULL
;
self
->
jar
=
NULL
;
return
0
;
return
0
;
}
}
static
int
static
int
cc_traverse
(
ccobject
*
self
,
visitproc
visit
,
void
*
arg
)
cc_traverse
(
ccobject
*
self
,
visitproc
visit
,
void
*
arg
)
{
{
int
err
;
int
err
;
CPersistentRing
*
here
;
CPersistentRing
*
here
;
/* If we're in the midst of cleaning up old objects, the ring contains
/* 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
* assorted junk we must not pass on to the visit() callback. This
* should be rare (our cleanup code would need to have called back
* should be rare (our cleanup code would need to have called back
* into Python, which in turn triggered Python's gc). When it happens,
* 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
* simply don't chase any pointers. The cache will appear to be a
* source of external references then, and at worst we miss cleaning
* source of external references then, and at worst we miss cleaning
* up a dead cycle until the next time Python's gc runs.
* up a dead cycle until the next time Python's gc runs.
*/
*/
if
(
self
->
ring_lock
)
if
(
self
->
ring_lock
)
return
0
;
return
0
;
#define VISIT(SLOT) \
if (SLOT) { \
err = visit((PyObject *)(SLOT), arg); \
if (err) \
return err; \
}
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.
here
=
self
->
ring_home
.
r_next
;
In that case, there is no ring.
*/
if
(
!
here
)
return
0
;
while
(
here
!=
&
self
->
ring_home
)
{
/* It is possible that an object is traversed after it is cleared.
cPersistentObject
*
o
=
OBJECT_FROM_RING
(
self
,
here
);
In that case, there is no ring.
VISIT
(
o
);
*/
here
=
here
->
r_next
;
if
(
!
here
)
return
0
;
while
(
here
!=
&
self
->
ring_home
)
{
cPersistentObject
*
o
=
OBJECT_FROM_RING
(
self
,
here
);
VISIT
(
o
);
here
=
here
->
r_next
;
}
}
#undef VISIT
#undef VISIT
return
0
;
return
0
;
}
}
static
in
t
static
Py_ssize_
t
cc_length
(
ccobject
*
self
)
cc_length
(
ccobject
*
self
)
{
{
return
PyObject_Length
(
self
->
data
);
return
PyObject_Length
(
self
->
data
);
}
}
static
PyObject
*
static
PyObject
*
cc_subscript
(
ccobject
*
self
,
PyObject
*
key
)
cc_subscript
(
ccobject
*
self
,
PyObject
*
key
)
{
{
PyObject
*
r
;
PyObject
*
r
;
r
=
PyDict_GetItem
(
self
->
data
,
key
);
r
=
PyDict_GetItem
(
self
->
data
,
key
);
if
(
r
==
NULL
)
{
if
(
r
==
NULL
)
PyErr_SetObject
(
PyExc_KeyError
,
key
);
{
return
NULL
;
PyErr_SetObject
(
PyExc_KeyError
,
key
);
return
NULL
;
}
}
Py_INCREF
(
r
);
Py_INCREF
(
r
);
return
r
;
return
r
;
}
}
static
int
static
int
cc_add_item
(
ccobject
*
self
,
PyObject
*
key
,
PyObject
*
v
)
cc_add_item
(
ccobject
*
self
,
PyObject
*
key
,
PyObject
*
v
)
{
{
int
result
;
int
result
;
PyObject
*
oid
,
*
object_again
,
*
jar
;
PyObject
*
oid
,
*
object_again
,
*
jar
;
cPersistentObject
*
p
;
cPersistentObject
*
p
;
/* Sanity check the value given to make sure it is allowed in the cache */
/* Sanity check the value given to make sure it is allowed in the cache */
if
(
PyType_Check
(
v
))
{
if
(
PyType_Check
(
v
))
/* Its a persistent class, such as a ZClass. Thats ok. */
{
/* Its a persistent class, such as a ZClass. Thats ok. */
}
}
else
if
(
v
->
ob_type
->
tp_basicsize
<
sizeof
(
cPersistentObject
))
{
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,
/* If it's not an instance of a persistent class, (ie Python
etc), report an error.
classes that derive from persistent.Persistent, BTrees,
etc), report an error.
TODO: checking sizeof() seems a poor test.
*/
TODO: checking sizeof() seems a poor test.
PyErr_SetString
(
PyExc_TypeError
,
*/
"Cache values must be persistent objects."
);
PyErr_SetString
(
PyExc_TypeError
,
return
-
1
;
"Cache values must be persistent objects."
);
return
-
1
;
}
}
/* Can't access v->oid directly because the object might be a
/* Can't access v->oid directly because the object might be a
* persistent class.
* persistent class.
*/
*/
oid
=
PyObject_GetAttr
(
v
,
py__p_oid
);
oid
=
PyObject_GetAttr
(
v
,
py__p_oid
);
if
(
oid
==
NULL
)
if
(
oid
==
NULL
)
return
-
1
;
return
-
1
;
if
(
!
PyString_Check
(
oid
))
{
if
(
!
PyString_Check
(
oid
))
PyErr_Format
(
PyExc_TypeError
,
{
"Cached object oid must be a string, not a %s"
,
PyErr_Format
(
PyExc_TypeError
,
oid
->
ob_type
->
tp_name
);
"Cached object oid must be a string, not a %s"
,
return
-
1
;
oid
->
ob_type
->
tp_name
);
return
-
1
;
}
}
/* we know they are both strings.
/* we know they are both strings.
* now check if they are the same string.
* now check if they are the same string.
*/
*/
result
=
PyObject_Compare
(
key
,
oid
);
result
=
PyObject_Compare
(
key
,
oid
);
if
(
PyErr_Occurred
())
{
if
(
PyErr_Occurred
())
Py_DECREF
(
oid
);
{
return
-
1
;
Py_DECREF
(
oid
);
return
-
1
;
}
}
Py_DECREF
(
oid
);
Py_DECREF
(
oid
);
if
(
result
)
{
if
(
result
)
PyErr_SetString
(
PyExc_ValueError
,
"Cache key does not match oid"
);
{
return
-
1
;
PyErr_SetString
(
PyExc_ValueError
,
"Cache key does not match oid"
);
return
-
1
;
}
}
/* useful sanity check, but not strictly an invariant of this class */
/* useful sanity check, but not strictly an invariant of this class */
jar
=
PyObject_GetAttr
(
v
,
py__p_jar
);
jar
=
PyObject_GetAttr
(
v
,
py__p_jar
);
if
(
jar
==
NULL
)
if
(
jar
==
NULL
)
return
-
1
;
return
-
1
;
if
(
jar
==
Py_None
)
{
if
(
jar
==
Py_None
)
Py_DECREF
(
jar
);
{
PyErr_SetString
(
PyExc_ValueError
,
Py_DECREF
(
jar
);
"Cached object jar missing"
);
PyErr_SetString
(
PyExc_ValueError
,
return
-
1
;
"Cached object jar missing"
);
return
-
1
;
}
}
Py_DECREF
(
jar
);
Py_DECREF
(
jar
);
object_again
=
PyDict_GetItem
(
self
->
data
,
key
);
object_again
=
PyDict_GetItem
(
self
->
data
,
key
);
if
(
object_again
)
{
if
(
object_again
)
if
(
object_again
!=
v
)
{
{
PyErr_SetString
(
PyExc_ValueError
,
if
(
object_again
!=
v
)
"A different object already has the same oid"
);
{
return
-
1
;
PyErr_SetString
(
PyExc_ValueError
,
}
else
{
"A different object already has the same oid"
);
/* re-register under the same oid - no work needed */
return
-
1
;
return
0
;
}
}
else
{
/* re-register under the same oid - no work needed */
return
0
;
}
}
}
if
(
PyType_Check
(
v
))
{
if
(
PyType_Check
(
v
))
if
(
PyDict_SetItem
(
self
->
data
,
key
,
v
)
<
0
)
{
return
-
1
;
if
(
PyDict_SetItem
(
self
->
data
,
key
,
v
)
<
0
)
self
->
klass_count
++
;
return
-
1
;
return
0
;
self
->
klass_count
++
;
}
else
{
return
0
;
PerCache
*
cache
=
((
cPersistentObject
*
)
v
)
->
cache
;
}
if
(
cache
)
{
else
if
(
cache
!=
(
PerCache
*
)
self
)
{
/* This object is already in a different cache. */
PerCache
*
cache
=
((
cPersistentObject
*
)
v
)
->
cache
;
PyErr_SetString
(
PyExc_ValueError
,
if
(
cache
)
"Cache values may only be in one cache."
);
{
return
-
1
;
if
(
cache
!=
(
PerCache
*
)
self
)
}
/* This object is already in a different cache. */
/* else:
PyErr_SetString
(
PyExc_ValueError
,
"Cache values may only be in one cache."
);
This object is already one of ours, which is ok. It
return
-
1
;
would be very strange if someone was trying to register
}
the same object under a different key.
/* 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
)
if
(
PyDict_SetItem
(
self
->
data
,
key
,
v
)
<
0
)
return
-
1
;
return
-
1
;
/* the dict should have a borrowed reference */
/* the dict should have a borrowed reference */
Py_DECREF
(
v
);
Py_DECREF
(
v
);
p
=
(
cPersistentObject
*
)
v
;
p
=
(
cPersistentObject
*
)
v
;
Py_INCREF
(
self
);
Py_INCREF
(
self
);
p
->
cache
=
(
PerCache
*
)
self
;
p
->
cache
=
(
PerCache
*
)
self
;
if
(
p
->
state
>=
0
)
{
if
(
p
->
state
>=
0
)
/* insert this non-ghost object into the ring just
{
behind the home position. */
/* insert this non-ghost object into the ring just
self
->
non_ghost_count
++
;
behind the home position. */
ring_add
(
&
self
->
ring_home
,
&
p
->
ring
);
self
->
non_ghost_count
++
;
/* this list should have a new reference to the object */
ring_add
(
&
self
->
ring_home
,
&
p
->
ring
);
Py_INCREF
(
v
);
/* this list should have a new reference to the object */
Py_INCREF
(
v
);
}
}
return
0
;
return
0
;
}
}
static
int
static
int
cc_del_item
(
ccobject
*
self
,
PyObject
*
key
)
cc_del_item
(
ccobject
*
self
,
PyObject
*
key
)
{
{
PyObject
*
v
;
PyObject
*
v
;
cPersistentObject
*
p
;
cPersistentObject
*
p
;
/* unlink this item from the ring */
/* unlink this item from the ring */
v
=
PyDict_GetItem
(
self
->
data
,
key
);
v
=
PyDict_GetItem
(
self
->
data
,
key
);
if
(
v
==
NULL
)
{
if
(
v
==
NULL
)
PyErr_SetObject
(
PyExc_KeyError
,
key
);
{
return
-
1
;
PyErr_SetObject
(
PyExc_KeyError
,
key
);
return
-
1
;
}
}
if
(
PyType_Check
(
v
))
{
if
(
PyType_Check
(
v
))
self
->
klass_count
--
;
{
}
else
{
self
->
klass_count
--
;
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
;
}
}
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
)
{
Py_DECREF
((
PyObject
*
)
p
->
cache
);
PyErr_SetString
(
PyExc_RuntimeError
,
p
->
cache
=
NULL
;
"unexpectedly couldn't remove key in cc_ass_sub"
);
return
-
1
;
}
}
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
static
int
cc_ass_sub
(
ccobject
*
self
,
PyObject
*
key
,
PyObject
*
v
)
cc_ass_sub
(
ccobject
*
self
,
PyObject
*
key
,
PyObject
*
v
)
{
{
if
(
!
PyString_Check
(
key
))
{
if
(
!
PyString_Check
(
key
))
PyErr_Format
(
PyExc_TypeError
,
{
"cPickleCache key must be a string, not a %s"
,
PyErr_Format
(
PyExc_TypeError
,
key
->
ob_type
->
tp_name
);
"cPickleCache key must be a string, not a %s"
,
return
-
1
;
key
->
ob_type
->
tp_name
);
return
-
1
;
}
}
if
(
v
)
if
(
v
)
return
cc_add_item
(
self
,
key
,
v
);
return
cc_add_item
(
self
,
key
,
v
);
else
else
return
cc_del_item
(
self
,
key
);
return
cc_del_item
(
self
,
key
);
}
}
static
PyMappingMethods
cc_as_mapping
=
{
static
PyMappingMethods
cc_as_mapping
=
(
inquiry
)
cc_length
,
/*mp_length*/
{
(
binaryfunc
)
cc_subscript
,
/*mp_subscript*/
(
lenfunc
)
cc_length
,
/*mp_length*/
(
objobjargproc
)
cc_ass_sub
,
/*mp_ass_subscript*/
(
binaryfunc
)
cc_subscript
,
/*mp_subscript*/
};
(
objobjargproc
)
cc_ass_sub
,
/*mp_ass_subscript*/
};
static
PyObject
*
static
PyObject
*
cc_cache_data
(
ccobject
*
self
,
void
*
context
)
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
},
{
"cache_data"
,
(
getter
)
cc_cache_data
},
{
NULL
}
{
NULL
}
};
};
static
PyMemberDef
cc_members
[]
=
{
static
PyMemberDef
cc_members
[]
=
{
{
"cache_size"
,
T_INT
,
offsetof
(
ccobject
,
cache_size
)},
{
"cache_size"
,
T_INT
,
offsetof
(
ccobject
,
cache_size
)},
{
"cache_size_bytes"
,
T_LONG
,
offsetof
(
ccobject
,
cache_size_bytes
)},
{
"cache_size_bytes"
,
T_LONG
,
offsetof
(
ccobject
,
cache_size_bytes
)},
{
"total_estimated_size"
,
T_LONG
,
offsetof
(
ccobject
,
total_estimated_size
),
{
"total_estimated_size"
,
T_LONG
,
offsetof
(
ccobject
,
total_estimated_size
),
RO
},
RO
},
{
"cache_drain_resistance"
,
T_INT
,
{
"cache_drain_resistance"
,
T_INT
,
offsetof
(
ccobject
,
cache_drain_resistance
)},
offsetof
(
ccobject
,
cache_drain_resistance
)},
{
"cache_non_ghost_count"
,
T_INT
,
offsetof
(
ccobject
,
non_ghost_count
),
RO
},
{
"cache_non_ghost_count"
,
T_INT
,
offsetof
(
ccobject
,
non_ghost_count
),
RO
},
{
"cache_klass_count"
,
T_INT
,
offsetof
(
ccobject
,
klass_count
),
RO
},
{
"cache_klass_count"
,
T_INT
,
offsetof
(
ccobject
,
klass_count
),
RO
},
{
NULL
}
{
NULL
}
};
};
/* This module is compiled as a shared library. Some compilers don't
/* This module is compiled as a shared library. Some compilers don't
...
@@ -1079,82 +1142,83 @@ static PyMemberDef cc_members[] = {
...
@@ -1079,82 +1142,83 @@ static PyMemberDef cc_members[] = {
#define DEFERRED_ADDRESS(ADDR) 0
#define DEFERRED_ADDRESS(ADDR) 0
static
PyTypeObject
Cctype
=
{
static
PyTypeObject
Cctype
=
{
PyObject_HEAD_INIT
(
DEFERRED_ADDRESS
(
&
PyType_Type
))
PyObject_HEAD_INIT
(
DEFERRED_ADDRESS
(
&
PyType_Type
))
0
,
/* ob_size */
0
,
/* ob_size */
"persistent.PickleCache"
,
/* tp_name */
"persistent.PickleCache"
,
/* tp_name */
sizeof
(
ccobject
),
/* tp_basicsize */
sizeof
(
ccobject
),
/* tp_basicsize */
0
,
/* tp_itemsize */
0
,
/* tp_itemsize */
(
destructor
)
cc_dealloc
,
/* tp_dealloc */
(
destructor
)
cc_dealloc
,
/* tp_dealloc */
0
,
/* tp_print */
0
,
/* tp_print */
0
,
/* tp_getattr */
0
,
/* tp_getattr */
0
,
/* tp_setattr */
0
,
/* tp_setattr */
0
,
/* tp_compare */
0
,
/* tp_compare */
0
,
/* tp_repr */
0
,
/* tp_repr */
0
,
/* tp_as_number */
0
,
/* tp_as_number */
0
,
/* tp_as_sequence */
0
,
/* tp_as_sequence */
&
cc_as_mapping
,
/* tp_as_mapping */
&
cc_as_mapping
,
/* tp_as_mapping */
0
,
/* tp_hash */
0
,
/* tp_hash */
0
,
/* tp_call */
0
,
/* tp_call */
0
,
/* tp_str */
0
,
/* tp_str */
0
,
/* tp_getattro */
0
,
/* tp_getattro */
0
,
/* tp_setattro */
0
,
/* tp_setattro */
0
,
/* tp_as_buffer */
0
,
/* tp_as_buffer */
Py_TPFLAGS_DEFAULT
|
Py_TPFLAGS_BASETYPE
|
Py_TPFLAGS_HAVE_GC
,
Py_TPFLAGS_DEFAULT
|
Py_TPFLAGS_BASETYPE
|
Py_TPFLAGS_HAVE_GC
,
/* tp_flags */
/* tp_flags */
0
,
/* tp_doc */
0
,
/* tp_doc */
(
traverseproc
)
cc_traverse
,
/* tp_traverse */
(
traverseproc
)
cc_traverse
,
/* tp_traverse */
(
inquiry
)
cc_clear
,
/* tp_clear */
(
inquiry
)
cc_clear
,
/* tp_clear */
0
,
/* tp_richcompare */
0
,
/* tp_richcompare */
0
,
/* tp_weaklistoffset */
0
,
/* tp_weaklistoffset */
0
,
/* tp_iter */
0
,
/* tp_iter */
0
,
/* tp_iternext */
0
,
/* tp_iternext */
cc_methods
,
/* tp_methods */
cc_methods
,
/* tp_methods */
cc_members
,
/* tp_members */
cc_members
,
/* tp_members */
cc_getsets
,
/* tp_getset */
cc_getsets
,
/* tp_getset */
0
,
/* tp_base */
0
,
/* tp_base */
0
,
/* tp_dict */
0
,
/* tp_dict */
0
,
/* tp_descr_get */
0
,
/* tp_descr_get */
0
,
/* tp_descr_set */
0
,
/* tp_descr_set */
0
,
/* tp_dictoffset */
0
,
/* tp_dictoffset */
(
initproc
)
cc_init
,
/* tp_init */
(
initproc
)
cc_init
,
/* tp_init */
};
};
void
void
initcPickleCache
(
void
)
initcPickleCache
(
void
)
{
{
PyObject
*
m
;
PyObject
*
m
;
Cctype
.
ob_type
=
&
PyType_Type
;
Cctype
.
ob_type
=
&
PyType_Type
;
Cctype
.
tp_new
=
&
PyType_GenericNew
;
Cctype
.
tp_new
=
&
PyType_GenericNew
;
if
(
PyType_Ready
(
&
Cctype
)
<
0
)
{
if
(
PyType_Ready
(
&
Cctype
)
<
0
)
return
;
{
return
;
}
}
m
=
Py_InitModule3
(
"cPickleCache"
,
NULL
,
cPickleCache_doc_string
);
m
=
Py_InitModule3
(
"cPickleCache"
,
NULL
,
cPickleCache_doc_string
);
capi
=
(
cPersistenceCAPIstruct
*
)
PyCObject_Import
(
capi
=
(
cPersistenceCAPIstruct
*
)
PyCObject_Import
(
"persistent.cPersistence"
,
"CAPI"
);
"persistent.cPersistence"
,
"CAPI"
);
if
(
!
capi
)
if
(
!
capi
)
return
;
return
;
capi
->
percachedel
=
(
percachedelfunc
)
cc_oid_unreferenced
;
capi
->
percachedel
=
(
percachedelfunc
)
cc_oid_unreferenced
;
py__p_changed
=
PyString_InternFromString
(
"_p_changed"
);
py__p_changed
=
PyString_InternFromString
(
"_p_changed"
);
if
(
!
py__p_changed
)
if
(
!
py__p_changed
)
return
;
return
;
py__p_deactivate
=
PyString_InternFromString
(
"_p_deactivate"
);
py__p_deactivate
=
PyString_InternFromString
(
"_p_deactivate"
);
if
(
!
py__p_deactivate
)
if
(
!
py__p_deactivate
)
return
;
return
;
py__p_jar
=
PyString_InternFromString
(
"_p_jar"
);
py__p_jar
=
PyString_InternFromString
(
"_p_jar"
);
if
(
!
py__p_jar
)
if
(
!
py__p_jar
)
return
;
return
;
py__p_oid
=
PyString_InternFromString
(
"_p_oid"
);
py__p_oid
=
PyString_InternFromString
(
"_p_oid"
);
if
(
!
py__p_oid
)
if
(
!
py__p_oid
)
return
;
return
;
if
(
PyModule_AddStringConstant
(
m
,
"cache_variant"
,
"stiff/c"
)
<
0
)
if
(
PyModule_AddStringConstant
(
m
,
"cache_variant"
,
"stiff/c"
)
<
0
)
return
;
return
;
/* This leaks a reference to Cctype, but it doesn't matter. */
/* This leaks a reference to Cctype, but it doesn't matter. */
if
(
PyModule_AddObject
(
m
,
"PickleCache"
,
(
PyObject
*
)
&
Cctype
)
<
0
)
if
(
PyModule_AddObject
(
m
,
"PickleCache"
,
(
PyObject
*
)
&
Cctype
)
<
0
)
return
;
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