Skip to content

Commit

Permalink
Added multiple-reader prevention code because it seems to be a fairly…
Browse files Browse the repository at this point in the history
… common pitfall. Also added defaults to the debug methods so you can call them no-args to reset them.
  • Loading branch information
whichlinden committed Mar 16, 2010
1 parent 160580c commit cb7c8c0
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 4 deletions.
12 changes: 8 additions & 4 deletions eventlet/debug.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def format_hub_timers():
result.append(repr(l))
return os.linesep.join(result)

def hub_listener_stacks(state):
def hub_listener_stacks(state = False):
"""Toggles whether or not the hub records the stack when clients register
listeners on file descriptors. This can be useful when trying to figure
out what the hub is up to at any given moment. To inspect the stacks
Expand All @@ -103,15 +103,19 @@ def hub_listener_stacks(state):
from eventlet import hubs
hubs.get_hub().set_debug_listeners(state)

def hub_timer_stacks(state):
def hub_timer_stacks(state = False):
"""Toggles whether or not the hub records the stack when timers are set.
To inspect the stacks of the current timers, call :func:`format_hub_timers`
at critical junctures in the application logic.
"""
from eventlet.hubs import timer
timer._g_debug = state

def hub_prevent_multiple_readers(state = True):
from eventlet.hubs import hub
hub.g_prevent_multiple_readers = state

def hub_exceptions(state):
def hub_exceptions(state = True):
"""Toggles whether the hub prints exceptions that are raised from its
timers. This can be useful to see how greenthreads are terminating.
"""
Expand All @@ -120,7 +124,7 @@ def hub_exceptions(state):
from eventlet import greenpool
greenpool.DEBUG = state

def tpool_exceptions(state):
def tpool_exceptions(state = False):
"""Toggles whether tpool itself prints exceptions that are raised from
functions that are executed in it, in addition to raising them like
it normally does."""
Expand Down
12 changes: 12 additions & 0 deletions eventlet/hubs/hub.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import heapq
import sys
import traceback
import warnings

from eventlet.support import greenlets as greenlet, clear_sys_exc_info
from eventlet.hubs import timer
from eventlet import patcher
time = patcher.original('time')

g_prevent_multiple_readers = True

READ="read"
WRITE="write"

Expand Down Expand Up @@ -74,6 +77,15 @@ def add(self, evtype, fileno, cb):
listener = self.lclass(evtype, fileno, cb)
bucket = self.listeners[evtype]
if fileno in bucket:
if g_prevent_multiple_readers:
raise RuntimeError("Second simultaneous %s on fileno %s "\
"detected. Unless you really know what you're doing, "\
"make sure that only one greenthread can %s any "\
"particular socket. Consider using a pools.Pool. "\
"If you do know what you're doing and want to disable "\
"this error, call "\
"eventlet.debug.hub_multiple_reader_prevention(False)" % (
evtype, fileno, evtype))
# store off the second listener in another structure
self.secondaries[evtype].setdefault(fileno, []).append(listener)
else:
Expand Down
24 changes: 24 additions & 0 deletions tests/greenio_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -483,11 +483,34 @@ def writer():

gt.wait()

@skip_with_pyevent
def test_raised_multiple_readers(self):
debug.hub_prevent_multiple_readers(True)

def handle(sock, addr):
sock.recv(1)
sock.sendall("a")
raise eventlet.StopServe()
listener = eventlet.listen(('127.0.0.1', 0))
server = eventlet.spawn(eventlet.serve,
listener,
handle)
def reader(s):
s.recv(1)

s = eventlet.connect(('127.0.0.1', listener.getsockname()[1]))
a = eventlet.spawn(reader, s)
eventlet.sleep(0)
self.assertRaises(RuntimeError, s.recv, 1)
s.sendall('b')
a.wait()


class TestGreenIoLong(LimitedTestCase):
TEST_TIMEOUT=10 # the test here might take a while depending on the OS
@skip_with_pyevent
def test_multiple_readers(self, clibufsize=False):
debug.hub_prevent_multiple_readers(False)
recvsize = 2 * min_buf_size()
sendsize = 10 * recvsize
# test that we can have multiple coroutines reading
Expand Down Expand Up @@ -534,6 +557,7 @@ def server():
listener.close()
self.assert_(len(results1) > 0)
self.assert_(len(results2) > 0)
debug.hub_prevent_multiple_readers()

@skipped # by rdw because it fails but it's not clear how to make it pass
@skip_with_pyevent
Expand Down
2 changes: 2 additions & 0 deletions tests/stdlib/all.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
Many of these tests make connections to external servers, and all.py tries to skip these tests rather than failing them, so you can get some work done on a plane.
"""

from eventlet import debug
debug.hub_prevent_multiple_readers(False)

def restart_hub():
from eventlet import hubs
Expand Down

0 comments on commit cb7c8c0

Please sign in to comment.