OSDN Git Service

2009-06-04 Benjamin Kosnik <bkoz@redhat.com>
[pf3gnuchains/gcc-fork.git] / libstdc++-v3 / include / ext / throw_allocator.h
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 <string>
51 #include <ostream>
52 #include <stdexcept>
53 #include <utility>
54 #include <bits/functexcept.h>
55 #include <bits/move.h>
56 #ifdef __GXX_EXPERIMENTAL_CXX0X__
57 # include <functional>
58 # include <random>
59 #else
60 # include <tr1/functional>
61 # include <tr1/random>
62 #endif
63
64 _GLIBCXX_BEGIN_NAMESPACE(__gnu_cxx)
65
66   /**
67    *  @brief Thown by throw_allocator.
68    *  @ingroup exceptions
69    */
70   struct forced_exception_error : public std::exception
71   { };
72
73   // Substitute for concurrence_error object in the case of -fno-exceptions.
74   inline void
75   __throw_forced_exception_error()
76   {
77 #if __EXCEPTIONS
78     throw forced_exception_error();
79 #else
80     __builtin_abort();
81 #endif
82   }
83
84   // Base class for checking address and label information about
85   // allocations. Create a std::map between the allocated address
86   // (void*) and a datum for annotations, which are a pair of numbers
87   // corresponding to label and allocated size.
88   struct annotate_base
89   {
90     annotate_base()
91     {
92       label();
93       map();
94     }
95     
96     static void
97     set_label(size_t l)
98     { label() = l; }
99
100     static size_t
101     get_label()
102     { return label(); }
103
104     void
105     insert(void* p, size_t size)
106     {
107       if (p == NULL)
108         {
109           std::string error("throw_allocator_base::insert null insert!\n");
110           log_to_string(error, make_entry(p, size));
111           std::__throw_logic_error(error.c_str());
112         }
113
114       const_iterator found = map().find(p);
115       if (found != map().end())
116         {
117           std::string error("throw_allocator_base::insert double insert!\n");
118           log_to_string(error, make_entry(p, size));
119           log_to_string(error, *found);
120           std::__throw_logic_error(error.c_str());
121         }
122
123       map().insert(make_entry(p, size));
124     }
125
126     void
127     erase(void* p, size_t size)
128     {
129       check_allocated(p, size);
130       map().erase(p);
131     }
132
133     // See if a particular address and size has been allocated.
134     inline void
135     check_allocated(void* p, size_t size)
136     {
137       const_iterator found = map().find(p);
138       if (found == map().end())
139         {
140           std::string error("annotate_base::check_allocated by value "
141                             "null erase!\n");
142           log_to_string(error, make_entry(p, size));
143           std::__throw_logic_error(error.c_str());
144         }
145       
146       if (found->second.second != size)
147         {
148           std::string error("annotate_base::check_allocated by value "
149                             "wrong-size erase!\n");
150           log_to_string(error, make_entry(p, size));
151           log_to_string(error, *found);
152           std::__throw_logic_error(error.c_str());
153         }
154     }
155
156     // See if a given label has been allocated.
157     inline void
158     check_allocated(size_t label)
159     {
160       const_iterator beg = map().begin();
161       const_iterator end = map().end();
162       std::string found;
163       while (beg != end)
164         {
165           if (beg->second.first == label)
166             log_to_string(found, *beg);
167           ++beg;
168         }
169       
170       if (!found.empty())
171         {
172           std::string error("annotate_base::check_allocated by label\n");
173           error += found;
174           std::__throw_logic_error(error.c_str());
175         }
176     }
177
178   private:
179     typedef std::pair<size_t, size_t>           data_type;
180     typedef std::map<void*, data_type>          map_type;
181     typedef map_type::value_type                entry_type;
182     typedef map_type::const_iterator            const_iterator;
183     typedef map_type::const_reference           const_reference;
184
185     friend std::ostream&
186     operator<<(std::ostream&, const annotate_base&);
187
188     entry_type
189     make_entry(void* p, size_t size)
190     { return std::make_pair(p, data_type(get_label(), size)); }
191
192     void
193     log_to_string(std::string& s, const_reference ref) const
194     {
195       char buf[40];
196       const char tab('\t');
197       s += "label: ";
198       unsigned long l = static_cast<unsigned long>(ref.second.first);
199       __builtin_sprintf(buf, "%lu", l);
200       s += buf;
201       s += tab;
202       s += "size: ";
203       l = static_cast<unsigned long>(ref.second.second);
204       __builtin_sprintf(buf, "%lu", l);
205       s += buf;
206       s += tab;
207       s += "address: ";
208       __builtin_sprintf(buf, "%p", ref.first);
209       s += buf;
210       s += '\n';
211     }
212
213     static size_t&
214     label()
215     {
216       static size_t ll;
217       return ll;
218     }
219
220     static map_type&
221     map()
222     {
223       static map_type mp;
224       return mp;
225     }
226   };
227
228   inline std::ostream&
229   operator<<(std::ostream& os, const annotate_base& __b)
230   {
231     std::string error;
232     typedef annotate_base base_type;
233     base_type::const_iterator beg = __b.map().begin();
234     base_type::const_iterator end = __b.map().end();
235     for (; beg != end; ++beg)
236       __b.log_to_string(error, *beg);
237     return os << error;
238   }
239
240   /// Base class for probability control and throw.
241   struct probability_base
242   {
243     // Scope-level probability adjustor objects: set probability for
244     // throw at the beginning of a scope block, and restores to
245     // previous probability when object is destroyed on exiting the
246     // block.
247     struct adjustor_base
248     {
249     private:
250       const double _M_prob;
251
252     public:
253       adjustor_base() : _M_prob(get_probability()) { }
254
255       virtual ~adjustor_base()
256       { set_probability(_M_prob); }
257     };
258
259     // Group condition.
260     struct group_adjustor : public adjustor_base
261     {
262       group_adjustor(size_t size)
263       { set_probability(1 - std::pow(double(1 - get_probability()),
264                                      double(0.5 / (size + 1))));
265       }
266     };
267
268     // Never enter the condition.
269     struct never_adjustor : public adjustor_base
270     {
271       never_adjustor() { set_probability(0); }
272     };
273
274     // Always enter the condition.
275     struct always_adjustor : public adjustor_base
276     {
277       always_adjustor() { set_probability(1); }
278     };
279
280     probability_base()
281     {
282       probability();
283       engine();
284     }
285
286     static void
287     set_probability(double __p)
288     { probability() = __p; }
289
290     static double&
291     get_probability()
292     { return probability(); }
293
294     void
295     throw_conditionally()
296     {
297       if (generate() < get_probability())
298         __throw_forced_exception_error();
299     }
300
301     void
302     seed(unsigned long __s)
303     { engine().seed(__s); }
304
305   private:
306 #ifdef __GXX_EXPERIMENTAL_CXX0X__
307     typedef std::uniform_real_distribution<double>      distribution_type;
308     typedef std::mt19937                                engine_type;
309 #else
310     typedef std::tr1::uniform_real<double>              distribution_type;
311     typedef std::tr1::mt19937                           engine_type;
312 #endif
313
314     double
315     generate()
316     {
317 #ifdef __GXX_EXPERIMENTAL_CXX0X__
318       const distribution_type distribution(0, 1);
319       static auto generator = std::bind(distribution, engine());
320 #else
321       // Use variate_generator to get normalized results.
322       typedef std::tr1::variate_generator<engine_type, distribution_type> gen_t;
323       distribution_type distribution(0, 1);
324       static gen_t generator(engine(), distribution);
325 #endif
326
327       double random = generator();
328       if (random < distribution.min() || random > distribution.max())
329         {
330           std::string __s("throw_allocator::throw_conditionally");
331           __s += "\n";
332           __s += "random number generated is: ";
333           char buf[40];
334           __builtin_sprintf(buf, "%f", random);
335           __s += buf;
336           std::__throw_out_of_range(__s.c_str());
337         }
338
339       return random;
340     }
341
342     static double&
343     probability()
344     {
345       static double __p;
346       return __p;
347     }
348
349     static engine_type&
350     engine()
351     {
352       static engine_type __e;
353       return __e;
354     }
355   };
356
357   /**
358    *  @brief Allocator class with logging and exception control.
359    *  @ingroup allocators
360    */
361   template<typename T>
362     class throw_allocator
363     : public probability_base, public annotate_base
364     {
365     public:
366       typedef size_t                            size_type;
367       typedef ptrdiff_t                         difference_type;
368       typedef T                                 value_type;
369       typedef value_type*                       pointer;
370       typedef const value_type*                 const_pointer;
371       typedef value_type&                       reference;
372       typedef const value_type&                 const_reference;
373
374     private:
375       std::allocator<value_type>                _M_allocator;
376
377     public:
378       template<typename U>
379       struct rebind
380       {
381         typedef throw_allocator<U> other;
382       };
383
384       throw_allocator() throw() { }
385
386       throw_allocator(const throw_allocator&) throw() { }
387
388       template<typename U>
389         throw_allocator(const throw_allocator<U>&) throw() { }
390
391       ~throw_allocator() throw() { }
392
393       size_type
394       max_size() const throw()
395       { return _M_allocator.max_size(); }
396
397       pointer
398       allocate(size_type __n, std::allocator<void>::const_pointer hint = 0)
399       {
400         if (__n > this->max_size())
401           std::__throw_bad_alloc();
402
403         throw_conditionally();
404         pointer const a = _M_allocator.allocate(__n, hint);
405         insert(a, sizeof(value_type) * __n);
406         return a;
407       }
408
409       void
410       construct(pointer __p, const T& val)
411       { return _M_allocator.construct(__p, val); }
412
413 #ifdef __GXX_EXPERIMENTAL_CXX0X__
414       template<typename... _Args>
415         void
416         construct(pointer __p, _Args&&... __args)
417         { return _M_allocator.construct(__p, std::forward<_Args>(__args)...); }
418 #endif
419
420       void
421       destroy(pointer __p)
422       { _M_allocator.destroy(__p); }
423
424       void
425       deallocate(pointer __p, size_type __n)
426       {
427         erase(__p, sizeof(value_type) * __n);
428         _M_allocator.deallocate(__p, __n);
429       }
430
431       void
432       check_allocated(pointer __p, size_type __n)
433       {
434         size_type __t = sizeof(value_type) * __n;
435         annotate_base::check_allocated(__p, __t);
436       }
437
438       using annotate_base::check_allocated;
439     };
440
441   template<typename T>
442     inline bool
443     operator==(const throw_allocator<T>&, const throw_allocator<T>&)
444     { return true; }
445
446   template<typename T>
447     inline bool
448     operator!=(const throw_allocator<T>&, const throw_allocator<T>&)
449     { return false; }
450
451 _GLIBCXX_END_NAMESPACE
452
453 #endif