From eb9ec26622be1f4e1959ebc1d0c04d10ea1f5873 Mon Sep 17 00:00:00 2001 From: David Wilson Date: Sun, 17 Feb 2019 21:46:47 +0000 Subject: [PATCH] issue #535: core: unicode.encode() may take importer lock on 2.x Found on Python 2.4, where import happens immediately following connect. - Main thread executes import statement, triggers request to parent - Broker thread attempts to deliver request via Router - Router discovers parent has disconnected, prepares a dead message - .dead() calls unicode.encode() to format reason string - .encode() attemptsto import a codec module - deadlock ---- (gdb) pystack /usr/local/python2.4.6/lib/python2.4/encodings/__init__.py (69): search_function (733): dead (2717): _maybe_send_dead (2724): _invoke (2749): _async_route (1635): _receive_one (1603): _internal_receive (1613): on_receive (2931): _call (2942): _loop_once (2988): _do_broker_main (545): _profile_hook (3007): _broker_main /usr/local/python2.4.6/lib/python2.4/threading.py (420): run /usr/local/python2.4.6/lib/python2.4/threading.py (424): __bootstrap --- mitogen/core.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/mitogen/core.py b/mitogen/core.py index 6bca1b1a..d8e3fdb6 100644 --- a/mitogen/core.py +++ b/mitogen/core.py @@ -103,6 +103,9 @@ IOLOG = logging.getLogger('mitogen.io') IOLOG.setLevel(logging.INFO) LATIN1_CODEC = encodings.latin_1.Codec() +# str.encode() may take import lock. Deadlock possible if broker calls +# .encode() on behalf of thread currently waiting for module. +UTF8_CODEC = encodings.latin_1.Codec() _v = False _vv = False @@ -271,9 +274,8 @@ class Kwargs(dict): def __init__(self, dct): for k, v in dct.iteritems(): if type(k) is unicode: - self[k.encode()] = v - else: - self[k] = v + k, _ = UTF8_CODEC.encode(k) + self[k] = v def __repr__(self): return 'Kwargs(%s)' % (dict.__repr__(self),) @@ -735,7 +737,7 @@ class Message(object): """ Syntax helper to construct a dead message. """ - kwargs['data'] = (reason or u'').encode() + kwargs['data'], _ = UTF8_CODEC.encode(reason or u'') return cls(reply_to=IS_DEAD, **kwargs) @classmethod @@ -1332,7 +1334,7 @@ class Importer(object): if mod.__package__ and not PY3: # 2.x requires __package__ to be exactly a string. - mod.__package__ = mod.__package__.encode() + mod.__package__, _ = UTF8_CODEC.encode(mod.__package__) source = self.get_source(fullname) try: