@@ -142,7 +142,8 @@ def __init__(self, registry, address, authkey, serializer):
142142
143143 self .id_to_obj = {'0' : (None , ())}
144144 self .id_to_refcount = {}
145- self .mutex = threading .RLock ()
145+ self .id_to_local_proxy_obj = {}
146+ self .mutex = threading .Lock ()
146147
147148 def serve_forever (self ):
148149 '''
@@ -227,7 +228,14 @@ def serve_client(self, conn):
227228 methodname = obj = None
228229 request = recv ()
229230 ident , methodname , args , kwds = request
230- obj , exposed , gettypeid = id_to_obj [ident ]
231+ try :
232+ obj , exposed , gettypeid = id_to_obj [ident ]
233+ except KeyError as ke :
234+ try :
235+ obj , exposed , gettypeid = \
236+ self .id_to_local_proxy_obj [ident ]
237+ except KeyError as second_ke :
238+ raise ke
231239
232240 if methodname not in exposed :
233241 raise AttributeError (
@@ -308,7 +316,7 @@ def debug_info(self, c):
308316 '''
309317 with self .mutex :
310318 result = []
311- keys = list (self .id_to_obj .keys ())
319+ keys = list (self .id_to_refcount .keys ())
312320 keys .sort ()
313321 for ident in keys :
314322 if ident != '0' :
@@ -321,7 +329,8 @@ def number_of_objects(self, c):
321329 '''
322330 Number of shared objects
323331 '''
324- return len (self .id_to_obj ) - 1 # don't count ident='0'
332+ # Doesn't use (len(self.id_to_obj) - 1) as we shouldn't count ident='0'
333+ return len (self .id_to_refcount )
325334
326335 def shutdown (self , c ):
327336 '''
@@ -363,13 +372,9 @@ def create(self, c, typeid, *args, **kwds):
363372 self .id_to_obj [ident ] = (obj , set (exposed ), method_to_typeid )
364373 if ident not in self .id_to_refcount :
365374 self .id_to_refcount [ident ] = 0
366- # increment the reference count immediately, to avoid
367- # this object being garbage collected before a Proxy
368- # object for it can be created. The caller of create()
369- # is responsible for doing a decref once the Proxy object
370- # has been created.
371- self .incref (c , ident )
372- return ident , tuple (exposed )
375+
376+ self .incref (c , ident )
377+ return ident , tuple (exposed )
373378
374379 def get_methods (self , c , token ):
375380 '''
@@ -387,15 +392,45 @@ def accept_connection(self, c, name):
387392
388393 def incref (self , c , ident ):
389394 with self .mutex :
390- self .id_to_refcount [ident ] += 1
395+ try :
396+ self .id_to_refcount [ident ] += 1
397+ except KeyError as ke :
398+ # If no external references exist but an internal (to the
399+ # manager) still does and a new external reference is created
400+ # from it, restore the manager's tracking of it from the
401+ # previously stashed internal ref.
402+ if ident in self .id_to_local_proxy_obj :
403+ self .id_to_refcount [ident ] = 1
404+ self .id_to_obj [ident ] = \
405+ self .id_to_local_proxy_obj [ident ]
406+ obj , exposed , gettypeid = self .id_to_obj [ident ]
407+ util .debug ('Server re-enabled tracking & INCREF %r' , ident )
408+ else :
409+ raise ke
391410
392411 def decref (self , c , ident ):
412+ if ident not in self .id_to_refcount and \
413+ ident in self .id_to_local_proxy_obj :
414+ util .debug ('Server DECREF skipping %r' , ident )
415+ return
416+
393417 with self .mutex :
394418 assert self .id_to_refcount [ident ] >= 1
395419 self .id_to_refcount [ident ] -= 1
396420 if self .id_to_refcount [ident ] == 0 :
397- del self .id_to_obj [ident ], self .id_to_refcount [ident ]
398- util .debug ('disposing of obj with id %r' , ident )
421+ del self .id_to_refcount [ident ]
422+
423+ if ident not in self .id_to_refcount :
424+ # Two-step process in case the object turns out to contain other
425+ # proxy objects (e.g. a managed list of managed lists).
426+ # Otherwise, deleting self.id_to_obj[ident] would trigger the
427+ # deleting of the stored value (another managed object) which would
428+ # in turn attempt to acquire the mutex that is already held here.
429+ self .id_to_obj [ident ] = (None , (), None ) # thread-safe
430+ util .debug ('disposing of obj with id %r' , ident )
431+ with self .mutex :
432+ del self .id_to_obj [ident ]
433+
399434
400435#
401436# Class to represent state of a manager
@@ -658,7 +693,7 @@ class BaseProxy(object):
658693 _mutex = util .ForkAwareThreadLock ()
659694
660695 def __init__ (self , token , serializer , manager = None ,
661- authkey = None , exposed = None , incref = True ):
696+ authkey = None , exposed = None , incref = True , manager_owned = False ):
662697 with BaseProxy ._mutex :
663698 tls_idset = BaseProxy ._address_to_local .get (token .address , None )
664699 if tls_idset is None :
@@ -680,6 +715,12 @@ def __init__(self, token, serializer, manager=None,
680715 self ._serializer = serializer
681716 self ._Client = listener_client [serializer ][1 ]
682717
718+ # Should be set to True only when a proxy object is being created
719+ # on the manager server; primary use case: nested proxy objects.
720+ # RebuildProxy detects when a proxy is being created on the manager
721+ # and sets this value appropriately.
722+ self ._owned_by_manager = manager_owned
723+
683724 if authkey is not None :
684725 self ._authkey = process .AuthenticationString (authkey )
685726 elif self ._manager is not None :
@@ -738,6 +779,10 @@ def _getvalue(self):
738779 return self ._callmethod ('#GETVALUE' )
739780
740781 def _incref (self ):
782+ if self ._owned_by_manager :
783+ util .debug ('owned_by_manager skipped INCREF of %r' , self ._token .id )
784+ return
785+
741786 conn = self ._Client (self ._token .address , authkey = self ._authkey )
742787 dispatch (conn , None , 'incref' , (self ._id ,))
743788 util .debug ('INCREF %r' , self ._token .id )
@@ -822,19 +867,19 @@ def __str__(self):
822867def RebuildProxy (func , token , serializer , kwds ):
823868 '''
824869 Function used for unpickling proxy objects.
825-
826- If possible the shared object is returned, or otherwise a proxy for it.
827870 '''
828871 server = getattr (process .current_process (), '_manager_server' , None )
829-
830872 if server and server .address == token .address :
831- return server .id_to_obj [token .id ][0 ]
832- else :
833- incref = (
834- kwds .pop ('incref' , True ) and
835- not getattr (process .current_process (), '_inheriting' , False )
836- )
837- return func (token , serializer , incref = incref , ** kwds )
873+ util .debug ('Rebuild a proxy owned by manager, token=%r' , token )
874+ kwds ['manager_owned' ] = True
875+ if token .id not in server .id_to_local_proxy_obj :
876+ server .id_to_local_proxy_obj [token .id ] = \
877+ server .id_to_obj [token .id ]
878+ incref = (
879+ kwds .pop ('incref' , True ) and
880+ not getattr (process .current_process (), '_inheriting' , False )
881+ )
882+ return func (token , serializer , incref = incref , ** kwds )
838883
839884#
840885# Functions to create proxies and proxy types
0 commit comments