fix a bugs in guarded_ptr and exempt_ptr
[libcds.git] / cds / urcu / exempt_ptr.h
1 //$$CDS-header$$
2
3 #ifndef __CDS_URCU_EXEMPT_PTR_H
4 #define __CDS_URCU_EXEMPT_PTR_H
5
6 #include <cds/details/defs.h>
7
8 namespace cds { namespace urcu {
9
10     //@cond
11     namespace details {
12         template <typename Node, typename Value>
13         struct conventional_exempt_member_cast
14         {
15             Value * operator()( Node * p ) const
16             {
17                 return &p->m_Value;
18             }
19         };
20
21         template <typename Node, typename Value>
22         struct conventional_exempt_pair_cast
23         {
24             Value * operator()( Node * p ) const
25             {
26                 return &p->m_Data;
27             }
28         };
29     } // namespace details
30     //@endcond
31
32     /// Exempt pointer for RCU
33     /**
34         This special pointer class is intended for returning extracted node from RCU-based container.
35         The destructor (and \p release() member function) invokes <tt>RCU::retire_ptr< Disposer >()</tt> function to dispose the node.
36         For non-intrusive containers from \p cds::container namespace \p Disposer is usually an invocation
37         of node deallocator. For intrusive containers the disposer can be empty or it can trigger an event "node can be reused safely".
38         In any case, the exempt pointer concept keeps RCU semantics.
39
40         You don't need use this helper class directly. Any RCU-based container typedefs a simplified version of this template.
41
42         Template arguments:
43         - \p RCU - one of \ref cds_urcu_gc "RCU type"
44         - \p NodeType - container's node type
45         - \p ValueType - value type stored in container's node. For intrusive containers it is the same as \p NodeType
46         - \p Disposer - a disposer functor
47         - \p Cast - a functor for casting from \p NodeType to \p ValueType. For intrusive containers 
48             the casting is usually disabled, i.e. \p Cast is \p void.
49     */
50     template <
51         class RCU,
52         typename NodeType,
53         typename ValueType,
54         typename Disposer,
55 #ifdef CDS_DOXYGEN_INVOKED
56         typename Cast
57 #else
58         typename Cast=details::conventional_exempt_member_cast<NodeType, ValueType>
59 #endif
60     >
61     class exempt_ptr
62     {
63     public:
64         typedef RCU         rcu         ;   ///< RCU type - one of <tt>cds::urcu::gc< ... ></tt>
65         typedef NodeType    node_type   ;   ///< Node type
66         typedef ValueType   value_type  ;   ///< Value type
67         typedef Disposer    disposer    ;   ///< Disposer calling when release
68         typedef Cast        node_to_value_cast  ;   ///< Functor converting \p node_type to \p value_type
69
70     private:
71         //@cond
72         node_type *     m_pNode;
73         //@endcond
74
75     public:
76         /// Constructs empty pointer
77         exempt_ptr() CDS_NOEXCEPT
78             : m_pNode( nullptr )
79         {}
80
81         //@cond
82         /// Creates exempt pointer for \p pNode. Only for internal use.
83         explicit exempt_ptr( node_type * pNode ) CDS_NOEXCEPT
84             : m_pNode( pNode )
85         {}
86         explicit exempt_ptr( std::nullptr_t ) CDS_NOEXCEPT
87             : m_pNode( nullptr )
88         {}
89         //@endcond
90
91         /// Move ctor
92         exempt_ptr( exempt_ptr&& p ) CDS_NOEXCEPT
93             : m_pNode( p.m_pNode )
94         {
95             p.m_pNode = nullptr;
96         }
97
98         /// The exempt pointer is not copy-constructible
99         exempt_ptr( exempt_ptr const& ) = delete;
100
101         /// Releases the pointer
102         ~exempt_ptr()
103         {
104             release();
105         }
106
107         /// Checks if the pointer is \p nullptr
108         bool empty() const CDS_NOEXCEPT
109         {
110             return m_pNode == nullptr;
111         }
112
113         /// \p bool operator returns <tt>!empty()</tt>
114         explicit operator bool() const CDS_NOEXCEPT
115         {
116             return !empty();
117         }
118
119         /// Dereference operator
120         value_type * operator->() const CDS_NOEXCEPT
121         {
122             return !empty() ? node_to_value_cast()(m_pNode) : nullptr;
123         }
124
125         /// Returns a reference to the value
126         value_type& operator *() CDS_NOEXCEPT
127         {
128             assert( !empty());
129             return *node_to_value_cast()( m_pNode );
130         }
131
132         /// Move assignment. Can be called only outside of RCU critical section
133         exempt_ptr& operator =( exempt_ptr&& p ) CDS_NOEXCEPT
134         {
135             release();
136             m_pNode = p.m_pNode;
137             p.m_pNode = nullptr;
138             return *this;
139         }
140
141         /// The exempt pointer is not copy-assignable
142         exempt_ptr& operator=(exempt_ptr const&) = delete;
143
144         /// Disposes the pointer. Should be called only outside of RCU critical section
145         void release()
146         {
147             assert( !rcu::is_locked() );
148             if ( !empty() ) {
149                 rcu::template retire_ptr<disposer>( m_pNode );
150                 m_pNode = nullptr;
151             }
152         }
153     };
154
155     //@cond
156     // Intrusive container specialization
157     template <
158         class RCU,
159             typename NodeType,
160             typename Disposer
161     >
162     class exempt_ptr< RCU, NodeType, NodeType, Disposer, void >
163     {
164     public:
165         typedef RCU         rcu         ;   ///< RCU type - one of <tt>cds::urcu::gc< ... ></tt>
166         typedef NodeType    node_type   ;   ///< Node type
167         typedef NodeType    value_type  ;   ///< Node type
168         typedef Disposer    disposer    ;   ///< Disposer calling when release
169         typedef void        node_to_value_cast; ///< No casting is needed
170
171     private:
172         node_type *     m_pNode;
173
174     public:
175         /// Constructs empty pointer
176         exempt_ptr() CDS_NOEXCEPT
177             : m_pNode( nullptr )
178         {}
179
180         /// Creates exempt pointer for \p pNode. Only for internal use.
181         explicit exempt_ptr( node_type * pNode ) CDS_NOEXCEPT
182             : m_pNode( pNode )
183         {}
184         explicit exempt_ptr( std::nullptr_t ) CDS_NOEXCEPT
185             : m_pNode( nullptr )
186         {}
187
188         /// Move ctor
189         exempt_ptr( exempt_ptr&& p ) CDS_NOEXCEPT
190             : m_pNode( p.m_pNode )
191         {
192             p.m_pNode = nullptr;
193         }
194
195
196         /// The exempt pointer is not copy-constructible
197         exempt_ptr( exempt_ptr const& ) = delete;
198
199         /// Releases the pointer
200         ~exempt_ptr()
201         {
202             release();
203         }
204
205         /// Checks if the pointer is \p nullptr
206         bool empty() const CDS_NOEXCEPT
207         {
208             return m_pNode == nullptr;
209         }
210
211         /// \p bool operator returns <tt>!empty()</tt>
212         explicit operator bool() const CDS_NOEXCEPT
213         {
214             return !empty();
215         }
216
217         /// Dereference operator.
218         value_type * operator->() const CDS_NOEXCEPT
219         {
220             return !empty() ? m_pNode : nullptr;
221         }
222
223         /// Returns a reference to the value
224         value_type& operator *() CDS_NOEXCEPT
225         {
226             assert( !empty());
227             return *m_pNode;
228         }
229
230         /// Move assignment. Can be called only outside of RCU critical section
231         exempt_ptr& operator =(exempt_ptr&& p) CDS_NOEXCEPT
232         {
233             release();
234             m_pNode = p.m_pNode;
235             p.m_pNode = nullptr;
236             return *this;
237         }
238
239         /// The exempt pointer is not copy-assignable
240         exempt_ptr& operator=(exempt_ptr const&) = delete;
241
242         /// Disposes the pointer. Should be called only outside of RCU critical section
243         void release()
244         {
245             if ( !empty() ) {
246                 assert( !rcu::is_locked() );
247                 rcu::template retire_ptr<disposer>( m_pNode );
248                 m_pNode = nullptr;
249             }
250         }
251     };
252     //@endcond
253
254 }} // namespace cds::urcu
255
256 #endif //#ifndef __CDS_URCU_EXEMPT_PTR_H