-
Notifications
You must be signed in to change notification settings - Fork 394
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
fix(profiling): lock Recorder on reset #1560
Changes from 3 commits
5eed924
d36bc6c
6ced7f2
00878fc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
# -*- encoding: utf-8 -*- | ||
"""This files exposes non-gevent Python original functions.""" | ||
import threading | ||
|
||
from ddtrace.vendor import six | ||
from ddtrace.vendor import attr | ||
|
||
|
||
try: | ||
import gevent.monkey | ||
except ImportError: | ||
|
||
def get_original(module, func): | ||
return getattr(__import__(module), func) | ||
|
||
def is_module_patched(module): | ||
return False | ||
|
||
|
||
else: | ||
get_original = gevent.monkey.get_original | ||
is_module_patched = gevent.monkey.is_module_patched | ||
|
||
|
||
sleep = get_original("time", "sleep") | ||
|
||
try: | ||
# Python ≥ 3.8 | ||
threading_get_native_id = get_original("threading", "get_native_id") | ||
except AttributeError: | ||
threading_get_native_id = None | ||
|
||
start_new_thread = get_original(six.moves._thread.__name__, "start_new_thread") | ||
thread_get_ident = get_original(six.moves._thread.__name__, "get_ident") | ||
Thread = get_original("threading", "Thread") | ||
Lock = get_original("threading", "Lock") | ||
|
||
|
||
if is_module_patched("threading"): | ||
|
||
@attr.s | ||
class DoubleLock(object): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 I was skeptical of the need to lock across both coroutine and thread at first but I think I was able to work through it in my head (with my limited knowledge of gevent). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, it's really not that obvious. Basically, if you have a thread with coroutines, you have concurrency in that thread between coroutines so you need a gevent-lock to make sure they are excluded and scheduled correctly. I think what I was scared of is that you can use a gevent-lock from 2 threads, but it seems to work fine if you use a thread-lock first to synchronize. Scary! import threading
import sys
import gevent.monkey
import time
gevent.monkey.patch_all()
gl = threading.Lock()
sleep = gevent.monkey.get_original("time", "sleep")
thread = gevent.monkey.get_original("_thread", "start_new_thread")
tl = gevent.monkey.get_original("threading", "Lock")()
def lock_unlock():
while True:
tl.acquire()
gl.acquire()
print("thread got it")
sleep(0.01)
tl.release()
gl.release()
sleep(0.01)
th = thread(lock_unlock, ())
while True:
tl.acquire()
gl.acquire()
print("gevent got it")
time.sleep(0.01)
tl.release()
gl.release()
sleep(0.01) If you remove the thread-lock, it explodes. I should write a blog post about that. 🤔 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah I was curious about the ordering! Thanks for adding the comment here and in the source below 😄. Blog post would be great! |
||
"""A lock that prevent concurrency from a gevent coroutine and from a threading.Thread at the same time.""" | ||
|
||
_lock = attr.ib(factory=threading.Lock, init=False, repr=False) | ||
_thread_lock = attr.ib(factory=Lock, init=False, repr=False) | ||
|
||
def acquire(self): | ||
# You cannot acquire a gevent-lock from another thread if it has been acquired already: | ||
# make sure we exclude the gevent-lock from being acquire by another thread by using a thread-lock first. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
self._thread_lock.acquire() | ||
self._lock.acquire() | ||
|
||
def release(self): | ||
self._lock.release() | ||
self._thread_lock.release() | ||
|
||
def __enter__(self): | ||
self.acquire() | ||
return self | ||
|
||
def __exit__(self, exc_type, exc_val, exc_tb): | ||
self.release() | ||
|
||
|
||
else: | ||
DoubleLock = threading.Lock |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lol "_nogevent.py" gotta love that we have to bend over backwards to deal with gevent 🙈 🤦
I can't really think of a better name... maybe "compat" or "truth" lol.. 🤷
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
truth
haha.I had this idea of module for a few weeks, but I was not able to come with a name.
Finally, it hit me. 😆