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

monkey patch breaks gevent.sleep(0) #744

Closed
tshuval opened this issue Feb 18, 2016 · 7 comments
Closed

monkey patch breaks gevent.sleep(0) #744

tshuval opened this issue Feb 18, 2016 · 7 comments
Labels
Type: Question User support and/or waiting for responses

Comments

@tshuval
Copy link

tshuval commented Feb 18, 2016

After monkey patching (specifically socket), gevent.sleep(0) does not yield and the spawned greenlet is not executed.
When setting the time of the sleep() to a positive number, the greenlet will start running.

Reproduce:

from gevent import monkey; monkey.patch_socket()
import gevent
import socket

def f():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(('', 8888))
    s.sendall('beep')

gevent.spawn(f)
gevent.sleep(0)    # ... BUG! nothing happens.... but:
gevent.sleep(0.1)  # ... and the greenlet will start running

As far as I understand, sleep(0) should behave the same as sleep(<positive number>)

Environment:
Python 2.7.6
gevent 1.0.2
greenlet 0.4.9

@jamadden
Copy link
Member

sleep(0) is not the same thing as sleeping for a specific amount of time. See the documentation for an explanation, but basically this code is working as expected.

@tshuval
Copy link
Author

tshuval commented Feb 18, 2016

OK, so I basically need to sleep to something like 0.001 seconds instead of 0.
Thanks for the clarification.

@jamadden
Copy link
Member

I'm assuming that the example is very simplified from your real use-case. So yes, in that simplified example, simply sleep with a positive number. In a real program with many greenlets, sleep() or sleep(0) is usually most efficient at yielding to other greenlets because it doesn't involve a trip around the event loop.

@jamadden jamadden added the Type: Question User support and/or waiting for responses label Feb 18, 2016
@tshuval
Copy link
Author

tshuval commented Feb 18, 2016

You are correct. My use case is a pool of greenlets getting items from a queue and sending them to a remote server.
However, using sleep(0) still creates the bug. My pseudo code is like this:

greenlet:
    while True:
        read 100 items from queue
        pack the items and send to remote server
        gevent.sleep(0)

main loop:
    spawn 10 greenlets
    while True:
        print 'beep'
        gevent.sleep(0.001)

I'm using the requests package.
Doing so, I can see in the log that a connection is created (requests.packages.urllib3.connectionpool:Starting new HTTP connection (1)) BUT the remote server DOES NOT receive any request until all greenlets have created a connection.
Another thing, if I change the value of the sleep of the main loop to 0 (instead of 0.001), then I can see something like 3 connections established, then about 50 'beep' messages, then 3 more connections, then 50 more 'beeps'.
This seems like a strange behavior.

@jamadden
Copy link
Member

Every time you sleep with a positive number, you're saying "don't run this greenlet again until the next iteration of the event loop where the time has elapsed".

At the top of the event loop, timeouts like this are checked and run, and then the operating system select call is processed to see if any I/O is ready to be processed. Last, greenlets that got scheduled during the previous steps, but have not yet run, are run---and they can schedule other greenlets for any of those phases, including to be run after themselves but (potentially) before the next loop iteration: that's what sleep(0) does.

Not all socket operations are going to be ready at the exact same instance, and even if they are, depending on the select implementation, it may take a few iterations to have them reported as ready.

Those are all implementation details, of course, and subject to change. My point is only that, while in principle all gevent operations are ordered, once you start mixing I/O and CPU bound operations (such as reading/writing a gevent queue, or sleep(0) vs sleep(1)), it becomes more difficult to reason about exactly what the order will be. In that way it's not much different than preemtible threads.

BTW, in this example, I probably wouldn't be sleep(0) in a while True loop (your greenlet). You're going to get better resource utilization if you let the natural blocking occur, either when you pull from a queue or try to spawn a new greenlet. Why? sleep(0) is going to unconditionally wake up this greenlet, even if there's nothing to do, whereas the blocking operations specifically schedule the greenlet only to wake up when there's something to do. Pretty much the same thing goes for the main loop. You can use Event objects or Semaphores or AsyncResult objects to schedule communication between greenlets when no natural blocking exists (pulling from a queue, joining/waiting on greenlets/pools, etc.)

@tshuval
Copy link
Author

tshuval commented Feb 18, 2016

@jamadden Thanks a lot for the helpful insights!

@jamadden
Copy link
Member

Cool. Can you close this then?

@tshuval tshuval closed this as completed Feb 18, 2016
hashbrowncipher pushed a commit to hashbrowncipher/gevent that referenced this issue Oct 20, 2018
'tox' has ignored the 'PYTHONDONTWRITEBYTECODE' environment option since
1.6.0 (commit 3a9c591f [1]). In the commit, an unknown issue with
'setuptools' was reported. However, this was incorrect: the issue at the
time lay not with 'setuptools' but rather with 'virtualenv'.  When using
particular versions of 'virtualenv', the following message would be
shown and the application would exit:

    The PYTHONDONTWRITEBYTECODE environment variable is not
    compatible with setuptools. Either use --distribute or unset
    PYTHONDONTWRITEBYTECODE.

The offending message was added and later refined in virtualenv 1.7
(commits a1b8632b [2] and 32367828 [3]). However, this log was removed
when support for 'setuptools' 0.7 and greater was added in 'virtualenv'
1.10 (commit cb01d9f9 [4]) There are no references to
'PYTHONDONTWRITEBYTECODE' in virtualenv 12.0 or any major release since
then, suggesting the issue works as expected. Meanwhile, setuptools
appears to have supported the 'PYTHONDONTWRITEBYTECODE' flag since
0.6.11 (commit 3a9c591f [5]).

All in all, it appears any release of 'virtualenv' or 'setuptools' made
in the past 5+ years is more than happy to work with this option and
there is therefore no reason for tox to treat it any differently. Stop
special casing this option and allow people to use it if they so desire.

[1] tox-dev/tox@700c877
[2] pypa/virtualenv@a1b8632
[3] pypa/virtualenv@3236782
[4] pypa/virtualenv@cb01d9f
[5] pypa/setuptools@3a9c591

Signed-off-by: Stephen Finucane <stephen@that.guru>
Fixes: gevent#744
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Type: Question User support and/or waiting for responses
Projects
None yet
Development

No branches or pull requests

2 participants