hmbdc
simplify-high-performance-messaging-programming
Message.hpp
1 #include "hmbdc/Copyright.hpp"
2 #pragma once
3 
4 #include "hmbdc/time/Time.hpp"
5 #include "hmbdc/Config.hpp"
6 #include "hmbdc/Endian.hpp"
7 #include <stdint.h>
8 #include <utility>
9 #include <ostream>
10 #include <type_traits>
11 #include <bitset>
12 #include <memory.h>
13 
14 struct epoll_event;
15 namespace hmbdc { namespace app {
16 
17 
18 /**
19 * see @example hmbdc.cpp
20 * @example perf-tcpcast.cpp
21 */
22 
23 /**
24  * @class hasTag<>
25  * @brief each message type has 16 bit tag
26  * @tparam tag 16 bit unsigned tag
27  */
28 template <uint16_t tag>
29 struct hasTag {
30  enum {typeTag = tag};
31 };
32 
33 /**
34  * @class MessageHead
35  * @details It is recommneded that a Message type do not have significant dtor defined
36  * because the message could travel through process bundaries or through network
37  * - won't compile if trying to send them.
38  * The only exception is the BlockingContext (licensed package), which does allow
39  * message's dtors and call them properly within a process boundary.
40  * @snippet hmbdc.cpp define a message
41  */
42 struct MessageHead {
43  MessageHead(uint16_t typeTag)
44  : reserved2(0)
45  , reserved(0)
46  , typeTag(typeTag)
47  {}
48  template <typename Message> Message& get();
49  template <typename Message> Message const& get() const;
50  template <typename Message> void setPayload(Message const&);
51  template <typename Message, typename ...Args> void setPayloadInPlace(Args&& ... args);
52  uint32_t reserved2;
53  uint16_t reserved;
54 
55  XmitEndian<uint16_t> typeTag;
56 
57  using Scratchpad =
58  union {
59  XmitEndian<uint16_t> payloadLen;
61  uint64_t reserved : 48;
62  uint8_t space[6];
63  } __attribute__((packed));
64  static_assert(sizeof(Scratchpad) == sizeof(reserved2) + sizeof(reserved), "");
65  static_assert(sizeof(Scratchpad::seq) == sizeof(reserved2) + sizeof(reserved), "");
66  Scratchpad& scratchpad() {return *reinterpret_cast<Scratchpad*>(&reserved2);}
67  Scratchpad const& scratchpad() const{return *reinterpret_cast<Scratchpad const*>(&reserved2);}
68  friend
69  std::ostream& operator << (std::ostream& os, MessageHead const & h) {
70  return os << h.scratchpad().reserved << ' ' << h.typeTag;
71  }
72 } __attribute__((packed));
73 static_assert(sizeof(MessageHead) == 8, "pakcing issue?");
74 
75 template <typename Message>
77  template <typename ...Args>
78  MessageWrap(Args&& ... args)
79  : MessageHead(Message::typeTag)
80  , payload(std::forward<Args>(args)...) {
81  static_assert(std::is_base_of<hasTag<Message::typeTag>, Message>::value
82  , "not a properly defined Message");
83  }
84 
85  MessageWrap(MessageWrap const&) = default;
86  MessageWrap(MessageWrap &&) = default;
87  MessageWrap& operator = (MessageWrap const&) = default;
88  MessageWrap& operator = (MessageWrap &&) = default;
89 
90  Message payload;
91 
92  friend
93  std::ostream& operator << (std::ostream& os, MessageWrap<Message> const & w) {
94  return os << static_cast<MessageHead const&>(w) << ' ' << w.payload;
95  }
96 };
97 
98 template <typename Message>
99 Message&
100 MessageHead::get() {
101  return static_cast<MessageWrap<Message>*>(this)->payload;
102 }
103 
104 template <typename Message>
105 Message const&
106 MessageHead::get() const{
107  return static_cast<MessageWrap<Message> const*>(this)->payload;
108 }
109 
110 template <typename Message>
111 void
112 MessageHead::
113 setPayload(Message const& m) {
114  new (&get<Message>()) Message(m);
115  typeTag = Message::typeTag;
116 }
117 
118 template <typename Message, typename ...Args>
119 void
120 MessageHead::
121 setPayloadInPlace(Args&& ... args) {
122  new (&get<Message>()) Message(std::forward<Args>(args)...);
123  typeTag = Message::typeTag;
124 }
125 
126 struct Flush
127 : hasTag<0> {
128 };
129 
130 template <size_t MaxStreamableSize>
131 struct LoggingT
132 : hasTag<3> {
133  char payload[MaxStreamableSize];
134 } __attribute__((__may_alias__, packed));
135 
136 /**
137  * @class JustBytes
138  * @brief A special type of message
139  * @details When a Client declare this type as interested, all
140  * messages regardless of their type will be delievered in byte form to this client
141  * in additon to regular messages - see Client.hpp and ConsoleClient.hpp documentation
142  */
143 struct JustBytes
144 : hasTag<4> {
145  JustBytes() = delete;
146 };
147 
148 struct Trace {
149  Trace()
150  : tsIndex(1) {
151  ts[0] = (time::SysTime::now());
152  }
153  uint8_t tsIndex;
154  time::SysTime ts[6];
155  friend
156  std::ostream& operator << (std::ostream& os, Trace const & t) {
157  for (auto i = 0u; i < t.tsIndex; ++i) os << t.ts[i] << ' ';
158  return os;
159  }
160 };
161 
162 template<>
164  MessageWrap(uint16_t tag, void const* bytes, size_t len)
165  : MessageHead(tag) {
166  memcpy(&payload, bytes, len);
167  }
168  uint8_t payload;
169 
170  friend
171  std::ostream& operator << (std::ostream& os, MessageWrap<JustBytes> const & w) {
172  return os << static_cast<MessageHead const&>(w) << " *";
173  }
174 };
175 static_assert(sizeof(MessageWrap<JustBytes>) == sizeof(MessageHead) + 1, "wrong packing");
176 static_assert(sizeof(MessageWrap<Flush>) == sizeof(MessageHead) + sizeof(Flush), "wrong packing");
177 
178 /**
179  * @class hasMemoryAttachment
180  * @brief if a specific hmbdc network transport (for example tcpcast, rmcast, and rnetmap) supports message
181  * with memory attachment, the message needs to be derived from this base - as the FIRST base,
182  * so it can be handled properly by the hmbdc network transport when sending and receiving it
183  * @details user on the sending side cannot directly free the attachment, instead the user can provide
184  * a callback func and hmbdc will call it - the default callback function is to call free on the attachment
185  */
187  using AfterConsumedCleanupFunc = void (*)(hasMemoryAttachment*);
188 
189  /**
190  * @brief default ctor
191  * @details will allocate and release thru heap
192  */
194  : afterConsumedCleanupFunc(hasMemoryAttachment::free)
195  , attachment(NULL)
196  , len(0) {
197  }
198  enum {
199  flag = 1
200  };
201 
202  /**
203  * @brief file mapped memory
204  * @param fileName file name to map
205  */
206  hasMemoryAttachment(char const* fileName)
207  : afterConsumedCleanupFunc(nullptr)
208  , attachment(nullptr)
209  , len(0) {
210  map(fileName);
211  }
212 
213  void release() {
214  if (afterConsumedCleanupFunc) {
215  afterConsumedCleanupFunc(this);
216  afterConsumedCleanupFunc = nullptr;
217  }
218  }
219 
220  /**
221  * @brief map this object's attached memory to a file
222  * @details the afterConsumedCleanupFunc is automatically set to do the unmap
223  *
224  * @param fileName file to map into memory
225  * @return size of the file mapped
226  */
227  size_t map(char const* fileName);
228  AfterConsumedCleanupFunc afterConsumedCleanupFunc; /// pointing to a func that handles cleaning up the attachment
229  /// it is automatically set, but user can change it as desire
230  /// when the sending side is done with this message,
231  /// this is called by hmbdc automatically.
232  /// however, this is not called on the recv side since
233  /// only the user code knows when to call it
234 
235  /**
236  * @brief does the reverse of map
237  */
238  void
239  unmap() {
240  unmap(this);
241  }
242  void* attachment;
243  XmitEndian<size_t> len;
244 
245  friend
246  std::ostream& operator << (std::ostream& os, hasMemoryAttachment const & m) {
247  return os << "hasMemoryAttachment " << m.attachment << ' ' << m.len;
248  }
249 
250  static
251  void
252  unmap(hasMemoryAttachment*);
253 
254  static
255  void
256  free(hasMemoryAttachment* a) {
257  ::free(a->attachment);
258  a->attachment = nullptr;
259  }
260 } __attribute__((aligned (8)));
261 
263 : hasTag<5> {
264  XmitEndian<uint16_t> underlyingMessageTypeTag;
266  XmitEndian<size_t> segCount;
267 
268  friend
269  std::ostream& operator << (std::ostream& os, StartMemorySegTrain const & m) {
270  return os << "StartAttachmentTrain " << m.underlyingMessageTypeTag
271  << ' ' << m.att << ' ' << m.segCount;
272  }
273 } __attribute__((aligned(8)));
274 
275 struct MemorySeg
276 : hasTag<6> {
277  void const * seg;
279  friend
280  std::ostream& operator << (std::ostream& os, MemorySeg const & m) {
281  return os << "MemorySeg " << m.seg << ' ' << m.len;
282  }
283 } __attribute__((packed));
284 
285 /**
286  * @class LastSystemMessage
287  * @brief hmbdc system messages use tag values less than this one
288  */
290 : hasTag<999> {
291 };
292 }} //hmbdc::app
Definition: Message.hpp:163
hasMemoryAttachment(char const *fileName)
file mapped memory
Definition: Message.hpp:206
each message type has 16 bit tag
Definition: Message.hpp:29
hasMemoryAttachment()
default ctor
Definition: Message.hpp:193
Definition: Message.hpp:131
Definition: Message.hpp:126
Definition: Message.hpp:42
A special type of message.
Definition: Message.hpp:143
Definition: Message.hpp:262
Definition: Time.hpp:14
hmbdc system messages use tag values less than this one
Definition: Message.hpp:289
Definition: Message.hpp:148
void unmap()
does the reverse of map
Definition: Message.hpp:239
Definition: Message.hpp:76
Definition: Message.hpp:275
if a specific hmbdc network transport (for example tcpcast, rmcast, and rnetmap) supports message wit...
Definition: Message.hpp:186
Definition: Base.hpp:12