root/core/shared_ptr.hpp

/* [<][>][^][v][top][bottom][index][help] */

INCLUDED FROM


DEFINITIONS

This source file includes following definitions.
  1. deleter
  2. sp_count
  3. sp_acquire
  4. sp_release
  5. sp_to_string

   1 #ifndef libjmmcg_core_shared_ptr_hpp
   2 #define libjmmcg_core_shared_ptr_hpp
   3 
   4 /******************************************************************************
   5 ** $Header: svn+ssh://jmmcg@svn.code.sf.net/p/libjmmcg/code/trunk/libjmmcg/core/shared_ptr.hpp 2359 2018-10-23 01:06:23Z jmmcg $
   6 **
   7 ** Copyright (c) 2004 by J.M.McGuiness, coder@hussar.me.uk
   8 **
   9 ** This library is free software; you can redistribute it and/or
  10 ** modify it under the terms of the GNU Lesser General Public
  11 ** License as published by the Free Software Foundation; either
  12 ** version 2.1 of the License, or (at your option) any later version.
  13 **
  14 ** This library is distributed in the hope that it will be useful,
  15 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17 ** Lesser General Public License for more details.
  18 **
  19 ** You should have received a copy of the GNU Lesser General Public
  20 ** License along with this library; if not, write to the Free Software
  21 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  22 */
  23 
  24 #include "atomic_counter.hpp"
  25 #include "exception.hpp"
  26 
  27 namespace jmmcg {
  28 
  29         namespace intrusive {
  30                 template<class, class> class slist;
  31         }
  32 
  33         /// The intrusive counter that an object must also inherit from for the shared_ptr class to work.
  34         /**
  35                 A client must inherit from this class to allow the shared_ptr to operate. If the client also wants to specify at run-time which counter or deletion method is to be used, then some base in their hierarchy must also inherit from sp_counter_itf_type.
  36 
  37                 By default the counter is a "simple" (cough) sequential counter, with the threading model specified via the lock_traits.
  38 
  39                 \see sp_counter_itf_type, shared_ptr
  40         */
  41         template<
  42                 class Val,
  43                 class LkT,
  44                 template<class> class Del=jmmcg::default_delete,        ///< The deleter to be used to delete the contained pointer.
  45                 template<class> class AtCtr=LkT::template atomic_counter_type
  46         >
  47         class sp_counter_type : public AtCtr<Val> {
  48         public:
  49                 using base_t=sp_counter_itf_type<Val>;
  50                 using value_type=typename base_t::value_type;
  51                 using atomic_ctr_t=AtCtr<value_type>;
  52                 using lock_traits=LkT;  ///< This does not have to be the same as the atomic_ctr_t, as we may want the flexibility to deal with the type differently in this case.
  53                 /// Make sure the correct object deletion mechanism is used.
  54                 /**
  55                         Note this this allows the user to specify multiple features:
  56                         1. That the correct deleter is used with the allocator.
  57                         2. That if the static type of the object is known at compile-time the deleter can be used with little or no performance loss.
  58                         3. That the deleter may be dynamically specified in a derived class, allowing the share_ptr to contain objects that also vary by the specific deleter that the derived type will use. For example there may be a collection of base-pointers, the derived objects of which may be dynamically, placement-new or stack-allocated and upon extracting them from the collection, the shared_ptr class can accommodate deleting all of them because of the flexibility introduced in sp_counter_itf_type::deleter().
  59 
  60                         \see sp_counter_itf_type::deleter_t, sp_counter_itf_type::deleter()
  61                 */
  62                 typedef Del<sp_counter_type> deleter_t;
  63 
  64                 /**
  65                         To assist in allowing compile-time computation of the algorithmic order of the threading model.
  66                 */
  67                 static constexpr ppd::generic_traits::memory_access_modes memory_access_mode=atomic_ctr_t::memory_access_mode;
  68 
  69                 static_assert((std::is_integral<typename atomic_ctr_t::value_type>::value && std::is_signed<typename atomic_ctr_t::value_type>::value), "The input type must be a signed integral type as defined in 3.9.1 of the Standard.");
  70 
  71                 virtual ~sp_counter_type() noexcept(true)=default;
  72 
  73                 /// Call the correct deleter_t object to delete the object.
  74                 /**
  75                         Note that we are calling the dtor on the object from within a virtual function, which is allowed. (The converse is not, of course.)
  76                 */
  77                 virtual void deleter() {
  78                         deleter_t().operator()(this);
  79                 }
  80 
  81                 value_type sp_count() const noexcept(true) final {
  82                         return ctr_.get();
  83                 }
  84                 typename atomic_ctr_t::value_type sp_acquire() noexcept(true) final {
  85                         const typename atomic_ctr_t::value_type ret=++ctr_;
  86                         return ret;
  87                 }
  88                 bool sp_release() noexcept(true) final {
  89                         return --ctr_==0;
  90                 }
  91 
  92                 bool __fastcall operator<(const value_type v) const noexcept(true) {
  93                         return ctr_<v;
  94                 }
  95                 bool __fastcall operator>(const value_type v) const noexcept(true) {
  96                         return ctr_>v;
  97                 }
  98                 bool __fastcall operator>=(const value_type v) const noexcept(true) final {
  99                         return ctr_>=v;
 100                 }
 101 
 102                 tstring
 103                 sp_to_string() const noexcept(false) final {
 104                         tostringstream os;
 105                         os<<"count="<<ctr_;
 106                         return os.str();
 107                 }
 108 
 109         protected:
 110                 constexpr sp_counter_type() noexcept(true) {}
 111 
 112         private:
 113                 template<class, class> friend class shared_ptr;
 114                 template<class, class> friend class intrusive::slist;
 115 
 116                 mutable atomic_ctr_t ctr_;
 117         };
 118 
 119         /// A shared pointer-type that has threading specified as a trait, and uses an intrusive count to save on calls to the memory allocator, which could cause excessive contention on the memory manager in a multi-threaded environment.
 120         /**
 121                 I don't use boost::shared_ptr nor std::shared_ptr as they are insufficiently flexible for my purposes: the multi-threading model they use is not a trait, moreover their counters & deleters can only be specified at compile-time, not also at run-time.
 122                 This class uses a lock-free implementation based upon the careful use of the sp_counter_type that uses a specialised atomic_counter_type.
 123                 For performance tests see 'examples/shared_ptr_parallel.cpp'.
 124 
 125                 \see sp_counter_itf_type, sp_counter_type
 126         */
 127         template<
 128                 class V,        ///< The type to be controlled, which must implement the sp_counter_itf_type interface, which would also specify the threading model which, by default, is single-threaded, so no cost would be paid. This allows the counter-type to be specified at either compile or run-time.
 129                 class LkT       ///< The lock_traits that provide the (potentially) atomic operations to be used upon pointers to the value_type.
 130         >
 131         class shared_ptr final {
 132         public:
 133                 using value_type=V;     ///< A convenience typedef to the type to be controlled.
 134                 using element_type=value_type;  ///< A convenience typedef to the type to be controlled.
 135                 using lock_traits=LkT;  ///< This does not have to be the same as the element_type, as it may not contain any lock_traits, or we may want the flexibility to deal with the type differently in this case.
 136                 /// The (potentially) lock-free pointer type.
 137                 /**
 138                         We need the operations on the contained pointer (to the managed object) to be atomic because the move operations might occur on more than one thread, and therefore there is a race condition on non-SMP architectures, which is avoided if (in the implementation) the operations on the contained pointer are atomic.
 139                 */
 140                 using atomic_ptr_t=typename lock_traits::template atomic<value_type *>;
 141                 /// The counter interface to be used on the controlled type, which allows the counter-type to be specified at compile or run-time.
 142                 using base_ctr_type=sp_counter_itf_type<long>;
 143                 using ctr_type=typename V::atomic_ctr_t;
 144                 using exception_type=typename lock_traits::exception_type;
 145                 /// Make sure the correct object-deletion mechanism is used.
 146                 /**
 147                         \see sp_counter_itf_type::deleter_t, sp_counter_itf_type::deleter(), ~shared_ptr()
 148                 */
 149                 using deleter_t=typename value_type::deleter_t;
 150                 /// The no-op atomic counter does nothing, therefore the shared_ptr must not manage the memory of such objects...
 151                 /**
 152                         \see noop_atomic_ctr
 153                 */
 154                 using no_ref_counting=typename lock_traits::template noop_atomic_ctr<typename ctr_type::value_type>;
 155                 /**
 156                         If you get a compilation issue here, check you aren't using std::default_delete, but jmmcg::default_delete instead.
 157 
 158                         \see jmmcg::default_delete
 159                 */
 160                 using no_deletion=noop_dtor<typename value_type::deleter_t::element_type>;
 161                 /**
 162                         To assist in allowing compile-time computation of the algorithmic order of the threading model.
 163                 */
 164                 static constexpr ppd::generic_traits::memory_access_modes memory_access_mode=(
 165                         atomic_ptr_t::memory_access_mode==ppd::generic_traits::memory_access_modes::crew_memory_access
 166                         && ctr_type::memory_access_mode==ppd::generic_traits::memory_access_modes::crew_memory_access
 167                         ? ppd::generic_traits::memory_access_modes::crew_memory_access
 168                         : ppd::generic_traits::memory_access_modes::erew_memory_access
 169                 );
 170 
 171                 BOOST_MPL_ASSERT((std::is_base_of<base_ctr_type, value_type>));
 172                 static_assert(!std::is_same<base_ctr_type, no_ref_counting>::value || std::is_same<deleter_t, no_deletion>::value, "If not refcounted, then the object must not have a defined deleter, as it should be stack-based.");
 173 
 174                 /**
 175                         Decrement the reference count & possibly delete the object, via calling value_type::delete() which uses the value_type::deleter_t method to delete the object & release the memory.
 176                 */
 177                 atomic_ptr_t __fastcall release() noexcept(true);
 178 
 179                 constexpr __stdcall
 180                 shared_ptr() noexcept(true) {}
 181 
 182                 /**
 183                         \param ptr      Note that this ptr could have a non-zero reference count, and this ctor will take ownership of the ptr, respecting that reference count, only deleting the ptr if it reaches zero.
 184                 */
 185                 explicit __stdcall
 186                 shared_ptr(value_type *ptr) noexcept(true);
 187                 /**
 188                         \param ptr      Note that this ptr could have a non-zero reference count, and this ctor will take ownership of the ptr, respecting that reference count, only deleting the ptr if it reaches zero. The type of the parameter must be the same type or a class non-privately derived from value_type.
 189                 */
 190                 template<class V1> explicit __stdcall
 191                 shared_ptr(V1 *ptr) noexcept(true);
 192                 /**
 193                         \param ptr      Note that this ptr could have a non-zero reference count, and this ctor will take ownership of the ptr, respecting that reference count, only deleting the ptr if it reaches zero.
 194                 */
 195                 explicit __stdcall
 196                 shared_ptr(atomic_ptr_t const &ptr) noexcept(true);
 197                 /**
 198                         \param ptr      Note that this ptr could have a non-zero reference count, and this ctor will take ownership of the ptr, respecting that reference count, only deleting the ptr if it reaches zero.
 199                 */
 200                 template<class V1> explicit __stdcall
 201                 shared_ptr(sp_counter_itf_type<V1> const &ptr) noexcept(true);
 202                 /**
 203                         \param ptr      Note that this ptr could have a non-zero reference count, and this ctor will take ownership of the ptr, respecting that reference count, only deleting the ptr if it reaches zero.
 204                 */
 205                 template<class V1, template<class> class At> explicit __stdcall
 206                 shared_ptr(At<V1*> const &ptr) noexcept(true);
 207                 /**
 208                         \param ptr      Note that this ptr could have a non-zero reference count, and this ctor will take ownership of the ptr, respecting that reference count, only deleting the ptr if it reaches zero.
 209                 */
 210                 explicit __stdcall
 211                 shared_ptr(std::unique_ptr<value_type, deleter_t> &ptr) noexcept(true);
 212                 /**
 213                         \param ptr      Note that this ptr could have a non-zero reference count, and this ctor will take ownership of the ptr, respecting that reference count, only deleting the ptr if it reaches zero. The type of the parameter must be the same type or a class non-privately derived from value_type.
 214                 */
 215                 template<class V1> explicit __stdcall
 216                 shared_ptr(std::unique_ptr<V1, typename V1::deleter_t> &ptr) noexcept(true);
 217                 /**
 218                         \param ptr      Note that this ptr could have a non-zero reference count, and this ctor will take ownership of the ptr, respecting that reference count, only deleting the ptr if it reaches zero.
 219                 */
 220                 explicit __stdcall
 221                 shared_ptr(std::unique_ptr<value_type, deleter_t> &&ptr) noexcept(true);
 222                 /**
 223                         \param ptr      Note that this ptr could have a non-zero reference count, and this ctor will take ownership of the ptr, respecting that reference count, only deleting the ptr if it reaches zero. The type of the parameter must be the same type or a class non-privately derived from value_type.
 224                 */
 225                 template<class V1> explicit __stdcall
 226                 shared_ptr(std::unique_ptr<V1, typename V1::deleter_t> &&ptr) noexcept(true);
 227                 /**
 228                         Note that the same deleter and threading model must be specified.
 229 
 230                         \param s        The type of the parameter must be the same type or a class non-privately derived from value_type.
 231                 */
 232                 template<class V2, class LkT2> explicit JMMCG_MSVC_STDCALL_HACK
 233                 shared_ptr(const shared_ptr<V2, LkT2> &s) noexcept(true);
 234                 /**
 235                         Note that the same deleter and threading model must be specified.
 236 
 237                         \param s        The type of the parameter must be the same type or a class non-privately derived from value_type.
 238                 */
 239                 template<class V2, class LkT2> explicit JMMCG_MSVC_STDCALL_HACK
 240                 shared_ptr(shared_ptr<V2, LkT2> &&s) noexcept(true);
 241 
 242                 shared_ptr(const shared_ptr &s) noexcept(true);
 243                 shared_ptr(shared_ptr &&s) noexcept(true);
 244 
 245                 /**
 246                         \see release()
 247                 */
 248                 __stdcall
 249                 ~shared_ptr() noexcept(true);
 250 
 251                 /**
 252                         Note that the same deleter and threading model must be specified.
 253 
 254                         \param s        The type of the parameter must be the same type or a class non-privately derived from value_type.
 255                 */
 256                 template<class V2, class LkT2> void
 257                 operator=(const shared_ptr<V2, LkT2> &s) noexcept(true);
 258                 /**
 259                         Note that the same deleter and threading model must be specified.
 260 
 261                         \param s        The type of the parameter must be the same type or a class non-privately derived from value_type.
 262                 */
 263                 template<class V2, class LkT2> void
 264                 operator=(shared_ptr<V2, LkT2> &&s) noexcept(true);
 265 
 266                 void
 267                 operator=(const shared_ptr &s) noexcept(true);
 268                 void
 269                 operator=(shared_ptr &&s) noexcept(true);
 270 
 271                 void __fastcall
 272                 reset() noexcept(true);
 273 
 274                 constexpr bool __fastcall
 275                 operator==(const shared_ptr &s) const noexcept(true);
 276                 constexpr bool __fastcall
 277                 operator!=(const shared_ptr &s) const noexcept(true);
 278                 constexpr bool __fastcall
 279                 operator<(const shared_ptr &s) const noexcept(true);
 280                 constexpr bool __fastcall
 281                 operator>(const shared_ptr &s) const noexcept(true);
 282                 explicit constexpr
 283                 operator bool() const noexcept(true);
 284 
 285                 constexpr const atomic_ptr_t &__fastcall
 286                 get() const noexcept(true);
 287                 atomic_ptr_t &__fastcall
 288                 get() noexcept(true);
 289                 constexpr const value_type & __fastcall
 290                 operator*() const noexcept(true);
 291                 value_type & __fastcall
 292                 operator*() noexcept(true);
 293                 constexpr value_type const * __fastcall
 294                 operator->() const noexcept(true);
 295                 value_type * __fastcall
 296                 operator->() noexcept(true);
 297 
 298                 void swap(shared_ptr &s) noexcept(true);
 299 
 300                 tstring to_string() const noexcept(false);
 301 
 302                 /**
 303                         \todo Implement using the advice given in "Standard C++ IOStreams and Locales" by A.Langer & K.Kreft, page 170.
 304                 */
 305                 friend tostream & __fastcall operator<<(tostream &os, shared_ptr const &ptr) noexcept(false) {
 306                         os<<ptr.to_string();
 307                         return os;
 308                 }
 309 
 310         private:
 311                 template<class, class> friend class shared_ptr;
 312                 atomic_ptr_t data_;
 313         };
 314 
 315 }
 316 
 317 #include "shared_ptr_impl.hpp"
 318 
 319 #endif

/* [<][>][^][v][top][bottom][index][help] */