Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-109461: Update logging module lock acquisition to use context manager #109462

Merged
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
02c1d58
Updated logging __init__.py to acquire its module lock using a contex…
dcollison Sep 15, 2023
f319ab0
📜🤖 Added by blurb_it.
blurb-it[bot] Sep 15, 2023
6664c4c
Merge branch 'main' into gh-109461-update-logging-lock-acquisition
dcollison Sep 15, 2023
fd635b2
Updated to leave the existing API alone and added a new method _acqui…
dcollison Sep 15, 2023
749b678
Merge remote-tracking branch 'origin/gh-109461-update-logging-lock-ac…
dcollison Sep 15, 2023
e5ad43b
Updated acquireLock usages to acquireModuleLock
dcollison Sep 15, 2023
0a2bb5f
Removed unnecessary changes
dcollison Sep 15, 2023
ea33592
Merge branch 'main' into gh-109461-update-logging-lock-acquisition
dcollison Sep 15, 2023
af9f7d1
Merge branch 'main' into gh-109461-update-logging-lock-acquisition
dcollison Sep 15, 2023
4f2d1c1
Updated _acquireModuleLock() to _get_lock() which gets the lock's con…
dcollison Sep 16, 2023
dc7df1e
Merge remote-tracking branch 'origin/gh-109461-update-logging-lock-ac…
dcollison Sep 16, 2023
892a5cb
Updated comments
dcollison Sep 16, 2023
a30d5da
Merge branch 'main' into gh-109461-update-logging-lock-acquisition
dcollison Sep 16, 2023
cf7a2e6
Updated blurb
dcollison Sep 16, 2023
0a53f3b
Merge remote-tracking branch 'origin/gh-109461-update-logging-lock-ac…
dcollison Sep 16, 2023
2760cc5
Removed unused import
dcollison Sep 16, 2023
b5e6651
Merge branch 'main' into gh-109461-update-logging-lock-acquisition
dcollison Sep 18, 2023
7f3b28a
Updated __init__.py to remove _get_lock() and "if _lock" checks since…
dcollison Sep 21, 2023
09c9133
Changed "with logging._get_lock():" to "with logging._lock:"
dcollison Sep 21, 2023
9f3607e
Changed "with logging._get_lock()" to "with logging._lock:"
dcollison Sep 21, 2023
b15e4bf
Changed "with logging._get_lock():" to "with logging._lock:"
dcollison Sep 21, 2023
a0c50bf
Update __init__.py
dcollison Sep 21, 2023
9d0675b
Update Misc/NEWS.d/next/Library/2023-09-15-17-12-53.gh-issue-109461.V…
dcollison Sep 21, 2023
a298a7b
Update __init__.py
dcollison Sep 21, 2023
1b14732
Merge branch 'main' into gh-109461-update-logging-lock-acquisition
dcollison Sep 21, 2023
1979a75
Updated handlers to use a context manager for lock acquisition
dcollison Sep 21, 2023
3fd33ef
Updated handlers to use a context manager for lock acquisition
dcollison Sep 21, 2023
22c868e
Copying _lock to local variable
dcollison Sep 21, 2023
335f814
Updated another lock acquisition
dcollison Sep 21, 2023
afa79ef
Update Misc/NEWS.d/next/Library/2023-09-15-17-12-53.gh-issue-109461.V…
dcollison Sep 21, 2023
600a126
Merge branch 'main' into gh-109461-update-logging-lock-acquisition
dcollison Sep 21, 2023
5e92580
Someone managed to duplicate a line in the docstring
dcollison Sep 21, 2023
20e46f6
Merge remote-tracking branch 'origin/gh-109461-update-logging-lock-ac…
dcollison Sep 21, 2023
c73e2b7
Someone managed to duplicate a line in the docstring
dcollison Sep 21, 2023
a9472c7
Reverted Handler acquire() and release() as it actually needs the Non…
dcollison Sep 21, 2023
df75801
Merge branch 'main' into gh-109461-update-logging-lock-acquisition
dcollison Sep 21, 2023
bca5ae8
Added comment to _acquireLock() to justify the try-except block aroun…
dcollison Sep 22, 2023
1ba5979
Renamed "_acquireLock" to "_prepareFork" and "_releaseLock" to "_afte…
dcollison Sep 22, 2023
c1a9651
Merge branch 'main' into gh-109461-update-logging-lock-acquisition
dcollison Sep 22, 2023
efd7462
Merge branch 'main' into gh-109461-update-logging-lock-acquisition
dcollison Sep 22, 2023
797ae0d
Merge branch 'main' into gh-109461-update-logging-lock-acquisition
AA-Turner Sep 26, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 29 additions & 67 deletions Lib/logging/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,12 +159,9 @@ def addLevelName(level, levelName):

This is used when converting levels to text during message formatting.
"""
_acquireLock()
try: #unlikely to cause an exception, but you never know...
with _lock:
_levelToName[level] = levelName
_nameToLevel[levelName] = level
finally:
_releaseLock()

if hasattr(sys, "_getframe"):
currentframe = lambda: sys._getframe(1)
Expand Down Expand Up @@ -237,19 +234,17 @@ def _acquireLock():

This should be released with _releaseLock().
"""
if _lock:
try:
_lock.acquire()
except BaseException:
_lock.release()
raise
try:
_lock.acquire()
except BaseException:
_lock.release()
dcollison marked this conversation as resolved.
Show resolved Hide resolved
raise

def _releaseLock():
"""
Release the module-level lock acquired by calling _acquireLock().
"""
if _lock:
_lock.release()
_lock.release()


# Prevent a held logging lock from blocking a child from logging.
Expand All @@ -264,11 +259,8 @@ def _register_at_fork_reinit_lock(instance):
_at_fork_reinit_lock_weakset = weakref.WeakSet()

def _register_at_fork_reinit_lock(instance):
_acquireLock()
try:
with _lock:
_at_fork_reinit_lock_weakset.add(instance)
finally:
_releaseLock()

def _after_at_fork_child_reinit_locks():
for handler in _at_fork_reinit_lock_weakset:
Expand Down Expand Up @@ -883,25 +875,20 @@ def _removeHandlerRef(wr):
# set to None. It can also be called from another thread. So we need to
# pre-emptively grab the necessary globals and check if they're None,
# to prevent race conditions and failures during interpreter shutdown.
acquire, release, handlers = _acquireLock, _releaseLock, _handlerList
if acquire and release and handlers:
acquire()
try:
handlers.remove(wr)
except ValueError:
pass
finally:
release()
handlers = _handlerList
dcollison marked this conversation as resolved.
Show resolved Hide resolved
dcollison marked this conversation as resolved.
Show resolved Hide resolved
if _lock and handlers:
with _lock:
try:
handlers.remove(wr)
except ValueError:
pass

def _addHandlerRef(handler):
"""
Add a handler to the internal cleanup list using a weak reference.
"""
_acquireLock()
try:
with _lock:
_handlerList.append(weakref.ref(handler, _removeHandlerRef))
finally:
_releaseLock()


def getHandlerByName(name):
Expand Down Expand Up @@ -946,15 +933,12 @@ def get_name(self):
return self._name

def set_name(self, name):
_acquireLock()
try:
with _lock:
if self._name in _handlers:
del _handlers[self._name]
self._name = name
if name:
_handlers[name] = self
finally:
_releaseLock()

name = property(get_name, set_name)

Expand Down Expand Up @@ -1058,13 +1042,10 @@ def close(self):
methods.
"""
#get the module data lock, as we're updating a shared structure.
_acquireLock()
try: #unlikely to raise an exception, but you never know...
with _lock:
self._closed = True
if self._name and self._name in _handlers:
del _handlers[self._name]
finally:
_releaseLock()

def handleError(self, record):
"""
Expand Down Expand Up @@ -1391,8 +1372,7 @@ def getLogger(self, name):
rv = None
if not isinstance(name, str):
raise TypeError('A logger name must be a string')
_acquireLock()
try:
with _lock:
if name in self.loggerDict:
rv = self.loggerDict[name]
if isinstance(rv, PlaceHolder):
Expand All @@ -1407,8 +1387,6 @@ def getLogger(self, name):
rv.manager = self
self.loggerDict[name] = rv
self._fixupParents(rv)
finally:
_releaseLock()
return rv

def setLoggerClass(self, klass):
Expand Down Expand Up @@ -1471,12 +1449,11 @@ def _clear_cache(self):
Called when level changes are made
"""

_acquireLock()
for logger in self.loggerDict.values():
if isinstance(logger, Logger):
logger._cache.clear()
self.root._cache.clear()
_releaseLock()
with _lock:
for logger in self.loggerDict.values():
if isinstance(logger, Logger):
logger._cache.clear()
self.root._cache.clear()

#---------------------------------------------------------------------------
# Logger classes and functions
Expand Down Expand Up @@ -1701,23 +1678,17 @@ def addHandler(self, hdlr):
"""
Add the specified handler to this logger.
"""
_acquireLock()
try:
with _lock:
if not (hdlr in self.handlers):
self.handlers.append(hdlr)
finally:
_releaseLock()

def removeHandler(self, hdlr):
"""
Remove the specified handler from this logger.
"""
_acquireLock()
try:
with _lock:
if hdlr in self.handlers:
self.handlers.remove(hdlr)
finally:
_releaseLock()

def hasHandlers(self):
"""
Expand Down Expand Up @@ -1795,16 +1766,13 @@ def isEnabledFor(self, level):
try:
return self._cache[level]
except KeyError:
_acquireLock()
try:
with _lock:
if self.manager.disable >= level:
is_enabled = self._cache[level] = False
else:
is_enabled = self._cache[level] = (
level >= self.getEffectiveLevel()
)
finally:
_releaseLock()
return is_enabled

def getChild(self, suffix):
Expand Down Expand Up @@ -1834,16 +1802,13 @@ def _hierlevel(logger):
return 1 + logger.name.count('.')

d = self.manager.loggerDict
_acquireLock()
try:
with _lock:
# exclude PlaceHolders - the last check is to ensure that lower-level
# descendants aren't returned - if there are placeholders, a logger's
# parent field might point to a grandparent or ancestor thereof.
return set(item for item in d.values()
if isinstance(item, Logger) and item.parent is self and
_hierlevel(item) == 1 + _hierlevel(item.parent))
finally:
_releaseLock()

def __repr__(self):
level = getLevelName(self.getEffectiveLevel())
Expand Down Expand Up @@ -2102,8 +2067,7 @@ def basicConfig(**kwargs):
"""
# Add thread safety in case someone mistakenly calls
# basicConfig() from multiple threads
_acquireLock()
try:
with _lock:
force = kwargs.pop('force', False)
encoding = kwargs.pop('encoding', None)
errors = kwargs.pop('errors', 'backslashreplace')
Expand Down Expand Up @@ -2152,8 +2116,6 @@ def basicConfig(**kwargs):
if kwargs:
keys = ', '.join(kwargs.keys())
raise ValueError('Unrecognised argument(s): %s' % keys)
finally:
_releaseLock()

#---------------------------------------------------------------------------
# Utility functions at module level.
Expand Down
30 changes: 9 additions & 21 deletions Lib/logging/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,15 +83,12 @@ def fileConfig(fname, defaults=None, disable_existing_loggers=True, encoding=Non
formatters = _create_formatters(cp)

# critical section
logging._acquireLock()
try:
with logging._lock:
_clearExistingHandlers()

# Handlers add themselves to logging._handlers
handlers = _install_handlers(cp, formatters)
_install_loggers(cp, handlers, disable_existing_loggers)
finally:
logging._releaseLock()


def _resolve(name):
Expand Down Expand Up @@ -516,8 +513,7 @@ def configure(self):
raise ValueError("Unsupported version: %s" % config['version'])
incremental = config.pop('incremental', False)
EMPTY_DICT = {}
logging._acquireLock()
try:
with logging._lock:
if incremental:
handlers = config.get('handlers', EMPTY_DICT)
for name in handlers:
Expand Down Expand Up @@ -661,8 +657,6 @@ def configure(self):
except Exception as e:
raise ValueError('Unable to configure root '
'logger') from e
finally:
logging._releaseLock()

def configure_formatter(self, config):
"""Configure a formatter from a dictionary."""
Expand Down Expand Up @@ -988,9 +982,8 @@ class ConfigSocketReceiver(ThreadingTCPServer):
def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT,
handler=None, ready=None, verify=None):
ThreadingTCPServer.__init__(self, (host, port), handler)
logging._acquireLock()
self.abort = 0
logging._releaseLock()
with logging._lock:
self.abort = 0
self.timeout = 1
self.ready = ready
self.verify = verify
Expand All @@ -1004,9 +997,8 @@ def serve_until_stopped(self):
self.timeout)
if rd:
self.handle_request()
logging._acquireLock()
abort = self.abort
logging._releaseLock()
with logging._lock:
abort = self.abort
self.server_close()

class Server(threading.Thread):
Expand All @@ -1027,9 +1019,8 @@ def run(self):
self.port = server.server_address[1]
self.ready.set()
global _listener
logging._acquireLock()
_listener = server
logging._releaseLock()
with logging._lock:
_listener = server
server.serve_until_stopped()

return Server(ConfigSocketReceiver, ConfigStreamHandler, port, verify)
Expand All @@ -1039,10 +1030,7 @@ def stopListening():
Stop the listening server which was created with a call to listen().
"""
global _listener
logging._acquireLock()
try:
with logging._lock:
if _listener:
_listener.abort = 1
_listener = None
finally:
logging._releaseLock()
6 changes: 1 addition & 5 deletions Lib/multiprocessing/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,7 @@ def get_logger():
global _logger
import logging

logging._acquireLock()
try:
with logging._lock:
if not _logger:

_logger = logging.getLogger(LOGGER_NAME)
Expand All @@ -79,9 +78,6 @@ def get_logger():
atexit._exithandlers.remove((_exit_function, (), {}))
atexit._exithandlers.append((_exit_function, (), {}))

finally:
logging._releaseLock()

return _logger

def log_to_stderr(level=None):
Expand Down
15 changes: 3 additions & 12 deletions Lib/test/test_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,7 @@ def setUp(self):
self._threading_key = threading_helper.threading_setup()

logger_dict = logging.getLogger().manager.loggerDict
logging._acquireLock()
try:
with logging._lock:
self.saved_handlers = logging._handlers.copy()
self.saved_handler_list = logging._handlerList[:]
self.saved_loggers = saved_loggers = logger_dict.copy()
Expand All @@ -101,8 +100,6 @@ def setUp(self):
for name in saved_loggers:
logger_states[name] = getattr(saved_loggers[name],
'disabled', None)
finally:
logging._releaseLock()

# Set two unused loggers
self.logger1 = logging.getLogger("\xab\xd7\xbb")
Expand Down Expand Up @@ -136,8 +133,7 @@ def tearDown(self):
self.root_logger.removeHandler(h)
h.close()
self.root_logger.setLevel(self.original_logging_level)
logging._acquireLock()
try:
with logging._lock:
logging._levelToName.clear()
logging._levelToName.update(self.saved_level_to_name)
logging._nameToLevel.clear()
Expand All @@ -154,8 +150,6 @@ def tearDown(self):
for name in self.logger_states:
if logger_states[name] is not None:
self.saved_loggers[name].disabled = logger_states[name]
finally:
logging._releaseLock()

self.doCleanups()
threading_helper.threading_cleanup(*self._threading_key)
Expand Down Expand Up @@ -759,8 +753,7 @@ def emit(self, record):
fork_happened__release_locks_and_end_thread = threading.Event()

def lock_holder_thread_fn():
logging._acquireLock()
try:
with logging._lock:
refed_h.acquire()
try:
# Tell the main thread to do the fork.
Expand All @@ -780,8 +773,6 @@ def lock_holder_thread_fn():
fork_happened__release_locks_and_end_thread.wait(0.5)
finally:
refed_h.release()
finally:
logging._releaseLock()

lock_holder_thread = threading.Thread(
target=lock_holder_thread_fn,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
logging: Use a context manager for lock acquisition.
dcollison marked this conversation as resolved.
Show resolved Hide resolved
Loading