Skip to content

Commit 9ceb5cd

Browse files
Invalidate all iterators when any data is deleted
This only works with swig >= v4.4, which enables weak references to swig objects. The data keeps weak references to all of its iterators and invalidates them when required.
1 parent 6ef2a30 commit 9ceb5cd

8 files changed

Lines changed: 529 additions & 28 deletions

File tree

src/interface/shared/data_iterator.i

Lines changed: 78 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
%noexception container_type##_iterator_base::__iter__;
3131
%noexception container_type##_iterator_base::operator==;
3232
%noexception container_type##_iterator_base::operator!=;
33-
%ignore container_type##_iterator_base::invalidate;
3433
%ignore container_type##_iterator_base::size;
3534
%ignore container_type##_iterator_base::##container_type##_iterator_base;
3635
%ignore container_type##_iterator_base::operator*;
@@ -90,8 +89,8 @@ public:
9089
return "iterator<end>";
9190
}
9291
bool valid() { return ptr != end; }
93-
// Provide C++ method to invalidate iterator
94-
void invalidate() { ptr = end; }
92+
// Provide method to invalidate iterator
93+
void _invalidate() { ptr = end; }
9594
// Provide size() C++ method for buffer size check
9695
size_t size() {
9796
if (valid())
@@ -119,6 +118,7 @@ public:
119118
}
120119
$1 = **argp;
121120
%}
121+
#if SWIG_VERSION < 0x040400
122122
// erase() invalidates the iterator
123123
%typemap(in) (Exiv2::container_type::iterator pos)
124124
(container_type##_iterator_base *argp=NULL),
@@ -130,8 +130,9 @@ public:
130130
argp = arg$argnum;
131131
}
132132
$1 = **argp;
133-
argp->invalidate();
133+
argp->_invalidate();
134134
}
135+
#endif
135136
// XmpData::eraseFamily takes an iterator reference (and invalidates it)
136137
%typemap(in) Exiv2::container_type::iterator&
137138
(Exiv2::container_type::iterator it,
@@ -143,7 +144,9 @@ public:
143144
}
144145
it = **argp;
145146
$1 = &it;
146-
argp->invalidate();
147+
#if SWIG_VERSION < 0x040400
148+
argp->_invalidate();
149+
#endif
147150
}
148151
// Check validity of pointer before dereferencing
149152
%typemap(check) container_type##_iterator* self {
@@ -152,19 +155,89 @@ public:
152155
"', invalid iterator cannot be dereferenced");
153156
}
154157
}
158+
159+
// Functions to store weak references to iterators (swig >= v4.4)
160+
%fragment("iterator_weakref_funcs", "header", fragment="private_data") {
161+
static void _process_list(PyObject* list, bool invalidate) {
162+
PyObject* ref = NULL;
163+
PyObject* iterator = NULL;
164+
for (Py_ssize_t idx = PyList_Size(list); idx > 0; idx--) {
165+
ref = PyList_GetItem(list, idx - 1);
166+
iterator = PyWeakref_GetObject(ref);
167+
if (iterator == Py_None)
168+
PyList_SetSlice(list, idx - 1, idx, NULL);
169+
else if (invalidate)
170+
Py_XDECREF(PyObject_CallMethod(iterator, "_invalidate", NULL));
171+
}
172+
};
173+
static void purge_iterators(PyObject* list) {
174+
_process_list(list, false);
175+
};
176+
static void invalidate_iterators(PyObject* py_self) {
177+
PyObject* list = private_store_get(py_self, "iterators");
178+
if (list)
179+
_process_list(list, true);
180+
};
181+
static int store_iterator_weakref(PyObject* py_self, PyObject* iterator) {
182+
PyObject* list = private_store_get(py_self, "iterators");
183+
if (list)
184+
purge_iterators(list);
185+
else {
186+
list = PyList_New(0);
187+
if (!list)
188+
return -1;
189+
int error = private_store_set(py_self, "iterators", list);
190+
Py_DECREF(list);
191+
if (error)
192+
return -1;
193+
}
194+
PyObject* ref = PyWeakref_NewRef(iterator, NULL);
195+
if (!ref)
196+
return -1;
197+
int result = PyList_Append(list, ref);
198+
Py_DECREF(ref);
199+
return result;
200+
};
201+
}
202+
203+
#if SWIG_VERSION >= 0x040400
204+
// clear() invalidates all iterators
205+
%typemap(ret, typemap="iterator_weakref_funcs") void clear {
206+
invalidate_iterators(self);
207+
}
208+
// erase() and eraseFamily() may invalidate iterators
209+
%typemap(check) (Exiv2::container_type::iterator pos),
210+
(Exiv2::container_type::iterator beg),
211+
(Exiv2::container_type::iterator&) {
212+
invalidate_iterators(self);
213+
}
214+
#endif // SWIG_VERSION
215+
155216
// Return types depend on validity of iterator
156217
%typemap(out) container_type##_iterator_base* {
157218
$result = SWIG_NewPointerObj((void*)$1,
158219
$1->valid() ? $descriptor(container_type##_iterator*) :
159220
$descriptor(container_type##_iterator_base*), 0);
160221
}
161222
// Assumes arg1 is the base class parent
223+
#if SWIG_VERSION >= 0x040400
224+
%typemap(out, fragment="iterator_weakref_funcs") Exiv2::container_type::iterator {
225+
#else
162226
%typemap(out) Exiv2::container_type::iterator {
227+
#endif
163228
container_type##_iterator_base* tmp = new container_type##_iterator_base($1, arg1->end());
164229
$result = SWIG_NewPointerObj((void*)tmp,
165230
tmp->valid() ? $descriptor(container_type##_iterator*) :
166231
$descriptor(container_type##_iterator_base*),
167232
SWIG_POINTER_OWN);
233+
#if SWIG_VERSION >= 0x040400
234+
if (tmp->valid()) {
235+
// Keep weak reference to the Python iterator
236+
if (store_iterator_weakref(self, $result)) {
237+
SWIG_fail;
238+
}
239+
}
240+
#endif // SWIG_VERSION
168241
}
169242
// Keep a reference to the data being iterated
170243
KEEP_REFERENCE(Exiv2::container_type::iterator)

src/swig-0_27_7/exif_wrap.cxx

Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4417,8 +4417,8 @@ class ExifData_iterator_base {
44174417
return "iterator<end>";
44184418
}
44194419
bool valid() { return ptr != end; }
4420-
// Provide C++ method to invalidate iterator
4421-
void invalidate() { ptr = end; }
4420+
// Provide method to invalidate iterator
4421+
void _invalidate() { ptr = end; }
44224422
// Provide size() C++ method for buffer size check
44234423
size_t size() {
44244424
if (valid())
@@ -5731,6 +5731,34 @@ SWIGINTERN PyObject *_wrap_ExifData_iterator_base___str__(PyObject *self, PyObje
57315731
}
57325732

57335733

5734+
SWIGINTERN PyObject *_wrap_ExifData_iterator_base__invalidate(PyObject *self, PyObject *args) {
5735+
PyObject *resultobj = 0;
5736+
ExifData_iterator_base *arg1 = (ExifData_iterator_base *) 0 ;
5737+
void *argp1 = 0 ;
5738+
int res1 = 0 ;
5739+
5740+
if (args && PyTuple_Check(args) && PyTuple_GET_SIZE(args) > 0) SWIG_exception_fail(SWIG_TypeError, "ExifData_iterator_base__invalidate takes no arguments");
5741+
res1 = SWIG_ConvertPtr(self, &argp1,SWIGTYPE_p_ExifData_iterator_base, 0 | 0 );
5742+
if (!SWIG_IsOK(res1)) {
5743+
SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "ExifData_iterator_base__invalidate" "', argument " "1"" of type '" "ExifData_iterator_base *""'");
5744+
}
5745+
arg1 = reinterpret_cast< ExifData_iterator_base * >(argp1);
5746+
{
5747+
try {
5748+
(arg1)->_invalidate();
5749+
}
5750+
catch(std::exception const& e) {
5751+
_set_python_exception();
5752+
SWIG_fail;
5753+
}
5754+
}
5755+
resultobj = SWIG_Py_Void();
5756+
return resultobj;
5757+
fail:
5758+
return NULL;
5759+
}
5760+
5761+
57345762
SWIGINTERN PyObject *_wrap_delete_ExifData_iterator_base(PyObject *self, PyObject *args) {
57355763
PyObject *resultobj = 0;
57365764
ExifData_iterator_base *arg1 = (ExifData_iterator_base *) 0 ;
@@ -9660,7 +9688,7 @@ SWIGINTERN PyObject *_wrap_ExifData_erase__SWIG_0(PyObject *self, PyObject *args
96609688
argp2 = arg2;
96619689
}
96629690
arg2 = **argp2;
9663-
argp2->invalidate();
9691+
argp2->_invalidate();
96649692
}
96659693
{
96669694
try {
@@ -9677,6 +9705,14 @@ SWIGINTERN PyObject *_wrap_ExifData_erase__SWIG_0(PyObject *self, PyObject *args
96779705
tmp->valid() ? SWIGTYPE_p_ExifData_iterator :
96789706
SWIGTYPE_p_ExifData_iterator_base,
96799707
SWIG_POINTER_OWN);
9708+
9709+
9710+
9711+
9712+
9713+
9714+
9715+
96809716
}
96819717

96829718
if (resultobj != Py_None)
@@ -9724,7 +9760,7 @@ SWIGINTERN PyObject *_wrap_ExifData_erase__SWIG_1(PyObject *self, PyObject *args
97249760
argp2 = arg2;
97259761
}
97269762
arg2 = **argp2;
9727-
argp2->invalidate();
9763+
argp2->_invalidate();
97289764
}
97299765

97309766
{
@@ -9753,6 +9789,14 @@ SWIGINTERN PyObject *_wrap_ExifData_erase__SWIG_1(PyObject *self, PyObject *args
97539789
tmp->valid() ? SWIGTYPE_p_ExifData_iterator :
97549790
SWIGTYPE_p_ExifData_iterator_base,
97559791
SWIG_POINTER_OWN);
9792+
9793+
9794+
9795+
9796+
9797+
9798+
9799+
97569800
}
97579801

97589802
if (resultobj != Py_None)
@@ -9897,6 +9941,14 @@ SWIGINTERN PyObject *_wrap_ExifData_begin(PyObject *self, PyObject *args) {
98979941
tmp->valid() ? SWIGTYPE_p_ExifData_iterator :
98989942
SWIGTYPE_p_ExifData_iterator_base,
98999943
SWIG_POINTER_OWN);
9944+
9945+
9946+
9947+
9948+
9949+
9950+
9951+
99009952
}
99019953

99029954
if (resultobj != Py_None)
@@ -9930,6 +9982,14 @@ SWIGINTERN PyObject *_wrap_ExifData_end(PyObject *self, PyObject *args) {
99309982
tmp->valid() ? SWIGTYPE_p_ExifData_iterator :
99319983
SWIGTYPE_p_ExifData_iterator_base,
99329984
SWIG_POINTER_OWN);
9985+
9986+
9987+
9988+
9989+
9990+
9991+
9992+
99339993
}
99349994

99359995
if (resultobj != Py_None)
@@ -9983,6 +10043,14 @@ SWIGINTERN PyObject *_wrap_ExifData_findKey(PyObject *self, PyObject *args) {
998310043
tmp->valid() ? SWIGTYPE_p_ExifData_iterator :
998410044
SWIGTYPE_p_ExifData_iterator_base,
998510045
SWIG_POINTER_OWN);
10046+
10047+
10048+
10049+
10050+
10051+
10052+
10053+
998610054
}
998710055

998810056
if (resultobj != Py_None)
@@ -10504,6 +10572,7 @@ SWIGINTERN PyMethodDef SwigPyBuiltin__ExifData_iterator_base_methods[] = {
1050410572
{ "__eq__", _wrap_ExifData_iterator_base___eq__, METH_VARARGS, "" },
1050510573
{ "__ne__", _wrap_ExifData_iterator_base___ne__, METH_VARARGS, "" },
1050610574
{ "__str__", _wrap_ExifData_iterator_base___str__, METH_VARARGS, "" },
10575+
{ "_invalidate", _wrap_ExifData_iterator_base__invalidate, METH_VARARGS, "" },
1050710576
{ NULL, NULL, 0, NULL } /* Sentinel */
1050810577
};
1050910578

0 commit comments

Comments
 (0)