Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.Sign up
Sleep time is not respected after monkey patch #1227
This repro on Python 3.5.3 and 3.6.5
Repro source code below (repro 100% : first sleep after monkey patch should result in 100 ms sleep, but got a 1 ms effective sleep).
Please note i have weird behavior with sleep not respected (90 ms instead of 100 ms) but i cannot repro this in a single isolated test.
""" # -*- coding: utf-8 -*- """ import time import gevent from gevent import monkey def _test_sleep(sleep_method, s_text): """ Test :param sleep_method: Test :param s_text: Test """ loop_count = 10 sleep_sec = 0.1 sleep_ms = sleep_sec * 1000.0 # python run_failed = 0 run_failed_ar = list() for i in range(0, loop_count): t_start = time.time() sleep_method(sleep_sec) t_end = time.time() t_ms = (t_end - t_start) * 1000.0 if t_ms < sleep_ms: run_failed += 1 run_failed_ar.append((i, t_ms)) print("%s run_failed =%s" % (s_text, run_failed)) print("%s run_failed_ar=%s" % (s_text, run_failed_ar)) return run_failed def test_sleep_all(): """ Test """ i = 0 i += _test_sleep(time.sleep, "python") i += _test_sleep(gevent.sleep, "gevent") # monkey monkey.patch_all(aggressive=True) # Here, the first call is dead... i += _test_sleep(gevent.sleep, "monkey_gevent") i += _test_sleep(time.sleep, "monkey_python") if i > 0: raise Exception("Failed with i=%s" % i) # run test_sleep_all()
import time import gevent from gevent import monkey import argparse parser = argparse.ArgumentParser() parser.add_argument("--sleep-before-patch", help="") args = parser.parse_args() if args.sleep_before_patch: gevent.sleep(0.1) monkey.patch_all(aggressive=True) t_start = time.time() gevent.sleep(0.2) t_end = time.time() t_ms = (t_end - t_start) print("time elapsed %s sec " % t_ms)
& time python debug.py --sleep-before-patch true time elapsed 0.03220248222351074 sec real 0m0.387s user 0m0.240s sys 0m0.012s $ time python debug.py time elapsed 0.2005610466003418 sec real 0m0.470s user 0m0.256s sys 0m0.008s
Why in the fist case the sleep 0.2 is durring 0.03 sec?
another stange test:
import time import gevent from gevent import monkey import argparse parser = argparse.ArgumentParser() parser.add_argument("--sleep-before-patch", help="") args = parser.parse_args() if args.sleep_before_patch: gevent.sleep(0.1) for _ in range(10): monkey.patch_all(aggressive=True) t_start = time.time() gevent.sleep(0.1) t_end = time.time() t_ms = (t_end - t_start) print("time elapsed %s sec " % t_ms)
The code is basically : get current millis or utc date, sleep 100 ms, compute millis difference (current millis|date - start millis|date), assert millis difference is greater than 100 ms. Simple, easy.
This was working until gevent <= 1.2.2.
Now the tests are failing, because millis difference after sleep 100 ms is lower then 100 ms (usually 80/90 ms), which is indeed an unexpected weird behavior => a sleep statement is something predictive.... If i want to sleep 100 ms, i want to sleep 100 ms, not 1 ms, not 80 ms.
Lets imagine you spawn a greenlet that refresh something every 500 ms. You expect a refresh every 500 ms, not 1 ms, not 400 ms...
So i dig into, but i cannot repro that 100% currently outside my bunch of code.
However i can repro 100% than the first sleep (after a monkey patch) is not respecting the sleep time.
I may dig more, but assuming this behavior is strange, i think you may an issue related to the switch interval recently introduced in 1.3....
What you're seeing is the event loop's notion of the current time and how that interacts with event loop timers. The current time is automatically updated between event loop iterations, and the current time is used to compute the timer expiration, in order to prevent timers from drifting too far. The current time can also be explicitly updated.
There were some changes around when gevent explicitly updates the current time in 1.3 as part of the libuv support.