summaryrefslogtreecommitdiff
path: root/coverage
diff options
context:
space:
mode:
Diffstat (limited to 'coverage')
-rw-r--r--coverage/collector.py5
-rw-r--r--coverage/ctracer/tracer.c8
-rw-r--r--coverage/pytracer.py3
3 files changed, 15 insertions, 1 deletions
diff --git a/coverage/collector.py b/coverage/collector.py
index cfdcf402..a318c106 100644
--- a/coverage/collector.py
+++ b/coverage/collector.py
@@ -374,7 +374,10 @@ class Collector(object):
def abs_file_dict(d):
"""Return a dict like d, but with keys modified by `abs_file`."""
- return dict((abs_file(k), v) for k, v in iitems(d))
+ # The call to list() ensures that the GIL protects the dictionary
+ # iterator against concurrent modifications by tracers running
+ # in other threads.
+ return dict((abs_file(k), v) for k, v in list(iitems(d)))
if self.branch:
covdata.add_arcs(abs_file_dict(self.data))
diff --git a/coverage/ctracer/tracer.c b/coverage/ctracer/tracer.c
index 625a45a6..236fbbfa 100644
--- a/coverage/ctracer/tracer.c
+++ b/coverage/ctracer/tracer.c
@@ -833,6 +833,14 @@ CTracer_trace(CTracer *self, PyFrameObject *frame, int what, PyObject *arg_unuse
return RET_OK;
#endif
+ if (!self->started) {
+ /* If CTracer.stop() has been called from another thread, the tracer
+ is still active in the current thread. Let's deactivate ourselves
+ now. */
+ PyEval_SetTrace(NULL, NULL);
+ return RET_OK;
+ }
+
#if WHAT_LOG || TRACE_LOG
PyObject * ascii = NULL;
#endif
diff --git a/coverage/pytracer.py b/coverage/pytracer.py
index b41f4059..6ebb60c7 100644
--- a/coverage/pytracer.py
+++ b/coverage/pytracer.py
@@ -69,6 +69,9 @@ class PyTracer(object):
"""The trace function passed to sys.settrace."""
if self.stopped:
+ # The PyTrace.stop() method has been called by another thread,
+ # let's deactivate ourselves now.
+ self.stop()
return
if self.last_exc_back: