diff options
Diffstat (limited to 'coverage')
-rw-r--r-- | coverage/collector.py | 5 | ||||
-rw-r--r-- | coverage/ctracer/tracer.c | 8 | ||||
-rw-r--r-- | coverage/pytracer.py | 3 |
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: |