Commit 09f3ae72 authored by Jason Madden's avatar Jason Madden

Add failing test for #1739

parent 33de7406
......@@ -331,7 +331,7 @@ class AbstractLinkable(object):
# If we added unswitched greenlets, however, don't add them back to the links yet.
# We wouldn't be able to call them in this hub anyway.
# TODO: Instead of just adding these back to self._links, we should try to detect their
# "home" hub and mode the callback to that hub. As it stands, there's a chance that
# "home" hub and move the callback to that hub. As it stands, there's a chance that
# if no greenlet tries to acquire/release this object in that hub, these objects
# will never get to run.
self._links.extend(unswitched)
......
......@@ -166,7 +166,8 @@ class Event(AbstractLinkable): # pylint:disable=undefined-variable
class AsyncResult(AbstractLinkable): # pylint:disable=undefined-variable
"""A one-time event that stores a value or an exception.
"""
A one-time event that stores a value or an exception.
Like :class:`Event` it wakes up all the waiters when :meth:`set` or :meth:`set_exception`
is called. Waiters may receive the passed value or exception by calling :meth:`get`
......
......@@ -201,6 +201,8 @@ def _ignores_DoNotPatch(func):
# maps module name -> {attribute name: original item}
# e.g. "time" -> {"sleep": built-in function sleep}
# NOT A PUBLIC API. However, third-party monkey-patchers may be using
# it? TODO: Provide better API for them.
saved = {}
......@@ -229,6 +231,18 @@ def is_object_patched(mod_name, item_name):
return is_module_patched(mod_name) and item_name in saved[mod_name]
def is_anything_patched():
# Check if this module has done any patching in the current process.
# This is currently only used in gevent tests.
#
# Not currently a documented, public API, because I'm not convinced
# it is 100% reliable in the event of third-party patch functions that
# don't use ``saved``.
#
# .. versionadded:: NEXT
return bool(saved)
def _get_original(name, items):
d = saved.get(name, {})
values = []
......
......@@ -435,3 +435,7 @@ class TestCase(TestCaseMetaClass("NewBase",
def assertStartsWith(self, it, has_prefix):
self.assertTrue(it.startswith(has_prefix), (it, has_prefix))
def assertNotMonkeyPatched(self):
from gevent import monkey
self.assertFalse(monkey.is_anything_patched())
from __future__ import absolute_import, print_function, division
from __future__ import absolute_import
from __future__ import print_function
from __future__ import division
import weakref
......@@ -138,6 +140,62 @@ class TestAsyncResult(greentest.TestCase):
self.assertRaises(gevent.Timeout, ar.get, block=False)
self.assertRaises(gevent.Timeout, ar.get_nowait)
def test_cross_thread_use(self):
# Issue 1739.
# AsyncResult has *never* been thread safe, and using it from one
# thread to another is not safe. However, in some very careful use cases
# that can actually work.
#
# This test makes sure it doesn't hang in one careful use
# scenario.
self.assertNotMonkeyPatched() # Need real threads, event objects
from threading import Thread as NativeThread
from threading import Event as NativeEvent
class Thread(NativeThread):
def __init__(self):
NativeThread.__init__(self)
self.daemon = True
self.running_event = NativeEvent()
self.finished_event = NativeEvent()
self.async_result = AsyncResult()
self.result = '<never set>'
def run(self):
# Give the loop in this thread something to do
g_event = Event()
def spin():
while not g_event.is_set():
g_event.wait(DELAY * 2)
glet = gevent.spawn(spin)
self.running_event.set()
print("Entering wait")
# XXX: If we use a timed wait(), the bug doesn't manifest.
# Why not?
self.result = self.async_result.wait()
g_event.set()
glet.join()
self.finished_event.set()
thread = Thread()
thread.start()
try:
print("Waiting for thread")
thread.running_event.wait()
print("Thread is running")
thread.async_result.set('from main')
thread.finished_event.wait(DELAY * 5)
finally:
thread.join(DELAY * 15)
self.assertEqual(thread.result, 'from main')
class TestAsyncResultAsLinkTarget(greentest.TestCase):
error_fatal = False
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment