From 6e901bac3024c15c7f7d0a0e0f4c84d52ed9eec6 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Wed, 24 Feb 2016 23:58:32 +0900 Subject: [PATCH] pybind/rados: track completions before calling aio functions Tracking completions is critical for memory safety - if the aio function succeeds, the completion must be tracked. However, if a KeyboardInterrupt or similar arrives between the call and the tracking, the completion will not be tracked. Fix this by tracking the completion before the aio call, and explicitly cleaning up in the failure case. This leaves the opposite problem, where an unexpected exception (other than simple error return from the aio function) will cause the completion to not be freed until the Ioctx is destroyed, but that is a relatively minor issue. Signed-off-by: Hector Martin --- src/pybind/rados/rados.pyx | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/pybind/rados/rados.pyx b/src/pybind/rados/rados.pyx index 54fe245aab67..c190286957b7 100644 --- a/src/pybind/rados/rados.pyx +++ b/src/pybind/rados/rados.pyx @@ -1802,13 +1802,13 @@ cdef class Ioctx(object): uint64_t _offset = offset completion = self.__get_completion(oncomplete, onsafe) - + self.__track_completion(completion) with nogil: ret = rados_aio_write(self.io, _object_name, completion.rados_comp, _to_write, size, _offset) if ret < 0: + completion._cleanup() raise make_ex(ret, "error writing object %s" % object_name) - self.__track_completion(completion) return completion def aio_write_full(self, object_name, to_write, @@ -1844,13 +1844,14 @@ cdef class Ioctx(object): size_t size = len(to_write) completion = self.__get_completion(oncomplete, onsafe) + self.__track_completion(completion) with nogil: ret = rados_aio_write_full(self.io, _object_name, completion.rados_comp, _to_write, size) if ret < 0: + completion._cleanup() raise make_ex(ret, "error writing object %s" % object_name) - self.__track_completion(completion) return completion def aio_append(self, object_name, to_append, oncomplete=None, onsafe=None): @@ -1884,13 +1885,14 @@ cdef class Ioctx(object): size_t size = len(to_append) completion = self.__get_completion(oncomplete, onsafe) + self.__track_completion(completion) with nogil: ret = rados_aio_append(self.io, _object_name, completion.rados_comp, _to_append, size) if ret < 0: + completion._cleanup() raise make_ex(ret, "error appending object %s" % object_name) - self.__track_completion(completion) return completion def aio_flush(self): @@ -1946,12 +1948,13 @@ cdef class Ioctx(object): completion = self.__get_completion(oncomplete_, None) completion.buf = PyBytes_FromStringAndSize(NULL, length) ret_buf = PyBytes_AsString(completion.buf) + self.__track_completion(completion) with nogil: ret = rados_aio_read(self.io, _object_name, completion.rados_comp, ret_buf, _length, _offset) if ret < 0: + completion._cleanup() raise make_ex(ret, "error reading %s" % object_name) - self.__track_completion(completion) return completion def aio_remove(self, object_name, oncomplete=None, onsafe=None): @@ -1977,12 +1980,13 @@ cdef class Ioctx(object): char* _object_name = object_name completion = self.__get_completion(oncomplete, onsafe) + self.__track_completion(completion) with nogil: ret = rados_aio_remove(self.io, _object_name, completion.rados_comp) if ret < 0: + completion._cleanup() raise make_ex(ret, "error removing %s" % object_name) - self.__track_completion(completion) return completion def require_ioctx_open(self): -- 2.47.3