w11 - cpp 0.794
Backend server for Rlink and w11
Loading...
Searching...
No Matches
RlinkServer.cpp
Go to the documentation of this file.
1// $Id: RlinkServer.cpp 1185 2019-07-12 17:29:12Z mueller $
2// SPDX-License-Identifier: GPL-3.0-or-later
3// Copyright 2013-2019 by Walter F.J. Mueller <W.F.J.Mueller@gsi.de>
4//
5// Revision History:
6// Date Rev Version Comment
7// 2019-06-15 1164 2.2.11 adapt to new ReventFd API
8// 2019-04-07 1127 2.2.10 trace now with timestamp and selective
9// 2019-02-23 1114 2.2.9 use std::bind instead of lambda
10// 2018-12-17 1088 2.2.8 use std::lock_guard, std::thread instead of boost
11// 2018-12-15 1083 2.2.7 for std::function setups: use rval ref and move
12// 2018-12-14 1081 2.2.6 use std::bind instead of boost
13// 2018-12-07 1078 2.2.5 use std::shared_ptr instead of boost
14// 2018-10-27 1059 2.2.4 coverity fixup (uncaught exception in dtor)
15// 2017-04-07 868 2.2.3 Dump(): add detail arg
16// 2015-06-05 686 2.2.2 BUGFIX: CallAttnHandler(): fix race in hnext
17// 2015-04-04 662 2.2.1 BUGFIX: fix race in Stop(), use UnStop()
18// 2015-01-10 632 2.2 Exec() without emsg now void, will throw
19// 2014-12-30 625 2.1 adopt to Rlink V4 attn logic
20// 2014-12-21 617 2.0.1 use kStat_M_RbTout for rbus timeout
21// 2014-12-11 611 2.0 re-organize for rlink v4
22// 2013-05-01 513 1.0.2 fTraceLevel now uint32_t
23// 2013-04-21 509 1.0.1 add Resume(), reorganize server start handling
24// 2013-03-06 495 1.0 Initial version
25// 2013-01-12 474 0.5 First draft
26// ---------------------------------------------------------------------------
27
32#include <unistd.h>
33
34#include <functional>
35
37#include "librtools/RosFill.hpp"
41#include "librtools/RlogMsg.hpp"
42#include "librtools/Rtools.hpp"
43
44#include "RlinkServer.hpp"
45
46using namespace std;
47using namespace std::placeholders;
48
54// all method definitions in namespace Retro
55namespace Retro {
56
57//------------------------------------------+-----------------------------------
59
61 : fspConn(),
62 fContext(),
63 fAttnDsc(),
64 fActnList(),
65 fWakeupEvent("RlinkServer::fWakeupEvent."),
66 fELoop(this),
67 fServerThread(),
68 fAttnPatt(0),
69 fAttnNotiPatt(0),
70 fTraceLevel(0),
71 fStats()
72{
76
78 fWakeupEvent.Fd(), POLLIN);
79
80 // Statistic setup
81 fStats.Define(kStatNEloopWait,"NEloopWait","event loop turns (wait)");
82 fStats.Define(kStatNEloopPoll,"NEloopPoll","event loop turns (poll)");
83 fStats.Define(kStatNWakeupEvt,"NWakeupEvt","Wakeup events");
84 fStats.Define(kStatNRlinkEvt, "NRlinkEvt", "Rlink data events");
85 fStats.Define(kStatNAttnHdl ,"NAttnHdl" ,"Attn handler calls");
86 fStats.Define(kStatNAttnNoti ,"NAttnNoti" ,"Attn notifies processed");
87 fStats.Define(kStatNAttnHarv ,"NAttnHarv" ,"Attn handler restarts");
88 fStats.Define(kStatNAttn00, "NAttn00", "Attn bit 0 set");
89 fStats.Define(kStatNAttn01, "NAttn01", "Attn bit 1 set");
90 fStats.Define(kStatNAttn02, "NAttn02", "Attn bit 2 set");
91 fStats.Define(kStatNAttn03, "NAttn03", "Attn bit 3 set");
92 fStats.Define(kStatNAttn04, "NAttn04", "Attn bit 4 set");
93 fStats.Define(kStatNAttn05, "NAttn05", "Attn bit 5 set");
94 fStats.Define(kStatNAttn06, "NAttn06", "Attn bit 6 set");
95 fStats.Define(kStatNAttn07, "NAttn07", "Attn bit 7 set");
96 fStats.Define(kStatNAttn08, "NAttn08", "Attn bit 8 set");
97 fStats.Define(kStatNAttn09, "NAttn09", "Attn bit 9 set");
98 fStats.Define(kStatNAttn10, "NAttn10", "Attn bit 10 set");
99 fStats.Define(kStatNAttn11, "NAttn11", "Attn bit 11 set");
100 fStats.Define(kStatNAttn12, "NAttn12", "Attn bit 12 set");
101 fStats.Define(kStatNAttn13, "NAttn13", "Attn bit 13 set");
102 fStats.Define(kStatNAttn14, "NAttn14", "Attn bit 14 set");
103 fStats.Define(kStatNAttn15, "NAttn15", "Attn bit 15 set");
104}
105
106//------------------------------------------+-----------------------------------
108
110{
111 Rtools::Catch2Cerr(__func__, [this](){ Stop(); } );
112 if (fspConn) fspConn->SetServer(0);
113}
114
115//------------------------------------------+-----------------------------------
117
118void RlinkServer::SetConnect(const std::shared_ptr<RlinkConnect>& spconn)
119{
120 if (!fspConn && !spconn) return; // allow 0 = 0 ...
121 if (fspConn)
122 throw Rexception("RlinkServer::SetConnect()",
123 "Bad state: fspConn already set");
124 if (!spconn)
125 throw Rexception("RlinkServer::SetConnect()", "Bad args: spconn==0");
126
127 fspConn = spconn;
128 fELoop.SetLogFile(fspConn->LogFileSPtr());
129 fspConn->SetServer(this);
130 return;
131}
132
133//------------------------------------------+-----------------------------------
135
136void RlinkServer::AddAttnHandler(attnhdl_t&& attnhdl, uint16_t mask,
137 void* cdata)
138{
139 if (mask == 0)
140 throw Rexception("RlinkServer::AddAttnHandler()", "Bad args: mask == 0");
141
142 lock_guard<RlinkConnect> lock(*fspConn);
143
144 AttnId id(mask, cdata);
145 for (size_t i=0; i<fAttnDsc.size(); i++) {
146 if (fAttnDsc[i].fId == id) {
147 throw Rexception("RlinkServer::AddAttnHandler()",
148 "Bad args: duplicate handler");
149 }
150 }
151 fAttnDsc.emplace_back(move(attnhdl), id);
152
153 return;
154}
155
156//------------------------------------------+-----------------------------------
158
160{
161 RlinkCommand& cmd0 = clist[0];
162 if (cmd0.Command() != RlinkCommand::kCmdAttn)
163 throw Rexception("RlinkServer::GetAttnInfo", "clist did't start with attn");
164
165 Exec(clist);
166
167 args.fAttnHarvest = cmd0.Data();
168 args.fHarvestDone = true;
169
170 return;
171}
172
173//------------------------------------------+-----------------------------------
175
177{
178 RlinkCommandList clist;
179 clist.AddAttn();
180 GetAttnInfo(args, clist);
181 return;
182}
183
184//------------------------------------------+-----------------------------------
186
187void RlinkServer::RemoveAttnHandler(uint16_t mask, void* cdata)
188{
189 lock_guard<RlinkConnect> lock(*fspConn);
190
191 AttnId id(mask, cdata);
192 for (size_t i=0; i<fAttnDsc.size(); i++) {
193 if (fAttnDsc[i].fId == id) {
194 fAttnDsc.erase(fAttnDsc.begin()+i);
195 return;
196 }
197 }
198
199 throw Rexception("RlinkServer::RemoveAttnHandler()",
200 "Bad args: unknown handler");
201}
202
203//------------------------------------------+-----------------------------------
205
207{
208 lock_guard<RlinkConnect> lock(*fspConn);
209 fActnList.push_back(move(actnhdl));
210 if (IsActiveOutside()) Wakeup();
211 return;
212}
213
214//------------------------------------------+-----------------------------------
216
217void RlinkServer::AddPollHandler(pollhdl_t&& pollhdl, int fd, short events)
218{
219 lock_guard<RlinkConnect> lock(*fspConn);
220 fELoop.AddPollHandler(move(pollhdl), fd, events);
221 if (IsActiveOutside()) Wakeup();
222 return;
223}
224
225//------------------------------------------+-----------------------------------
227
228bool RlinkServer::TestPollHandler(int fd, short events)
229{
230 lock_guard<RlinkConnect> lock(*fspConn);
231 return fELoop.TestPollHandler(fd, events);
232}
233
234//------------------------------------------+-----------------------------------
236
237void RlinkServer::RemovePollHandler(int fd, short events, bool nothrow)
238{
239 lock_guard<RlinkConnect> lock(*fspConn);
240 fELoop.RemovePollHandler(fd, events,nothrow);
241 if (IsActiveOutside()) Wakeup();
242 return;
243}
244
245//------------------------------------------+-----------------------------------
247
249{
250 lock_guard<RlinkConnect> lock(*fspConn);
252 if (IsActiveOutside()) Wakeup();
253 return;
254}
255
256//------------------------------------------+-----------------------------------
258
260{
261 StartOrResume(false);
262 return;
263}
264
265//------------------------------------------+-----------------------------------
267
269{
270 if (!IsActive()) return;
271 fELoop.Stop();
272 Wakeup();
273 fServerThread.join();
274 return;
275}
276
277//------------------------------------------+-----------------------------------
279
281{
282 StartOrResume(true);
283 return;
284}
285
286//------------------------------------------+-----------------------------------
288
290{
292 return;
293}
294
295//------------------------------------------+-----------------------------------
297
299{
300 // only called under lock !!
301 if (apat & fAttnNotiPatt) {
302 RlogMsg lmsg(LogFile(), 'W');
303 lmsg << "SignalAttnNotify: redundant notify:"
304 << " have=" << RosPrintBvi(fAttnNotiPatt,16)
305 << " apat=" << RosPrintBvi(apat,16);
306 }
307 fAttnNotiPatt |= apat;
308 Wakeup();
309 return;
310}
311
312//------------------------------------------+-----------------------------------
314
319{
320 return fServerThread.joinable();
321}
322
323//------------------------------------------+-----------------------------------
325
330{
331 return IsActive() && this_thread::get_id() == fServerThread.get_id();
332}
333
334//------------------------------------------+-----------------------------------
336
342{
343 return IsActive() && this_thread::get_id() != fServerThread.get_id();
344}
345
346//------------------------------------------+-----------------------------------
348
349void RlinkServer::SetTraceLevel(uint32_t level)
350{
351 fTraceLevel = level;
352 fELoop.SetTraceLevel(level);
353 return;
354}
355
356//------------------------------------------+-----------------------------------
358
359void RlinkServer::Print(std::ostream& os) const
360{
361 os << "RlinkServer::Print(std::ostream& os)" << endl;
362 return;
363}
364
365//------------------------------------------+-----------------------------------
367
368void RlinkServer::Dump(std::ostream& os, int ind, const char* text,
369 int detail) const
370{
371 // FIXME_code: is that thread safe ??? fActnList.size() ???
372 RosFill bl(ind);
373 os << bl << (text?text:"--") << "RlinkServer @ " << this << endl;
374 os << bl << " fspConn: " << fspConn << endl;
375
376 os << bl << " fAttnDsc: " << endl;
377 for (size_t i=0; i<fAttnDsc.size(); i++)
378 os << bl << " [" << RosPrintf(i,"d",3) << "]: "
379 << RosPrintBvi(fAttnDsc[i].fId.fMask,16)
380 << ", " << fAttnDsc[i].fId.fCdata << endl;
381 os << bl << " fActnList.size: " << fActnList.size() << endl;
382 os << bl << " fWakeupEvent: " << fWakeupEvent.Fd() << endl;
383 fELoop.Dump(os, ind+2, "fELoop", detail);
384 os << bl << " fServerThread: " << fServerThread.get_id() << endl;
385 os << bl << " fAttnPatt: " << RosPrintBvi(fAttnPatt,16) << endl;
386 os << bl << " fAttnNotiPatt: " << RosPrintBvi(fAttnNotiPatt,16) << endl;
387 fStats.Dump(os, ind+2, "fStats: ", detail-1);
388 return;
389}
390
391//------------------------------------------+-----------------------------------
393
395{
396 if (IsActive())
397 throw Rexception("RlinkServer::StartOrResume()",
398 "Bad state: server thread already running");
399 if (!fspConn->IsOpen())
400 throw Rexception("RlinkServer::StartOrResume()",
401 "Bad state: RlinkConnect not open");
402
403 lock_guard<RlinkConnect> lock(Connect());
404 // enable attn notify send
405 RlinkCommandList clist;
406 if (!resume) clist.AddAttn();
408 Exec(clist);
409
410 // setup poll handler for Rlink traffic
411 int rlinkfd = fspConn->Port().FdRead();
412 if (!fELoop.TestPollHandler(rlinkfd, POLLIN))
414 rlinkfd, POLLIN);
415
416 // and start server thread
417 fELoop.UnStop();
418 fServerThread = thread([this](){ fELoop.EventLoop(); });
419
420 if (resume) {
421 RerrMsg emsg;
422 if (!Connect().SndAttn(emsg)) {
423 RlogMsg lmsg(LogFile(), 'E');
424 lmsg << "attn send for server resume failed:" << emsg;
425 }
426 }
427
428 return;
429}
430
431//------------------------------------------+-----------------------------------
433
435{
437 if (fTraceLevel > 1) {
438 RlogMsg lmsg(LogFile(),'I');
439 lmsg << "attnhdl-beg: patt=" << RosPrintBvi(fAttnPatt,8);
440 }
441
442 // if notifier pending, transfer it to current attn pattern
443 if (fAttnNotiPatt) {
444 lock_guard<RlinkConnect> lock(*fspConn);
446 if (fTraceLevel > 1) {
447 RlogMsg lmsg(LogFile(),'I');
448 lmsg << "attnhdl-add: patt=" << RosPrintBvi(fAttnPatt,8)
449 << " noti=" << RosPrintBvi(fAttnNotiPatt,8);
450 }
452 fAttnNotiPatt = 0;
453 }
454
455 // do stats for pending attentions
456 for (size_t i=0; i<16; i++) {
457 if (fAttnPatt & (uint16_t(1)<<i)) fStats.Inc(kStatNAttn00+i);
458 }
459
460 // now call handlers, multiple handlers may be called for one attn bit
461 uint16_t hnext = 0;
462 uint16_t hdone = 0;
463 for (size_t i=0; i<fAttnDsc.size(); i++) {
464 uint16_t hmatch = fAttnPatt & fAttnDsc[i].fId.fMask;
465 if (hmatch) {
466 AttnArgs args(fAttnPatt, fAttnDsc[i].fId.fMask);
467 lock_guard<RlinkConnect> lock(*fspConn);
468
469 if (fTraceLevel > 0) {
470 RlogMsg lmsg(LogFile(),'I');
471 lmsg << "attnhdl-bef: patt=" << RosPrintBvi(fAttnPatt,8)
472 << " hmat=" << RosPrintBvi(hmatch,8);
473 }
474
475 // FIXME_code: return code not used, yet
476 fAttnDsc[i].fHandler(args);
477 if (!args.fHarvestDone)
478 Rexception("RlinkServer::CallAttnHandler()",
479 "Handler didn't set fHarvestDone");
480
481 uint16_t hnew = args.fAttnHarvest & ~fAttnDsc[i].fId.fMask;
482 hnext |= hnew;
483 hnext &= ~hmatch; // FIXME_code: this is a patch
484 // works for single lam handlers only
485 // ok for now, but will not work in general !!
486 hdone |= hmatch;
487
488 if (fTraceLevel > 1) {
489 RlogMsg lmsg(LogFile(),'I');
490 lmsg << "attnhdl-aft: patt=" << RosPrintBvi(fAttnPatt,8)
491 << " done=" << RosPrintBvi(hdone,8)
492 << " next=" << RosPrintBvi(hnext,8);
493 }
494
495 }
496 }
497 fAttnPatt &= ~hdone; // clear handled bits
498
499 // if there are any unhandled attenions, do default handling which will
500 // ensure that attention harvest is done
501 if (fAttnPatt) {
503 GetAttnInfo(args);
504 hnext |= args.fAttnHarvest & ~fAttnPatt;
505 if (fTraceLevel > 0) {
506 RlogMsg lmsg(LogFile(), 'I');
507 lmsg << "eloop: unhandled attn, mask="
508 << RosPrintBvi(fAttnPatt,16) << endl;
509 }
510 }
511
512 // finally replace current attn pattern by the attentions found during
513 // harvest and not yet handled
514 fAttnPatt = hnext;
516
517 return;
518}
519
520//------------------------------------------+-----------------------------------
522
524{
525 if (!ActnPending()) return;
526
527 // call first action
528 lock_guard<RlinkConnect> lock(*fspConn);
529
530 int irc = fActnList.front()();
531
532 // if irc>0 requeue to end, otherwise drop
533 if (irc > 0) {
534 fActnList.splice(fActnList.end(), fActnList, fActnList.begin());
535 } else {
536 fActnList.pop_front();
537 }
538
539 return;
540}
541
542//------------------------------------------+-----------------------------------
544
545int RlinkServer::WakeupHandler(const pollfd& pfd)
546{
548
549 // bail-out and cancel handler if poll returns an error event
550 if (pfd.revents & (~pfd.events)) return -1;
551
552 fWakeupEvent.Wait(); // read event
553 return 0;
554}
555
556//------------------------------------------+-----------------------------------
558
559int RlinkServer::RlinkHandler(const pollfd& pfd)
560{
562
563 // bail-out and cancel handler if poll returns an error event
564 if (pfd.revents & (~pfd.events)) return -1;
565
566 fspConn->HandleUnsolicitedData();
567 return 0;
568}
569
570} // end namespace Retro
FIXME_docs.
Definition: RerrMsg.hpp:25
uint64_t Wait()
FIXME_docs.
Definition: ReventFd.cpp:68
void Signal(uint64_t val=1)
FIXME_docs.
Definition: ReventFd.cpp:56
void SetLogFile(const std::shared_ptr< RlogFile > &splog)
FIXME_docs.
Definition: ReventLoop.ipp:50
virtual void Dump(std::ostream &os, int ind=0, const char *text=0, int detail=0) const
FIXME_docs.
Definition: ReventLoop.cpp:181
void Stop()
FIXME_docs.
Definition: ReventLoop.ipp:24
void UnStop()
FIXME_docs.
Definition: ReventLoop.ipp:33
void SetTraceLevel(uint32_t level)
FIXME_docs.
Definition: ReventLoop.ipp:59
void AddPollHandler(pollhdl_t &&pollhdl, int fd, short events=POLLIN)
FIXME_docs.
Definition: ReventLoop.cpp:75
bool TestPollHandler(int fd, short events=POLLIN)
FIXME_docs.
Definition: ReventLoop.cpp:124
void RemovePollHandler(int fd, short events, bool nothrow=false)
FIXME_docs.
Definition: ReventLoop.cpp:101
FIXME_docs.
Definition: Rexception.hpp:29
int Fd() const
FIXME_docs.
Definition: Rfd.ipp:20
size_t AddWreg(uint16_t addr, uint16_t data)
FIXME_docs.
size_t AddAttn()
FIXME_docs.
uint16_t Data() const
FIXME_docs.
static const uint8_t kStat_M_RbErr
stat: rberr flag set
static const uint8_t kStat_M_RbNak
stat: rbnak flag set
static const uint8_t kStat_M_RbTout
stat: rbtout flag set
uint8_t Command() const
FIXME_docs.
static const uint8_t kCmdAttn
command code get attention
static const uint16_t kRLCNTL_M_AnEna
RLCNTL: an enable.
static const uint16_t kRbaddr_RLCNTL
rlink core reg RLCNTL
void SetStatus(uint8_t stat, uint8_t statmsk=0x00)
FIXME_docs.
virtual void EventLoop()
FIXME_docs.
bool TestPollHandler(int fd, short events=POLLIN)
FIXME_docs.
uint16_t fAttnNotiPatt
attn notifier pattern
std::function< int(AttnArgs &)> attnhdl_t
Definition: RlinkServer.hpp:61
bool IsActiveInside() const
Indicates whether server is active and caller is inside server thread.
std::vector< AttnDsc > fAttnDsc
RlinkServer()
Default constructor.
Definition: RlinkServer.cpp:60
void AddAttnHandler(attnhdl_t &&attnhdl, uint16_t mask, void *cdata=nullptr)
FIXME_docs.
RlinkConnect & Connect() const
FIXME_docs.
Definition: RlinkServer.ipp:36
void Wakeup()
FIXME_docs.
RlogFile & LogFile() const
FIXME_docs.
Definition: RlinkServer.ipp:44
void CallActnHandler()
FIXME_docs.
std::shared_ptr< RlinkConnect > fspConn
ReventLoop::pollhdl_t pollhdl_t
Definition: RlinkServer.hpp:60
void StartOrResume(bool resume)
FIXME_docs.
virtual ~RlinkServer()
Destructor.
int RlinkHandler(const pollfd &pfd)
FIXME_docs.
void QueueAction(actnhdl_t &&actnhdl)
FIXME_docs.
int WakeupHandler(const pollfd &pfd)
FIXME_docs.
Rstats fStats
statistics
RlinkContext fContext
default server context
std::list< actnhdl_t > fActnList
void RemovePollHandler(int fd, short events, bool nothrow=false)
FIXME_docs.
void GetAttnInfo(AttnArgs &args, RlinkCommandList &clist)
FIXME_docs.
bool IsActive() const
Indicates whether server is active.
uint16_t fAttnPatt
current attn pattern
void SetConnect(const std::shared_ptr< RlinkConnect > &spconn)
FIXME_docs.
void CallAttnHandler()
FIXME_docs.
void Stop()
FIXME_docs.
void Print(std::ostream &os) const
FIXME_docs.
bool Exec(RlinkCommandList &clist, RerrMsg &emsg)
FIXME_docs.
Definition: RlinkServer.ipp:60
RlinkServerEventLoop fELoop
void RemoveAttnHandler(uint16_t mask, void *cdata=nullptr)
FIXME_docs.
uint32_t fTraceLevel
trace level
void SetTraceLevel(uint32_t level)
FIXME_docs.
std::thread fServerThread
bool IsActiveOutside() const
Indicates whether server is active and caller is outside server thread.
@ kStatNAttn13
Attn bit 13 set.
@ kStatNAttn06
Attn bit 6 set.
@ kStatNAttn00
Attn bit 0 set.
@ kStatNAttnHarv
Attn handler restarts.
@ kStatNAttn05
Attn bit 5 set.
@ kStatNAttn12
Attn bit 12 set.
@ kStatNAttn11
Attn bit 11 set.
@ kStatNAttnHdl
Attn handler calls.
@ kStatNAttn09
Attn bit 9 set.
@ kStatNAttn08
Attn bit 8 set.
@ kStatNWakeupEvt
Wakeup events.
@ kStatNAttn04
Attn bit 4 set.
@ kStatNAttn15
Attn bit 15 set.
@ kStatNAttn14
Attn bit 14 set.
@ kStatNAttn02
Attn bit 2 set.
@ kStatNAttnNoti
Attn notifies processed.
@ kStatNEloopPoll
event loop turns (poll)
@ kStatNAttn07
Attn bit 7 set.
@ kStatNAttn01
Attn bit 1 set.
@ kStatNAttn03
Attn bit 3 set.
@ kStatNAttn10
Attn bit 10 set.
@ kStatNEloopWait
event loop turns (wait)
@ kStatNRlinkEvt
Rlink data events.
void Dump(std::ostream &os, int ind=0, const char *text=0, int detail=0) const
FIXME_docs.
void Resume()
FIXME_docs.
bool ActnPending() const
FIXME_docs.
void SignalAttnNotify(uint16_t apat)
FIXME_docs.
void Start()
FIXME_docs.
void AddPollHandler(pollhdl_t &&pollhdl, int fd, short events=POLLIN)
FIXME_docs.
std::function< int()> actnhdl_t
Definition: RlinkServer.hpp:62
FIXME_docs.
Definition: RlogMsg.hpp:24
I/O appicator to generate fill characters.
Definition: RosFill.hpp:24
void Dump(std::ostream &os, int ind=0, const char *text=0, int detail=0) const
FIXME_docs.
Definition: Rstats.cpp:178
void Inc(size_t ind, double val=1.)
FIXME_docs.
Definition: Rstats.ipp:29
void Define(size_t ind, const std::string &name, const std::string &text)
FIXME_docs.
Definition: Rstats.cpp:72
RosPrintfS< bool > RosPrintf(bool value, const char *form=0, int width=0, int prec=0)
Creates a print object for the formatted output of a bool value.
Definition: RosPrintf.ipp:38
void Catch2Cerr(const char *msg, std::function< void()> func)
FIXME_docs.
Definition: Rtools.cpp:170
Declaration of class ReventLoop.
Definition: ReventLoop.cpp:47
uint16_t fAttnHarvest
out: harvested attentions
Definition: RlinkServer.hpp:54
bool fHarvestDone
out: set true when harvested
Definition: RlinkServer.hpp:55