ChimeraTK-DeviceAccess  03.18.00
EventFile.cc
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 #include "EventFile.h"
5 
6 #include "Exception.h"
7 
8 #include <iostream>
9 
10 namespace io = boost::asio;
11 
12 namespace ChimeraTK {
13  EventThread::EventThread(EventFile& owner, std::promise<void> subscriptionDonePromise)
14  : _owner{owner}, _ctx{}, _sd{_ctx, owner._file}, _thread{
15  &EventThread::start, this, std::move(subscriptionDonePromise)} {
16 #ifdef _DEBUG
17  std::cout << "XDMA: EventThread " << _owner._file.name() << " ctor\n";
18 #endif
19  }
20 
22 #ifdef _DEBUG
23  std::cout << "XDMA: EventThread " << _owner._file.name() << " dtor\n" << std::endl;
24 #endif
25  _ctx.stop();
26  _thread.join();
27  }
28 
29  void EventThread::start(std::promise<void> subscriptionDonePromise) {
30  // The thread has started, next thing is going to be the wait.
31  // This is the time to fulfil the promise that the subscription is done.
32  subscriptionDonePromise.set_value();
33  waitForEvent();
34  // We also put timeout handlers for a concurrently running timer into the same context, and check the health
35  // state of our event device from there.
36  timer.expires_after(std::chrono::seconds(timerSleepSec));
37  timer.async_wait([&](auto ec) { timerEvent(ec); });
38 
39  try {
40  _ctx.run();
41  }
42  catch(runtime_error& e) {
43  // forward exception to backend client
44  _owner._backend->setException(e.what());
45  // we leave device in non-functional state. next call to open() will reinit and clear exception.
46  }
47  }
48 
50 #ifdef _DEBUG
51  std::cout << "XDMA: waitForEvent " << _owner._file.name() << "\n";
52 #endif
53  // We have to wait seperately from the read operation,
54  // since the read op will not be canceled by _ctx.stop() in the dtor
55  _sd.async_wait(io::posix::stream_descriptor::wait_read,
56  std::bind(&EventThread::readEvent, this,
57  std::placeholders::_1 // io::placeholders::error
58  ));
59  }
60 
61  void EventThread::readEvent(const boost::system::error_code& ec) {
62 #ifdef _DEBUG
63  std::cout << "XDMA: readEvent " << _owner._file.name() << "\n";
64 #endif
65  if(ec) {
66  const std::string msg = "EventThread::readEvent() I/O error: " + ec.message();
67  throw runtime_error(msg);
68  }
69  _sd.async_read_some(io::buffer(_result),
70  std::bind(&EventThread::handleEvent, this,
71  std::placeholders::_1, // io::placeholders::error
72  std::placeholders::_2 // io::placeholders::bytes_transferred
73  ));
74  }
75 
76  void EventThread::handleEvent(const boost::system::error_code& ec, std::size_t bytes_transferred) {
77 #ifdef _DEBUG
78  std::cout << "XDMA: handleEvent " << _owner._file.name() << "\n";
79 #endif
80  if(ec) {
81  const std::string msg = "EventThread::handleEvent() I/O error: " + ec.message();
82  throw runtime_error(msg);
83  }
84  if(bytes_transferred != sizeof(_result[0])) {
85  throw runtime_error("EventThread::handleEvent() incomplete read");
86  }
87 
88  uint32_t numInterrupts = _result[0];
89 #ifdef _DEBUG
90  std::cout << "XDMA: Event " << _owner._file.name() << " received: " << bytes_transferred << " bytes, "
91  << numInterrupts << " interrupts\n";
92 #endif
93  // Only distribute once. If numInterrupts is > 1 we are discarding missed interrupts here.
94  // FIXME: should we have a variable to report this (accessible via RegisterAccessor)?
95  if(numInterrupts != 0) {
96  _owner._asyncDomain->distribute(nullptr);
97  }
98  waitForEvent();
99  }
100 
101  void EventThread::timerEvent(const boost::system::error_code& ec) {
102 #ifdef _DEBUG
103  std::cout << "XDMA: timerEvent for " << _owner._file.name() << "\n";
104 #endif
105  if(ec) {
106  const std::string msg = "EventThread::timerEvent() I/O error: " + ec.message();
107  throw runtime_error(msg);
108  }
109  if(!_owner._file.goodState()) {
110  throw runtime_error("bad device node " + _owner._file.name());
111  }
112  timer.expires_after(std::chrono::seconds(timerSleepSec));
113  timer.async_wait([&](auto ec_) { timerEvent(ec_); });
114  }
115 
116  EventFile::EventFile(DeviceBackend* backend, const std::string& devicePath, size_t interruptIdx,
117  boost::shared_ptr<async::DomainImpl<std::nullptr_t>> asyncDomain)
118  : _backend(backend), _file{devicePath + "/events" + std::to_string(interruptIdx), O_RDONLY}, _asyncDomain{
119  asyncDomain} {}
120 
122  _evtThread.reset(nullptr);
123  }
124 
125  void EventFile::startThread(std::promise<void> subscriptionDonePromise) {
126  if(_evtThread) {
127  subscriptionDonePromise.set_value();
128  return;
129  }
130  _evtThread = std::make_unique<EventThread>(*this, std::move(subscriptionDonePromise));
131  }
132 
133 } // namespace ChimeraTK
ChimeraTK::runtime_error::what
const char * what() const noexcept override
Return the message describing what exactly went wrong.
Definition: Exception.cpp:14
ChimeraTK::EventFile::startThread
void startThread(std::promise< void > subscriptionDonePromise)
Definition: EventFile.cc:125
ChimeraTK::DeviceFile::goodState
bool goodState() const
Definition: DeviceFile.cc:50
ChimeraTK::EventThread::timer
boost::asio::steady_timer timer
Definition: EventFile.h:38
ChimeraTK::DeviceBackend::setException
virtual void setException(const std::string &message) noexcept=0
Set the backend into an exception state.
ChimeraTK::DeviceFile::name
std::string name() const
Definition: DeviceFile.cc:46
ChimeraTK::EventFile::EventFile
EventFile()=delete
ChimeraTK::runtime_error
Exception thrown when a runtime error has occured.
Definition: Exception.h:18
ChimeraTK::DeviceBackend
The base class for backends providing IO functionality for the Device class.
Definition: DeviceBackend.h:28
ChimeraTK::EventThread::readEvent
void readEvent(const boost::system::error_code &ec)
Definition: EventFile.cc:61
ChimeraTK::EventFile::~EventFile
~EventFile()
Definition: EventFile.cc:121
ChimeraTK::EventThread::timerSleepSec
const int timerSleepSec
Definition: EventFile.h:39
ChimeraTK::EventThread::start
void start(std::promise< void > subscriptionDonePromise)
Definition: EventFile.cc:29
ChimeraTK::EventThread::timerEvent
void timerEvent(const boost::system::error_code &ec)
Definition: EventFile.cc:101
EventFile.h
ChimeraTK::async::DomainImpl
Definition: DomainImpl.h:15
ChimeraTK::EventThread::waitForEvent
void waitForEvent()
Definition: EventFile.cc:49
ChimeraTK::EventFile
Definition: EventFile.h:43
Exception.h
ChimeraTK
Definition: DummyBackend.h:16
ChimeraTK::EventThread::EventThread
EventThread()=delete
ChimeraTK::to_string
std::string to_string(Boolean &value)
Definition: SupportedUserTypes.h:59
ChimeraTK::EventThread::handleEvent
void handleEvent(const boost::system::error_code &ec, std::size_t bytes_transferred)
Definition: EventFile.cc:76
ChimeraTK::EventThread::~EventThread
~EventThread()
Definition: EventFile.cc:21