1 /*
2 * Copyright (C) 2009-2019 Codership Oy <info@codership.com>
3 */
4
5 /*!
6 * @file protolay.hpp
7 *
8 * @brief Protocol layer interface definitions.
9 *
10 * Protocol layer interface allows construction of protocol stacks
11 * with consistent interface to send messages upwards or downwards in
12 * stack.
13 */
14
15 #ifndef GCOMM_PROTOLAY_HPP
16 #define GCOMM_PROTOLAY_HPP
17
18 #include "gcomm/view.hpp"
19 #include "gcomm/exception.hpp"
20 #include "gcomm/order.hpp"
21 #include "gcomm/datagram.hpp"
22
23 #include "gu_logger.hpp"
24 #include "gu_datetime.hpp"
25 #include "gu_config.hpp"
26 #include "gu_status.hpp"
27
28 #include <cerrno>
29
30 #include <list>
31 #include <utility>
32
33 #include <boost/function.hpp>
34
35 // Declarations
36 namespace gcomm
37 {
38 /*!
39 * @class ProtoUpMeta
40 *
41 * Container for metadata passed upwards in protocol stack.
42 */
43 class ProtoUpMeta;
44 std::ostream& operator<<(std::ostream&, const ProtoUpMeta&);
45
46 /*!
47 * @class ProtoDownMeta
48 *
49 * Container for metadata passed downwards in protocol stack.
50 */
51 class ProtoDownMeta;
52
53 /*!
54 * @class Protolay
55 *
56 * Protocol layer interface.
57 */
58 class Protolay;
59
60 /*!
61 * @class Toplay
62 *
63 * Protolay that is on the top of the protocol stack.
64 */
65 class Toplay;
66
67 /*!
68 * @class Bottomlay
69 *
70 * Protolay that is on the bottom of the protocol stack.
71 */
72 class Bottomlay;
73
74 void connect(Protolay*, Protolay*);
75 void disconnect(Protolay*, Protolay*);
76 }
77
78 /* message context to pass up with the data buffer? */
79 class gcomm::ProtoUpMeta
80 {
81 public:
ProtoUpMeta(const int err_no)82 ProtoUpMeta(const int err_no) :
83 source_(),
84 source_view_id_(),
85 user_type_(),
86 order_(),
87 to_seq_(),
88 err_no_(err_no),
89 view_(0)
90 { }
91
ProtoUpMeta(const UUID source=UUID::nil (),const ViewId source_view_id=ViewId (),const View * view=0,const uint8_t user_type=0xff,const Order order=O_DROP,const int64_t to_seq=-1,const int err_no=0)92 ProtoUpMeta(const UUID source = UUID::nil(),
93 const ViewId source_view_id = ViewId(),
94 const View* view = 0,
95 const uint8_t user_type = 0xff,
96 const Order order = O_DROP,
97 const int64_t to_seq = -1,
98 const int err_no = 0) :
99 source_ (source ),
100 source_view_id_ (source_view_id ),
101 user_type_ (user_type ),
102 order_ (order ),
103 to_seq_ (to_seq ),
104 err_no_ (err_no ),
105 view_ (view != 0 ? new View(*view) : 0)
106 { }
107
ProtoUpMeta(const ProtoUpMeta & um)108 ProtoUpMeta(const ProtoUpMeta& um) :
109 source_ (um.source_ ),
110 source_view_id_ (um.source_view_id_ ),
111 user_type_ (um.user_type_ ),
112 order_ (um.order_ ),
113 to_seq_ (um.to_seq_ ),
114 err_no_ (um.err_no_ ),
115 view_ (um.view_ ? new View(*um.view_) : 0)
116 { }
117
~ProtoUpMeta()118 ~ProtoUpMeta() { delete view_; }
119
source() const120 const UUID& source() const { return source_; }
121
source_view_id() const122 const ViewId& source_view_id() const { return source_view_id_; }
123
user_type() const124 uint8_t user_type() const { return user_type_; }
125
order() const126 Order order() const { return order_; }
127
to_seq() const128 int64_t to_seq() const { return to_seq_; }
129
err_no() const130 int err_no() const { return err_no_; }
131
has_view() const132 bool has_view() const { return view_ != 0; }
133
view() const134 const View& view() const { return *view_; }
135
136 private:
137 ProtoUpMeta& operator=(const ProtoUpMeta&);
138
139 UUID const source_;
140 ViewId const source_view_id_;
141 uint8_t const user_type_;
142 Order const order_;
143 int64_t const to_seq_;
144 int const err_no_;
145 View* const view_;
146 };
147
operator <<(std::ostream & os,const ProtoUpMeta & um)148 inline std::ostream& gcomm::operator<<(std::ostream& os, const ProtoUpMeta& um)
149 {
150 os << "proto_up_meta: { ";
151 if (not (um.source() == UUID::nil()))
152 {
153 os << "source=" << um.source() << ",";
154 }
155 if (um.source_view_id().type() != V_NONE)
156 {
157 os << "source_view_id=" << um.source_view_id() << ",";
158 }
159 os << "user_type=" << static_cast<int>(um.user_type()) << ",";
160 os << "to_seq=" << um.to_seq() << ",";
161 if (um.has_view() == true)
162 {
163 os << "view=" << um.view();
164 }
165 os << "}";
166 return os;
167 }
168
169 /* message context to pass down? */
170 class gcomm::ProtoDownMeta
171 {
172 public:
ProtoDownMeta(const uint8_t user_type=0xff,const Order order=O_SAFE,const UUID & source=UUID::nil (),const int segment=0)173 ProtoDownMeta(const uint8_t user_type = 0xff,
174 const Order order = O_SAFE,
175 const UUID& source = UUID::nil(),
176 const int segment = 0) :
177 user_type_ (user_type),
178 order_ (order),
179 source_ (source),
180 target_ (UUID::nil()),
181 segment_ (segment)
182 { }
183
ProtoDownMeta(const UUID & target)184 ProtoDownMeta(const UUID& target)
185 : user_type_(0xff)
186 , order_ (O_SAFE)
187 , source_(UUID::nil())
188 , target_(target)
189 , segment_(0)
190 { }
191
user_type() const192 uint8_t user_type() const { return user_type_; }
order() const193 Order order() const { return order_; }
source() const194 const UUID& source() const { return source_; }
target() const195 const UUID& target() const { return target_; }
segment() const196 int segment() const { return segment_; }
197 private:
198 const uint8_t user_type_;
199 const Order order_;
200 const UUID source_;
201 const UUID target_;
202 const int segment_;
203 };
204
205 class gcomm::Protolay
206 {
207 public:
208 typedef Map<UUID, gu::datetime::Date> EvictList;
209
210 typedef boost::function<void()> sync_param_cb_t;
211
~Protolay()212 virtual ~Protolay() {}
213
connect(bool)214 virtual void connect(bool) { }
close(bool force=false)215 virtual void close(bool force = false) { }
close(const UUID & uuid)216 virtual void close(const UUID& uuid) { }
217
218 /* apparently handles data from upper layer. what is return value? */
219 virtual int handle_down (Datagram&, const ProtoDownMeta&) = 0;
220 virtual void handle_up (const void*, const Datagram&, const ProtoUpMeta&) = 0;
221
set_up_context(Protolay * up)222 void set_up_context(Protolay *up)
223 {
224 if (std::find(up_context_.begin(),
225 up_context_.end(),
226 up) != up_context_.end())
227 {
228 gu_throw_fatal << "up context already exists";
229 }
230 up_context_.push_back(up);
231 }
232
set_down_context(Protolay * down)233 void set_down_context(Protolay *down)
234 {
235 if (std::find(down_context_.begin(),
236 down_context_.end(),
237 down) != down_context_.end())
238 {
239 gu_throw_fatal << "down context already exists";
240 }
241 down_context_.push_back(down);
242 }
243
unset_up_context(Protolay * up)244 void unset_up_context(Protolay* up)
245 {
246 CtxList::iterator i;
247 if ((i = std::find(up_context_.begin(),
248 up_context_.end(),
249 up)) == up_context_.end())
250 {
251 gu_throw_fatal << "up context does not exist";
252 }
253 up_context_.erase(i);
254 }
255
256
unset_down_context(Protolay * down)257 void unset_down_context(Protolay* down)
258 {
259 CtxList::iterator i;
260 if ((i = std::find(down_context_.begin(),
261 down_context_.end(),
262 down)) == down_context_.end())
263 {
264 gu_throw_fatal << "down context does not exist";
265 }
266 down_context_.erase(i);
267 }
268
269 /* apparently passed data buffer to the upper layer */
send_up(const Datagram & dg,const ProtoUpMeta & up_meta)270 void send_up(const Datagram& dg, const ProtoUpMeta& up_meta)
271 {
272 if (up_context_.empty() == true)
273 {
274 gu_throw_fatal << this << " up context(s) not set";
275 }
276
277 CtxList::iterator i, i_next;
278 for (i = up_context_.begin(); i != up_context_.end(); i = i_next)
279 {
280 i_next = i, ++i_next;
281 (*i)->handle_up(this, dg, up_meta);
282 }
283 }
284
285 /* apparently passes data buffer to lower layer, what is return value? */
send_down(Datagram & dg,const ProtoDownMeta & down_meta)286 int send_down(Datagram& dg, const ProtoDownMeta& down_meta)
287 {
288 if (down_context_.empty() == true)
289 {
290 log_warn << this << " down context(s) not set";
291 return ENOTCONN;
292 }
293
294 int ret = 0;
295 for (CtxList::iterator i = down_context_.begin();
296 i != down_context_.end(); ++i)
297 {
298 const size_t hdr_offset(dg.header_offset());
299 int err = (*i)->handle_down(dg, down_meta);
300 // Verify that lower layer rolls back any modifications to
301 // header
302 if (hdr_offset != dg.header_offset())
303 {
304 gu_throw_fatal;
305 }
306 if (err != 0)
307 {
308 ret = err;
309 }
310 }
311 return ret;
312 }
313
handle_stable_view(const View & view)314 virtual void handle_stable_view(const View& view) { }
315
set_stable_view(const View & view)316 void set_stable_view(const View& view)
317 {
318 for (CtxList::iterator i(down_context_.begin());
319 i != down_context_.end(); ++i)
320 {
321 (*i)->handle_stable_view(view);
322 }
323 }
324
325
handle_evict(const UUID & uuid)326 virtual void handle_evict(const UUID& uuid) { }
327
evict(const UUID & uuid)328 void evict(const UUID& uuid)
329 {
330 evict_list_.insert(
331 std::make_pair(uuid, gu::datetime::Date::monotonic()));
332 handle_evict(uuid);
333 for (CtxList::iterator i(down_context_.begin());
334 i != down_context_.end(); ++i)
335 {
336 (*i)->evict(uuid);
337 }
338 }
339
unevict(const UUID & uuid)340 void unevict(const UUID& uuid)
341 {
342 evict_list_.erase(uuid);
343 for (CtxList::iterator i(down_context_.begin());
344 i != down_context_.end(); ++i)
345 {
346 (*i)->unevict(uuid);
347 }
348 }
349
is_evicted(const UUID & uuid) const350 bool is_evicted(const UUID& uuid) const
351 {
352 if (down_context_.empty())
353 {
354 return (evict_list_.find(uuid) != evict_list_.end());
355 }
356 else
357 {
358 return (*down_context_.begin())->is_evicted(uuid);
359 }
360 }
361
evict_list() const362 const EvictList& evict_list() const { return evict_list_; }
363
handle_get_status(gu::Status & status) const364 virtual void handle_get_status(gu::Status& status) const
365 { }
366
get_status(gu::Status & status) const367 void get_status(gu::Status& status) const
368 {
369 for (CtxList::const_iterator i(down_context_.begin());
370 i != down_context_.end(); ++i)
371 {
372 (*i)->get_status(status);
373 }
374 handle_get_status(status);
375 }
376
377
get_address(const UUID & uuid) const378 std::string get_address(const UUID& uuid) const
379 {
380 if (down_context_.empty()) return handle_get_address(uuid);
381 else return (*down_context_.begin())->get_address(uuid);
382 }
383
handle_get_address(const UUID & uuid) const384 virtual std::string handle_get_address(const UUID& uuid) const
385 {
386 return "(unknown)";
387 }
388
389
handle_timers()390 virtual gu::datetime::Date handle_timers()
391 {
392 return gu::datetime::Date::max();
393 }
394
set_param(const std::string & key,const std::string & val,sync_param_cb_t & sync_param_cb)395 virtual bool set_param(const std::string& key, const std::string& val,
396 sync_param_cb_t& sync_param_cb)
397 {
398 return false;
399 }
400
id() const401 const Protolay* id() const { return this; }
402
403 protected:
Protolay(gu::Config & conf)404 Protolay(gu::Config& conf)
405 :
406 conf_(conf),
407 up_context_(0),
408 down_context_(0),
409 evict_list_()
410 { }
411
412 gu::Config& conf_;
413
414 private:
415 typedef std::list<Protolay*> CtxList;
416 CtxList up_context_;
417 CtxList down_context_;
418
419 EvictList evict_list_;
420
421
422 Protolay (const Protolay&);
423 Protolay& operator=(const Protolay&);
424 };
425
426
427 class gcomm::Toplay : protected Conf::Check, public Protolay
428 {
429 public:
Toplay(gu::Config & conf)430 Toplay(gu::Config& conf) : Conf::Check(conf), Protolay(conf) { }
431 private:
handle_down(Datagram & dg,const ProtoDownMeta & dm)432 int handle_down(Datagram& dg, const ProtoDownMeta& dm)
433 {
434 gu_throw_fatal << "Toplay handle_down() called";
435 }
436 };
437
438 class gcomm::Bottomlay : public Protolay
439 {
440 public:
Bottomlay(gu::Config & conf)441 Bottomlay(gu::Config& conf) : Protolay(conf) { }
442 private:
handle_up(const void * id,const Datagram &,const ProtoUpMeta & um)443 void handle_up(const void* id, const Datagram&, const ProtoUpMeta& um)
444 {
445 gu_throw_fatal << "Bottomlay handle_up() called";
446 }
447 };
448
connect(Protolay * down,Protolay * up)449 inline void gcomm::connect(Protolay* down, Protolay* up)
450 {
451 down->set_up_context(up);
452 up->set_down_context(down);
453 }
454
disconnect(Protolay * down,Protolay * up)455 inline void gcomm::disconnect(Protolay* down, Protolay* up)
456 {
457 down->unset_up_context(up);
458 up->unset_down_context(down);
459 }
460
461
462 #endif /* GCOMM_PROTOLAY_HPP */
463