prompt: 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()
  
  ```
  
golden_diff: 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()
  
verification_info: {"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"}]}