From 3847339f43fde782d4c7f314df204995bb286985 Mon Sep 17 00:00:00 2001 From: Brian Warner Date: Wed, 14 Feb 2018 00:39:26 -0800 Subject: [PATCH] _input: reject attempts to call Helper from a non-main thread This causes two threads to use the reactor at the same time, with horrible results. The _rlcompleter code currently violates this requirement, causing occasional failures if the messages arrive in just the wrong way (refs #280). --- src/wormhole/_input.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/wormhole/_input.py b/src/wormhole/_input.py index d2c6379..56e0f96 100644 --- a/src/wormhole/_input.py +++ b/src/wormhole/_input.py @@ -1,4 +1,8 @@ from __future__ import print_function, absolute_import, unicode_literals +# We use 'threading' defensively here, to detect if we're being called from a +# non-main thread. _rlcompleter.py is the only internal Wormhole code that +# deliberately creates a new thread. +import threading from zope.interface import implementer from attr import attrs, attrib from attr.validators import provides @@ -240,19 +244,28 @@ class Input(object): class Helper(object): _input = attrib() + def __attrs_post_init__(self): + self._main_thread = threading.current_thread().ident + def refresh_nameplates(self): + assert threading.current_thread().ident == self._main_thread self._input.refresh_nameplates() def get_nameplate_completions(self, prefix): + assert threading.current_thread().ident == self._main_thread return self._input.get_nameplate_completions(prefix) def choose_nameplate(self, nameplate): + assert threading.current_thread().ident == self._main_thread self._input._debug("I.choose_nameplate") self._input.choose_nameplate(nameplate) self._input._debug("I.choose_nameplate finished") def when_wordlist_is_available(self): + assert threading.current_thread().ident == self._main_thread return self._input.when_wordlist_is_available() def get_word_completions(self, prefix): + assert threading.current_thread().ident == self._main_thread return self._input.get_word_completions(prefix) def choose_words(self, words): + assert threading.current_thread().ident == self._main_thread self._input._debug("I.choose_words") self._input.choose_words(words) self._input._debug("I.choose_words finished")