Merge branch 'dev'
[libcds.git] / cds / details / allocator.h
1 //$$CDS-header$$
2
3 #ifndef CDSLIB_DETAILS_ALLOCATOR_H
4 #define CDSLIB_DETAILS_ALLOCATOR_H
5
6 #include <type_traits>
7 #include <memory>
8 #include <cds/details/defs.h>
9 #include <cds/user_setup/allocator.h>
10
11 namespace cds {
12     namespace details {
13
14         /// Extends \p std::allocator interface to provide semantics like operator \p new and \p delete
15         /**
16             The class is the wrapper around underlying \p Alloc class.
17             \p Alloc provides the \p std::allocator interface.
18         */
19         template <typename T, class Alloc = CDS_DEFAULT_ALLOCATOR >
20         class Allocator
21             : public std::conditional<
22                         std::is_same< T, typename Alloc::value_type>::value
23                         , Alloc
24                         , typename Alloc::template rebind<T>::other
25                      >::type
26         {
27         public:
28             /// Underlying allocator type
29             typedef typename std::conditional<
30                 std::is_same< T, typename Alloc::value_type>::value
31                 , Alloc
32                 , typename Alloc::template rebind<T>::other
33             >::type allocator_type;
34
35             /// \p true if underlined allocator is \p std::allocator, \p false otherwise
36             static CDS_CONSTEXPR bool const c_bStdAllocator = std::is_same< allocator_type, std::allocator<T>>::value;
37
38             /// Element type
39             typedef T   value_type;
40
41             /// Analogue of operator new T(\p src... )
42             template <typename... S>
43             value_type *  New( S const&... src )
44             {
45 #           if CDS_THREAD_SANITIZER_ENABLED
46                 if ( c_bStdAllocator ) {
47                     CDS_TSAN_ANNOTATE_IGNORE_WRITES_BEGIN;
48                 }
49                 value_type * pv = Construct( allocator_type::allocate(1), src... );
50                 if ( c_bStdAllocator ) {
51                     CDS_TSAN_ANNOTATE_IGNORE_WRITES_END;
52                 }
53                 return pv;
54 #           else
55                 return Construct( allocator_type::allocate(1), src... );
56 #           endif
57             }
58
59             /// Analogue of <tt>operator new T( std::forward<Args>(args)... )</tt> (move semantics)
60             template <typename... Args>
61             value_type * MoveNew( Args&&... args )
62             {
63 #           if CDS_THREAD_SANITIZER_ENABLED
64                 if ( c_bStdAllocator ) {
65                     CDS_TSAN_ANNOTATE_IGNORE_WRITES_BEGIN;
66                 }
67                 value_type * pv = MoveConstruct( allocator_type::allocate(1), std::forward<Args>(args)... );
68                 if ( c_bStdAllocator ) {
69                     CDS_TSAN_ANNOTATE_IGNORE_WRITES_END;
70                 }
71                 return pv;
72 #           else
73                 return MoveConstruct( allocator_type::allocate(1), std::forward<Args>(args)... );
74 #           endif
75             }
76
77             /// Analogue of operator new T[\p nCount ]
78             value_type * NewArray( size_t nCount )
79             {
80 #           if CDS_THREAD_SANITIZER_ENABLED
81                 if ( c_bStdAllocator ) {
82                     CDS_TSAN_ANNOTATE_IGNORE_WRITES_BEGIN;
83                 }
84 #           endif
85                 value_type * p = allocator_type::allocate( nCount );
86 #           if CDS_THREAD_SANITIZER_ENABLED
87                 if ( c_bStdAllocator ) {
88                     CDS_TSAN_ANNOTATE_IGNORE_WRITES_END;
89                 }
90 #           endif
91                 for ( size_t i = 0; i < nCount; ++i )
92                     Construct( p + i );
93                 return p;
94             }
95
96             /// Analogue of operator new T[\p nCount ].
97             /**
98                 Each item of array of type T is initialized by parameter \p src: T( src )
99             */
100             template <typename S>
101             value_type * NewArray( size_t nCount, S const& src )
102             {
103 #           if CDS_THREAD_SANITIZER_ENABLED
104                 if ( c_bStdAllocator ) {
105                     CDS_TSAN_ANNOTATE_IGNORE_WRITES_BEGIN;
106                 }
107 #           endif
108                 value_type * p = allocator_type::allocate( nCount );
109 #           if CDS_THREAD_SANITIZER_ENABLED
110                 if ( c_bStdAllocator ) {
111                     CDS_TSAN_ANNOTATE_IGNORE_WRITES_END;
112                 }
113 #           endif
114                 for ( size_t i = 0; i < nCount; ++i )
115                     Construct( p + i, src );
116                 return p;
117             }
118
119 #       if CDS_COMPILER == CDS_COMPILER_INTEL
120             //@cond
121             value_type * NewBlock( size_t nSize )
122             {
123                 return Construct( heap_alloc( nSize ));
124             }
125             //@endcond
126 #       endif
127             /// Allocates block of memory of size at least \p nSize bytes.
128             /**
129                 Internally, the block is allocated as an array of \p void* pointers,
130                 then \p Construct() method is called to initialize \p T.
131
132                 Precondition: <tt> nSize >= sizeof(T) </tt>
133             */
134             template <typename... S>
135             value_type *  NewBlock( size_t nSize, S const&... src )
136             {
137                 return Construct( heap_alloc( nSize ), src... );
138             }
139
140             /// Analogue of operator delete
141             void Delete( value_type * p )
142             {
143                 // TSan false positive possible
144                 CDS_TSAN_ANNOTATE_IGNORE_WRITES_BEGIN;
145                 allocator_type::destroy( p );
146                 allocator_type::deallocate( p, 1 );
147                 CDS_TSAN_ANNOTATE_IGNORE_WRITES_END;
148             }
149
150             /// Analogue of operator delete []
151             void Delete( value_type * p, size_t nCount )
152             {
153                 // TSan false positive possible
154                 CDS_TSAN_ANNOTATE_IGNORE_WRITES_BEGIN;
155                  for ( size_t i = 0; i < nCount; ++i )
156                      allocator_type::destroy( p + i );
157                 allocator_type::deallocate( p, nCount );
158                 CDS_TSAN_ANNOTATE_IGNORE_WRITES_END;
159             }
160
161 #       if CDS_COMPILER == CDS_COMPILER_INTEL
162             //@cond
163             value_type * Construct( void * p )
164             {
165                 return new( p ) value_type;
166             }
167             //@endcond
168 #       endif
169             /// Analogue of placement operator new( \p p ) T( src... )
170             template <typename... S>
171             value_type * Construct( void * p, S const&... src )
172             {
173                 // TSan false positive possible
174                 CDS_TSAN_ANNOTATE_IGNORE_WRITES_BEGIN;
175                 value_type * pv = new( p ) value_type( src... );
176                 CDS_TSAN_ANNOTATE_IGNORE_WRITES_END;
177                 return pv;
178             }
179
180             /// Analogue of placement <tt>operator new( p ) T( std::forward<Args>(args)... )</tt>
181             template <typename... Args>
182             value_type * MoveConstruct( void * p, Args&&... args )
183             {
184                 // TSan false positive possible
185                 CDS_TSAN_ANNOTATE_IGNORE_WRITES_BEGIN;
186                 value_type * pv = new( p ) value_type( std::forward<Args>(args)... );
187                 CDS_TSAN_ANNOTATE_IGNORE_WRITES_END;
188                 return pv;
189             }
190
191             /// Rebinds allocator to other type \p Q instead of \p T
192             template <typename Q>
193             struct rebind {
194                 typedef Allocator< Q, typename Alloc::template rebind<Q>::other >    other ; ///< Rebinding result
195             };
196
197         private:
198             //@cond
199             void * heap_alloc( size_t nByteSize )
200             {
201                 assert( nByteSize >= sizeof(value_type));
202
203                 size_t const nPtrSize = ( nByteSize + sizeof(void *) - 1 ) / sizeof(void *);
204                 typedef typename allocator_type::template rebind< void * >::other void_allocator;
205 #           if CDS_THREAD_SANITIZER_ENABLED
206                 if ( c_bStdAllocator ) {
207                     CDS_TSAN_ANNOTATE_IGNORE_WRITES_BEGIN;
208                 }
209 #           endif
210                 void * p = void_allocator().allocate( nPtrSize );
211 #           if CDS_THREAD_SANITIZER_ENABLED
212                 if ( c_bStdAllocator ) {
213                     CDS_TSAN_ANNOTATE_IGNORE_WRITES_END;
214                 }
215 #           endif
216                 return p;
217             }
218             //@endcond
219         };
220
221         /// Deferral removing of the object of type \p T. Helper class
222         template <typename T, typename Alloc = CDS_DEFAULT_ALLOCATOR>
223         struct deferral_deleter {
224             typedef T           type            ; ///< Type
225             typedef Alloc       allocator_type  ; ///< Allocator for removing
226
227             /// Frees the object \p p
228             /**
229                 Caveats: this function uses temporary object of type \ref cds::details::Allocator to free the node \p p.
230                 So, the node allocator should be stateless. It is standard requirement for \p std::allocator class objects.
231
232                 Do not use this function directly.
233             */
234             static void free( T * p )
235             {
236                 Allocator<type, allocator_type> a;
237                 a.Delete( p );
238             }
239         };
240
241     }    // namespace details
242 }    // namespace cds
243
244 #endif    // #ifndef CDSLIB_DETAILS_ALLOCATOR_H