stlab.adobe.com Adobe Systems Incorporated

copy_on_write.hpp

Go to the documentation of this file.
00001 /*
00002     Copyright 2005-2007 Adobe Systems Incorporated
00003     Distributed under the MIT License (see accompanying file LICENSE_1_0_0.txt
00004     or a copy at http://stlab.adobe.com/licenses.html)
00005 */
00006 
00007 /**************************************************************************************************/
00008 
00009 #ifndef ADOBE_COPY_ON_WRITE_HPP
00010 #define ADOBE_COPY_ON_WRITE_HPP
00011 
00012 /**************************************************************************************************/
00013 
00014 #include <adobe/config.hpp>
00015 
00016 #include <algorithm>
00017 #include <cassert>
00018 #include <cstdlib>
00019 
00020 #include <boost/noncopyable.hpp>
00021 #include <boost/operators.hpp>
00022 #include <boost/static_assert.hpp>
00023 
00024 #include <adobe/counter.hpp>
00025 #include <adobe/memory.hpp>
00026 #include <adobe/move.hpp>
00027 #include <adobe/once.hpp>
00028 #include <adobe/typeinfo.hpp>
00029 
00030 /**************************************************************************************************/
00031 
00032 namespace adobe {
00033 
00034 /**************************************************************************************************/
00035 
00036 namespace version_1 {
00037 
00038 /**************************************************************************************************/
00039 
00069 template <typename T,                                   // T models Regular
00070           typename A = adobe::capture_allocator<T> >    // A models Allocator
00071 class copy_on_write
00072 {
00073 public:
00075     typedef T value_type;
00077     typedef A allocator_type;
00078 
00079 #if !defined(ADOBE_NO_DOCUMENTATION)
00080 private:
00081     struct implementation_t;
00082     typedef typename allocator_type::template rebind<implementation_t>::other implementation_allocator_type;
00083 public:
00084 #endif
00085 
00091     copy_on_write()
00092     {
00093         adobe::call_once(init_default, flag_s);
00094         object_m = default_s;
00095         object_m->header_m.get().count_m.increment();
00096     }
00097 
00101     explicit copy_on_write(const allocator_type& a) :
00102         object_m(0)
00103     {
00104         implementation_allocator_type other_allocator(a);
00105 
00106         object_m = allocate(other_allocator);
00107     }
00108 
00116     template <typename U>
00117     copy_on_write(U x, typename move_sink<U, T>::type = 0) :
00118         object_m(0)
00119     {
00120         /*
00121             NOTE : This should not move to the initializer; in case the
00122             allocation or the move should fail (throw) we want to keep
00123             the copy_on_write instance in a valid state.
00124         */
00125         object_m = allocate_move(0, move(x));
00126     }
00127     
00135     template <typename U>
00136     copy_on_write(const U& x, typename copy_sink<U, T>::type = 0) :
00137         object_m(0)
00138     {
00139         /*
00140             NOTE : This should not move to the initializer; in case the
00141             allocation or the move should fail (throw) we want to keep
00142             the copy_on_write instance in a valid state.
00143         */
00144         object_m = allocate(static_cast<implementation_t*>(0), x);
00145     }
00146 
00151     copy_on_write(const copy_on_write& x) :
00152         object_m(x.object_m)
00153     {
00154         if (object_m)
00155             object_m->header_m.get().count_m.increment();
00156     }
00157     
00158     copy_on_write(move_from<copy_on_write> x) :
00159         object_m(x.source.object_m)
00160     {
00161         x.source.object_m = 0;
00162     }
00163 
00164     ~copy_on_write()
00165     {
00166         release(object_m);
00167     }
00168 
00174     copy_on_write& operator=(copy_on_write x)
00175     { swap(*this, x); return *this; }
00176     
00177 
00178     template<typename U>
00179     typename move_sink<U, T, copy_on_write&>::type operator=(U x)
00180     {
00181         if (!object_m)
00182             object_m = allocate_move(0, move(x));
00183         else if (object_m->header_m.get().count_m.is_one())
00184             object_m->value_m = move(x);
00185         else
00186             reset(allocate_move(object_m, move(x)));
00187 
00188         return *this;
00189     }
00190 
00191     /*
00192         Doxygen has a bug where documentation is used only from the last item in
00193         the group.
00194     */
00199     template<typename U>
00200     typename copy_sink<U, T, copy_on_write&>::type operator=(const U& x)
00201     {
00202         if (!object_m)
00203             object_m = allocate(0, x);
00204         else if (object_m->header_m.get().count_m.is_one())
00205             object_m->value_m = x;
00206         else
00207             reset(allocate(object_m, x));
00208 
00209         return *this;
00210     }
00211 
00224     value_type& write()
00225     {
00226         assert(object_m && "FATAL (sparent) : using a moved copy_on_write object");
00227 
00228         if (!object_m->header_m.get().count_m.is_one())
00229             reset(allocate(object_m, object_m->value_m));
00230 
00231         return object_m->value_m;
00232     }
00233 
00239     const value_type& read() const
00240     {
00241         assert(object_m && "FATAL (sparent) : using a moved copy_on_write object");
00242         return object_m->value_m;
00243     }
00244 
00250     operator const value_type& () const
00251     { return read(); }
00252 
00263     const value_type& operator*() const
00264     { return read(); }
00265 
00276     const value_type* operator->() const
00277     { return &read(); }
00278 
00286     bool unique_instance() const
00287     { return !object_m || object_m->header_m.get().count_m.is_one(); }
00288     
00296     bool identity(const copy_on_write& x) const
00297     { return object_m == x.object_m; }
00298 
00299     friend inline void swap(copy_on_write& x, copy_on_write& y)
00300     { std::swap(x.object_m, y.object_m); }
00301 
00302     friend inline bool operator<(const copy_on_write& x, const copy_on_write& y)
00303     { return y.object_m && (!x.object_m || (!x.identity(y) && *x < *y)); }
00304 
00305     friend inline bool operator>(const copy_on_write& x, const copy_on_write& y)
00306     { return y < x; }
00307 
00308     friend inline bool operator<=(const copy_on_write& x, const copy_on_write& y)
00309     { return !(y < x); }
00310 
00311     friend inline bool operator>=(const copy_on_write& x, const copy_on_write& y)
00312     { return !(x < y); }
00313 
00314     friend inline bool operator==(const copy_on_write& x, const copy_on_write& y)
00315     { return x.identity(y) || (x.object_m && y.object_m && *x == *y); }
00316 
00317     friend inline bool operator!=(const copy_on_write& x, const copy_on_write& y)
00318     { return !(x == y); }
00319 
00320     allocator_type get_allocator() const
00321     { return object_m ? allocator_type(object_m->get_allocator()) : allocator_type(); }
00322 
00323 private:
00324 #if !defined(ADOBE_NO_DOCUMENTATION)
00325     static implementation_t* allocate(const implementation_t* alloc_src, const T& x = T())
00326     {
00327         implementation_allocator_type allocator(alloc_src ?
00328                                                 alloc_src->get_allocator() :
00329                                                 implementation_allocator_type());
00330 
00331         return allocate(allocator, x);
00332     }
00333 
00334     static implementation_t* allocate(implementation_allocator_type& allocator, const T& x = T())
00335     {
00336         implementation_t* tmp(allocator.allocate(1));
00337 
00338         try {
00339             ::new(static_cast<void*>(tmp)) implementation_t(x);
00340         } catch (...) {
00341             tmp->get_allocator().deallocate(tmp, 1);
00342             throw;
00343         }
00344 
00345         return tmp;
00346     }
00347 
00348     template <typename U>
00349     static implementation_t* allocate_move(const implementation_t* alloc_src, U x)
00350     {
00351         implementation_allocator_type allocator(alloc_src ?
00352                                                 alloc_src->get_allocator() :
00353                                                 implementation_allocator_type());
00354         implementation_t*             tmp(allocator.allocate(1));
00355 
00356         try {
00357             ::new(static_cast<void*>(tmp)) implementation_t(move_from<U>(x));
00358         } catch (...) {
00359             tmp->get_allocator().deallocate(tmp, 1);
00360             throw;
00361         }
00362 
00363         return tmp;
00364     }
00365 
00366     static void release(implementation_t* x)
00367     {
00368         /*
00369             I thought about returning a bool from this routine (denoting whether
00370             or not a deallocation took place) but decided not to in the end.
00371             Release's semantics are that of giving up ownership of the
00372             implementation instance, so you are not allowed to subsequently
00373             interact with the implementation instance you've released. Thus,
00374             notifying the caller of a deallocation is a moot point.
00375         */
00376 
00377         if (x == 0 || x->header_m.get().count_m.decrement() == false)
00378             return;
00379 
00380         implementation_allocator_type allocator(x->get_allocator());
00381 
00382         destroy(x);
00383 
00384         allocator.deallocate(x, 1);
00385     }
00386 
00387     void reset(implementation_t* to)
00388     {
00389         release(object_m);
00390         object_m = to;
00391     }
00392 
00393     implementation_t* object_m;
00394 
00395     static once_flag         flag_s;
00396     static implementation_t* default_s;
00397 
00398     static void release_default();
00399     static void init_default();
00400 #endif
00401 };
00402 
00403 /**************************************************************************************************/
00404 
00405 #if !defined(ADOBE_NO_DOCUMENTATION)
00406 
00407 /**************************************************************************************************/
00408 
00409 template <typename T, typename A>
00410 once_flag copy_on_write<T, A>::flag_s = ADOBE_ONCE_INIT;
00411 
00412 template <typename T, typename A>
00413 typename copy_on_write<T, A>::implementation_t* copy_on_write<T, A>::default_s;
00414 
00415 template <typename T, typename A>
00416 struct copy_on_write<T, A>::implementation_t : private boost::noncopyable
00417 {
00418     // Assert proper size for counter_t
00419     BOOST_STATIC_ASSERT((sizeof(counter_t) == sizeof(std::size_t)));
00420     // Assert proper alignment for counter_t
00421     BOOST_STATIC_ASSERT((sizeof(counter_t) == sizeof(void*)));
00422 
00423     struct header_t
00424     {
00425         counter_t      count_m;
00426         allocator_type allocator_m;
00427     };
00428 
00429     template <typename U>
00430     explicit implementation_t(const U& x, typename copy_sink<U, T>::type = 0) :
00431         value_m(x)
00432     { }
00433 
00434     template <typename U>
00435     explicit implementation_t(U x, typename move_sink<U, T>::type = 0) :
00436         value_m(move(x))
00437     { }
00438 
00439     implementation_allocator_type get_allocator() const
00440     { return implementation_allocator_type(header_m.get().allocator_m); }
00441 
00442     aligned_storage<header_t> header_m;
00443     value_type                value_m;
00444 };
00445 
00446 template <typename T, typename A>
00447 void copy_on_write<T, A>::init_default()
00448 {
00449     implementation_allocator_type allocator;
00450 
00451     default_s = allocate(allocator);
00452 
00453     std::atexit(release_default); // ignore failure
00454 }
00455 
00456 template <typename T, typename A>
00457 void copy_on_write<T, A>::release_default()
00458 {
00459     release(default_s);
00460 }
00461 
00462 #endif
00463 
00464 /**************************************************************************************************/
00465 
00466 } // namespace version_1
00467 
00468 /**************************************************************************************************/
00469 
00470 using version_1::copy_on_write;
00471 
00472 /**************************************************************************************************/
00473 
00474 template <typename T, typename A> struct is_movable<copy_on_write<T, A> > : boost::mpl::true_ { };
00475 
00476 /**************************************************************************************************/
00477 
00478 } // namespace adobe
00479 
00480 /**************************************************************************************************/
00481 
00482 ADOBE_NAME_TYPE_1("copy_on_write:version_1:adobe", adobe::version_1::copy_on_write<T0, adobe::capture_allocator<T0> >)
00483 ADOBE_NAME_TYPE_2("copy_on_write:version_1:adobe", adobe::version_1::copy_on_write<T0, T1>)
00484 
00485 /**************************************************************************************************/
00486 
00487 #endif
00488 
00489 /**************************************************************************************************/

Copyright © 2006-2007 Adobe Systems Incorporated.

Use of this website signifies your agreement to the Terms of Use and Online Privacy Policy.

Search powered by Google