ChimeraTK-DeviceAccess  03.18.00
testAsyncRead.cpp
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
2 // SPDX-License-Identifier: LGPL-3.0-or-later
3 
4 #define BOOST_TEST_DYN_LINK
5 #define BOOST_TEST_MODULE AsyncReadTest
6 #include <boost/test/unit_test.hpp>
7 using namespace boost::unit_test_framework;
8 
9 #include "Device.h"
10 #include "DeviceAccessVersion.h"
11 #include "DeviceBackendImpl.h"
12 #include "ReadAnyGroup.h"
13 
14 #include <boost/thread.hpp>
15 
16 #include <algorithm>
17 #include <atomic>
18 #include <chrono>
19 #include <future>
20 #include <thread>
21 
22 using namespace boost::unit_test_framework;
23 using namespace ChimeraTK;
24 
25 std::string cdd = "(AsyncTestDummy)";
26 
27 /**********************************************************************************************************************/
28 
30  public:
31  explicit AsyncTestDummy() { FILL_VIRTUAL_FUNCTION_TEMPLATE_VTABLE(getRegisterAccessor_impl); }
32 
33  std::string readDeviceInfo() override { return "AsyncTestDummy"; }
34 
35  RegisterCatalogue getRegisterCatalogue() const override { throw; }
36 
37  static boost::shared_ptr<DeviceBackend> createInstance(std::string, std::map<std::string, std::string>) {
38  return boost::shared_ptr<DeviceBackend>(new AsyncTestDummy());
39  }
40 
41  template<typename UserType>
42  class Accessor : public NDRegisterAccessor<UserType> {
43  public:
44  Accessor(AsyncTestDummy* backend, const RegisterPath& registerPathName, AccessModeFlags& flags)
45  : NDRegisterAccessor<UserType>(registerPathName, flags), _backend(backend) {
46  buffer_2D.resize(1);
47  buffer_2D[0].resize(1);
48  this->_readQueue = {3}; // this accessor is using a queue length of 3
49  _backend->notificationQueue[getName()] = this->_readQueue;
50  }
51 
52  void doReadTransferSynchronously() override {}
53 
54  bool doWriteTransfer(ChimeraTK::VersionNumber) override { return false; }
55 
57 
59 
60  void doPreRead(TransferType) override {}
61 
62  void doPostRead(TransferType, bool hasNewData) override {
63  if constexpr(!std::is_same<UserType, Void>::value) { // Will not be used for Void-Type
64  ++nPostReadCalled;
65  if(!hasNewData) return;
66  buffer_2D[0][0] = _backend->registers.at(getName());
67  this->_versionNumber = {};
68  }
69  }
70 
71  [[nodiscard]] bool isReadOnly() const override { return false; }
72  [[nodiscard]] bool isReadable() const override { return true; }
73  [[nodiscard]] bool isWriteable() const override { return true; }
74 
75  std::vector<boost::shared_ptr<TransferElement>> getHardwareAccessingElements() override {
76  return {this->shared_from_this()};
77  }
78  std::list<boost::shared_ptr<TransferElement>> getInternalElements() override { return {}; }
79 
80  size_t nPostReadCalled{0};
81 
82  protected:
86  };
87 
88  template<typename UserType>
89  boost::shared_ptr<NDRegisterAccessor<UserType>> getRegisterAccessor_impl(
90  const RegisterPath& registerPathName, size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags) {
91  assert(numberOfWords == 1);
92  assert(wordOffsetInRegister == 0);
93  (void)numberOfWords;
94  (void)wordOffsetInRegister;
95  boost::shared_ptr<NDRegisterAccessor<UserType>> retval =
96  boost::make_shared<Accessor<UserType>>(this, registerPathName, flags);
97  retval->setExceptionBackend(shared_from_this());
98  return retval;
99  }
100 
102 
103  void open() override {
104  _opened = true;
105  _hasActiveException = false;
106  }
107 
108  void close() override { _opened = false; }
109 
110  std::map<std::string, cppext::future_queue<void>> notificationQueue;
111  std::map<std::string, size_t> registers;
112 
113  void setExceptionImpl() noexcept override {
114  // FIXME !!!!
115  assert(false); // Wrong implementation. All notification queues must see an exception.
116  }
117  bool _hasActiveException{false};
118 };
119 
120 /**********************************************************************************************************************/
121 
122 struct Fixture {
124  BackendFactory::getInstance().registerBackendType(
125  "AsyncTestDummy", &AsyncTestDummy::createInstance, {}, CHIMERATK_DEVICEACCESS_VERSION);
126  BackendFactory::getInstance().setDMapFilePath("dummies.dmap");
127  }
128 };
129 static Fixture fixture;
130 
131 /**********************************************************************************************************************/
132 /**********************************************************************************************************************/
133 
134 BOOST_AUTO_TEST_CASE(testAsyncRead) {
135  std::cout << "testAsyncRead" << std::endl;
136 
137  Device device;
138  device.open(cdd);
139  auto backend = boost::dynamic_pointer_cast<AsyncTestDummy>(BackendFactory::getInstance().createBackend(cdd));
140  BOOST_CHECK(backend != nullptr);
141 
142  // obtain register accessor with integral type
143  auto accessor = device.getScalarRegisterAccessor<int>("REG", 0, {AccessMode::wait_for_new_data});
144 
145  // simple reading through readAsync without actual need
146  backend->registers["/REG"] = 5;
147 
148  auto waitForRead = std::async(std::launch::async, [&accessor] { accessor.read(); });
149  auto waitStatus = waitForRead.wait_for(std::chrono::seconds(1));
150  BOOST_CHECK(waitStatus != std::future_status::ready); // future not ready yet, i.e. read() not fninished.
151 
152  backend->notificationQueue["/REG"].push(); // trigger transfer
153  waitForRead.wait(); // wait for the read to finish
154 
155  BOOST_CHECK(accessor == 5);
156  BOOST_CHECK(backend->notificationQueue["/REG"].empty());
157 
158  backend->registers["/REG"] = 6;
159  waitForRead = std::async(std::launch::async, [&accessor] { accessor.read(); });
160  waitStatus = waitForRead.wait_for(std::chrono::seconds(1));
161  BOOST_CHECK(waitStatus != std::future_status::ready); // future not ready yet, i.e. read() not fninished.
162 
163  backend->notificationQueue["/REG"].push(); // trigger transfer
164  waitForRead.wait();
165 
166  BOOST_CHECK(accessor == 6);
167  BOOST_CHECK(backend->notificationQueue["/REG"].empty());
168 
169  device.close();
170 }
171 
172 /**********************************************************************************************************************/
173 
174 BOOST_AUTO_TEST_CASE(testReadAny) {
175  std::cout << "testReadAny" << std::endl;
176 
177  Device device;
178  device.open(cdd);
179  auto backend = boost::dynamic_pointer_cast<AsyncTestDummy>(BackendFactory::getInstance().createBackend(cdd));
180  BOOST_CHECK(backend != nullptr);
181 
182  // obtain register accessor with integral type
183  auto a1 = device.getScalarRegisterAccessor<uint8_t>("a1", 0, {AccessMode::wait_for_new_data});
184  auto a2 = device.getScalarRegisterAccessor<int32_t>("a2", 0, {AccessMode::wait_for_new_data});
185  auto a3 = device.getScalarRegisterAccessor<int32_t>("a3", 0, {AccessMode::wait_for_new_data});
186  auto a4 = device.getScalarRegisterAccessor<int32_t>("a4", 0, {AccessMode::wait_for_new_data});
187 
188  // initialise the buffers of the accessors
189  a1 = 1;
190  a2 = 2;
191  a3 = 3;
192  a4 = 4;
193 
194  // initialise the dummy registers
195  backend->registers["/a1"] = 42;
196  backend->registers["/a2"] = 123;
197  backend->registers["/a3"] = 120;
198  backend->registers["/a4"] = 345;
199 
200  // Create ReadAnyGroup
201  ReadAnyGroup group;
202  group.add(a1);
203  group.add(a2);
204  group.add(a3);
205  group.add(a4);
206  group.finalise();
207 
209 
210  // register 1
211  {
212  // launch the readAny in a background thread
213  std::atomic<bool> flag{false};
214  std::thread thread([&group, &flag, &id] {
215  id = group.readAny();
216  flag = true;
217  });
218 
219  // check that it doesn't return too soon
220  usleep(100000);
221  BOOST_CHECK(flag == false);
222 
223  // write register and check that readAny() completes
224  backend->notificationQueue["/a1"].push(); // trigger transfer
225  thread.join();
226  BOOST_CHECK(a1 == 42);
227  BOOST_CHECK(a2 == 2);
228  BOOST_CHECK(a3 == 3);
229  BOOST_CHECK(a4 == 4);
230  BOOST_CHECK(id == a1.getId());
231  }
232 
233  // register 3
234  {
235  // launch the readAny in a background thread
236  std::atomic<bool> flag{false};
237  std::thread thread([&group, &flag, &id] {
238  id = group.readAny();
239  flag = true;
240  });
241 
242  // check that it doesn't return too soon
243  usleep(100000);
244  BOOST_CHECK(flag == false);
245 
246  // write register and check that readAny() completes
247  backend->notificationQueue["/a3"].push(); // trigger transfer
248  thread.join();
249  BOOST_CHECK(a1 == 42);
250  BOOST_CHECK(a2 == 2);
251  BOOST_CHECK(a3 == 120);
252  BOOST_CHECK(a4 == 4);
253  BOOST_CHECK(id == a3.getId());
254  }
255 
256  // register 3 again
257  {
258  // launch the readAny in a background thread
259  std::atomic<bool> flag{false};
260  std::thread thread([&group, &flag, &id] {
261  id = group.readAny();
262  flag = true;
263  });
264 
265  // check that it doesn't return too soon
266  usleep(100000);
267  BOOST_CHECK(flag == false);
268 
269  // write register and check that readAny() completes
270  backend->registers["/a3"] = 121;
271  backend->notificationQueue["/a3"].push(); // trigger transfer
272  thread.join();
273  BOOST_CHECK(a1 == 42);
274  BOOST_CHECK(a2 == 2);
275  BOOST_CHECK(a3 == 121);
276  BOOST_CHECK(a4 == 4);
277  BOOST_CHECK(id == a3.getId());
278  }
279 
280  // register 2
281  {
282  // launch the readAny in a background thread
283  std::atomic<bool> flag{false};
284  std::thread thread([&group, &flag, &id] {
285  id = group.readAny();
286  flag = true;
287  });
288 
289  // check that it doesn't return too soon
290  usleep(100000);
291  BOOST_CHECK(flag == false);
292 
293  // write register and check that readAny() completes
294  backend->notificationQueue["/a2"].push(); // trigger transfer
295  thread.join();
296  BOOST_CHECK(a1 == 42);
297  BOOST_CHECK(a2 == 123);
298  BOOST_CHECK(a3 == 121);
299  BOOST_CHECK(a4 == 4);
300  BOOST_CHECK(id == a2.getId());
301  }
302 
303  // register 4
304  {
305  // launch the readAny in a background thread
306  std::atomic<bool> flag{false};
307  std::thread thread([&group, &flag, &id] {
308  id = group.readAny();
309  flag = true;
310  });
311 
312  // check that it doesn't return too soon
313  usleep(100000);
314  BOOST_CHECK(flag == false);
315 
316  // write register and check that readAny() completes
317  backend->notificationQueue["/a4"].push(); // trigger transfer
318  thread.join();
319  BOOST_CHECK(a1 == 42);
320  BOOST_CHECK(a2 == 123);
321  BOOST_CHECK(a3 == 121);
322  BOOST_CHECK(a4 == 345);
323  BOOST_CHECK(id == a4.getId());
324  }
325 
326  // register 4 again
327  {
328  // launch the readAny in a background thread
329  std::atomic<bool> flag{false};
330  std::thread thread([&group, &flag, &id] {
331  id = group.readAny();
332  flag = true;
333  });
334 
335  // check that it doesn't return too soon
336  usleep(100000);
337  BOOST_CHECK(flag == false);
338 
339  // write register and check that readAny() completes
340  backend->notificationQueue["/a4"].push(); // trigger transfer
341  thread.join();
342  BOOST_CHECK(a1 == 42);
343  BOOST_CHECK(a2 == 123);
344  BOOST_CHECK(a3 == 121);
345  BOOST_CHECK(a4 == 345);
346  BOOST_CHECK(id == a4.getId());
347  }
348 
349  // register 3 a 3rd time
350  {
351  // launch the readAny in a background thread
352  std::atomic<bool> flag{false};
353  std::thread thread([&group, &flag, &id] {
354  id = group.readAny();
355  flag = true;
356  });
357 
358  // check that it doesn't return too soon
359  usleep(100000);
360  BOOST_CHECK(flag == false);
361 
362  // write register and check that readAny() completes
363  backend->registers["/a3"] = 122;
364  backend->notificationQueue["/a3"].push(); // trigger transfer
365  thread.join();
366  BOOST_CHECK(a1 == 42);
367  BOOST_CHECK(a2 == 123);
368  BOOST_CHECK(a3 == 122);
369  BOOST_CHECK(a4 == 345);
370  BOOST_CHECK(id == a3.getId());
371  }
372 
373  // register 1 and then register 2 (order should be guaranteed)
374  {
375  // write to register 1 and trigger transfer
376  backend->registers["/a1"] = 55;
377  backend->notificationQueue["/a1"].push(); // trigger transfer
378 
379  // same with register 2
380  backend->registers["/a2"] = 66;
381  backend->notificationQueue["/a2"].push(); // trigger transfer
382 
383  BOOST_CHECK_EQUAL((int)a1, 42);
384  BOOST_CHECK_EQUAL((int)a2, 123);
385 
386  // no point to use a thread here
387  auto r = group.readAny();
388  BOOST_CHECK(a1.getId() == r);
389  BOOST_CHECK_EQUAL((int)a1, 55);
390  BOOST_CHECK_EQUAL((int)a2, 123);
391 
392  r = group.readAny();
393  BOOST_CHECK(a2.getId() == r);
394  BOOST_CHECK(a1 == 55);
395  BOOST_CHECK(a2 == 66);
396  }
397 
398  // registers in order: 4, 2, 3 and 1
399  {
400  // register 4 (see above for explanation)
401  backend->registers["/a4"] = 11;
402  backend->notificationQueue["/a4"].push(); // trigger transfer
403 
404  // register 2
405  backend->registers["/a2"] = 22;
406  backend->notificationQueue["/a2"].push(); // trigger transfer
407 
408  // register 3
409  backend->registers["/a3"] = 33;
410  backend->notificationQueue["/a3"].push(); // trigger transfer
411 
412  // register 1
413  backend->registers["/a1"] = 44;
414  backend->notificationQueue["/a1"].push(); // trigger transfer
415 
416  // no point to use a thread here
417  auto r = group.readAny();
418  BOOST_CHECK(a4.getId() == r);
419  BOOST_CHECK(a1 == 55);
420  BOOST_CHECK(a2 == 66);
421  BOOST_CHECK(a3 == 122);
422  BOOST_CHECK(a4 == 11);
423 
424  r = group.readAny();
425  BOOST_CHECK(a2.getId() == r);
426  BOOST_CHECK(a1 == 55);
427  BOOST_CHECK(a2 == 22);
428  BOOST_CHECK(a3 == 122);
429  BOOST_CHECK(a4 == 11);
430 
431  r = group.readAny();
432  BOOST_CHECK(a3.getId() == r);
433  BOOST_CHECK(a1 == 55);
434  BOOST_CHECK(a2 == 22);
435  BOOST_CHECK(a3 == 33);
436  BOOST_CHECK(a4 == 11);
437 
438  r = group.readAny();
439  BOOST_CHECK(a1.getId() == r);
440  BOOST_CHECK(a1 == 44);
441  BOOST_CHECK(a2 == 22);
442  BOOST_CHECK(a3 == 33);
443  BOOST_CHECK(a4 == 11);
444  }
445 
446  device.close();
447 }
448 
449 /**********************************************************************************************************************/
450 
451 BOOST_AUTO_TEST_CASE(testReadAnyWithPoll) {
452  std::cout << "testReadAnyWithPoll" << std::endl;
453 
454  Device device;
455  device.open(cdd);
456  auto backend = boost::dynamic_pointer_cast<AsyncTestDummy>(BackendFactory::getInstance().createBackend(cdd));
457  BOOST_CHECK(backend != nullptr);
458 
459  // obtain register accessor with integral type
460  auto a1 = device.getScalarRegisterAccessor<uint8_t>("a1", 0, {AccessMode::wait_for_new_data});
461  auto a2 = device.getScalarRegisterAccessor<int32_t>("a2", 0, {AccessMode::wait_for_new_data});
462  auto a3 = device.getScalarRegisterAccessor<int32_t>("a3");
463  auto a4 = device.getScalarRegisterAccessor<int32_t>("a4");
464 
465  // initialise the buffers of the accessors
466  a1 = 1;
467  a2 = 2;
468  a3 = 3;
469  a4 = 4;
470 
471  // initialise the dummy registers
472  backend->registers["/a1"] = 42;
473  backend->registers["/a2"] = 123;
474  backend->registers["/a3"] = 120;
475  backend->registers["/a4"] = 345;
476 
477  // Create ReadAnyGroup
478  ReadAnyGroup group;
479  group.add(a1);
480  group.add(a2);
481  group.add(a3);
482  group.add(a4);
483  group.finalise();
484 
486 
487  // register 1
488  {
489  // launch the readAny in a background thread
490  std::atomic<bool> flag{false};
491  std::thread thread([&group, &flag, &id] {
492  id = group.readAny();
493  flag = true;
494  });
495 
496  // check that it doesn't return too soon
497  usleep(100000);
498  BOOST_CHECK(flag == false);
499 
500  // write register and check that readAny() completes
501  backend->notificationQueue["/a1"].push(); // trigger transfer
502  thread.join();
503  BOOST_CHECK(a1 == 42);
504  BOOST_CHECK(a2 == 2);
505  BOOST_CHECK(a3 == 120);
506  BOOST_CHECK(a4 == 345);
507  BOOST_CHECK(id == a1.getId());
508  }
509 
510  backend->registers["/a3"] = 121;
511  backend->registers["/a4"] = 346;
512 
513  // register 2
514  {
515  // launch the readAny in a background thread
516  std::atomic<bool> flag{false};
517  std::thread thread([&group, &flag, &id] {
518  id = group.readAny();
519  flag = true;
520  });
521 
522  // check that it doesn't return too soon
523  usleep(100000);
524  BOOST_CHECK(flag == false);
525 
526  // write register and check that readAny() completes
527  backend->notificationQueue["/a2"].push(); // trigger transfer
528  thread.join();
529  BOOST_CHECK(a1 == 42);
530  BOOST_CHECK(a2 == 123);
531  BOOST_CHECK(a3 == 121);
532  BOOST_CHECK(a4 == 346);
533  BOOST_CHECK(id == a2.getId());
534  }
535 
536  device.close();
537 }
538 
539 /**********************************************************************************************************************/
540 
541 BOOST_AUTO_TEST_CASE(testWaitAny) {
542  std::cout << "testWaitAny" << std::endl;
543 
544  Device device;
545  device.open(cdd);
546  auto backend = boost::dynamic_pointer_cast<AsyncTestDummy>(BackendFactory::getInstance().createBackend(cdd));
547  BOOST_CHECK(backend != nullptr);
548 
549  // obtain register accessor with integral type
550  auto a1 = device.getScalarRegisterAccessor<uint8_t>("a1", 0, {AccessMode::wait_for_new_data});
551  auto a2 = device.getScalarRegisterAccessor<int32_t>("a2", 0, {AccessMode::wait_for_new_data});
552  auto a3 = device.getScalarRegisterAccessor<int32_t>("a3");
553  auto a4 = device.getScalarRegisterAccessor<int32_t>("a4");
554 
555  // initialise the buffers of the accessors
556  a1 = 1;
557  a2 = 2;
558  a3 = 3;
559  a4 = 4;
560 
561  // initialise the dummy registers
562  backend->registers["/a1"] = 42;
563  backend->registers["/a2"] = 123;
564  backend->registers["/a3"] = 120;
565  backend->registers["/a4"] = 345;
566 
567  // Create ReadAnyGroup
568  ReadAnyGroup group;
569  group.add(a1);
570  group.add(a2);
571  group.add(a3);
572  group.add(a4);
573  group.finalise();
574 
575  ReadAnyGroup::Notification notification;
576 
577  // register 1
578  {
579  // launch the readAny in a background thread
580  std::atomic<bool> flag{false};
581  std::thread thread([&group, &flag, &notification] {
582  notification = group.waitAny();
583  flag = true;
584  });
585 
586  // check that it doesn't return too soon
587  usleep(100000);
588  BOOST_CHECK(flag == false);
589 
590  // write register and check that readAny() completes
591  backend->notificationQueue["/a1"].push(); // trigger transfer
592  thread.join();
593  BOOST_CHECK(notification.getId() == a1.getId());
594  BOOST_CHECK(a1 == 1);
595  BOOST_CHECK(a2 == 2);
596  BOOST_CHECK(a3 == 3);
597  BOOST_CHECK(a4 == 4);
598  BOOST_CHECK(notification.accept());
599  BOOST_CHECK(a1 == 42);
600  BOOST_CHECK(a2 == 2);
601  BOOST_CHECK(a3 == 3);
602  BOOST_CHECK(a4 == 4);
603  group.processPolled();
604  BOOST_CHECK(a1 == 42);
605  BOOST_CHECK(a2 == 2);
606  BOOST_CHECK(a3 == 120);
607  BOOST_CHECK(a4 == 345);
608  }
609 
610  backend->registers["/a3"] = 121;
611  backend->registers["/a4"] = 346;
612 
613  // register 2
614  {
615  // launch the readAny in a background thread
616  std::atomic<bool> flag{false};
617  std::thread thread([&group, &flag, &notification] {
618  notification = group.waitAny();
619  flag = true;
620  });
621 
622  // check that it doesn't return too soon
623  usleep(100000);
624  BOOST_CHECK(flag == false);
625 
626  // write register and check that readAny() completes
627  backend->notificationQueue["/a2"].push(); // trigger transfer
628  thread.join();
629  BOOST_CHECK(notification.getId() == a2.getId());
630  BOOST_CHECK(a1 == 42);
631  BOOST_CHECK(a2 == 2);
632  BOOST_CHECK(a3 == 120);
633  BOOST_CHECK(a4 == 345);
634  BOOST_CHECK(notification.accept());
635  group.processPolled();
636  BOOST_CHECK(a1 == 42);
637  BOOST_CHECK(a2 == 123);
638  BOOST_CHECK(a3 == 121);
639  BOOST_CHECK(a4 == 346);
640  }
641 
642  device.close();
643 }
644 
645 /**********************************************************************************************************************/
646 
647 BOOST_AUTO_TEST_CASE(testReadAnyException) {
648  std::cout << "testReadAnyException" << std::endl;
649 
650  Device device;
651  device.open(cdd);
652  auto backend = boost::dynamic_pointer_cast<AsyncTestDummy>(BackendFactory::getInstance().createBackend(cdd));
653  BOOST_CHECK(backend != nullptr);
654 
655  // obtain register accessor with integral type
656  auto a1 = device.getScalarRegisterAccessor<uint8_t>("a1", 0, {AccessMode::wait_for_new_data});
657  auto a2 = device.getScalarRegisterAccessor<int32_t>("a2", 0, {AccessMode::wait_for_new_data});
658  auto a3 = device.getScalarRegisterAccessor<int32_t>("a3", 0, {AccessMode::wait_for_new_data});
659  auto a4 = device.getScalarRegisterAccessor<int32_t>("a4", 0, {AccessMode::wait_for_new_data});
660  auto a1_casted = boost::dynamic_pointer_cast<AsyncTestDummy::Accessor<uint8_t>>(a1.getHighLevelImplElement());
661  assert(a1_casted);
662 
663  // initialise the buffers of the accessors
664  a1 = 1;
665  a2 = 2;
666  a3 = 3;
667  a4 = 4;
668 
669  // initialise the dummy registers
670  backend->registers["/a1"] = 42;
671  backend->registers["/a2"] = 123;
672  backend->registers["/a3"] = 120;
673  backend->registers["/a4"] = 345;
674 
675  // Create ReadAnyGroup
676  ReadAnyGroup group;
677  group.add(a1);
678  group.add(a2);
679  group.add(a3);
680  group.add(a4);
681  group.finalise();
682 
683  // ChimeraTK::runtime_error
684  {
685  auto nPostReadCalledReference = a1_casted->nPostReadCalled;
686 
687  // launch the readAny in a background thread
688  bool exceptionFound{false};
689  std::thread thread([&group, &exceptionFound] {
690  try {
691  group.readAny();
692  }
693  catch(ChimeraTK::runtime_error&) {
694  exceptionFound = true;
695  }
696  });
697 
698  // put exception to queue
699  try {
700  throw ChimeraTK::runtime_error("Test exception");
701  }
702  catch(...) {
703  backend->notificationQueue["/a1"].push_exception(std::current_exception()); // trigger transfer
704  }
705  thread.join();
706  BOOST_TEST(exceptionFound == true);
707  BOOST_TEST(a1_casted->nPostReadCalled == nPostReadCalledReference + 1);
708  }
709 
710  // boost::thread_interrupted
711  {
712  auto nPostReadCalledReference = a1_casted->nPostReadCalled;
713 
714  // launch the readAny in a background thread
715  bool exceptionFound{false};
716  std::thread thread([&group, &exceptionFound] {
717  try {
718  group.readAny();
719  }
720  catch(boost::thread_interrupted&) {
721  exceptionFound = true;
722  }
723  });
724 
725  // put exception to queue
726  backend->notificationQueue["/a1"].push_exception(
727  std::make_exception_ptr(boost::thread_interrupted())); // trigger transfer
728 
729  thread.join();
730  BOOST_TEST(exceptionFound == true);
731  BOOST_TEST(a1_casted->nPostReadCalled == nPostReadCalledReference + 1);
732  }
733 
734  device.close();
735 }
736 
737 /**********************************************************************************************************************/
738 
739 BOOST_AUTO_TEST_CASE(testReadAnyInvalid) {
740  std::cout << "testReadAnyInvalid" << std::endl;
741 
742  Device device;
743  device.open(cdd);
744  auto backend = boost::dynamic_pointer_cast<AsyncTestDummy>(BackendFactory::getInstance().createBackend(cdd));
745  BOOST_CHECK(backend != nullptr);
746 
747  // obtain register accessor with integral type
748  auto a1 = device.getScalarRegisterAccessor<uint8_t>("a1", 0, {AccessMode::wait_for_new_data});
749 
750  // Create ReadAnyGroup
751  ReadAnyGroup group{a1};
752 
753  {
754  // direct read on accessor in ReadAnyGroup is not allowed and should issue exception.
755  BOOST_CHECK_THROW(a1.read(), ChimeraTK::logic_error);
756  }
757 
758  device.close();
759 }
760 
761 /**********************************************************************************************************************/
ChimeraTK::ReadAnyGroup::add
void add(TransferElementAbstractor &element)
Add register to group.
Definition: ReadAnyGroup.h:446
AsyncTestDummy::createInstance
static boost::shared_ptr< DeviceBackend > createInstance(std::string, std::map< std::string, std::string >)
Definition: testAsyncRead.cpp:37
device
ctk::Device device
Definition: testExceptionDummyDevice.cc:18
ChimeraTK::Device::close
void close()
Close the device.
Definition: Device.cc:66
AsyncTestDummy::Accessor::doWriteTransfer
bool doWriteTransfer(ChimeraTK::VersionNumber) override
Definition: testAsyncRead.cpp:54
ReadAnyGroup.h
AsyncTestDummy::Accessor::doReadTransferSynchronously
void doReadTransferSynchronously() override
Definition: testAsyncRead.cpp:52
ChimeraTK::ReadAnyGroup::Notification
Notification object returned by waitAny().
Definition: ReadAnyGroup.h:27
AsyncTestDummy::Accessor::Accessor
Accessor(AsyncTestDummy *backend, const RegisterPath &registerPathName, AccessModeFlags &flags)
Definition: testAsyncRead.cpp:44
AsyncTestDummy::Accessor::getHardwareAccessingElements
std::vector< boost::shared_ptr< TransferElement > > getHardwareAccessingElements() override
Definition: testAsyncRead.cpp:75
AsyncTestDummy::close
void close() override
Close the device.
Definition: testAsyncRead.cpp:108
AsyncTestDummy::Accessor::_backend
AsyncTestDummy * _backend
Definition: testAsyncRead.cpp:83
AsyncTestDummy::Accessor::doPostRead
void doPostRead(TransferType, bool hasNewData) override
Definition: testAsyncRead.cpp:62
ChimeraTK::ReadAnyGroup::Notification::getId
TransferElementID getId()
Return the ID of the transfer element for which this notification has been generated.
Definition: ReadAnyGroup.h:380
FILL_VIRTUAL_FUNCTION_TEMPLATE_VTABLE
#define FILL_VIRTUAL_FUNCTION_TEMPLATE_VTABLE(functionName)
Fill the vtable of a virtual function template defined with DEFINE_VIRTUAL_FUNCTION_TEMPLATE.
Definition: VirtualFunctionTemplate.h:84
DeviceBackendImpl.h
AsyncTestDummy::Accessor::isReadable
bool isReadable() const override
Definition: testAsyncRead.cpp:72
cdd
std::string cdd
Definition: testAsyncRead.cpp:25
AsyncTestDummy::getRegisterAccessor_impl
boost::shared_ptr< NDRegisterAccessor< UserType > > getRegisterAccessor_impl(const RegisterPath &registerPathName, size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags)
Definition: testAsyncRead.cpp:89
ChimeraTK::runtime_error
Exception thrown when a runtime error has occured.
Definition: Exception.h:18
ChimeraTK::RegisterCatalogue
Catalogue of register information.
Definition: RegisterCatalogue.h:20
AsyncTestDummy::Accessor::getInternalElements
std::list< boost::shared_ptr< TransferElement > > getInternalElements() override
Definition: testAsyncRead.cpp:78
ChimeraTK::ReadAnyGroup::Notification::accept
bool accept()
Accept the notification.
Definition: ReadAnyGroup.h:351
AsyncTestDummy::Accessor::isReadOnly
bool isReadOnly() const override
Definition: testAsyncRead.cpp:71
ChimeraTK::ReadAnyGroup::processPolled
void processPolled()
Process polled transfer elements (update them if new values are available).
Definition: ReadAnyGroup.h:620
AsyncTestDummy::Accessor::doPreWrite
void doPreWrite(TransferType, VersionNumber) override
Definition: testAsyncRead.cpp:56
Device.h
AsyncTestDummy
Definition: testAsyncRead.cpp:29
ChimeraTK::ReadAnyGroup::finalise
void finalise()
Finalise the group.
Definition: ReadAnyGroup.h:478
ChimeraTK::TransferType
TransferType
Used to indicate the applicable operation on a Transferelement.
Definition: TransferElement.h:51
AsyncTestDummy::open
void open() override
Open the device.
Definition: testAsyncRead.cpp:103
AsyncTestDummy::Accessor
Definition: testAsyncRead.cpp:42
ChimeraTK::ReadAnyGroup::readAny
TransferElementID readAny()
Wait until one of the elements in this group has received an update.
Definition: ReadAnyGroup.h:498
AsyncTestDummy::notificationQueue
std::map< std::string, cppext::future_queue< void > > notificationQueue
Definition: testAsyncRead.cpp:110
Fixture
Definition: testAsyncRead.cpp:122
AsyncTestDummy::Accessor::doPostWrite
void doPostWrite(TransferType, VersionNumber) override
Definition: testAsyncRead.cpp:58
ChimeraTK::Device
Class allows to read/write registers from device.
Definition: Device.h:39
AsyncTestDummy::AsyncTestDummy
AsyncTestDummy()
Definition: testAsyncRead.cpp:31
ChimeraTK::DeviceBackendImpl
DeviceBackendImpl implements some basic functionality which should be available for all backends.
Definition: DeviceBackendImpl.h:25
ChimeraTK::Device::open
void open(std::string const &aliasName)
Open a device by the given alias name from the DMAP file.
Definition: Device.cc:58
AsyncTestDummy::readDeviceInfo
std::string readDeviceInfo() override
Return a device information string containing hardware details like the firmware version number or th...
Definition: testAsyncRead.cpp:33
ChimeraTK::RegisterPath
Class to store a register path name.
Definition: RegisterPath.h:16
AsyncTestDummy::setExceptionImpl
void setExceptionImpl() noexcept override
Function to be (optionally) implemented by backends if additional actions are needed when switching t...
Definition: testAsyncRead.cpp:113
ChimeraTK::Device::getScalarRegisterAccessor
ScalarRegisterAccessor< UserType > getScalarRegisterAccessor(const RegisterPath &registerPathName, size_t wordOffsetInRegister=0, const AccessModeFlags &flags=AccessModeFlags({})) const
Get a ScalarRegisterObject object for the given register.
Definition: Device.h:263
ChimeraTK::ReadAnyGroup
Group several registers (= TransferElement) to allow waiting for an update of any of the registers.
Definition: ReadAnyGroup.h:21
ChimeraTK::VersionNumber
Class for generating and holding version numbers without exposing a numeric representation.
Definition: VersionNumber.h:23
DEFINE_VIRTUAL_FUNCTION_TEMPLATE_VTABLE_FILLER
#define DEFINE_VIRTUAL_FUNCTION_TEMPLATE_VTABLE_FILLER(className, functionName, numberOfArguments)
Compatibility, do not use.
Definition: VirtualFunctionTemplate.h:108
AsyncTestDummy::getRegisterCatalogue
RegisterCatalogue getRegisterCatalogue() const override
Return the register catalogue with detailed information on all registers.
Definition: testAsyncRead.cpp:35
ChimeraTK::AccessModeFlags
Set of AccessMode flags with additional functionality for an easier handling.
Definition: AccessMode.h:48
ChimeraTK::TransferElementID
Simple class holding a unique ID for a TransferElement.
Definition: TransferElementID.h:17
Fixture::Fixture
Fixture()
Definition: testAsyncRead.cpp:123
ChimeraTK
Definition: DummyBackend.h:16
BOOST_AUTO_TEST_CASE
BOOST_AUTO_TEST_CASE(testAsyncRead)
Definition: testAsyncRead.cpp:134
AsyncTestDummy::Accessor::isWriteable
bool isWriteable() const override
Definition: testAsyncRead.cpp:73
AsyncTestDummy::registers
std::map< std::string, size_t > registers
Definition: testAsyncRead.cpp:111
AsyncTestDummy::Accessor::doPreRead
void doPreRead(TransferType) override
Definition: testAsyncRead.cpp:60
ChimeraTK::NDRegisterAccessor
N-dimensional register accessor.
Definition: ForwardDeclarations.h:17
ChimeraTK::ReadAnyGroup::waitAny
Notification waitAny()
Wait until one of the elements received an update notification, but do not actually process the updat...
Definition: ReadAnyGroup.h:550
ChimeraTK::logic_error
Exception thrown when a logic error has occured.
Definition: Exception.h:51