• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright 2003-2020 Joaquin M Lopez Munoz.
2  * Distributed under the Boost Software License, Version 1.0.
3  * (See accompanying file LICENSE_1_0.txt or copy at
4  * http://www.boost.org/LICENSE_1_0.txt)
5  *
6  * See http://www.boost.org/libs/multi_index for library home page.
7  */
8 
9 #ifndef BOOST_MULTI_INDEX_DETAIL_SAFE_MODE_HPP
10 #define BOOST_MULTI_INDEX_DETAIL_SAFE_MODE_HPP
11 
12 #if defined(_MSC_VER)
13 #pragma once
14 #endif
15 
16 /* Safe mode machinery, in the spirit of Cay Hortmann's "Safe STL"
17  * (http://www.horstmann.com/safestl.html).
18  * In this mode, containers of type Container are derived from
19  * safe_container<Container>, and their corresponding iterators
20  * are wrapped with safe_iterator. These classes provide
21  * an internal record of which iterators are at a given moment associated
22  * to a given container, and properly mark the iterators as invalid
23  * when the container gets destroyed.
24  * Iterators are chained in a single attached list, whose header is
25  * kept by the container. More elaborate data structures would yield better
26  * performance, but I decided to keep complexity to a minimum since
27  * speed is not an issue here.
28  * Safe mode iterators automatically check that only proper operations
29  * are performed on them: for instance, an invalid iterator cannot be
30  * dereferenced. Additionally, a set of utilty macros and functions are
31  * provided that serve to implement preconditions and cooperate with
32  * the framework within the container.
33  * Iterators can also be unchecked, i.e. they do not have info about
34  * which container they belong in. This situation arises when the iterator
35  * is restored from a serialization archive: only information on the node
36  * is available, and it is not possible to determine to which container
37  * the iterator is associated to. The only sensible policy is to assume
38  * unchecked iterators are valid, though this can certainly generate false
39  * positive safe mode checks.
40  * This is not a full-fledged safe mode framework, and is only intended
41  * for use within the limits of Boost.MultiIndex.
42  */
43 
44 /* Assertion macros. These resolve to no-ops if
45  * !defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE).
46  */
47 
48 #if !defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
49 #undef BOOST_MULTI_INDEX_SAFE_MODE_ASSERT
50 #define BOOST_MULTI_INDEX_SAFE_MODE_ASSERT(expr,error_code) ((void)0)
51 #else
52 #if !defined(BOOST_MULTI_INDEX_SAFE_MODE_ASSERT)
53 #include <boost/assert.hpp>
54 #define BOOST_MULTI_INDEX_SAFE_MODE_ASSERT(expr,error_code) BOOST_ASSERT(expr)
55 #endif
56 #endif
57 
58 #define BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(it)                           \
59   BOOST_MULTI_INDEX_SAFE_MODE_ASSERT(                                        \
60     safe_mode::check_valid_iterator(it),                                     \
61     safe_mode::invalid_iterator);
62 
63 #define BOOST_MULTI_INDEX_CHECK_DEREFERENCEABLE_ITERATOR(it)                 \
64   BOOST_MULTI_INDEX_SAFE_MODE_ASSERT(                                        \
65     safe_mode::check_dereferenceable_iterator(it),                           \
66     safe_mode::not_dereferenceable_iterator);
67 
68 #define BOOST_MULTI_INDEX_CHECK_INCREMENTABLE_ITERATOR(it)                   \
69   BOOST_MULTI_INDEX_SAFE_MODE_ASSERT(                                        \
70     safe_mode::check_incrementable_iterator(it),                             \
71     safe_mode::not_incrementable_iterator);
72 
73 #define BOOST_MULTI_INDEX_CHECK_DECREMENTABLE_ITERATOR(it)                   \
74   BOOST_MULTI_INDEX_SAFE_MODE_ASSERT(                                        \
75     safe_mode::check_decrementable_iterator(it),                             \
76     safe_mode::not_decrementable_iterator);
77 
78 #define BOOST_MULTI_INDEX_CHECK_IS_OWNER(it,cont)                            \
79   BOOST_MULTI_INDEX_SAFE_MODE_ASSERT(                                        \
80     safe_mode::check_is_owner(it,cont),                                      \
81     safe_mode::not_owner);
82 
83 #define BOOST_MULTI_INDEX_CHECK_SAME_OWNER(it0,it1)                          \
84   BOOST_MULTI_INDEX_SAFE_MODE_ASSERT(                                        \
85     safe_mode::check_same_owner(it0,it1),                                    \
86     safe_mode::not_same_owner);
87 
88 #define BOOST_MULTI_INDEX_CHECK_VALID_RANGE(it0,it1)                         \
89   BOOST_MULTI_INDEX_SAFE_MODE_ASSERT(                                        \
90     safe_mode::check_valid_range(it0,it1),                                   \
91     safe_mode::invalid_range);
92 
93 #define BOOST_MULTI_INDEX_CHECK_OUTSIDE_RANGE(it,it0,it1)                    \
94   BOOST_MULTI_INDEX_SAFE_MODE_ASSERT(                                        \
95     safe_mode::check_outside_range(it,it0,it1),                              \
96     safe_mode::inside_range);
97 
98 #define BOOST_MULTI_INDEX_CHECK_IN_BOUNDS(it,n)                              \
99   BOOST_MULTI_INDEX_SAFE_MODE_ASSERT(                                        \
100     safe_mode::check_in_bounds(it,n),                                        \
101     safe_mode::out_of_bounds);
102 
103 #define BOOST_MULTI_INDEX_CHECK_DIFFERENT_CONTAINER(cont0,cont1)             \
104   BOOST_MULTI_INDEX_SAFE_MODE_ASSERT(                                        \
105     safe_mode::check_different_container(cont0,cont1),                       \
106     safe_mode::same_container);
107 
108 #define BOOST_MULTI_INDEX_CHECK_EQUAL_ALLOCATORS(cont0,cont1)                 \
109   BOOST_MULTI_INDEX_SAFE_MODE_ASSERT(                                        \
110     safe_mode::check_equal_allocators(cont0,cont1),                           \
111     safe_mode::unequal_allocators);
112 
113 #if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
114 #include <boost/config.hpp> /* keep it first to prevent nasty warns in MSVC */
115 #include <algorithm>
116 #include <boost/multi_index/detail/access_specifier.hpp>
117 #include <boost/multi_index/detail/iter_adaptor.hpp>
118 #include <boost/multi_index/safe_mode_errors.hpp>
119 #include <boost/noncopyable.hpp>
120 
121 #if !defined(BOOST_MULTI_INDEX_DISABLE_SERIALIZATION)
122 #include <boost/serialization/split_member.hpp>
123 #include <boost/serialization/version.hpp>
124 #endif
125 
126 #if defined(BOOST_HAS_THREADS)
127 #include <boost/detail/lightweight_mutex.hpp>
128 #endif
129 
130 namespace boost{
131 
132 namespace multi_index{
133 
134 namespace safe_mode{
135 
136 /* Checking routines. Assume the best for unchecked iterators
137  * (i.e. they pass the checking when there is not enough info
138  * to know.)
139  */
140 
141 template<typename Iterator>
check_valid_iterator(const Iterator & it)142 inline bool check_valid_iterator(const Iterator& it)
143 {
144   return it.valid()||it.unchecked();
145 }
146 
147 template<typename Iterator>
check_dereferenceable_iterator(const Iterator & it)148 inline bool check_dereferenceable_iterator(const Iterator& it)
149 {
150   return (it.valid()&&it!=it.owner()->end())||it.unchecked();
151 }
152 
153 template<typename Iterator>
check_incrementable_iterator(const Iterator & it)154 inline bool check_incrementable_iterator(const Iterator& it)
155 {
156   return (it.valid()&&it!=it.owner()->end())||it.unchecked();
157 }
158 
159 template<typename Iterator>
check_decrementable_iterator(const Iterator & it)160 inline bool check_decrementable_iterator(const Iterator& it)
161 {
162   return (it.valid()&&it!=it.owner()->begin())||it.unchecked();
163 }
164 
165 template<typename Iterator>
check_is_owner(const Iterator & it,const typename Iterator::container_type & cont)166 inline bool check_is_owner(
167   const Iterator& it,const typename Iterator::container_type& cont)
168 {
169   return (it.valid()&&it.owner()==&cont)||it.unchecked();
170 }
171 
172 template<typename Iterator>
check_same_owner(const Iterator & it0,const Iterator & it1)173 inline bool check_same_owner(const Iterator& it0,const Iterator& it1)
174 {
175   return (it0.valid()&&it1.valid()&&it0.owner()==it1.owner())||
176          it0.unchecked()||it1.unchecked();
177 }
178 
179 template<typename Iterator>
check_valid_range(const Iterator & it0,const Iterator & it1)180 inline bool check_valid_range(const Iterator& it0,const Iterator& it1)
181 {
182   if(!check_same_owner(it0,it1))return false;
183 
184   if(it0.valid()){
185     Iterator last=it0.owner()->end();
186     if(it1==last)return true;
187 
188     for(Iterator first=it0;first!=last;++first){
189       if(first==it1)return true;
190     }
191     return false;
192   }
193   return true;
194 }
195 
196 template<typename Iterator>
check_outside_range(const Iterator & it,const Iterator & it0,const Iterator & it1)197 inline bool check_outside_range(
198   const Iterator& it,const Iterator& it0,const Iterator& it1)
199 {
200   if(!check_same_owner(it0,it1))return false;
201 
202   if(it0.valid()){
203     Iterator last=it0.owner()->end();
204     bool found=false;
205 
206     Iterator first=it0;
207     for(;first!=last;++first){
208       if(first==it1)break;
209 
210       /* crucial that this check goes after previous break */
211 
212       if(first==it)found=true;
213     }
214     if(first!=it1)return false;
215     return !found;
216   }
217   return true;
218 }
219 
220 template<typename Iterator,typename Difference>
check_in_bounds(const Iterator & it,Difference n)221 inline bool check_in_bounds(const Iterator& it,Difference n)
222 {
223   if(it.unchecked())return true;
224   if(!it.valid())   return false;
225   if(n>0)           return it.owner()->end()-it>=n;
226   else              return it.owner()->begin()-it<=n;
227 }
228 
229 template<typename Container>
check_different_container(const Container & cont0,const Container & cont1)230 inline bool check_different_container(
231   const Container& cont0,const Container& cont1)
232 {
233   return &cont0!=&cont1;
234 }
235 
236 template<typename Container0,typename Container1>
check_equal_allocators(const Container0 & cont0,const Container1 & cont1)237 inline bool check_equal_allocators(
238   const Container0& cont0,const Container1& cont1)
239 {
240   return cont0.get_allocator()==cont1.get_allocator();
241 }
242 
243 /* Invalidates all iterators equivalent to that given. Safe containers
244  * must call this when deleting elements: the safe mode framework cannot
245  * perform this operation automatically without outside help.
246  */
247 
248 template<typename Iterator>
detach_equivalent_iterators(Iterator & it)249 inline void detach_equivalent_iterators(Iterator& it)
250 {
251   if(it.valid()){
252     {
253 #if defined(BOOST_HAS_THREADS)
254       boost::detail::lightweight_mutex::scoped_lock lock(it.cont->mutex);
255 #endif
256 
257       Iterator *prev_,*next_;
258       for(
259         prev_=static_cast<Iterator*>(&it.cont->header);
260         (next_=static_cast<Iterator*>(prev_->next))!=0;){
261         if(next_!=&it&&*next_==it){
262           prev_->next=next_->next;
263           next_->cont=0;
264         }
265         else prev_=next_;
266       }
267     }
268     it.detach();
269   }
270 }
271 
272 template<typename Container> class safe_container; /* fwd decl. */
273 
274 } /* namespace multi_index::safe_mode */
275 
276 namespace detail{
277 
278 class safe_container_base;                 /* fwd decl. */
279 
280 class safe_iterator_base
281 {
282 public:
valid() const283   bool valid()const{return cont!=0;}
unchecked() const284   bool unchecked()const{return unchecked_;}
285 
286   inline void detach();
287 
uncheck()288   void uncheck()
289   {
290     detach();
291     unchecked_=true;
292   }
293 
294 protected:
safe_iterator_base()295   safe_iterator_base():cont(0),next(0),unchecked_(false){}
296 
safe_iterator_base(safe_container_base * cont_)297   explicit safe_iterator_base(safe_container_base* cont_):
298     unchecked_(false)
299   {
300     attach(cont_);
301   }
302 
safe_iterator_base(const safe_iterator_base & it)303   safe_iterator_base(const safe_iterator_base& it):
304     unchecked_(it.unchecked_)
305   {
306     attach(it.cont);
307   }
308 
operator =(const safe_iterator_base & it)309   safe_iterator_base& operator=(const safe_iterator_base& it)
310   {
311     unchecked_=it.unchecked_;
312     safe_container_base* new_cont=it.cont;
313     if(cont!=new_cont){
314       detach();
315       attach(new_cont);
316     }
317     return *this;
318   }
319 
~safe_iterator_base()320   ~safe_iterator_base()
321   {
322     detach();
323   }
324 
owner() const325   const safe_container_base* owner()const{return cont;}
326 
327 BOOST_MULTI_INDEX_PRIVATE_IF_MEMBER_TEMPLATE_FRIENDS:
328   friend class safe_container_base;
329 
330 #if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS)
331   template<typename>          friend class safe_mode::safe_container;
332   template<typename Iterator> friend
333     void safe_mode::detach_equivalent_iterators(Iterator&);
334 #endif
335 
336   inline void attach(safe_container_base* cont_);
337 
338   safe_container_base* cont;
339   safe_iterator_base*  next;
340   bool                 unchecked_;
341 };
342 
343 class safe_container_base:private noncopyable
344 {
345 public:
safe_container_base()346   safe_container_base(){}
347 
348 BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
349   friend class safe_iterator_base;
350 
351 #if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS)
352   template<typename Iterator> friend
353     void safe_mode::detach_equivalent_iterators(Iterator&);
354 #endif
355 
~safe_container_base()356   ~safe_container_base()
357   {
358     /* Detaches all remaining iterators, which by now will
359      * be those pointing to the end of the container.
360      */
361 
362     for(safe_iterator_base* it=header.next;it;it=it->next)it->cont=0;
363     header.next=0;
364   }
365 
swap(safe_container_base & x)366   void swap(safe_container_base& x)
367   {
368     for(safe_iterator_base* it0=header.next;it0;it0=it0->next)it0->cont=&x;
369     for(safe_iterator_base* it1=x.header.next;it1;it1=it1->next)it1->cont=this;
370     std::swap(header.cont,x.header.cont);
371     std::swap(header.next,x.header.next);
372   }
373 
374   safe_iterator_base header;
375 
376 #if defined(BOOST_HAS_THREADS)
377   boost::detail::lightweight_mutex mutex;
378 #endif
379 };
380 
attach(safe_container_base * cont_)381 void safe_iterator_base::attach(safe_container_base* cont_)
382 {
383   cont=cont_;
384   if(cont){
385 #if defined(BOOST_HAS_THREADS)
386     boost::detail::lightweight_mutex::scoped_lock lock(cont->mutex);
387 #endif
388 
389     next=cont->header.next;
390     cont->header.next=this;
391   }
392 }
393 
detach()394 void safe_iterator_base::detach()
395 {
396   if(cont){
397 #if defined(BOOST_HAS_THREADS)
398     boost::detail::lightweight_mutex::scoped_lock lock(cont->mutex);
399 #endif
400 
401     safe_iterator_base *prev_,*next_;
402     for(prev_=&cont->header;(next_=prev_->next)!=this;prev_=next_){}
403     prev_->next=next;
404     cont=0;
405   }
406 }
407 
408 } /* namespace multi_index::detail */
409 
410 namespace safe_mode{
411 
412 /* In order to enable safe mode on a container:
413  *   - The container must derive from safe_container<container_type>,
414  *   - iterators must be generated via safe_iterator, which adapts a
415  *     preexistent unsafe iterator class.
416  */
417 
418 template<typename Container>
419 class safe_container;
420 
421 template<typename Iterator,typename Container>
422 class safe_iterator:
423   public detail::iter_adaptor<safe_iterator<Iterator,Container>,Iterator>,
424   public detail::safe_iterator_base
425 {
426   typedef detail::iter_adaptor<safe_iterator,Iterator> super;
427   typedef detail::safe_iterator_base                   safe_super;
428 
429 public:
430   typedef Container                                    container_type;
431   typedef typename Iterator::reference                 reference;
432   typedef typename Iterator::difference_type           difference_type;
433 
safe_iterator()434   safe_iterator(){}
safe_iterator(safe_container<container_type> * cont_)435   explicit safe_iterator(safe_container<container_type>* cont_):
436     safe_super(cont_){}
437   template<typename T0>
safe_iterator(const T0 & t0,safe_container<container_type> * cont_)438   safe_iterator(const T0& t0,safe_container<container_type>* cont_):
439     super(Iterator(t0)),safe_super(cont_){}
440   template<typename T0,typename T1>
safe_iterator(const T0 & t0,const T1 & t1,safe_container<container_type> * cont_)441   safe_iterator(
442     const T0& t0,const T1& t1,safe_container<container_type>* cont_):
443     super(Iterator(t0,t1)),safe_super(cont_){}
safe_iterator(const safe_iterator & x)444   safe_iterator(const safe_iterator& x):super(x),safe_super(x){}
445 
operator =(const safe_iterator & x)446   safe_iterator& operator=(const safe_iterator& x)
447   {
448     BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(x);
449     this->base_reference()=x.base_reference();
450     safe_super::operator=(x);
451     return *this;
452   }
453 
owner() const454   const container_type* owner()const
455   {
456     return
457       static_cast<const container_type*>(
458         static_cast<const safe_container<container_type>*>(
459           this->safe_super::owner()));
460   }
461 
462   /* get_node is not to be used by the user */
463 
464   typedef typename Iterator::node_type node_type;
465 
get_node() const466   node_type* get_node()const{return this->base_reference().get_node();}
467 
468 private:
469   friend class boost::multi_index::detail::iter_adaptor_access;
470 
dereference() const471   reference dereference()const
472   {
473     BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(*this);
474     BOOST_MULTI_INDEX_CHECK_DEREFERENCEABLE_ITERATOR(*this);
475     return *(this->base_reference());
476   }
477 
equal(const safe_iterator & x) const478   bool equal(const safe_iterator& x)const
479   {
480     BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(*this);
481     BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(x);
482     BOOST_MULTI_INDEX_CHECK_SAME_OWNER(*this,x);
483     return this->base_reference()==x.base_reference();
484   }
485 
increment()486   void increment()
487   {
488     BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(*this);
489     BOOST_MULTI_INDEX_CHECK_INCREMENTABLE_ITERATOR(*this);
490     ++(this->base_reference());
491   }
492 
decrement()493   void decrement()
494   {
495     BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(*this);
496     BOOST_MULTI_INDEX_CHECK_DECREMENTABLE_ITERATOR(*this);
497     --(this->base_reference());
498   }
499 
advance(difference_type n)500   void advance(difference_type n)
501   {
502     BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(*this);
503     BOOST_MULTI_INDEX_CHECK_IN_BOUNDS(*this,n);
504     this->base_reference()+=n;
505   }
506 
distance_to(const safe_iterator & x) const507   difference_type distance_to(const safe_iterator& x)const
508   {
509     BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(*this);
510     BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(x);
511     BOOST_MULTI_INDEX_CHECK_SAME_OWNER(*this,x);
512     return x.base_reference()-this->base_reference();
513   }
514 
515 #if !defined(BOOST_MULTI_INDEX_DISABLE_SERIALIZATION)
516   /* Serialization. Note that Iterator::save and Iterator:load
517    * are assumed to be defined and public: at first sight it seems
518    * like we could have resorted to the public serialization interface
519    * for doing the forwarding to the adapted iterator class:
520    *   ar<<base_reference();
521    *   ar>>base_reference();
522    * but this would cause incompatibilities if a saving
523    * program is in safe mode and the loading program is not, or
524    * viceversa --in safe mode, the archived iterator data is one layer
525    * deeper, this is especially relevant with XML archives.
526    * It'd be nice if Boost.Serialization provided some forwarding
527    * facility for use by adaptor classes.
528    */
529 
530   friend class boost::serialization::access;
531 
BOOST_SERIALIZATION_SPLIT_MEMBER()532   BOOST_SERIALIZATION_SPLIT_MEMBER()
533 
534   template<class Archive>
535   void save(Archive& ar,const unsigned int version)const
536   {
537     BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(*this);
538     this->base_reference().save(ar,version);
539   }
540 
541   template<class Archive>
load(Archive & ar,const unsigned int version)542   void load(Archive& ar,const unsigned int version)
543   {
544     this->base_reference().load(ar,version);
545     safe_super::uncheck();
546   }
547 #endif
548 };
549 
550 template<typename Container>
551 class safe_container:public detail::safe_container_base
552 {
553   typedef detail::safe_container_base super;
554 
555 public:
detach_dereferenceable_iterators()556   void detach_dereferenceable_iterators()
557   {
558     typedef typename Container::iterator iterator;
559 
560     iterator end_=static_cast<Container*>(this)->end();
561     iterator *prev_,*next_;
562     for(
563       prev_=static_cast<iterator*>(&this->header);
564       (next_=static_cast<iterator*>(prev_->next))!=0;){
565       if(*next_!=end_){
566         prev_->next=next_->next;
567         next_->cont=0;
568       }
569       else prev_=next_;
570     }
571   }
572 
swap(safe_container<Container> & x)573   void swap(safe_container<Container>& x)
574   {
575     super::swap(x);
576   }
577 };
578 
579 } /* namespace multi_index::safe_mode */
580 
581 } /* namespace multi_index */
582 
583 #if !defined(BOOST_MULTI_INDEX_DISABLE_SERIALIZATION)
584 namespace serialization{
585 template<typename Iterator,typename Container>
586 struct version<
587   boost::multi_index::safe_mode::safe_iterator<Iterator,Container>
588 >
589 {
590   BOOST_STATIC_CONSTANT(
591     int,value=boost::serialization::version<Iterator>::value);
592 };
593 } /* namespace serialization */
594 #endif
595 
596 } /* namespace boost */
597 
598 #endif /* BOOST_MULTI_INDEX_ENABLE_SAFE_MODE */
599 
600 #endif
601