@@ -1377,9 +1377,10 @@ def getLogger(self, name):
13771377 raise TypeError ('A logger name must be a string' )
13781378 # Fast path: an already-registered, non-placeholder logger can be
13791379 # returned without taking the lock. dict.get() is atomic under both
1380- # the GIL and free threading, and a Logger is fully initialised before
1381- # being inserted into loggerDict under the lock, so this never sees a
1382- # partially-constructed object.
1380+ # the GIL and free threading. A Logger is inserted into loggerDict only
1381+ # after it is fully wired up (parent/child references fixed) under the
1382+ # lock, so the fast path never observes a logger whose parent is not yet
1383+ # set.
13831384 rv = self .loggerDict .get (name )
13841385 if rv is not None and not isinstance (rv , PlaceHolder ):
13851386 return rv
@@ -1390,14 +1391,18 @@ def getLogger(self, name):
13901391 ph = rv
13911392 rv = (self .loggerClass or _loggerClass )(name )
13921393 rv .manager = self
1393- self .loggerDict [name ] = rv
13941394 self ._fixupChildren (ph , rv )
13951395 self ._fixupParents (rv )
1396+ # Publish only after rv is fully wired: the fast path reads
1397+ # loggerDict without the lock.
1398+ self .loggerDict [name ] = rv
13961399 else :
13971400 rv = (self .loggerClass or _loggerClass )(name )
13981401 rv .manager = self
1399- self .loggerDict [name ] = rv
14001402 self ._fixupParents (rv )
1403+ # Publish only after rv is fully wired: the fast path reads
1404+ # loggerDict without the lock.
1405+ self .loggerDict [name ] = rv
14011406 return rv
14021407
14031408 def setLoggerClass (self , klass ):
0 commit comments