AAO DRAMA/DRAMA2 C++ Interface
DRAMA C++11 and later interface
threadaction.hh
Go to the documentation of this file.
1#ifndef _DRAMA2_THREADACTION_INC
2#define _DRAMA2_THREADACTION_INC
29/*
30 * History:
31 07-Jan-2014 - TJF - Original version
32 30-Sep-2016 - TJF - Start of history after development.
33 SignalWaitingThreads() returns number of threads
34 signaled.
35
36 * The above ID is for Doxygen, this one has the format ACMM is looking for.
37 * "@(#) $Id$"
38 */
40#include "drama/thread.hh"
41#include <thread>
42#include <queue>
43#include <deque>
44#include <map>
45#include <chrono>
46#include <future>
47#include <condition_variable>
48#include <functional>
49namespace drama {
50
51 namespace thread {
52
53 /*
54 * This enum is used for indicating what type of event a signal
55 * from the thread to DRAMA is about.
56 */
57 enum class ThreadSignalType {
58 Complete, // Main thread has completed.
59 WaitSignal,// The thread is waiting for a signal.
60 WaitKick // The thread is interested in kicks
61 };
62
63 /*
64 * Details on a signal from a thread.
65 */
66 struct DramaSignalDetails {
67 std::thread::id _fromThread;
68 ThreadSignalType _sigType;
69
70 DramaSignalDetails(ThreadSignalType sigType) :
71 _fromThread(std::this_thread::get_id()),
72 _sigType(sigType) {}
73
74
75 };
76 /*
77 * A queue of signal details. We have a queue of these as
78 * we support multiple threads as part of implementing the
79 * action, so we could have multiple signals outstanding.
80 *
81 * A std::queue would be a better representation of this
82 * then "std::deque", but we do want to clear the queue and
83 * std::queue does not support that.
84 */
85 typedef std::deque<DramaSignalDetails> SignalDetailsQueue;
86
87
88
89 class TAction;
90
91 /* Used internally by TAction, this class is used as the
92 * reschedule handler for DRAMA on Obey reschedule messages to
93 * threaded actions.
94 *
95 * TAction::_obeyRescheduleObj member is of this type.
96 *
97 */
98 class _ThreadMessage : public MessageHandler {
99 TAction *_threadHandlerObj;
100 public:
101 _ThreadMessage(TAction *threadHandler) :
102 _threadHandlerObj(threadHandler) {}
103
104 private:
105 /*
106 * Made private, parent class has it as protected.
107 * But I don't want users to invoke it
108 */
109 Request MessageReceived() override final;
110 };
111 /* Used internally by TAction, this class is used as the
112 * reschedule handler for DRAMA on Kick messages to
113 * threaded actions.
114 *
115 * TAction::_kickRescheduleObj member is of this type.
116 */
117 class _ThreadKick : public MessageHandler {
118 TAction *_threadHandlerObj;
119 public:
120 _ThreadKick(TAction *threadHandler) :
121 _threadHandlerObj(threadHandler) {}
122
123 private:
124 /*
125 * Made private, parent class has it as protected.
126 * But I don't want users to invoke it
127 */
128 Request MessageReceived() override final;
129 };
130
131
137 using ThreadActionFunction = std::function<void (TAction *, const sds::Id &)> ;
138
175 // TMessHandler and RunDramaExitNotifier are abstract only.
176 class TAction : public MessageHandler,
177 public TMessHandler, public RunDramaExitNotifier {
178
179 // We need a function to implement the thread that
180 // is not part of TAction, but which can call
181 // ActionThread. This must be in the drama namespace,
182 // since that is how we are declaring it now.
183
184 //friend void MyThread(TAction *handler, const sds::Id &obeyArg);
185
186 friend class _ThreadMessage;
187 friend class _ThreadKick;
188
189 private:
190 std::future<void> _actionFuture; // Future of first thread in action.
191
192 /*
193 * Pointer to the DRAMA task we are part of.
194 * The pointer is const - we never change it after the constructor.
195 * The task pointed to is NOT const - since we can operate on it.
196 */
197 std::weak_ptr<Task> _theTask;
198 double _timeout; // Thread wait timeout, seconds.
199
200
201 void RunActionThread(const sds::IdPtr obeyArg);
202
204 public:
205
222 TAction(std::weak_ptr<Task> dramaTask, double timeout=0) :
223 _theTask(dramaTask), _timeout(timeout),
224 _obeyRescheduleObj(this),_kickMessageObj(this) {
225
226
227 }
228
233 virtual ~TAction();
234
235
238 TAction& operator=(const TAction &rhs) = delete;
241 TAction(const TAction &source) = delete;
242
243 private:
244 /*
245 * These are make private, as they are not meant to be
246 * called outside the object, just implemented outside
247 */
248
249 /*
250 * The method invoked to handle the obey message. This
251 * is an implementation of MessageHandler::MessageReceived().
252 */
253 virtual Request MessageReceived() final;
254
255
264 virtual void ActionThread(const drama::sds::Id &obeyArg) = 0;
266
278 virtual int RunDramaHasExited() override;
297 virtual bool JoinThreads(std::chrono::steady_clock::time_point until) override;
298
299
321 virtual void KickedWhenNotWaiting(sds::IdPtr Arg);
322
344 virtual void SignaledWhenNotWaiting(sds::IdPtr Arg);
345
346
347 public:
352 std::shared_ptr<Task> GetTask() const override {
353 return std::shared_ptr<Task>(_theTask);
354 }
362 bool HasTaskExpired() const noexcept {
363 return _theTask.expired();
364 }
365
366
382 void MessageUser(const std::string &text) const override;
383
384
411 template<typename... Types>
412#ifndef RUNNING_DOXYGEN
413 D2_FMT_DEPRECATED("Replace MessageUser() by MessageUserF(). See MessageUserF() page for details.")
414#endif
415 void MessageUser(const char *format, Types... args) {
416
417 /*
418 * Our approach is to write the output to a string, via
419 * SafePrintf(), then output that in one operation. Since
420 * std::ostream devices will then do the output in one operation.
421 */
422 std::stringstream sstrm;
424 MessageUser(sstrm.str());
425
426
427 }
444 template<typename... Args>
445 void MessageUserF(const fmt::format_string<Args...> fmt, Args&&... args) {
446
447 MessageUser(fmt::vformat(fmt.get(), fmt::make_format_args(args...)));
448
449 }
450
457 /* SdsListToUser() is in both TMessHandler and MessageHandler,
458 two of our base classes. So we must sort out the
459 confusion here
460 */
461 virtual const sds::PrintObjectCR &SdsListToUser() const override {
463 }
464
465
470 void SendTrigger(const sds::Id &arg) const;
471
476 double GetTimeout() const {
477 return _timeout;
478 }
487 void SetTimeout(double newTimeout) {
488 _timeout = newTimeout;
489 }
490
506 void WaitForEvent(EntryCode *event, sds::IdPtr * const arg=nullptr) {
507 /*
508 * Signal DRAMA that we are waiting for an event.
509 * This will put an entry in _waitEventMap for this
510 * thread.
511 */
512 SignalAmWaitingForKick();
513
514 /*
515 * Access the entry in _waitEventMap.
516 *
517 * Note - this first call will throw if the weak pointer _theTask
518 * does not have a valid object.
519 *
520 */
522 std::shared_ptr<Task>(_theTask)->Lock());
523
524 std::thread::id threadId(std::this_thread::get_id());
525 auto mapItem = _waitEventMap.find(threadId);
526 assert (mapItem != _waitEventMap.end());
527 WaitEventDetails *waitEvent = &(mapItem->second);
528
529
530 /*
531 * Wait for the condition
532 *
533 * We use a Lambda function to access the DataAvail() method
534 * in waitEvent.
535 *
536 */
537 waitEvent->_condition->wait(
538 DramaLock,
539 [waitEvent]{return waitEvent->DataAvail();});
540
541
542 WaitEventData details = waitEvent->_dataQueue.front();
543
544 *event = details.eventInfo.entryReason;
545 /*
546 * If the user wants the argument, return it.
547 */
548 if (arg)
549 {
550 *arg = details.arg;
551 }
552 std::shared_ptr<Task>(_theTask)->Logger().LogNF(D2LOG_DRAMA2,
553 "d2::TACT::WaitForEvent",
554 "Event received");
555
556
557 waitEvent->_dataQueue.pop();
558 // Should we complain here (throw execption?) if queue not empty?
559 ClearWait(false);
560 }
589 template <class Rep, class Period>
590 bool WaitForEventTimeoutIn(
592 const std::chrono::duration<Rep,Period>& rel_time,
593 sds::IdPtr * const arg=0) {
594 /*
595 * Signal DRAMA that we are waiting for a kick.
596 * This will put an entry in _waitEventMap for this
597 * thread.
598 */
599 SignalAmWaitingForKick();
600 /*
601 * Access the entry in _waitEventMap.
602 */
604 std::shared_ptr<Task>(_theTask)->Lock());
605 std::thread::id threadId(std::this_thread::get_id());
606 auto mapItem = _waitEventMap.find(threadId);
607 assert (mapItem != _waitEventMap.end());
608 WaitEventDetails *waitEvent = &(mapItem->second);
609 /*
610 * Wait for the condition
611 *
612 * We use a Lambda function to access the DataAvail() method
613 * in waitEvent.
614 *
615 */
616 waitEvent->_condition->wait_for(
618 [waitEvent]{return waitEvent->DataAvail();});
619 if (!waitEvent->DataAvail())
620 {
621 std::shared_ptr<Task>(_theTask)->Logger().LogNF(
623 "d2::TACT::WaitEventTOIn",
624 "Timeout waiting for signal/kick");
625
626
627 // Timeout.
628 //fprintf(stderr,"TAction::WaitKickFor() - timeout\n");
629 ClearWait(false);
630 *event = EntryCode::DramaAbortWaits;
631 return false;
632 }
633 //fprintf(stderr,"TAction::WaitEventFor() - event/signal\n");
634
635 WaitEventData details = waitEvent->_dataQueue.front();
636 *event = details.eventInfo.entryReason;
637 /*
638 * If the user wants the argument, return it.
639 */
640 if (arg)
641 {
642 *arg = details.arg;
643 }
644 std::shared_ptr<Task>(_theTask)->Logger().LogNF(D2LOG_DRAMA2,
645 "d2::TACT::WaitEventTOIn",
646 "kick/signal received");
647
648
649 waitEvent->_dataQueue.pop();
650 // Should complain here (throw execption?) if queue not empty
651 ClearWait(false);
652
653 return true;
654
655 }
676 bool WaitForEventTimeoutIn(
678 unsigned seconds,
679 sds::IdPtr * const arg=0) {
680
681 return WaitForEventTimeoutIn(event, std::chrono::seconds(seconds), arg);
682
683 }
684
685
686
687
688
718 template <class Clock, class Duration>
719 bool WaitEventTimeoutAt(
721 const std::chrono::time_point<Clock,Duration>& abs_time,
722 sds::IdPtr * const arg=0 ) {
723
724 /*
725 * Signal DRAMA that we are waiting for a kick.
726 * This will put an entry in _waitEventMap for this
727 * thread.
728 */
729 SignalAmWaitingForKick();
730 /*
731 * Access the entry in _waitEventMap.
732 */
734 std::shared_ptr<Task>(_theTask)->Lock());
735 std::thread::id threadId(std::this_thread::get_id());
736 auto mapItem = _waitEventMap.find(threadId);
737 assert (mapItem != _waitEventMap.end());
738 WaitEventDetails *waitEvent = &(mapItem->second);
739 /*
740 * Wait for the condition
741 *
742 * We use a Lambda function to access the DataAvail() method
743 * in waitEvent.
744 */
745 waitEvent->_condition->wait_until(
747 [waitEvent]{return waitEvent->DataAvail();});
748 if (!waitEvent->DataAvail())
749 {
750 // Timeout.
751 std::shared_ptr<Task>(_theTask)->Logger().LogNF(D2LOG_DRAMA2,
752 "d2::TACT::WaitEventTOAT",
753 "Timeout waiting for signal/kick");
754
755 ClearWait(false);
756 *event = EntryCode::DramaAbortWaits;
757 return false;
758 }
759 //fprintf(stderr,"TAction::WaitKickFor() - kicked\n");
760 WaitEventData details = waitEvent->_dataQueue.front();
761 *event = details.eventInfo.entryReason;
762 /*
763 * If the user wants the argument, return it.
764 */
765 if (arg)
766 {
767 *arg = details.arg;
768 }
769 waitEvent->_dataQueue.pop();
770 std::shared_ptr<Task>(_theTask)->Logger().LogNF(
772 "d2::TACT::WaitEventTOAt",
773 "Signal/Kick received");
774
775
776
777 // Should complain here (throw execption?) if queue not empty
778 ClearWait(false);
779
780 return true;
781
782 }
797 void WaitForKick(sds::IdPtr * const arg=nullptr) {
798
799 // Can be implemented by WaitForEvent(), with a check on the event.
801 WaitForEvent(&event, arg);
802 if (event != EntryCode::Kick)
803 {
805 "TAction::WaitForKick - signal received instead of kick, application programming error. You may want WaitForEvent()");
806 }
807 }
834 template <class Rep, class Period>
835 bool WaitForKickTimeoutIn(
836 const std::chrono::duration<Rep,Period>& rel_time,
837 sds::IdPtr * const arg=0) {
838
839 // Can be implemented by WaitForEventTimeoutIn(), with a check on the event.
841 bool kicked = WaitForEventTimeoutIn(&event, rel_time, arg);
842 if (!kicked)
843 return false;
844 if (event != EntryCode::Kick)
845 {
847 "TAction::WaitForKickTimeoutIn - signal received instead of kick, application programming error. You may want WaitForEventTimeoutIn()");
848 }
849 return true;
850
851
852 }
853
872 bool WaitForKickTimeoutIn(
873 unsigned seconds,
874 sds::IdPtr * const arg=0) {
875
876 return WaitForKickTimeoutIn(std::chrono::seconds(seconds), arg);
877
878 }
879
907 template <class Clock, class Duration>
908 bool WaitKickForTimeoutAt(
909 const std::chrono::time_point<Clock,Duration>& abs_time,
910 sds::IdPtr * const arg=0 ) {
911
912
913 // Can be implemented by WaitForEventTimeoutAt(), with a check on the event.
916 if (!kicked)
917 return false;
918 if (event != EntryCode::Kick)
919 {
921 "TAction::WaitForKickTimeoutAt - signal received instead of kick, application programming error. You may want WaitForEventTimeoutAt()");
922 }
923 return true;
924
925
926
927 }
928
952 void SetupWaitEvent(DitsTransIdType tid, drama::Path *pathObj) override;
953
968 void SetReturnArg(const sds::Id &arg, bool copy=true) {
969
970 if (copy)
971 {
972 _outArg = arg.Copy();
973 _outArgDelete = true;
974 }
975 else
976 {
977 _outArg.ShallowCopy(arg);
978 _outArgDelete = false;
980
981 _outArgSet = true;
982 }
987 void SetExitOnCompletion() {
988 _exitTask = true;
989 }
994 void ClearExitOnCompletion() {
995 _exitTask = false;
996 }
1008 void SetReturnArg(sds::Id *arg) {
1009
1010 _outArg.ShallowCopy(arg, true);
1011 _outArgDelete = true;
1012 _outArgSet = true;
1013
1015
1023 void PutObeyHandler(MessageHandlerPtr obj) override final;
1024
1038 void PutKickHandler(MessageHandlerPtr obj) override final;
1039
1040
1047 Task::mutexType & Lock() const override;
1048
1056 Dits___CurActType GetMessageContext() const override;
1057
1058
1059
1060
1061
1071 WaitEventDetails *SetupWaitForKick() {
1072
1073 /*
1074 * Signal DRAMA that we are waiting for a kick.
1075 * This will put an entry in _waitEventMap for this
1076 * thread.
1077 */
1078 SignalAmWaitingForKick();
1079
1080 /*
1081 * Access the entry in _waitEventMap.
1082 */
1084 std::shared_ptr<Task>(_theTask)->Lock());
1085 std::thread::id threadId(std::this_thread::get_id());
1086 auto mapItem = _waitEventMap.find(threadId);
1087 assert (mapItem != _waitEventMap.end());
1088 return &(mapItem->second);
1089
1090
1091 }
1109 bool DoWaitForKick(WaitEventDetails *waitEvent,
1110 sds::IdPtr * const arg=nullptr) {
1111
1113 /*
1114 * Wait for the condition
1115 *
1116 * We use a Lambda function to access the DataAvail() method
1117 * in waitEvent.
1118 *
1119 */
1121 std::shared_ptr<Task>(_theTask)->Lock());
1122 waitEvent->_condition->wait(
1123 DramaLock,
1124 [waitEvent]{return waitEvent->DataAvail();});
1125
1126
1127 WaitEventData details = waitEvent->_dataQueue.front();
1128 /*
1129 * If the user wants the argument, return it.
1130 */
1131 if (arg)
1132 {
1133 *arg = details.arg;
1134 }
1135 std::shared_ptr<Task>(_theTask)->Logger().LogNF(
1137 "d2::TACT::DoWaitKick",
1138 "Kick received");
1139
1140
1141 waitEvent->_dataQueue.pop();
1142 // Should we complain here (throw execption?) if queue not empty?
1143 ClearWait(false);
1144
1145 if (details.eventInfo.entryReason ==
1146 EntryCode::DramaAbortWaits)
1147 return false;
1148 return true;
1149 }
1150
1151 private:
1152 /*
1153 * Objects used as part of handling obey reschedule and
1154 * kick messages.
1155 *
1156 * They simply result in the ObeyReschedule() and KickMessage()
1157 * methods below being invoked.
1158 */
1159 _ThreadMessage _obeyRescheduleObj;
1160 _ThreadKick _kickMessageObj;
1161
1162 /*
1163 * The details on the DITS action are recorded here before
1164 * we start the thread.
1165 */
1166 int _actionPtr;
1167 Dits___CurActType _actionDetails;
1168
1169 std::string _actionName;
1170
1171
1172 /*
1173 * Count of kick messages received.
1174 */
1175 unsigned _numKicks;
1176 /*
1177 * Argument to the last kick, if any.
1178 */
1179 //sds::IdPtr _lastKickArg;
1180
1181 /*
1182 * Argument to the obey, if any.
1183 */
1184 sds::IdPtr _obeyArg;
1185
1186 /*
1187 * Will task exit on action completion.
1188 */
1189 bool _exitTask = false;
1190 /*
1191 * Output argument details.
1192 */
1193 sds::Id _outArg; // Actual argument.
1194 bool _outArgDelete = false; // Should it be deleted by DRAMA?
1195 bool _outArgSet = false; // Has it been set.
1196
1197 /*
1198 * A double ended queue of information about signals sent from
1199 * the threads to DRAMA.
1200 *
1201 * We add signals to the front, remove them from the back.
1202 */
1203 SignalDetailsQueue _signalQueue;
1204
1205 /*
1206 * A map which contains all the events we are waiting on.
1207 * (one entry in the map per subsidiary thread).
1208 */
1209 WaitEventMapType _waitEventMap;
1210
1211
1212 /*
1213 * Method invoked to handle obey reschedule events.
1214 */
1215 Request ObeyReschedule();
1216 /*
1217 * Method invoked to handle kick messages
1218 */
1219 Request KickMessage();
1220
1221 /*
1222 * Method used to signal the action code from threads.
1223 */
1224 void SignalDrama(ThreadSignalType why);
1225
1226 /*
1227 * Signal that we are waiting for a kick.
1228 */
1229 void SignalAmWaitingForKick();
1230 /* Process a DRAMA Signal event message.
1231 */
1232 Request ProcessSignal();
1233 /* Process a DRAMA 2 Signal event message (used for communications
1234 * between threads and the Main DRAMA code)
1235 */
1236 Request ProcessDrama2Signal();
1237 /*
1238 * Process a Subsidiary action message.
1239 */
1240 Request ProcessSubsidiaryMessage();
1241
1242 /*
1243 * Clear the thread' waiting flag in _waitEventMap.
1244 *
1245 * @param complete If true, then it is known the transaction is
1246 * actually complete. Set false if know to be not
1247 * complete or unknown. Determines if we orphan
1248 * the transaction.
1249 */
1250 void ClearWait(bool complete) override;
1251
1252 /*
1253 * Return a pointer to the event details for the specified thread.
1254 */
1255 WaitEventDetails *FindWaitEventDetails(std::thread::id)
1256 override;
1257
1258
1259 /*
1260 * Wake up threads waiting on messages. Used to wake up waiting
1261 * action threads after a kick or signal is received.
1262 *
1263 * @return The count of threads found and signaled.
1264 */
1265 unsigned WakeUpWaitingThreads(sds::IdPtr arg);
1266
1267 /*
1268 * Indicate and action thread is complete. Called at the end
1269 * of the thread.
1270 */
1271 void ActionThreadComplete();
1272
1273 /*
1274 * Invoked to signal any threads waiting for messages to
1275 * cancel their wait events. Returns the number of signals
1276 * sent.
1277 */
1278 unsigned SignalWaitingThreads(EntryCode, StatusType);
1279
1280 /*
1281 * Invoke when the action is ending - will make any outstanding
1282 * events into orphans.
1283 */
1284 void OrphanOutstandingEvents();
1285
1286 public:
1287 void SignalWaitingEvent(WaitEventDetails *, EntryCode, StatusType);
1293 unsigned GetKickCount() const {
1294 return _numKicks;
1295 }
1301#if 0
1302 sds::IdPtr GetLastKickArg() const {
1303 return _lastKickArg;
1304 }
1305#endif
1309 void ResetKickCount();
1310
1316 void AbortMessageWaits(StatusType status);
1317
1318
1325 std::string GetActionName() const {
1326 return _actionName;
1327 }
1328
1329 }; // class TAction
1330
1331
1344 private:
1345 const ThreadActionFunction _func; // The function.
1346 public:
1353 TActionViaFunctor(std::weak_ptr<Task> dramaTask,
1354 const ThreadActionFunction func) :
1355 TAction(dramaTask, 0), _func(func) { }
1356
1363 void ActionThread(const drama::sds::Id &obeyArg) override {
1364 _func(this, obeyArg);
1365 }
1366 };
1367
1368
1391 class KickNotifier : public RunDramaExitNotifier {
1392 private:
1393 bool _wasKicked = false; // Has action been kicked.
1394 bool _waiting = false; // Is thread waiting for kick.
1395 bool _threadThrew = false; // Has the thread died by throwing?
1396 TAction *_theAction = nullptr; // Action we are part of.
1397
1398
1399 std::future<void> _threadFuture; // Future used for WaitThread
1400 // The condition variable is used to ensure the constructor
1401 // does not return until the child thread is running and
1402 // has initialised itself, indicated by _waitEvent being
1403 // valid (not a nullptr).
1404 std::condition_variable_any _threadReadyCond;
1405 WaitEventDetails *_waitEvent = nullptr;
1406
1407 void WaitThread(); // Entry point for child thread.
1408 public:
1432 KickNotifier(TAction *action);
1433
1464 virtual bool Kicked(const sds::Id & arg);
1465
1466
1471 bool WasKicked() {
1472 Task::guardType DramaLock(std::shared_ptr<Task>(_theAction->GetTask())->Lock());
1473 return _wasKicked;
1474 }
1475
1480 bool ThreadThrewException() const {
1481 return _threadThrew;
1482 }
1483
1488 virtual ~KickNotifier();
1489
1492 KickNotifier& operator=(const KickNotifier &rhs) = delete;
1495 KickNotifier(const KickNotifier &source) = delete;
1496
1508 virtual int RunDramaHasExited() override;
1509
1528 bool JoinThreads(std::chrono::steady_clock::time_point until) override;
1529
1530
1531 }; // class KickNotifier
1532
1533 } // namespace thread
1534} // namespace drama
1536#endif
void MessageUserF(const fmt::format_string< Args... > fmt, Args &&... args)
Use DRAMA to send a message to the user - format using fmt::format.
Definition messagehandler.hh:404
virtual void PutObeyHandler(MessageHandlerPtr obj)
Put a message handler object for the next Obey reschedule event.
Definition messagehandler.hh:690
MessageHandler & operator=(const MessageHandler &rhs)=delete
Copy operator deleted.
virtual std::shared_ptr< Task > GetTask() const
Returns a pointer to the task.
Definition messagehandler.hh:439
virtual void PutKickHandler(MessageHandlerPtr obj)
Put a message handler object for the next Kick event.
Definition messagehandler.hh:709
virtual void MessageUser(const std::string &text) const
Use DRAMA to send a message to the user.
MessageHandler()
Create a DRAMA action/message handler object.
Definition messagehandler.hh:177
virtual const sds::PrintObjectCR & SdsListToUser() const
Get a reference to an SDS printer object which can be used to list an SDS object using MessageUser.
void SendTrigger(const sds::Id &arg) const
Send a trigger message to the parent action.
void SetReturnArg(const sds::Id &arg, bool copy=true)
Set the argument to be sent as part of the action completion message.
Definition messagehandler.hh:616
A class which implements a DRAMA Message Handler.
Definition messagehandler.hh:141
A Class which provides access to DRAMA's message sending facilities.
Definition path.hh:689
Class used by Obey and Kick handlers to indicate rescheduling requirements.
Definition request.hh:78
Class used to arrange for notifications when the RunDrama exits.
Definition task.hh:393
std::unique_lock< mutexType > uniqueLockType
Defines the type of a unique_lock type using our mutex type.
Definition task.hh:474
std::recursive_timed_mutex mutexType
Defines the type of our mutex.
Definition task.hh:466
std::lock_guard< mutexType > guardType
Defines the type of a lock guard using our mutex type.
Definition task.hh:471
A class which implements a DRAMA task.
Definition task.hh:452
virtual Id Copy() const
Factory constructor method Id Copy constructor.
virtual void ShallowCopy(const Id &source)
Shallow copy from a const sds::Id which will outlive this object.
Definition sds.hh:2521
A C++ Interface to the handling SDS structures.
Definition sds.hh:428
Abstract class which is sub-classed to print SDS item listings.
Definition sds.hh:310
KickNotifier & operator=(const KickNotifier &rhs)=delete
Assignment operator - deleted.
KickNotifier(TAction *action)
KickNotifier constructor.
virtual bool Kicked(const sds::Id &arg)
Method is invoked when a Kick occurs.
bool ThreadThrewException() const
Return true if the thread has died after thrown an exception.
Definition threadaction.hh:1507
bool JoinThreads(std::chrono::steady_clock::time_point until) override
Invoked when the drama::task::RunDrama() loop exits.
virtual int RunDramaHasExited() override
Invoked when the drama::task::RunDrama() method exits.
virtual ~KickNotifier()
Destructor.
bool WasKicked()
Indicates if the action was kicked.
Definition threadaction.hh:1498
KickNotifier(const KickNotifier &source)=delete
Copy constructor - deleted.
An object used to obtain notifications of kicks.
Definition threadaction.hh:1418
TActionViaFunctor(std::weak_ptr< Task > dramaTask, const ThreadActionFunction func)
Initialize object with the specified function, which meets the ThreadActionFunction prototype.
Definition threadaction.hh:1380
void ActionThread(const drama::sds::Id &obeyArg) override
Invoke function.
Definition threadaction.hh:1390
This class is used to creating TAction objects referring to functions.
Definition threadaction.hh:1370
bool WaitForKickTimeoutIn(const std::chrono::duration< Rep, Period > &rel_time, sds::IdPtr *const arg=0)
Block the current thread until a kick for the action is received or a duration has passed.
Definition threadaction.hh:862
void MessageUser(const std::string &text) const override
Use DRAMA to send a message to the user.
virtual const sds::PrintObjectCR & SdsListToUser() const override
Get a reference to an SDS printer object which can be used to list an SDS object using MessageUser.
Definition threadaction.hh:488
TAction(const TAction &source)=delete
Copy constructor - deleted.
void WaitForKick(sds::IdPtr *const arg=nullptr)
Block the current thread until a kick for the action is received.
Definition threadaction.hh:824
void SetTimeout(double newTimeout)
Set a new timeout.
Definition threadaction.hh:514
double GetTimeout() const
Return the current action timeout.
Definition threadaction.hh:503
bool WaitKickForTimeoutAt(const std::chrono::time_point< Clock, Duration > &abs_time, sds::IdPtr *const arg=0)
Block the current thread until a kick for the action is received or until a given time.
Definition threadaction.hh:935
TAction & operator=(const TAction &rhs)=delete
Assignment operator - deleted.
void MessageUserF(const fmt::format_string< Args... > fmt, Args &&... args)
Use DRAMA to send a message to the user - format using fmt::format.
Definition threadaction.hh:472
void PutKickHandler(MessageHandlerPtr obj) override final
We cannot change the kick handler in a threaded action, it does not make sense.
void SetupWaitEvent(DitsTransIdType tid, drama::Path *pathObj) override
Sets up a wait event for this thread.
virtual ~TAction()
TAction destructor.
bool WaitEventTimeoutAt(EntryCode *event, const std::chrono::time_point< Clock, Duration > &abs_time, sds::IdPtr *const arg=0)
Block the current thread until a signal/kick for the action is received or until a given time.
Definition threadaction.hh:746
void SetExitOnCompletion()
Set exit on completion.
Definition threadaction.hh:1014
unsigned GetKickCount() const
Returns the number of times this action has been kicked since started or the last reset of the count.
Definition threadaction.hh:1320
void AbortMessageWaits(StatusType status)
Tells any thread waiting for messages to abort the wait event.
bool WaitForEventTimeoutIn(EntryCode *event, unsigned seconds, sds::IdPtr *const arg=0)
Block the current thread until a signal/kick for the action is received or a duration in seconds has ...
Definition threadaction.hh:703
bool DoWaitForKick(WaitEventDetails *waitEvent, sds::IdPtr *const arg=nullptr)
Wait for a kick event with the waitEvent details given.
Definition threadaction.hh:1136
void SetReturnArg(sds::Id *arg)
Set the argument to be sent as part of the action completion message.
Definition threadaction.hh:1035
void ClearExitOnCompletion()
Clear exit on completion.
Definition threadaction.hh:1021
void MessageUser(const char *format, Types... args)
Use DRAMA to send a message to the user - safe format.
Definition threadaction.hh:442
bool HasTaskExpired() const noexcept
Indicate if the underlying DRAMA task weak pointer has expired.
Definition threadaction.hh:389
std::shared_ptr< Task > GetTask() const override
Get a reference to the DRAMA task we are part of.
Definition threadaction.hh:379
TAction(std::weak_ptr< Task > dramaTask, double timeout=0)
Create a DRAMA action/message handler object which runs a thread when the Obey message is received.
Definition threadaction.hh:249
void WaitForEvent(EntryCode *event, sds::IdPtr *const arg=nullptr)
Block the current thread until a kick/signal message for the action is received.
Definition threadaction.hh:533
std::string GetActionName() const
Return the name of the action this thread is implementing.
Definition threadaction.hh:1352
void SetReturnArg(const sds::Id &arg, bool copy=true)
Set the argument to be sent as part of the action completion message.
Definition threadaction.hh:995
WaitEventDetails * SetupWaitForKick()
Set up to wait for a kick message.
Definition threadaction.hh:1098
void SendTrigger(const sds::Id &arg) const
Send a trigger message to the parent action.
Dits___CurActType GetMessageContext() const override
Get the DRAMA Context associated with the action event.
bool WaitForKickTimeoutIn(unsigned seconds, sds::IdPtr *const arg=0)
Block the current thread until a kick for the action is received or a duration in seconds has passed.
Definition threadaction.hh:899
void PutObeyHandler(MessageHandlerPtr obj) override final
We cannot change the obey handler in a threaded action, it does not make sense as the action never re...
bool WaitForEventTimeoutIn(EntryCode *event, const std::chrono::duration< Rep, Period > &rel_time, sds::IdPtr *const arg=0)
Block the current thread until a signal/kick for the action is received or a duration has passed.
Definition threadaction.hh:617
Task::mutexType & Lock() const override
Get a reference the DRAMA Task lock.
void ResetKickCount()
Returns a shared pointer to the last kick message argument.
A class which implements a DRAMA Action with runs a thread.
Definition threadaction.hh:204
virtual const sds::PrintObjectCR & SdsListToUser() const
Get a reference to an SDS printer object which can be used to list an SDS object using MessageUser.
This interface class must be implemented by classes which have threads waiting for messages.
Definition thread.hh:308
#define DramaTHROW(status_, message_)
Throw a Drama exception.
Definition exception.hh:90
#define D2LOG_DRAMA2
DRAMA 2 events - thread events etc.
Definition logger.hh:107
DRAMA 2 include file - Message Handler class definition.
std::shared_ptr< Id > IdPtr
A shared pointer for sds::Id items.
Definition sds.hh:3613
std::function< void(TAction *, const sds::Id &)> ThreadActionFunction
Type used for functions specified drama::Task::Add().
Definition task.hh:102
void CreateRunDramaTask()
Create and run a DRAMA task, with standard exception handling.
Definition task.hh:1339
EntryCode
Entry type code - indicates the type of a DRAMA event.
Definition entryinfo.hh:67
@ Kick
Action has been kicked.
@ Complete
Obey/Kick etc message completed.
std::shared_ptr< MessageHandler > MessageHandlerPtr
This type is used for passing MessageHandler object addresses around.
Definition messagehandler.hh:96
void SafePrintf(std::ostream &ostream, const char *str)
Safe formatted write to a stream.
The drama namespace contains all the classes, types etc of the DRAMA 2 implementation.
Definition drama.hh:99
DRAMA 2 include file - Code common to DRAMA 2 features supporting threading.