1# -*- encoding: utf-8 -*-
2"""This files exposes non-gevent Python original functions."""
3import threading
4
5import attr
6import six
7
8from ddtrace.internal import compat
9from ddtrace.internal import forksafe
10
11
12try:
13    import gevent.monkey
14except ImportError:
15
16    def get_original(module, func):
17        return getattr(__import__(module), func)
18
19    def is_module_patched(module):
20        return False
21
22
23else:
24    get_original = gevent.monkey.get_original
25    is_module_patched = gevent.monkey.is_module_patched
26
27
28sleep = get_original("time", "sleep")
29
30try:
31    # Python ≥ 3.8
32    threading_get_native_id = get_original("threading", "get_native_id")
33except AttributeError:
34    threading_get_native_id = None
35
36start_new_thread = get_original(six.moves._thread.__name__, "start_new_thread")
37thread_get_ident = get_original(six.moves._thread.__name__, "get_ident")
38Thread = get_original("threading", "Thread")
39Lock = get_original("threading", "Lock")
40
41
42if is_module_patched("threading"):
43
44    @attr.s
45    class DoubleLock(object):
46        """A lock that prevent concurrency from a gevent coroutine and from a threading.Thread at the same time."""
47
48        # This is a gevent-patched threading.Lock (= a gevent Lock)
49        _lock = attr.ib(factory=forksafe.Lock, init=False, repr=False)
50        # This is a unpatched threading.Lock (= a real threading.Lock)
51        _thread_lock = attr.ib(factory=lambda: forksafe.ResetObject(Lock), init=False, repr=False)
52
53        def acquire(self):
54            # type: () -> None
55            # You cannot acquire a gevent-lock from another thread if it has been acquired already:
56            # make sure we exclude the gevent-lock from being acquire by another thread by using a thread-lock first.
57            self._thread_lock.acquire()
58            self._lock.acquire()
59
60        def release(self):
61            # type: () -> None
62            self._lock.release()
63            self._thread_lock.release()
64
65        def __enter__(self):
66            # type: () -> DoubleLock
67            self.acquire()
68            return self
69
70        def __exit__(self, exc_type, exc_val, exc_tb):
71            self.release()
72
73
74else:
75    DoubleLock = threading.Lock  # type:  ignore[misc,assignment]
76
77
78if is_module_patched("threading"):
79    # NOTE: bold assumption: this module is always imported by the MainThread.
80    # The python `threading` module makes that assumption and it's beautiful we're going to do the same.
81    # We don't have the choice has we can't access the original MainThread
82    main_thread_id = thread_get_ident()
83else:
84    main_thread_id = compat.main_thread.ident
85