ChimeraTK-DeviceAccess 03.25.00
Loading...
Searching...
No Matches
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
10namespace io = boost::asio;
11
12namespace ChimeraTK {
13 EventThread::EventThread(EventFile& owner, std::promise<void> subscriptionDonePromise)
14 : _owner{owner}, _ctx{}, _sd{_ctx, owner._file},
15 _thread{&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();
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 }
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},
119 _asyncDomain{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
The base class for backends providing IO functionality for the Device class.
virtual void setException(const std::string &message) noexcept=0
Set the backend into an exception state.
bool goodState() const
Definition DeviceFile.cc:52
std::string name() const
Definition DeviceFile.cc:48
void startThread(std::promise< void > subscriptionDonePromise)
Definition EventFile.cc:125
void timerEvent(const boost::system::error_code &ec)
Definition EventFile.cc:101
void handleEvent(const boost::system::error_code &ec, std::size_t bytes_transferred)
Definition EventFile.cc:76
void start(std::promise< void > subscriptionDonePromise)
Definition EventFile.cc:29
boost::asio::steady_timer timer
Definition EventFile.h:38
void readEvent(const boost::system::error_code &ec)
Definition EventFile.cc:61
const int timerSleepSec
Definition EventFile.h:39
Exception thrown when a runtime error has occured.
Definition Exception.h:18
const char * what() const noexcept override
Return the message describing what exactly went wrong.
Definition Exception.cpp:14
std::string to_string(Boolean &value)
STL namespace.