We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Stuck in "Waiting for GIL"
Hi,

We found a thread that seems blocked forever:

```
gdb python 30107
```

Here's the single thread:

```
(gdb) info threads
  Id   Target Id         Frame
* 1    Thread 0x7f1dd852e4c0 (LWP 30107) "/opt/simon/rele" 0x00007f1dd7d2e146 in do_futex_wait.constprop () from /lib64/libpthread.so.0
```

Here's the backtrace:

```
raceback (most recent call first):
  Waiting for the GIL
  File "/opt/simon/releases/b038662ce7d140609b1e4883a3ea0edf77851537/venv/lib/python2.7/site-packages/ddtrace/sampler.py", line 88, in sample
    with self._lock:
  File "/opt/simon/releases/b038662ce7d140609b1e4883a3ea0edf77851537/venv/lib/python2.7/site-packages/ddtrace/tracer.py", line 251, in start_span
    if self.priority_sampler.sample(span):
  File "/opt/simon/releases/b038662ce7d140609b1e4883a3ea0edf77851537/venv/lib/python2.7/site-packages/ddtrace/tracer.py", line 376, in trace
    span_type=span_type,
  File "/opt/simon/releases/b038662ce7d140609b1e4883a3ea0edf77851537/venv/lib/python2.7/site-packages/ddtrace/contrib/django/cache.py", line 56, in wrapped
    with tracer.trace('django.cache', span_type=TYPE, service=cache_service_name) as span:
```

I'm not sure how to troubleshoot from here.  Have you seen this before, or have any suggestions on what I could look at?  It's the only thread in process so nothing should be holding the GIL.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `ddtrace/sampler.py`
Content:
```
1 """Samplers manage the client-side trace sampling
2 
3 Any `sampled = False` trace won't be written, and can be ignored by the instrumentation.
4 """
5 from threading import Lock
6 
7 from .compat import iteritems
8 from .internal.logger import get_logger
9 
10 log = get_logger(__name__)
11 
12 MAX_TRACE_ID = 2 ** 64
13 
14 # Has to be the same factor and key as the Agent to allow chained sampling
15 KNUTH_FACTOR = 1111111111111111111
16 
17 
18 class AllSampler(object):
19     """Sampler sampling all the traces"""
20 
21     def sample(self, span):
22         return True
23 
24 
25 class RateSampler(object):
26     """Sampler based on a rate
27 
28     Keep (100 * `sample_rate`)% of the traces.
29     It samples randomly, its main purpose is to reduce the instrumentation footprint.
30     """
31 
32     def __init__(self, sample_rate=1):
33         if sample_rate <= 0:
34             log.error('sample_rate is negative or null, disable the Sampler')
35             sample_rate = 1
36         elif sample_rate > 1:
37             sample_rate = 1
38 
39         self.set_sample_rate(sample_rate)
40 
41         log.debug('initialized RateSampler, sample %s%% of traces', 100 * sample_rate)
42 
43     def set_sample_rate(self, sample_rate):
44         self.sample_rate = sample_rate
45         self.sampling_id_threshold = sample_rate * MAX_TRACE_ID
46 
47     def sample(self, span):
48         sampled = ((span.trace_id * KNUTH_FACTOR) % MAX_TRACE_ID) <= self.sampling_id_threshold
49 
50         return sampled
51 
52 
53 def _key(service=None, env=None):
54     service = service or ''
55     env = env or ''
56     return 'service:' + service + ',env:' + env
57 
58 
59 _default_key = _key()
60 
61 
62 class RateByServiceSampler(object):
63     """Sampler based on a rate, by service
64 
65     Keep (100 * `sample_rate`)% of the traces.
66     The sample rate is kept independently for each service/env tuple.
67     """
68 
69     def __init__(self, sample_rate=1):
70         self._lock = Lock()
71         self._by_service_samplers = {}
72         self._by_service_samplers[_default_key] = RateSampler(sample_rate)
73 
74     def _set_sample_rate_by_key(self, sample_rate, key):
75         with self._lock:
76             if key in self._by_service_samplers:
77                 self._by_service_samplers[key].set_sample_rate(sample_rate)
78             else:
79                 self._by_service_samplers[key] = RateSampler(sample_rate)
80 
81     def set_sample_rate(self, sample_rate, service='', env=''):
82         self._set_sample_rate_by_key(sample_rate, _key(service, env))
83 
84     def sample(self, span):
85         tags = span.tracer().tags
86         env = tags['env'] if 'env' in tags else None
87         key = _key(span.service, env)
88         with self._lock:
89             if key in self._by_service_samplers:
90                 return self._by_service_samplers[key].sample(span)
91             return self._by_service_samplers[_default_key].sample(span)
92 
93     def set_sample_rate_by_service(self, rate_by_service):
94         for key, sample_rate in iteritems(rate_by_service):
95             self._set_sample_rate_by_key(sample_rate, key)
96         with self._lock:
97             for key in list(self._by_service_samplers):
98                 if key not in rate_by_service and key != _default_key:
99                     del self._by_service_samplers[key]
100
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/ddtrace/sampler.py b/ddtrace/sampler.py
--- a/ddtrace/sampler.py
+++ b/ddtrace/sampler.py
@@ -2,8 +2,6 @@
 
 Any `sampled = False` trace won't be written, and can be ignored by the instrumentation.
 """
-from threading import Lock
-
 from .compat import iteritems
 from .internal.logger import get_logger
 
@@ -50,15 +48,6 @@
         return sampled
 
 
-def _key(service=None, env=None):
-    service = service or ''
-    env = env or ''
-    return 'service:' + service + ',env:' + env
-
-
-_default_key = _key()
-
-
 class RateByServiceSampler(object):
     """Sampler based on a rate, by service
 
@@ -66,34 +55,40 @@
     The sample rate is kept independently for each service/env tuple.
     """
 
+    @staticmethod
+    def _key(service=None, env=None):
+        """Compute a key with the same format used by the Datadog agent API."""
+        service = service or ''
+        env = env or ''
+        return 'service:' + service + ',env:' + env
+
     def __init__(self, sample_rate=1):
-        self._lock = Lock()
-        self._by_service_samplers = {}
-        self._by_service_samplers[_default_key] = RateSampler(sample_rate)
+        self.sample_rate = sample_rate
+        self._by_service_samplers = self._get_new_by_service_sampler()
 
-    def _set_sample_rate_by_key(self, sample_rate, key):
-        with self._lock:
-            if key in self._by_service_samplers:
-                self._by_service_samplers[key].set_sample_rate(sample_rate)
-            else:
-                self._by_service_samplers[key] = RateSampler(sample_rate)
+    def _get_new_by_service_sampler(self):
+        return {
+            self._default_key: RateSampler(self.sample_rate)
+        }
 
     def set_sample_rate(self, sample_rate, service='', env=''):
-        self._set_sample_rate_by_key(sample_rate, _key(service, env))
+        self._by_service_samplers[self._key(service, env)] = RateSampler(sample_rate)
 
     def sample(self, span):
         tags = span.tracer().tags
         env = tags['env'] if 'env' in tags else None
-        key = _key(span.service, env)
-        with self._lock:
-            if key in self._by_service_samplers:
-                return self._by_service_samplers[key].sample(span)
-            return self._by_service_samplers[_default_key].sample(span)
+        key = self._key(span.service, env)
+        return self._by_service_samplers.get(
+            key, self._by_service_samplers[self._default_key]
+        ).sample(span)
 
     def set_sample_rate_by_service(self, rate_by_service):
+        new_by_service_samplers = self._get_new_by_service_sampler()
         for key, sample_rate in iteritems(rate_by_service):
-            self._set_sample_rate_by_key(sample_rate, key)
-        with self._lock:
-            for key in list(self._by_service_samplers):
-                if key not in rate_by_service and key != _default_key:
-                    del self._by_service_samplers[key]
+            new_by_service_samplers[key] = RateSampler(sample_rate)
+
+        self._by_service_samplers = new_by_service_samplers
+
+
+# Default key for service with no specific rate
+RateByServiceSampler._default_key = RateByServiceSampler._key()

{"golden_diff": "diff --git a/ddtrace/sampler.py b/ddtrace/sampler.py\n--- a/ddtrace/sampler.py\n+++ b/ddtrace/sampler.py\n@@ -2,8 +2,6 @@\n \n Any `sampled = False` trace won't be written, and can be ignored by the instrumentation.\n \"\"\"\n-from threading import Lock\n-\n from .compat import iteritems\n from .internal.logger import get_logger\n \n@@ -50,15 +48,6 @@\n         return sampled\n \n \n-def _key(service=None, env=None):\n-    service = service or ''\n-    env = env or ''\n-    return 'service:' + service + ',env:' + env\n-\n-\n-_default_key = _key()\n-\n-\n class RateByServiceSampler(object):\n     \"\"\"Sampler based on a rate, by service\n \n@@ -66,34 +55,40 @@\n     The sample rate is kept independently for each service/env tuple.\n     \"\"\"\n \n+    @staticmethod\n+    def _key(service=None, env=None):\n+        \"\"\"Compute a key with the same format used by the Datadog agent API.\"\"\"\n+        service = service or ''\n+        env = env or ''\n+        return 'service:' + service + ',env:' + env\n+\n     def __init__(self, sample_rate=1):\n-        self._lock = Lock()\n-        self._by_service_samplers = {}\n-        self._by_service_samplers[_default_key] = RateSampler(sample_rate)\n+        self.sample_rate = sample_rate\n+        self._by_service_samplers = self._get_new_by_service_sampler()\n \n-    def _set_sample_rate_by_key(self, sample_rate, key):\n-        with self._lock:\n-            if key in self._by_service_samplers:\n-                self._by_service_samplers[key].set_sample_rate(sample_rate)\n-            else:\n-                self._by_service_samplers[key] = RateSampler(sample_rate)\n+    def _get_new_by_service_sampler(self):\n+        return {\n+            self._default_key: RateSampler(self.sample_rate)\n+        }\n \n     def set_sample_rate(self, sample_rate, service='', env=''):\n-        self._set_sample_rate_by_key(sample_rate, _key(service, env))\n+        self._by_service_samplers[self._key(service, env)] = RateSampler(sample_rate)\n \n     def sample(self, span):\n         tags = span.tracer().tags\n         env = tags['env'] if 'env' in tags else None\n-        key = _key(span.service, env)\n-        with self._lock:\n-            if key in self._by_service_samplers:\n-                return self._by_service_samplers[key].sample(span)\n-            return self._by_service_samplers[_default_key].sample(span)\n+        key = self._key(span.service, env)\n+        return self._by_service_samplers.get(\n+            key, self._by_service_samplers[self._default_key]\n+        ).sample(span)\n \n     def set_sample_rate_by_service(self, rate_by_service):\n+        new_by_service_samplers = self._get_new_by_service_sampler()\n         for key, sample_rate in iteritems(rate_by_service):\n-            self._set_sample_rate_by_key(sample_rate, key)\n-        with self._lock:\n-            for key in list(self._by_service_samplers):\n-                if key not in rate_by_service and key != _default_key:\n-                    del self._by_service_samplers[key]\n+            new_by_service_samplers[key] = RateSampler(sample_rate)\n+\n+        self._by_service_samplers = new_by_service_samplers\n+\n+\n+# Default key for service with no specific rate\n+RateByServiceSampler._default_key = RateByServiceSampler._key()\n", "issue": "Stuck in \"Waiting for GIL\"\nHi,\r\n\r\nWe found a thread that seems blocked forever:\r\n\r\n```\r\ngdb python 30107\r\n```\r\n\r\nHere's the single thread:\r\n\r\n```\r\n(gdb) info threads\r\n  Id   Target Id         Frame\r\n* 1    Thread 0x7f1dd852e4c0 (LWP 30107) \"/opt/simon/rele\" 0x00007f1dd7d2e146 in do_futex_wait.constprop () from /lib64/libpthread.so.0\r\n```\r\n\r\nHere's the backtrace:\r\n\r\n```\r\nraceback (most recent call first):\r\n  Waiting for the GIL\r\n  File \"/opt/simon/releases/b038662ce7d140609b1e4883a3ea0edf77851537/venv/lib/python2.7/site-packages/ddtrace/sampler.py\", line 88, in sample\r\n    with self._lock:\r\n  File \"/opt/simon/releases/b038662ce7d140609b1e4883a3ea0edf77851537/venv/lib/python2.7/site-packages/ddtrace/tracer.py\", line 251, in start_span\r\n    if self.priority_sampler.sample(span):\r\n  File \"/opt/simon/releases/b038662ce7d140609b1e4883a3ea0edf77851537/venv/lib/python2.7/site-packages/ddtrace/tracer.py\", line 376, in trace\r\n    span_type=span_type,\r\n  File \"/opt/simon/releases/b038662ce7d140609b1e4883a3ea0edf77851537/venv/lib/python2.7/site-packages/ddtrace/contrib/django/cache.py\", line 56, in wrapped\r\n    with tracer.trace('django.cache', span_type=TYPE, service=cache_service_name) as span:\r\n```\r\n\r\nI'm not sure how to troubleshoot from here.  Have you seen this before, or have any suggestions on what I could look at?  It's the only thread in process so nothing should be holding the GIL.\n", "before_files": [{"content": "\"\"\"Samplers manage the client-side trace sampling\n\nAny `sampled = False` trace won't be written, and can be ignored by the instrumentation.\n\"\"\"\nfrom threading import Lock\n\nfrom .compat import iteritems\nfrom .internal.logger import get_logger\n\nlog = get_logger(__name__)\n\nMAX_TRACE_ID = 2 ** 64\n\n# Has to be the same factor and key as the Agent to allow chained sampling\nKNUTH_FACTOR = 1111111111111111111\n\n\nclass AllSampler(object):\n    \"\"\"Sampler sampling all the traces\"\"\"\n\n    def sample(self, span):\n        return True\n\n\nclass RateSampler(object):\n    \"\"\"Sampler based on a rate\n\n    Keep (100 * `sample_rate`)% of the traces.\n    It samples randomly, its main purpose is to reduce the instrumentation footprint.\n    \"\"\"\n\n    def __init__(self, sample_rate=1):\n        if sample_rate <= 0:\n            log.error('sample_rate is negative or null, disable the Sampler')\n            sample_rate = 1\n        elif sample_rate > 1:\n            sample_rate = 1\n\n        self.set_sample_rate(sample_rate)\n\n        log.debug('initialized RateSampler, sample %s%% of traces', 100 * sample_rate)\n\n    def set_sample_rate(self, sample_rate):\n        self.sample_rate = sample_rate\n        self.sampling_id_threshold = sample_rate * MAX_TRACE_ID\n\n    def sample(self, span):\n        sampled = ((span.trace_id * KNUTH_FACTOR) % MAX_TRACE_ID) <= self.sampling_id_threshold\n\n        return sampled\n\n\ndef _key(service=None, env=None):\n    service = service or ''\n    env = env or ''\n    return 'service:' + service + ',env:' + env\n\n\n_default_key = _key()\n\n\nclass RateByServiceSampler(object):\n    \"\"\"Sampler based on a rate, by service\n\n    Keep (100 * `sample_rate`)% of the traces.\n    The sample rate is kept independently for each service/env tuple.\n    \"\"\"\n\n    def __init__(self, sample_rate=1):\n        self._lock = Lock()\n        self._by_service_samplers = {}\n        self._by_service_samplers[_default_key] = RateSampler(sample_rate)\n\n    def _set_sample_rate_by_key(self, sample_rate, key):\n        with self._lock:\n            if key in self._by_service_samplers:\n                self._by_service_samplers[key].set_sample_rate(sample_rate)\n            else:\n                self._by_service_samplers[key] = RateSampler(sample_rate)\n\n    def set_sample_rate(self, sample_rate, service='', env=''):\n        self._set_sample_rate_by_key(sample_rate, _key(service, env))\n\n    def sample(self, span):\n        tags = span.tracer().tags\n        env = tags['env'] if 'env' in tags else None\n        key = _key(span.service, env)\n        with self._lock:\n            if key in self._by_service_samplers:\n                return self._by_service_samplers[key].sample(span)\n            return self._by_service_samplers[_default_key].sample(span)\n\n    def set_sample_rate_by_service(self, rate_by_service):\n        for key, sample_rate in iteritems(rate_by_service):\n            self._set_sample_rate_by_key(sample_rate, key)\n        with self._lock:\n            for key in list(self._by_service_samplers):\n                if key not in rate_by_service and key != _default_key:\n                    del self._by_service_samplers[key]\n", "path": "ddtrace/sampler.py"}], "after_files": [{"content": "\"\"\"Samplers manage the client-side trace sampling\n\nAny `sampled = False` trace won't be written, and can be ignored by the instrumentation.\n\"\"\"\nfrom .compat import iteritems\nfrom .internal.logger import get_logger\n\nlog = get_logger(__name__)\n\nMAX_TRACE_ID = 2 ** 64\n\n# Has to be the same factor and key as the Agent to allow chained sampling\nKNUTH_FACTOR = 1111111111111111111\n\n\nclass AllSampler(object):\n    \"\"\"Sampler sampling all the traces\"\"\"\n\n    def sample(self, span):\n        return True\n\n\nclass RateSampler(object):\n    \"\"\"Sampler based on a rate\n\n    Keep (100 * `sample_rate`)% of the traces.\n    It samples randomly, its main purpose is to reduce the instrumentation footprint.\n    \"\"\"\n\n    def __init__(self, sample_rate=1):\n        if sample_rate <= 0:\n            log.error('sample_rate is negative or null, disable the Sampler')\n            sample_rate = 1\n        elif sample_rate > 1:\n            sample_rate = 1\n\n        self.set_sample_rate(sample_rate)\n\n        log.debug('initialized RateSampler, sample %s%% of traces', 100 * sample_rate)\n\n    def set_sample_rate(self, sample_rate):\n        self.sample_rate = sample_rate\n        self.sampling_id_threshold = sample_rate * MAX_TRACE_ID\n\n    def sample(self, span):\n        sampled = ((span.trace_id * KNUTH_FACTOR) % MAX_TRACE_ID) <= self.sampling_id_threshold\n\n        return sampled\n\n\nclass RateByServiceSampler(object):\n    \"\"\"Sampler based on a rate, by service\n\n    Keep (100 * `sample_rate`)% of the traces.\n    The sample rate is kept independently for each service/env tuple.\n    \"\"\"\n\n    @staticmethod\n    def _key(service=None, env=None):\n        \"\"\"Compute a key with the same format used by the Datadog agent API.\"\"\"\n        service = service or ''\n        env = env or ''\n        return 'service:' + service + ',env:' + env\n\n    def __init__(self, sample_rate=1):\n        self.sample_rate = sample_rate\n        self._by_service_samplers = self._get_new_by_service_sampler()\n\n    def _get_new_by_service_sampler(self):\n        return {\n            self._default_key: RateSampler(self.sample_rate)\n        }\n\n    def set_sample_rate(self, sample_rate, service='', env=''):\n        self._by_service_samplers[self._key(service, env)] = RateSampler(sample_rate)\n\n    def sample(self, span):\n        tags = span.tracer().tags\n        env = tags['env'] if 'env' in tags else None\n        key = self._key(span.service, env)\n        return self._by_service_samplers.get(\n            key, self._by_service_samplers[self._default_key]\n        ).sample(span)\n\n    def set_sample_rate_by_service(self, rate_by_service):\n        new_by_service_samplers = self._get_new_by_service_sampler()\n        for key, sample_rate in iteritems(rate_by_service):\n            new_by_service_samplers[key] = RateSampler(sample_rate)\n\n        self._by_service_samplers = new_by_service_samplers\n\n\n# Default key for service with no specific rate\nRateByServiceSampler._default_key = RateByServiceSampler._key()\n", "path": "ddtrace/sampler.py"}]}