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