From 0435245b8ca16596cf41f60a76c902bd68769deb Mon Sep 17 00:00:00 2001 From: "William H.P. Nielsen" Date: Fri, 29 Sep 2017 16:22:58 +0200 Subject: [PATCH] fix: protect threading users against clutter (#771) --- qcodes/actions.py | 23 ++++++++++++++++++++++- qcodes/tests/test_threading.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 qcodes/tests/test_threading.py diff --git a/qcodes/actions.py b/qcodes/actions.py index 17f910cf94b..ed95c211e0f 100644 --- a/qcodes/actions.py +++ b/qcodes/actions.py @@ -8,6 +8,12 @@ _NO_SNAPSHOT = {'type': None, 'description': 'Action without snapshot'} +# exception when threading is attempted used to simultaneously +# query the same instrument for several values +class UnsafeThreadingException(Exception): + pass + + def _actions_snapshot(actions, update): """Make a list of snapshots from a list of actions.""" snapshot = [] @@ -113,9 +119,13 @@ def __init__(self, params_indices, data_set, use_threads): self.getters = [] self.param_ids = [] self.composite = [] + paramcheck = [] # list to check if parameters are unique for param, action_indices in params_indices: self.getters.append(param.get) + if param._instrument: + paramcheck.append((param, param._instrument)) + if hasattr(param, 'names'): part_ids = [] for i in range(len(param.names)): @@ -128,6 +138,17 @@ def __init__(self, params_indices, data_set, use_threads): self.param_ids.append(param_id) self.composite.append(False) + if self.use_threads: + insts = [p[1] for p in paramcheck] + if (len(set(insts)) != len(insts)): + duplicates = [p for p in paramcheck if insts.count(p[1]) > 1] + raise UnsafeThreadingException('Can not use threading to ' + 'read ' + 'several things from the same ' + 'instrument. Specifically, you ' + 'asked for' + ' {}.'.format(duplicates)) + def __call__(self, loop_indices, **ignore_kwargs): out_dict = {} if self.use_threads: @@ -166,7 +187,7 @@ class BreakIf: """ Loop action that breaks out of the loop if a condition is truthy. - + Args: condition (callable): a callable taking no arguments. Can be a simple function that returns truthy when it's time to quit diff --git a/qcodes/tests/test_threading.py b/qcodes/tests/test_threading.py new file mode 100644 index 00000000000..54f38d17f3c --- /dev/null +++ b/qcodes/tests/test_threading.py @@ -0,0 +1,32 @@ +import gc + +from unittest import TestCase + +from qcodes import Loop +from qcodes.actions import UnsafeThreadingException +from qcodes.tests.instrument_mocks import DummyInstrument + + +class TestUnsafeThreading(TestCase): + + def setUp(self): + self.inst1 = DummyInstrument(name='inst1', + gates=['v1', 'v2']) + self.inst2 = DummyInstrument(name='inst2', + gates=['v1', 'v2']) + + def tearDown(self): + self.inst1.close() + self.inst2.close() + + del self.inst1 + del self.inst2 + + gc.collect() + + def test_unsafe_exception(self): + to_meas = (self.inst1.v1, self.inst1.v2) + loop = Loop(self.inst2.v1.sweep(0, 1, num=10)).each(*to_meas) + + with self.assertRaises(UnsafeThreadingException): + loop.run(use_threads=True)