From f7c2c688b6ee197f72b6daa98925993895531688 Mon Sep 17 00:00:00 2001 From: Amir Miron Date: Thu, 1 Sep 2016 11:28:22 +0300 Subject: [PATCH 1/2] fixed refcount leakage when unboxing from cache --- rpyc/core/netref.py | 22 +++++++++++++--------- rpyc/core/protocol.py | 6 +++++- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/rpyc/core/netref.py b/rpyc/core/netref.py index cd8db094..34ece3c4 100644 --- a/rpyc/core/netref.py +++ b/rpyc/core/netref.py @@ -10,7 +10,7 @@ _local_netref_attrs = frozenset([ - '____conn__', '____oid__', '__class__', '__cmp__', '__del__', '__delattr__', + '____conn__', '____oid__', '____refcount__', '__class__', '__cmp__', '__del__', '__delattr__', '__dir__', '__doc__', '__getattr__', '__getattribute__', '__hash__', '__init__', '__metaclass__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__slots__', '__str__', @@ -43,6 +43,7 @@ _builtin_types.extend([ bytes, bytearray, type(iter(range(10))), memoryview, ]) + xrange = range else: _builtin_types.extend([ basestring, unicode, long, xrange, type(iter(xrange(10))), file, @@ -116,18 +117,21 @@ class BaseNetref(object): """ # this is okay with py3k -- see below __metaclass__ = NetrefMetaclass - __slots__ = ["____conn__", "____oid__", "__weakref__"] + __slots__ = ["____conn__", "____oid__", "__weakref__", "____refcount__"] def __init__(self, conn, oid): self.____conn__ = conn self.____oid__ = oid + self.____refcount__ = 1 + def __del__(self): - try: - asyncreq(self, consts.HANDLE_DEL) - except Exception: - # raised in a destructor, most likely on program termination, - # when the connection might have already been closed. - # it's safe to ignore all exceptions here - pass + for _ in xrange(self.____refcount__): + try: + asyncreq(self, consts.HANDLE_DEL) + except Exception: + # raised in a destructor, most likely on program termination, + # when the connection might have already been closed. + # it's safe to ignore all exceptions here + pass def __getattribute__(self, name): if name in _local_netref_attrs: diff --git a/rpyc/core/protocol.py b/rpyc/core/protocol.py index aa986f2b..c56cbbb7 100644 --- a/rpyc/core/protocol.py +++ b/rpyc/core/protocol.py @@ -298,7 +298,11 @@ def _unbox(self, package): if label == consts.LABEL_REMOTE_REF: oid, clsname, modname = value if oid in self._proxy_cache: - return self._proxy_cache[oid] + proxy = self._proxy_cache[oid] + proxy.____refcount__ += 1 # other side increased refcount on boxing, + # if I'm returning from cache instead of new object, + # must increase refcount to match + return proxy proxy = self._netref_factory(oid, clsname, modname) self._proxy_cache[oid] = proxy return proxy From d9449fcaeaefc75077af8fdbe929aaffe9f27c8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Gl=C3=A4=C3=9Fle?= Date: Fri, 28 Jul 2017 12:30:48 +0200 Subject: [PATCH 2/2] Transmit count with delete request Cleaner and faster than sending multiple requests. --- rpyc/core/netref.py | 15 +++++++-------- rpyc/core/protocol.py | 2 +- rpyc/lib/colls.py | 6 +++--- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/rpyc/core/netref.py b/rpyc/core/netref.py index 34ece3c4..6c3b8886 100644 --- a/rpyc/core/netref.py +++ b/rpyc/core/netref.py @@ -124,14 +124,13 @@ def __init__(self, conn, oid): self.____refcount__ = 1 def __del__(self): - for _ in xrange(self.____refcount__): - try: - asyncreq(self, consts.HANDLE_DEL) - except Exception: - # raised in a destructor, most likely on program termination, - # when the connection might have already been closed. - # it's safe to ignore all exceptions here - pass + try: + asyncreq(self, consts.HANDLE_DEL, self.____refcount__) + except Exception: + # raised in a destructor, most likely on program termination, + # when the connection might have already been closed. + # it's safe to ignore all exceptions here + pass def __getattribute__(self, name): if name in _local_netref_attrs: diff --git a/rpyc/core/protocol.py b/rpyc/core/protocol.py index c56cbbb7..27bf556d 100644 --- a/rpyc/core/protocol.py +++ b/rpyc/core/protocol.py @@ -567,7 +567,7 @@ def _handle_close(self): self._cleanup() def _handle_getroot(self): return self._local_root - def _handle_del(self, oid): + def _handle_del(self, oid, count=1): self._local_objects.decref(oid) def _handle_repr(self, oid): return repr(self._local_objects[oid]) diff --git a/rpyc/lib/colls.py b/rpyc/lib/colls.py index 5e946a7a..13018620 100644 --- a/rpyc/lib/colls.py +++ b/rpyc/lib/colls.py @@ -73,13 +73,13 @@ def add(self, obj): def clear(self): with self._lock: self._dict.clear() - def decref(self, key): + def decref(self, key, count=1): with self._lock: slot = self._dict[key] - if slot[1] < 1: + if slot[1] < count: del self._dict[key] else: - slot[1] -= 1 + slot[1] -= count self._dict[key] = slot def __getitem__(self, key): with self._lock: