40b6e7da8c6ebec90f648c8bbd2b5df4ad01be18
[libcds.git] / cds / algo / flat_combining / wait_strategy.h
1 /*
2     This file is a part of libcds - Concurrent Data Structures library
3
4     (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2016
5
6     Source code repo: http://github.com/khizmax/libcds/
7     Download: http://sourceforge.net/projects/libcds/files/
8
9     Redistribution and use in source and binary forms, with or without
10     modification, are permitted provided that the following conditions are met:
11
12     * Redistributions of source code must retain the above copyright notice, this
13       list of conditions and the following disclaimer.
14
15     * Redistributions in binary form must reproduce the above copyright notice,
16       this list of conditions and the following disclaimer in the documentation
17       and/or other materials provided with the distribution.
18
19     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20     AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23     FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24     DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25     SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26     CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27     OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28     OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #ifndef CDSLIB_ALGO_FLAT_COMBINING_WAIT_STRATEGY_H
32 #define CDSLIB_ALGO_FLAT_COMBINING_WAIT_STRATEGY_H
33
34 #include <cds/algo/flat_combining/defs.h>
35 #include <cds/algo/backoff_strategy.h>
36 #include <mutex>
37 #include <condition_variable>
38 #include <boost/thread/tss.hpp>  // thread_specific_ptr
39
40
41 namespace cds { namespace opt {
42
43     /// Wait strategy option for \p flat_combining::kernel
44     template <typename Strategy>
45     struct wait_strategy {
46         //@cond
47         template <typename Base> struct pack: public Base
48         {
49             typedef Strategy wait_strategy;
50         };
51         //@endcond
52     };
53
54 }} // namespace cds::opt
55
56 namespace cds { namespace algo { namespace flat_combining {
57
58     /// Wait strategies for \p flat_combining technique
59     namespace wait_strategy {
60
61         /// Empty wait strategy
62         struct empty
63         {
64         //@cond
65             template <typename PublicationRecord>
66             struct make_publication_record {
67                 typedef PublicationRecord type;
68             };
69
70             template <typename PublicationRecord>
71             void prepare( PublicationRecord& /*rec*/ )
72             {}
73
74             template <typename FCKernel, typename PublicationRecord>
75             bool wait( FCKernel& /*fc*/, PublicationRecord& /*rec*/ )
76             {
77                 return false;
78             }
79
80             template <typename FCKernel, typename PublicationRecord>
81             void notify( FCKernel& /*fc*/, PublicationRecord& /*rec*/ )
82             {}
83
84             template <typename FCKernel>
85             void wakeup( FCKernel& /*fc*/ )
86             {}
87         //@endcond
88         };
89
90         /// Back-off wait strategy
91         template <typename BackOff = cds::backoff::delay_of<2>>
92         struct backoff
93         {
94         //@cond
95             typedef BackOff back_off;
96
97             template <typename PublicationRecord>
98             struct make_publication_record {
99                 struct type: public PublicationRecord
100                 {
101                     back_off bkoff;
102                 };
103             };
104
105             template <typename PublicationRecord>
106             void prepare( PublicationRecord& rec )
107             {
108                 rec.bkoff.reset();
109             }
110
111             template <typename FCKernel, typename PublicationRecord>
112             bool wait( FCKernel& /*fc*/, PublicationRecord& rec )
113             {
114                 rec.bkoff();
115                 return false;
116             }
117
118             template <typename FCKernel, typename PublicationRecord>
119             void notify( FCKernel& /*fc*/, PublicationRecord& /*rec*/ )
120             {}
121
122             template <typename FCKernel>
123             void wakeup( FCKernel& )
124             {}
125         //@endcond
126         };
127
128         /// Wait strategy based on the single mutex and the condition variable
129         /**
130             The strategy shares the mutex and conditional variable for all thread.
131
132             Template parameter \p Milliseconds specifies waiting duration;
133             the minimal value is 1.
134         */
135         template <int Milliseconds = 2>
136         class single_mutex_single_condvar
137         {
138         //@cond
139             std::mutex m_mutex;
140             std::condition_variable m_condvar;
141
142             typedef std::unique_lock< std::mutex > unique_lock;
143
144         public:
145             enum {
146                 c_nWaitMilliseconds = Milliseconds < 1 ? 1 : Milliseconds
147             };
148
149             template <typename PublicationRecord>
150             struct make_publication_record {
151                 typedef PublicationRecord type;
152             };
153
154             template <typename PublicationRecord>
155             void prepare( PublicationRecord& /*rec*/ )
156             {}
157
158             template <typename FCKernel, typename PublicationRecord>
159             bool wait( FCKernel& fc, PublicationRecord& rec )
160             {
161                 if ( fc.get_operation( rec ) >= req_Operation ) {
162                     unique_lock lock( m_mutex );
163                     if ( fc.get_operation( rec ) >= req_Operation )
164                         return m_condvar.wait_for( lock, std::chrono::milliseconds( c_nWaitMilliseconds )) == std::cv_status::no_timeout;
165                 }
166                 return false;
167             }
168
169             template <typename FCKernel, typename PublicationRecord>
170             void notify( FCKernel& fc, PublicationRecord& rec )
171             {
172                 m_condvar.notify_all();
173             }
174
175             template <typename FCKernel>
176             void wakeup( FCKernel& /*fc*/ )
177             {
178                 m_condvar.notify_all();
179             }
180         //@endcond
181         };
182
183         /// Wait strategy based on the single mutex and thread-local condition variables
184         /**
185             The strategy shares the mutex, but each thread has its own conditional variable
186
187             Template parameter \p Milliseconds specifies waiting duration;
188             the minimal value is 1.
189         */
190         template <int Milliseconds = 2>
191         class single_mutex_multi_condvar
192         {
193         //@cond
194             std::mutex m_mutex;
195
196             typedef std::unique_lock< std::mutex > unique_lock;
197
198         public:
199             enum {
200                 c_nWaitMilliseconds = Milliseconds < 1 ? 1 : Milliseconds
201             };
202
203             template <typename PublicationRecord>
204             struct make_publication_record {
205                 struct type: public PublicationRecord
206                 {
207                     std::condition_variable m_condvar;
208                 };
209             };
210
211             template <typename PublicationRecord>
212             void prepare( PublicationRecord& /*rec*/ )
213             {}
214
215             template <typename FCKernel, typename PublicationRecord>
216             bool wait( FCKernel& fc, PublicationRecord& rec )
217             {
218                 if ( fc.get_operation( rec ) >= req_Operation ) {
219                     unique_lock lock( m_mutex );
220                     if ( fc.get_operation( rec ) >= req_Operation )
221                         return rec.m_condvar.wait_for( lock, std::chrono::milliseconds( c_nWaitMilliseconds )) == std::cv_status::no_timeout;
222                 }
223                 return false;
224             }
225
226             template <typename FCKernel, typename PublicationRecord>
227             void notify( FCKernel& fc, PublicationRecord& rec )
228             {
229                 rec.m_condvar.notify_one();
230             }
231
232             template <typename FCKernel>
233             void wakeup( FCKernel& fc )
234             {
235                 fc.wakeup_any();
236             }
237         //@endcond
238         };
239
240         /// Wait strategy where each thread has a mutex and a condition variable
241         /**
242             Template parameter \p Milliseconds specifies waiting duration;
243             the minimal value is 1.
244         */
245         template <int Milliseconds = 10>
246         class multi_mutex_multi_condvar
247         {
248         //@cond
249             typedef std::unique_lock< std::mutex > unique_lock;
250
251         public:
252             enum {
253                 c_nWaitMilliseconds = Milliseconds < 1 ? 1 : Milliseconds
254             };
255
256             template <typename PublicationRecord>
257             struct make_publication_record {
258                 struct type: public PublicationRecord
259                 {
260                     std::mutex              m_mutex;
261                     std::condition_variable m_condvar;
262                 };
263             };
264
265             template <typename PublicationRecord>
266             void prepare( PublicationRecord& /*rec*/ )
267             {}
268
269             template <typename FCKernel, typename PublicationRecord>
270             bool wait( FCKernel& fc, PublicationRecord& rec )
271             {
272                 if ( fc.get_operation( rec ) >= req_Operation ) {
273                     unique_lock lock( rec.m_mutex );
274                     if ( fc.get_operation( rec ) >= req_Operation )
275                         return rec.m_condvar.wait_for( lock, std::chrono::milliseconds( c_nWaitMilliseconds )) == std::cv_status::no_timeout;
276                 }
277                 return false;
278             }
279
280             template <typename FCKernel, typename PublicationRecord>
281             void notify( FCKernel& /*fc*/, PublicationRecord& rec )
282             {
283                 rec.m_condvar.notify_one();
284             }
285
286             template <typename FCKernel>
287             void wakeup( FCKernel& fc )
288             {
289                 fc.wakeup_any();
290             }
291         //@endcond
292         };
293
294     } // namespace wait_strategy
295 }}} // namespace cds::algo::flat_combining
296
297 #endif //CDSLIB_ALGO_FLAT_COMBINING_WAIT_STRATEGY_H