hmbdc
simplify-high-performance-messaging-programming
Client.hpp
1 #include "hmbdc/Copyright.hpp"
2 #pragma once
3 
4 #include "hmbdc/app/MessageDispacher.hpp"
5 #include "hmbdc/MetaUtils.hpp"
6 #include "hmbdc/Compile.hpp"
7 
8 #include <iostream>
9 #include <tuple>
10 #include <type_traits>
11 
12 namespace hmbdc { namespace app {
13 
14 /**
15  * @class single_thread_powered_client
16  * @brief a trait class, if a Client can only run on a single specific thread in Pool,
17  * derive the Client from it, hmbdc will check to ensure that is the case
18  */
20 
21 /**
22  * @class Client<>
23  * @brief A Client represents a thread of execution/a task. The execution is managed by
24  * a Context.
25  * a Client object could participate in message dispatching as the receiver of specifed
26  * message types
27  * @details message is dispatched thru callbacks in the loose
28  * form of void handleMessageCb(Message const& m) or void handleMessageCb(Message& m).
29  * some callbacks have default implementations.
30  * however, all callbacks are overridable to provide desired effects;
31  * When running in a Context, all callbacks (*Cb methods) for a hmbdc Client instance
32  * are garanteed to be called from a single OS thread, ie. no 2 callabacks of an Client
33  * instance are called concurrently, so the Client programmer can assume a
34  * single thread programming model callback-wise.
35  * The above also holds true for timer firing callbacks when a concrete Client deriving
36  * from TimerManager that manages the timers
37  *
38  * See in example hmbdc.cpp @snippet hmbdc.cpp write a Client
39  *
40  * @tparam CcClient the concrete Client type
41  * @tparam typename ... Messages message types that the Client interested
42  * if the type of JustBytes is declared, use a special callback see below
43  *
44  * @code
45  * void handleJustBytesCb(uint16_t tag, uint8_t* bytes){...}
46  * @endcode
47  * is expected
48  * see ConsoleClient.hpp
49  *
50  * There are 3 categories of Messages can be specified here regular, REQUEST, REPLY
51  * REQUEST, REPLY are for synchronous request reply messages, this is like
52  * remote procedure call (RPC) function except it is more powerful that it gives
53  * more flexible forms of RPC returning behavior - see requestReply() sendReply()
54  * in Context.hpp, and RequestReply.hpp for details
55  */
56 template <typename CcClient, MessageC ... Messages>
57 struct Client {
58  using Interests
59  = typename templatized_subtractor<REPLY, std::tuple<Messages ...>>::type;
60  using Requests
61  = typename templatized_aggregator<REQUEST, std::tuple<Messages ...>>::type;
62  using Replies
63  = typename templatized_aggregator<REPLY, std::tuple<Messages ...>>::type;
64 
65  enum {
66  INTERESTS_SIZE = std::tuple_size<Interests>::value,
67  };
68  // static_assert(std::is_base_of<Client, CcClient>::value, "");
69 
70  /**
71  * @brief return the name of thread that runs this client, override if necessary
72  * @details this only used when the Client is running in direct mode
73  * @return thread name - default to be hmbdc0, hmbdc1 ...
74  */
75  char const* hmbdcName() const { return "hmbdc-anonymous"; }
76 
77  /**
78  * @brief an overrideable method.
79  * returns the schedule policy and priority, override if necessary
80  * priority is only used when policy is "SCHED_RR", or "SCHED_FIFO"
81  * @details this is only used when the Client is running in direct mode
82  * supported policy are "SCHED_OTHER"(=nullptr), "SCHED_RR", "SCHED_FIFO"
83  * @return a tuple made of schedule policy and priority, default to be SCHED_OTHER
84  */
85  std::tuple<char const*, int> schedSpec() const {
86 #ifndef _QNX_SOURCE
87  return std::make_tuple<char const*, int>(nullptr, 0);
88 #else
89  return std::make_tuple<char const*, int>(nullptr, 20);
90 #endif
91  }
92 
93  /**
94  * @brief an overridable method.
95  * client receives events in batches and the max batch size is controllable when running
96  * in direct mode Context. Here is to specify the max size.
97  * @details a message could only be reaching one client when using partition Context. In this
98  * case, reduce the size (reduce the greediness) might be useful
99  * @return the max number of messages when grabbing messages to process
100  */
101  size_t maxBatchMessageCount() const {return std::numeric_limits<size_t>::max();}
102 
103 
104  /**
105  * @brief called before any messages got dispatched - only once
106  * @details this is the place some preparation code goes to
107  *
108  * @param threadSerialNumber normally the number
109  * indicating which thread is in action, except when INTERESTS_SIZE == 0
110  * it is another undefined value
111  *
112  */
113  virtual void messageDispatchingStartedCb(uint16_t threadSerialNumber) {
114  //default do nothing
115  };
116 
117  /**
118  * @brief callback called when this Client is taken out of message dispatching
119  * @details after this call the Client is still at hook from the Context point of view
120  * (until droppedCb is called), so don't delete this Client yet or add it back to
121  * the Context. any exception thrown here is ignored,
122  *
123  * @param e the exception that caused the Client to be taken out of message dispatching
124  * e could be thrown by the Client itself in a callback function
125  * to voluntarily take itself out
126  */
127  virtual void stoppedCb(std::exception const& e) {
128  // called when this client is stopped, e.what() might have reason
129  std::cerr << "Client " << hmbdcName()
130  << " exited with reason: " << e.what() << std::endl;
131  };
132 
133  /**
134  * @brief callback called after the Client is safely taken out of the Context
135  * @details exception thrown here is ignored and return true is assumed
136  * @return if false, this Client is added back to the Context to process messages
137  * otherwise, no more callback. You could even safely "delete this; return true;"
138  */
139  virtual bool droppedCb() {
140  // called when this client is dropped and free to be deleted
141  return true;
142  };
143 
144  /**
145  * @brief this callback is called all the time (frequently) - the exact timing is
146  * after a batch of messages are dispatched. After this call returns, the previously
147  * dispatched message's addresses are no longer valid, which means if you cache the
148  * event addresses in the previous handleMessageCb()s, you cannot use those after
149  * the return of the next invokeCb function.
150  * @details you can collectively process the messages received/cached so far here, or
151  * do something needs to be done all the time like powering another message loop
152  *
153  * @param dispatched the number of messages dispatched since last invokedCb called
154  */
155  virtual void invokedCb(size_t dispatched) {
156  // called as frequently as workload allowed
157  }
158 
159  /**
160  * @brief trivial
161  */
162  virtual ~Client(){}
163 
164 protected:
165  /**
166  * @brief the derived user's Client has the option to stop the current batch of event
167  * dispatching.
168  * @details for example, if the user's Client decides it has handled all interested
169  * messages and wants to skip the remaining messages within the CURRENT batch, call
170  * this within any callback functions.
171  * use with caution, only when it is absolutely certain that there is NO messages of
172  * ANY type the Client need to care are within the current batch.
173  */
174  void batchDone() {
175  batchDone_ = true;
176  }
177 
178 public:
179  /**
180  * @brief the following are for internal use, don't change or override
181  */
182  void stopped(std::exception const&e) noexcept {
183  try {
184  stoppedCb(e);
185  } catch (...) {}
186  }
187 
188  bool dropped() noexcept {
189  bool res = true;
190  try {
191  res = droppedCb();
192  } catch (...) {}
193  return res;
194  }
195 
196  template <typename Iterator>
197  size_t handleRangeImpl(Iterator it,
198  Iterator end, uint16_t threadId) {
199  CcClient& c = static_cast<CcClient&>(*this);
200  size_t res = 0;
201  for (;hmbdc_likely(!batchDone_ && it != end); ++it) {
202  if (MessageDispacher<CcClient, Interests>()(
203  c, *static_cast<MessageHead*>(*it))) res++;
204  }
205  batchDone_ = false;
206  return res;
207  }
208 
209 protected:
210  bool batchDone_ = false; ///not part of API, do not touch
211 };
212 
213 }} //hmbdc::app
virtual void invokedCb(size_t dispatched)
this callback is called all the time (frequently) - the exact timing is after a batch of messages are...
Definition: Client.hpp:155
char const * hmbdcName() const
return the name of thread that runs this client, override if necessary
Definition: Client.hpp:75
virtual ~Client()
trivial
Definition: Client.hpp:162
std::tuple< char const *, int > schedSpec() const
an overrideable method. returns the schedule policy and priority, override if necessary priority is o...
Definition: Client.hpp:85
template that convert a regular message to be a reply used in request / reply sync messaging - see Re...
Definition: Message.hpp:363
virtual bool droppedCb()
callback called after the Client is safely taken out of the Context
Definition: Client.hpp:139
a std tuple holding REPLY messages types it can dispatch
void batchDone()
the derived user&#39;s Client has the option to stop the current batch of event dispatching.
Definition: Client.hpp:174
void stopped(std::exception const &e) noexcept
the following are for internal use, don&#39;t change or override
Definition: Client.hpp:182
Definition: Message.hpp:78
Definition: MetaUtils.hpp:205
size_t maxBatchMessageCount() const
an overridable method. client receives events in batches and the max batch size is controllable when ...
Definition: Client.hpp:101
virtual void stoppedCb(std::exception const &e)
callback called when this Client is taken out of message dispatching
Definition: Client.hpp:127
virtual void messageDispatchingStartedCb(uint16_t threadSerialNumber)
called before any messages got dispatched - only once
Definition: Client.hpp:113
a trait class, if a Client can only run on a single specific thread in Pool, derive the Client from i...
Definition: Client.hpp:19
Definition: MetaUtils.hpp:187
A Client represents a thread of execution/a task. The execution is managed by a Context. a Client object could participate in message dispatching as the receiver of specifed message types.
Definition: Client.hpp:57
a std tuple holding messages types it can dispatch except REPLYs
Definition: BlockingContext.hpp:183
Definition: Base.hpp:13
a std tuple holding REQUEST messages types it can dispatch
template that convert a regular message to be a request used in request / reply sync messaging - see ...
Definition: Message.hpp:339