hmbdc
simplify-high-performance-messaging-programming
 All Classes Namespaces Functions Variables Friends Pages
Message.hpp
1 #include "hmbdc/Copyright.hpp"
2 #pragma once
3 
4 #include "hmbdc/comm/Uuid.hpp"
5 #include "hmbdc/time/Time.hpp"
6 #include "hmbdc/Config.hpp"
7 #include "hmbdc/Endian.hpp"
8 #include "hmbdc/MetaUtils.hpp"
9 
10 #include <stdint.h>
11 #include <utility>
12 #include <ostream>
13 #include <type_traits>
14 #include <bitset>
15 #include <memory.h>
16 
17 struct epoll_event;
18 namespace hmbdc { namespace app {
19 
20 #if __GNUC_PREREQ(9,0)
21 
22 template <typename M>
23 concept MessageC = std::decay<M>::type::typeTag >= 0x0
24  && std::decay<M>::type::typeTag <= 0xffffu;
25 
26 namespace message_detail {
27 template <typename ...M>
28 constexpr bool is_message_tuple = false;
29 
30 template <MessageC ...M>
31 constexpr bool is_message_tuple<std::tuple<M...>> = true;
32 }
33 
34 template <typename MT>
35 concept MessageTupleC = message_detail::is_message_tuple<MT>;
36 
37 template <typename MIter>
38 concept MessageForwardIterC = MessageC<decltype(*(MIter{}))>
39  && requires(MIter it) {++it;};
40 
41 #else
42 
43 #define MessageC typename
44 #define MessageTupleC typename
45 #define MessageForwardIterC typename
46 
47 #endif
48 
49 /**
50 * see @example hmbdc.cpp
51 * @example perf-tcpcast.cpp
52 */
53 
54 /**
55  * @class hasTag<>
56  * @brief each message type has 16 bit tag
57  * @tparam tag 16 bit unsigned tag
58  */
59 template <uint16_t tag>
60 struct hasTag {
61  enum {typeTag = tag};
62  friend
63  std::ostream&
64  operator << (std::ostream & os, hasTag const& t) {
65  return os << "hasTag=" << tag << std::endl;
66  }
67 };
68 
69 /**
70  * @class MessageHead
71  * @details It is recommneded that a Message type do not have significant dtor defined
72  * because the message could travel through process bundaries or through network
73  * - won't compile if trying to send them.
74  * The only exception is the BlockingContext (licensed package), which does allow
75  * message's dtors and call them properly within a process boundary.
76  * @snippet hmbdc.cpp define a message
77  */
78 struct MessageHead {
79  MessageHead(uint16_t typeTag)
80  : reserved2(0)
81  , reserved(0)
82  , typeTag(typeTag)
83  {}
84  template <typename Message> Message& get();
85  template <typename Message> Message const& get() const;
86  template <typename Message> void setPayload(Message const&);
87  template <typename Message, typename ...Args> void setPayloadInPlace(Args&& ... args);
88  uint32_t reserved2;
89  uint16_t reserved;
90 
91  XmitEndian<uint16_t> typeTag;
92 
93  using Scratchpad =
94  union {
95  XmitEndian<uint16_t> payloadLen;
97  uint64_t reserved : 48;
98  uint8_t space[6];
99  } __attribute__((packed));
100  static_assert(sizeof(Scratchpad) == sizeof(reserved2) + sizeof(reserved), "");
101  static_assert(sizeof(Scratchpad::seq) == sizeof(reserved2) + sizeof(reserved), "");
102  Scratchpad& scratchpad() {return *reinterpret_cast<Scratchpad*>(&reserved2);}
103  Scratchpad const& scratchpad() const{return *reinterpret_cast<Scratchpad const*>(&reserved2);}
104  friend
105  std::ostream& operator << (std::ostream& os, MessageHead const & h) {
106  return os << h.scratchpad().reserved << ' ' << h.typeTag;
107  }
108 } __attribute__((packed));
109 static_assert(sizeof(MessageHead) == 8, "pakcing issue?");
110 
111 template <typename Message>
113  template <typename ...Args>
114  MessageWrap(Args&& ... args)
115  : MessageHead(Message::typeTag)
116  , payload(std::forward<Args>(args)...) {
117  static_assert(std::is_base_of<hasTag<Message::typeTag>, Message>::value
118  , "not a properly defined Message");
119  }
120 
121  MessageWrap(MessageWrap const&) = default;
122  MessageWrap(MessageWrap &) = default;
123  MessageWrap(MessageWrap &&) = default;
124  MessageWrap& operator = (MessageWrap const&) = default;
125  MessageWrap& operator = (MessageWrap &&) = default;
126 
127  Message payload;
128 
129  friend
130  std::ostream& operator << (std::ostream& os, MessageWrap<Message> const & w) {
131  return os << static_cast<MessageHead const&>(w) << ' ' << w.payload;
132  }
133 };
134 
135 template <typename Message>
136 Message&
137 MessageHead::get() {
138  return static_cast<MessageWrap<Message>*>(this)->payload;
139 }
140 
141 template <typename Message>
142 Message const&
143 MessageHead::get() const{
144  return static_cast<MessageWrap<Message> const*>(this)->payload;
145 }
146 
147 template <typename Message>
148 void
149 MessageHead::
150 setPayload(Message const& m) {
151  new (&get<Message>()) Message(m);
152  typeTag = Message::typeTag;
153 }
154 
155 template <typename Message, typename ...Args>
156 void
157 MessageHead::
158 setPayloadInPlace(Args&& ... args) {
159  new (&get<Message>()) Message(std::forward<Args>(args)...);
160  typeTag = Message::typeTag;
161 }
162 
163 struct Flush
164 : hasTag<0> {
165 };
166 
167 template <size_t MaxStreamableSize>
168 struct LoggingT
169 : hasTag<3> {
170  char payload[MaxStreamableSize];
171 } __attribute__((__may_alias__, packed));
172 
173 /**
174  * @class JustBytes
175  * @brief A special type of message only used on the receiving side
176  * @details When a Client declare this type among its interested message types, all
177  * messages types (WITHOUT the destructor) will be delievered in
178  * byte form to this client. It is normmaly listed as the last Message type to
179  * in the interested message type list to catch all
180  * - see Client.hpp and ConsoleClient.hpp documentation
181  */
182 struct JustBytes
183 : hasTag<4> {
184  JustBytes() = delete;
185 };
186 
187 struct Trace {
188  Trace()
189  : tsIndex(1) {
190  ts[0] = (time::SysTime::now());
191  }
192  uint8_t tsIndex;
193  time::SysTime ts[6];
194  friend
195  std::ostream& operator << (std::ostream& os, Trace const & t) {
196  for (auto i = 0u; i < t.tsIndex; ++i) os << t.ts[i] << ' ';
197  return os;
198  }
199 };
200 
201 template<>
203  MessageWrap(uint16_t tag, void const* bytes, size_t len)
204  : MessageHead(tag) {
205  memcpy(&payload, bytes, len);
206  }
207  uint8_t payload;
208 
209  friend
210  std::ostream& operator << (std::ostream& os, MessageWrap<JustBytes> const & w) {
211  return os << static_cast<MessageHead const&>(w) << " *";
212  }
213 };
214 static_assert(sizeof(MessageWrap<JustBytes>) == sizeof(MessageHead) + 1, "wrong packing");
215 static_assert(sizeof(MessageWrap<Flush>) == sizeof(MessageHead) + sizeof(Flush), "wrong packing");
216 
217 /**
218  * @class hasMemoryAttachment
219  * @brief if a specific hmbdc network transport (for example tcpcast, rmcast, and rnetmap) supports message
220  * with memory attachment, the message needs to be derived from this base - as the FIRST base,
221  * so it can be handled properly by the hmbdc network transport when sending and receiving it
222  * @details user on the sending side cannot directly free the attachment, instead the user can provide
223  * a callback func and hmbdc will call it - the default callback function is to call free on the attachment
224  */
226  using AfterConsumedCleanupFunc = void (*)(hasMemoryAttachment*);
227 
228  /**
229  * @brief default ctor
230  * @details will allocate and release thru heap
231  */
233  : afterConsumedCleanupFunc(hasMemoryAttachment::free)
234  , attachment(NULL)
235  , len(0) {
236  }
237  enum {
238  flag = 1
239  };
240 
241  /**
242  * @brief file mapped memory
243  * @param fileName file name to map
244  */
245  hasMemoryAttachment(char const* fileName)
246  : afterConsumedCleanupFunc(nullptr)
247  , attachment(nullptr)
248  , len(0) {
249  map(fileName);
250  }
251 
252  void release() {
253  if (afterConsumedCleanupFunc) {
254  afterConsumedCleanupFunc(this);
255  afterConsumedCleanupFunc = nullptr;
256  }
257  }
258 
259  /**
260  * @brief map this object's attached memory to a file
261  * @details the afterConsumedCleanupFunc is automatically set to do the unmap
262  *
263  * @param fileName file to map into memory
264  * @return size of the file mapped
265  */
266  size_t map(char const* fileName);
267  AfterConsumedCleanupFunc afterConsumedCleanupFunc; /// pointing to a func that handles cleaning up the attachment
268  /// it is automatically set, but user can change it as desire
269  /// when the sending side is done with this message,
270  /// this is called by hmbdc automatically.
271  /// however, this is not called on the recv side since
272  /// only the user code knows when to call it
273 
274  /**
275  * @brief does the reverse of map
276  */
277  void
278  unmap() {
279  unmap(this);
280  }
281  void* attachment;
282  XmitEndian<size_t> len;
283 
284  friend
285  std::ostream& operator << (std::ostream& os, hasMemoryAttachment const & m) {
286  return os << "hasMemoryAttachment " << m.attachment << ' ' << m.len;
287  }
288 
289  static
290  void
292 
293  static
294  void
295  free(hasMemoryAttachment* a) {
296  ::free(a->attachment);
297  a->attachment = nullptr;
298  }
299 } __attribute__((aligned (8)));
300 
302 : hasTag<5> {
303  XmitEndian<uint16_t> underlyingMessageTypeTag;
305  XmitEndian<size_t> segCount;
306 
307  friend
308  std::ostream& operator << (std::ostream& os, StartMemorySegTrain const & m) {
309  return os << "StartAttachmentTrain " << m.underlyingMessageTypeTag
310  << ' ' << m.att << ' ' << m.segCount;
311  }
312 } __attribute__((aligned(8)));
313 
314 struct MemorySeg
315 : hasTag<6> {
316  void const * seg;
318  friend
319  std::ostream& operator << (std::ostream& os, MemorySeg const & m) {
320  return os << "MemorySeg " << m.seg << ' ' << m.len;
321  }
322 } __attribute__((packed));
323 
324 /**
325  * @class LastSystemMessage
326  * @brief hmbdc system messages use tag values less than this one
327  */
329 : hasTag<999> {
330 };
331 
332 /**
333  * @brief template that convert a regular message to be a request
334  * used in request / reply sync messaging - see RequestReply.hpp for details
335  *
336  * @tparam Message a regular message type
337  */
338 template <MessageC Message>
339 struct REQUEST
340 : hasTag<Message::typeTag> {
341  comm::Uuid uuid;
342  Message request;
343 
344  template <typename ...Args>
345  explicit REQUEST(Args&&... args)
346  : request(std::forward<Args>(args)...)
347  {}
348 
349  REQUEST(REQUEST const&) = default;
350  REQUEST(REQUEST &) = default;
351  REQUEST(REQUEST &&) = default;
352  REQUEST& operator = (REQUEST const&) = default;
353  REQUEST& operator = (REQUEST &&) = default;
354 };
355 
356 /**
357  * @brief template that convert a regular message to be a reply
358  * used in request / reply sync messaging - see RequestReply.hpp for details
359  *
360  * @tparam Message a regular message type
361  */
362 template <MessageC Message>
363 struct REPLY
364 : hasTag<Message::typeTag> {
365  template <typename Request, typename ...Args>
366  explicit REPLY(REQUEST<Request> const& req
367  , Args&&... args)
368  : uuid(req.uuid)
369  , reply(std::forward<Args>(args)...)
370  {}
371 
372  comm::Uuid uuid;
373  Message reply;
374 };
375 
376 template <>
378 : hasTag<7> {
379  comm::Uuid uuid;
380  char reply;
381 };
382 
383 template<>
385  MessageWrap(uint16_t tag, void const* bytes, size_t len)
386  : MessageHead(tag) {
387  memcpy(&payload, bytes, len);
388  }
389  REPLY<JustBytes> payload;
390 
391  friend
392  std::ostream& operator << (std::ostream& os, MessageWrap<REPLY<JustBytes>> const & w) {
393  return os << static_cast<MessageHead const&>(w) << " *";
394  }
395 };
396 
397 #if __GNUC_PREREQ(9,0)
398 
399 template <typename R>
400 concept RequestC = hmbdc::is_template<REQUEST, typename std::decay<R>::type>;
401 template <typename R>
402 concept ReplyC = hmbdc::is_template<REPLY, typename std::decay<R>::type>;
403 
404 #else
405 
406 #define RequestC typename
407 #define ReplyC typename
408 
409 #endif
410 
411 }} //hmbdc::app
Definition: Message.hpp:202
hasMemoryAttachment(char const *fileName)
file mapped memory
Definition: Message.hpp:245
size_t map(char const *fileName)
map this object&#39;s attached memory to a file
each message type has 16 bit tag
Definition: Message.hpp:60
hasMemoryAttachment()
default ctor
Definition: Message.hpp:232
template that convert a regular message to be a reply used in request / reply sync messaging - see Re...
Definition: Message.hpp:363
Definition: Message.hpp:168
Definition: Message.hpp:163
Definition: Message.hpp:78
A special type of message only used on the receiving side.
Definition: Message.hpp:182
Definition: Message.hpp:301
Definition: Time.hpp:14
Definition: Uuid.hpp:10
hmbdc system messages use tag values less than this one
Definition: Message.hpp:328
Definition: Message.hpp:187
void unmap()
does the reverse of map
Definition: Message.hpp:278
Definition: Message.hpp:112
Definition: Message.hpp:377
Definition: Message.hpp:314
if a specific hmbdc network transport (for example tcpcast, rmcast, and rnetmap) supports message wit...
Definition: Message.hpp:225
template that convert a regular message to be a request used in request / reply sync messaging - see ...
Definition: Message.hpp:339