Home | History | Annotate | Download | only in ext
      1 // -*- C++ -*-
      2 
      3 // Copyright (C) 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
      4 //
      5 // This file is part of the GNU ISO C++ Library.  This library is free
      6 // software; you can redistribute it and/or modify it under the terms
      7 // of the GNU General Public License as published by the Free Software
      8 // Foundation; either version 3, or (at your option) any later
      9 // version.
     10 
     11 // This library is distributed in the hope that it will be useful, but
     12 // WITHOUT ANY WARRANTY; without even the implied warranty of
     13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14 // General Public License for more details.
     15 
     16 // Under Section 7 of GPL version 3, you are granted additional
     17 // permissions described in the GCC Runtime Library Exception, version
     18 // 3.1, as published by the Free Software Foundation.
     19 
     20 // You should have received a copy of the GNU General Public License and
     21 // a copy of the GCC Runtime Library Exception along with this program;
     22 // see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
     23 // <http://www.gnu.org/licenses/>.
     24 
     25 // Copyright (C) 2004 Ami Tavory and Vladimir Dreizin, IBM-HRL.
     26 
     27 // Permission to use, copy, modify, sell, and distribute this software
     28 // is hereby granted without fee, provided that the above copyright
     29 // notice appears in all copies, and that both that copyright notice
     30 // and this permission notice appear in supporting documentation. None
     31 // of the above authors, nor IBM Haifa Research Laboratories, make any
     32 // representation about the suitability of this software for any
     33 // purpose. It is provided "as is" without express or implied
     34 // warranty.
     35 
     36 /** @file ext/throw_allocator.h
     37  *  This file is a GNU extension to the Standard C++ Library.
     38  *
     39  *  Contains an exception-throwing allocator, useful for testing
     40  *  exception safety. In addition, allocation addresses are stored and
     41  *  sanity checked.
     42  */
     43 
     44 #ifndef _THROW_ALLOCATOR_H
     45 #define _THROW_ALLOCATOR_H 1
     46 
     47 #include <cmath>
     48 #include <ctime>
     49 #include <map>
     50 #include <set>
     51 #include <string>
     52 #include <ostream>
     53 #include <stdexcept>
     54 #include <utility>
     55 #include <tr1/random>
     56 #include <bits/functexcept.h>
     57 #include <bits/move.h>
     58 
     59 _GLIBCXX_BEGIN_NAMESPACE(__gnu_cxx)
     60 
     61   class twister_rand_gen
     62   {
     63   private:
     64     std::tr1::mt19937 _M_generator;
     65 
     66   public:
     67     twister_rand_gen(unsigned int s = static_cast<unsigned int>(std::time(0)));
     68 
     69     void
     70     init(unsigned int);
     71 
     72     double
     73     get_prob();
     74   };
     75 
     76   /**
     77    *  @brief Thown by throw_allocator.
     78    *  @ingroup exceptions
     79    */
     80   struct forced_exception_error : public std::exception
     81   { };
     82 
     83   // Substitute for concurrence_error object in the case of -fno-exceptions.
     84   inline void
     85   __throw_forced_exception_error()
     86   {
     87 #if __EXCEPTIONS
     88     throw forced_exception_error();
     89 #else
     90     __builtin_abort();
     91 #endif
     92   }
     93 
     94   /// Base class.
     95   class throw_allocator_base
     96   {
     97   public:
     98     void
     99     init(unsigned long seed);
    100 
    101     static void
    102     set_throw_prob(double throw_prob);
    103 
    104     static double
    105     get_throw_prob();
    106 
    107     static void
    108     set_label(size_t l);
    109 
    110     static bool
    111     empty();
    112 
    113     struct group_throw_prob_adjustor
    114     {
    115       group_throw_prob_adjustor(size_t size) : _M_throw_prob_orig(_S_throw_prob)
    116       {
    117 	_S_throw_prob =
    118 	  1 - std::pow(double(1 - _S_throw_prob), double(0.5 / (size + 1)));
    119       }
    120 
    121       ~group_throw_prob_adjustor()
    122       { _S_throw_prob = _M_throw_prob_orig; }
    123 
    124     private:
    125       const double _M_throw_prob_orig;
    126     };
    127 
    128     struct zero_throw_prob_adjustor
    129     {
    130       zero_throw_prob_adjustor() : _M_throw_prob_orig(_S_throw_prob)
    131       { _S_throw_prob = 0; }
    132 
    133       ~zero_throw_prob_adjustor()
    134       { _S_throw_prob = _M_throw_prob_orig; }
    135 
    136     private:
    137       const double _M_throw_prob_orig;
    138     };
    139 
    140   protected:
    141     static void
    142     insert(void*, size_t);
    143 
    144     static void
    145     erase(void*, size_t);
    146 
    147     static void
    148     throw_conditionally();
    149 
    150     // See if a particular address and size has been allocated by this
    151     // allocator.
    152     static void
    153     check_allocated(void*, size_t);
    154 
    155     // See if a given label has been allocated by this allocator.
    156     static void
    157     check_allocated(size_t);
    158 
    159   private:
    160     typedef std::pair<size_t, size_t> 		alloc_data_type;
    161     typedef std::map<void*, alloc_data_type> 	map_type;
    162     typedef map_type::value_type 		entry_type;
    163     typedef map_type::const_iterator 		const_iterator;
    164     typedef map_type::const_reference 		const_reference;
    165 
    166     friend std::ostream&
    167     operator<<(std::ostream&, const throw_allocator_base&);
    168 
    169     static entry_type
    170     make_entry(void*, size_t);
    171 
    172     static void
    173     print_to_string(std::string&);
    174 
    175     static void
    176     print_to_string(std::string&, const_reference);
    177 
    178     static twister_rand_gen 	_S_g;
    179     static map_type 		_S_map;
    180     static double 		_S_throw_prob;
    181     static size_t 		_S_label;
    182   };
    183 
    184   /**
    185    *  @brief Allocator class with logging and exception control.
    186    *  @ingroup allocators
    187    */
    188   template<typename T>
    189     class throw_allocator : public throw_allocator_base
    190     {
    191     public:
    192       typedef size_t 				size_type;
    193       typedef ptrdiff_t 			difference_type;
    194       typedef T 				value_type;
    195       typedef value_type* 			pointer;
    196       typedef const value_type* 		const_pointer;
    197       typedef value_type& 			reference;
    198       typedef const value_type& 		const_reference;
    199 
    200 
    201       template<typename U>
    202       struct rebind
    203       {
    204         typedef throw_allocator<U> other;
    205       };
    206 
    207       throw_allocator() throw() { }
    208 
    209       throw_allocator(const throw_allocator&) throw() { }
    210 
    211       template<typename U>
    212       throw_allocator(const throw_allocator<U>&) throw() { }
    213 
    214       ~throw_allocator() throw() { }
    215 
    216       size_type
    217       max_size() const throw()
    218       { return std::allocator<value_type>().max_size(); }
    219 
    220       pointer
    221       allocate(size_type __n, std::allocator<void>::const_pointer hint = 0)
    222       {
    223 	if (__builtin_expect(__n > this->max_size(), false))
    224 	  std::__throw_bad_alloc();
    225 
    226 	throw_conditionally();
    227 	value_type* const a = std::allocator<value_type>().allocate(__n, hint);
    228 	insert(a, sizeof(value_type) * __n);
    229 	return a;
    230       }
    231 
    232       void
    233       construct(pointer __p, const T& val)
    234       { return std::allocator<value_type>().construct(__p, val); }
    235 
    236 #ifdef __GXX_EXPERIMENTAL_CXX0X__
    237       template<typename... _Args>
    238         void
    239         construct(pointer __p, _Args&&... __args)
    240 	{
    241 	  return std::allocator<value_type>().
    242 	    construct(__p, std::forward<_Args>(__args)...);
    243 	}
    244 #endif
    245 
    246       void
    247       destroy(pointer __p)
    248       { std::allocator<value_type>().destroy(__p); }
    249 
    250       void
    251       deallocate(pointer __p, size_type __n)
    252       {
    253 	erase(__p, sizeof(value_type) * __n);
    254 	std::allocator<value_type>().deallocate(__p, __n);
    255       }
    256 
    257       void
    258       check_allocated(pointer __p, size_type __n)
    259       { throw_allocator_base::check_allocated(__p, sizeof(value_type) * __n); }
    260 
    261       void
    262       check_allocated(size_type label)
    263       { throw_allocator_base::check_allocated(label); }
    264     };
    265 
    266   template<typename T>
    267     inline bool
    268     operator==(const throw_allocator<T>&, const throw_allocator<T>&)
    269     { return true; }
    270 
    271   template<typename T>
    272     inline bool
    273     operator!=(const throw_allocator<T>&, const throw_allocator<T>&)
    274     { return false; }
    275 
    276   std::ostream&
    277   operator<<(std::ostream& os, const throw_allocator_base& alloc)
    278   {
    279     std::string error;
    280     throw_allocator_base::print_to_string(error);
    281     os << error;
    282     return os;
    283   }
    284 
    285   // XXX Should be in .cc.
    286   twister_rand_gen::
    287   twister_rand_gen(unsigned int seed) : _M_generator(seed)  { }
    288 
    289   void
    290   twister_rand_gen::
    291   init(unsigned int seed)
    292   { _M_generator.seed(seed); }
    293 
    294   double
    295   twister_rand_gen::
    296   get_prob()
    297   {
    298     const double min = _M_generator.min();
    299     const double res = static_cast<const double>(_M_generator() - min);
    300     const double range = static_cast<const double>(_M_generator.max() - min);
    301     const double ret = res / range;
    302     _GLIBCXX_DEBUG_ASSERT(ret >= 0 && ret <= 1);
    303     return ret;
    304   }
    305 
    306   twister_rand_gen throw_allocator_base::_S_g;
    307 
    308   throw_allocator_base::map_type
    309   throw_allocator_base::_S_map;
    310 
    311   double throw_allocator_base::_S_throw_prob;
    312 
    313   size_t throw_allocator_base::_S_label = 0;
    314 
    315   throw_allocator_base::entry_type
    316   throw_allocator_base::make_entry(void* p, size_t size)
    317   { return std::make_pair(p, alloc_data_type(_S_label, size)); }
    318 
    319   void
    320   throw_allocator_base::init(unsigned long seed)
    321   { _S_g.init(seed); }
    322 
    323   void
    324   throw_allocator_base::set_throw_prob(double throw_prob)
    325   { _S_throw_prob = throw_prob; }
    326 
    327   double
    328   throw_allocator_base::get_throw_prob()
    329   { return _S_throw_prob; }
    330 
    331   void
    332   throw_allocator_base::set_label(size_t l)
    333   { _S_label = l; }
    334 
    335   void
    336   throw_allocator_base::insert(void* p, size_t size)
    337   {
    338     const_iterator found_it = _S_map.find(p);
    339     if (found_it != _S_map.end())
    340       {
    341 	std::string error("throw_allocator_base::insert");
    342 	error += "double insert!";
    343 	error += '\n';
    344 	print_to_string(error, make_entry(p, size));
    345 	print_to_string(error, *found_it);
    346 	std::__throw_logic_error(error.c_str());
    347       }
    348     _S_map.insert(make_entry(p, size));
    349   }
    350 
    351   bool
    352   throw_allocator_base::empty()
    353   { return _S_map.empty(); }
    354 
    355   void
    356   throw_allocator_base::erase(void* p, size_t size)
    357   {
    358     check_allocated(p, size);
    359     _S_map.erase(p);
    360   }
    361 
    362   void
    363   throw_allocator_base::check_allocated(void* p, size_t size)
    364   {
    365     const_iterator found_it = _S_map.find(p);
    366     if (found_it == _S_map.end())
    367       {
    368 	std::string error("throw_allocator_base::check_allocated by value ");
    369 	error += "null erase!";
    370 	error += '\n';
    371 	print_to_string(error, make_entry(p, size));
    372 	std::__throw_logic_error(error.c_str());
    373       }
    374 
    375     if (found_it->second.second != size)
    376       {
    377 	std::string error("throw_allocator_base::check_allocated by value ");
    378 	error += "wrong-size erase!";
    379 	error += '\n';
    380 	print_to_string(error, make_entry(p, size));
    381 	print_to_string(error, *found_it);
    382 	std::__throw_logic_error(error.c_str());
    383       }
    384   }
    385 
    386   void
    387   throw_allocator_base::check_allocated(size_t label)
    388   {
    389     std::string found;
    390     const_iterator it = _S_map.begin();
    391     while (it != _S_map.end())
    392       {
    393 	if (it->second.first == label)
    394 	  {
    395 	    print_to_string(found, *it);
    396 	  }
    397 	++it;
    398       }
    399 
    400     if (!found.empty())
    401       {
    402 	std::string error("throw_allocator_base::check_allocated by label ");
    403 	error += '\n';
    404 	error += found;
    405 	std::__throw_logic_error(error.c_str());
    406       }
    407   }
    408 
    409   void
    410   throw_allocator_base::throw_conditionally()
    411   {
    412     if (_S_g.get_prob() < _S_throw_prob)
    413       __throw_forced_exception_error();
    414   }
    415 
    416   void
    417   throw_allocator_base::print_to_string(std::string& s)
    418   {
    419     const_iterator begin = throw_allocator_base::_S_map.begin();
    420     const_iterator end = throw_allocator_base::_S_map.end();
    421     for (; begin != end; ++begin)
    422       print_to_string(s, *begin);
    423   }
    424 
    425   void
    426   throw_allocator_base::print_to_string(std::string& s, const_reference ref)
    427   {
    428     char buf[40];
    429     const char tab('\t');
    430     s += "address: ";
    431     __builtin_sprintf(buf, "%p", ref.first);
    432     s += buf;
    433     s += tab;
    434     s += "label: ";
    435     unsigned long l = static_cast<unsigned long>(ref.second.first);
    436     __builtin_sprintf(buf, "%lu", l);
    437     s += buf;
    438     s += tab;
    439     s += "size: ";
    440     l = static_cast<unsigned long>(ref.second.second);
    441     __builtin_sprintf(buf, "%lu", l);
    442     s += buf;
    443     s += '\n';
    444   }
    445 
    446 _GLIBCXX_END_NAMESPACE
    447 
    448 #endif
    449