Lely core libraries  2.2.5
loop_driver.cpp
Go to the documentation of this file.
1 
24 #include "coapp.hpp"
25 
26 #if !LELY_NO_COAPP_MASTER && !LELY_NO_THREADS
27 
29 
30 #include <atomic>
31 #ifdef __MINGW32__
32 #include <lely/libc/threads.h>
33 #else
34 #include <thread>
35 #endif
36 
37 namespace lely {
38 
39 namespace canopen {
40 
43  explicit Impl_(LoopDriver* self, io::ContextBase ctx);
44  Impl_(const Impl_&) = delete;
45  Impl_& operator=(const Impl_&) = delete;
46  ~Impl_();
47 
48  void Start();
49  void Shutdown();
50  void Join();
51 
52  static const io_svc_vtbl svc_vtbl;
53 
54  LoopDriver* self{nullptr};
55  io::ContextBase ctx{nullptr};
56  ::std::atomic_flag shutdown;
58 #ifdef __MINGW32__
59  thrd_t thr;
60 #else
61  ::std::thread thread;
62 #endif
63  ::std::atomic_flag joined;
64 };
65 
66 // clang-format off
67 const io_svc_vtbl LoopDriver::Impl_::svc_vtbl = {
68  nullptr,
69  [](io_svc* svc) noexcept {
70  static_cast<LoopDriver::Impl_*>(svc)->Shutdown();
71  }
72 };
73 // clang-format on
74 
75 LoopDriver::LoopDriver(AsyncMaster& master, uint8_t id)
76  : BasicDriver(strand.get_inner_executor(), master, id),
77  impl_(new Impl_(this, master.GetContext())) {}
78 
79 LoopDriver::~LoopDriver() = default;
80 
81 void
83  impl_->Join();
84 }
85 
88  return impl_->stopped.get_future();
89 }
90 
91 void
92 LoopDriver::Wait(SdoFuture<void> f, ::std::error_code& ec) {
93  GetLoop().wait(f, ec);
94  try {
95  f.get().value();
96  } catch (const ::std::system_error& e) {
97  ec = e.code();
98  } catch (const ev::future_not_ready& e) {
99  ec = ::std::make_error_code(::std::errc::operation_canceled);
100  }
101 }
102 
103 void
104 LoopDriver::USleep(uint_least64_t usec) {
105  ::std::error_code ec;
106  USleep(usec, ec);
107  if (ec) throw ::std::system_error(ec, "USleep");
108 }
109 
110 void
111 LoopDriver::USleep(uint_least64_t usec, ::std::error_code& ec) noexcept {
112  GetLoop().run_for(::std::chrono::microseconds(usec), ec);
113  if (ec == ::std::errc::timed_out) {
114  ec = {};
115  } else if (!ec && GetLoop().stopped()) {
116  ec = ::std::make_error_code(::std::errc::operation_canceled);
117  }
118 }
119 
120 LoopDriver::Impl_::Impl_(LoopDriver* self_, io::ContextBase ctx_)
121  : io_svc IO_SVC_INIT(&svc_vtbl),
122  self(self_),
123  ctx(ctx_)
124 #ifndef __MINGW32__
125  ,
126  thread(&Impl_::Start, this)
127 #endif
128 {
129 #ifdef __MINGW32__
130  if (thrd_create(
131  &thr,
132  [](void* arg) noexcept {
133  static_cast<LoopDriver::Impl_*>(arg)->Start();
134  return 0;
135  },
136  this) != thrd_success)
137  util::throw_errc("thrd_create");
138 #endif
139  ctx.insert(*this);
140 }
141 
142 LoopDriver::Impl_::~Impl_() {
143  Join();
144  ctx.remove(*this);
145 }
146 
147 void
148 LoopDriver::Impl_::Start() {
149  auto& loop = self->GetLoop();
150  auto exec = self->GetExecutor();
151 
152  // Start the event loop. Signal the existence of a fake task to prevent the
153  // loop for stopping early.
154  exec.on_task_init();
155  loop.run();
156  exec.on_task_fini();
157 
158  // Deregister the driver to prevent the master from queueing new events. This
159  // also cancels any outstanding SDO requests.
160  self->master.Erase(*self);
161 
162  // Finish remaining tasks, but do not block.
163  loop.restart();
164  loop.poll();
165 
166  // Satisfy the promise to signal that the thread is about to terminate and it
167  // a call to the destructor will not block.
168  stopped.set(0);
169 }
170 
171 void
172 LoopDriver::Impl_::Shutdown() {
173  if (!shutdown.test_and_set()) {
174  // Stop receiving CANopen events.
175  self->master.Erase(*self);
176  // Stop the blocking run of the event loop.
177  self->GetLoop().stop();
178  }
179 }
180 
181 void
182 LoopDriver::Impl_::Join() {
183  if (!joined.test_and_set()) {
184  Shutdown();
185 #ifdef __MINGW32__
186  thrd_join(thr, nullptr);
187 #else
188  thread.join();
189 #endif
190  }
191 }
192 
193 } // namespace canopen
194 
195 } // namespace lely
196 
197 #endif // !LELY_NO_COAPP_MASTER && !LELY_NO_THREADS
An asynchronous CANopen master.
Definition: master.hpp:1296
The base class for drivers for remote CANopen nodes.
Definition: driver.hpp:279
A CANopen driver running its own dedicated event loop in a separate thread.
Definition: loop_driver.hpp:51
void USleep(uint_least64_t usec)
Runs the event loop for usec microseconds.
T Wait(SdoFuture< T > f)
Waits for the specified future to become ready by running pending tasks on the dedicated event loop o...
ev::Loop & GetLoop() noexcept
Returns a reference to the dedicated event loop of the driver.
Definition: loop_driver.hpp:74
void Join()
Stops the dedicated event loop of the driver and waits until the thread running the event loop finish...
Definition: loop_driver.cpp:82
ev::Future< void, void > AsyncStoppped() noexcept
Returns a future which becomes ready once the dedicated event loop of the driver is stopped and the t...
Definition: loop_driver.cpp:87
~LoopDriver()
Stops the event loop and terminates the thread in which it was running before destroying the driver.
A future.
Definition: future.hpp:384
result_type & get()
Returns the result of a ready future.
Definition: future.hpp:490
::std::size_t wait(ev_future_t *future)
Definition: loop.hpp:104
The exception thrown when retrieving the result of a future which is not ready or does not contain a ...
Definition: future.hpp:45
A refence to an I/O context. This class is a wrapper around #io_ctx_t*.
Definition: ctx.hpp:49
void remove(io_svc &svc) noexcept
Definition: ctx.hpp:63
value_type & value()
Returns a reference to the value if *this contains a value, and throws an exception if *this contains...
Definition: result.hpp:274
This is the internal header file of the C++ CANopen application library.
#define IO_SVC_INIT(vptr)
The static initializer for io_svc.
Definition: ctx.h:57
This header file is part of the C++ CANopen application library; it contains the declarations for the...
::std::error_code make_error_code(SdoErrc e) noexcept
Creates an error code corresponding to an SDO abort code.
Definition: sdo_error.cpp:170
The virtual table of an I/O service.
Definition: ctx.h:67
An I/O service.
Definition: ctx.h:49
The internal implementation of lely::canopen::LoopDriver.
Definition: loop_driver.cpp:42
This header file is part of the C11 and POSIX compatibility library; it includes <threads....
int thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
Creates a new thread executing func(arg).
pthread_t thrd_t
A complete object type that holds an identifier for a thread.
Definition: threads.h:85
int thrd_join(thrd_t thr, int *res)
Joins the thread identified by thr with the current thread by blocking until the other thread has ter...
@ thrd_success
Indicates that the requested operation succeeded.
Definition: threads.h:121