summaryrefslogtreecommitdiff
path: root/boost/interprocess/sync/shm/named_condition.hpp
blob: 9d7cd77e1176092e35ddced7f07c78e2f29da74f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
//////////////////////////////////////////////////////////////////////////////
//
// (C) Copyright Ion Gaztanaga 2005-2011. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// See http://www.boost.org/libs/interprocess for documentation.
//
//////////////////////////////////////////////////////////////////////////////

#ifndef BOOST_INTERPROCESS_SHM_NAMED_CONDITION_HPP
#define BOOST_INTERPROCESS_SHM_NAMED_CONDITION_HPP

#if (defined _MSC_VER) && (_MSC_VER >= 1200)
#  pragma once
#endif

#include <boost/interprocess/detail/config_begin.hpp>
#include <boost/interprocess/detail/workaround.hpp>
#include <boost/static_assert.hpp>
#include <boost/interprocess/detail/type_traits.hpp>
#include <boost/interprocess/creation_tags.hpp>
#include <boost/interprocess/exceptions.hpp>
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/sync/interprocess_condition.hpp>
#include <boost/interprocess/detail/managed_open_or_create_impl.hpp>
#include <boost/interprocess/detail/posix_time_types_wrk.hpp>
#include <boost/interprocess/sync/shm/named_creation_functor.hpp>
#include <boost/interprocess/sync/named_mutex.hpp>
#include <boost/interprocess/permissions.hpp>
#if defined (BOOST_INTERPROCESS_NAMED_MUTEX_USES_POSIX_SEMAPHORES)
#include <boost/interprocess/sync/interprocess_mutex.hpp>
#include <boost/interprocess/sync/scoped_lock.hpp>
#endif


//!\file
//!Describes process-shared variables interprocess_condition class

namespace boost {
namespace interprocess {
namespace ipcdetail {

/// @cond
class interprocess_tester;
/// @endcond

//! A global condition variable that can be created by name.
//! This condition variable is designed to work with named_mutex and
//! can't be placed in shared memory or memory mapped files.
class shm_named_condition
{
   /// @cond
   //Non-copyable
   shm_named_condition();
   shm_named_condition(const shm_named_condition &);
   shm_named_condition &operator=(const shm_named_condition &);
   /// @endcond
   public:
   //!Creates a global condition with a name.
   //!If the condition can't be created throws interprocess_exception
   shm_named_condition(create_only_t create_only, const char *name, const permissions &perm = permissions());

   //!Opens or creates a global condition with a name.
   //!If the condition is created, this call is equivalent to
   //!shm_named_condition(create_only_t, ... )
   //!If the condition is already created, this call is equivalent
   //!shm_named_condition(open_only_t, ... )
   //!Does not throw
   shm_named_condition(open_or_create_t open_or_create, const char *name, const permissions &perm = permissions());

   //!Opens a global condition with a name if that condition is previously
   //!created. If it is not previously created this function throws
   //!interprocess_exception.
   shm_named_condition(open_only_t open_only, const char *name);

   //!Destroys *this and indicates that the calling process is finished using
   //!the resource. The destructor function will deallocate
   //!any system resources allocated by the system for use by this process for
   //!this resource. The resource can still be opened again calling
   //!the open constructor overload. To erase the resource from the system
   //!use remove().
   ~shm_named_condition();

   //!If there is a thread waiting on *this, change that
   //!thread's state to ready. Otherwise there is no effect.*/
   void notify_one();

   //!Change the state of all threads waiting on *this to ready.
   //!If there are no waiting threads, notify_all() has no effect.
   void notify_all();

   //!Releases the lock on the named_mutex object associated with lock, blocks
   //!the current thread of execution until readied by a call to
   //!this->notify_one() or this->notify_all(), and then reacquires the lock.
   template <typename L>
   void wait(L& lock);

   //!The same as:
   //!while (!pred()) wait(lock)
   template <typename L, typename Pr>
   void wait(L& lock, Pr pred);

   //!Releases the lock on the named_mutex object associated with lock, blocks
   //!the current thread of execution until readied by a call to
   //!this->notify_one() or this->notify_all(), or until time abs_time is reached,
   //!and then reacquires the lock.
   //!Returns: false if time abs_time is reached, otherwise true.
   template <typename L>
   bool timed_wait(L& lock, const boost::posix_time::ptime &abs_time);

   //!The same as:   while (!pred()) {
   //!                  if (!timed_wait(lock, abs_time)) return pred();
   //!               } return true;
   template <typename L, typename Pr>
   bool timed_wait(L& lock, const boost::posix_time::ptime &abs_time, Pr pred);

   //!Erases a named condition from the system.
   //!Returns false on error. Never throws.
   static bool remove(const char *name);

   /// @cond
   private:

   struct condition_holder
   {
      interprocess_condition cond_;
      //If named_mutex is implemented using semaphores
      //we need to store an additional mutex
      #if defined (BOOST_INTERPROCESS_NAMED_MUTEX_USES_POSIX_SEMAPHORES)
      interprocess_mutex mutex_;
      #endif
   };

   interprocess_condition *condition() const
   {  return &static_cast<condition_holder*>(m_shmem.get_user_address())->cond_; }

   template <class Lock>
   class lock_inverter
   {
      Lock &l_;
      public:
      lock_inverter(Lock &l)
         :  l_(l)
      {}
      void lock()    {   l_.unlock();   }
      void unlock()  {   l_.lock();     }
   };

   //If named mutex uses POSIX semaphores, then the shm based condition variable
   //must use it's internal lock to wait, as sem_t does not store a pthread_mutex_t
   //instance needed by pthread_mutex_cond_t
   #if defined (BOOST_INTERPROCESS_NAMED_MUTEX_USES_POSIX_SEMAPHORES)
      interprocess_mutex *mutex() const
      {  return &static_cast<condition_holder*>(m_shmem.get_user_address())->mutex_; }

      template <class Lock>
      void do_wait(Lock& lock)
      {
         //shm_named_condition only works with named_mutex
         BOOST_STATIC_ASSERT((is_convertible<typename Lock::mutex_type&, named_mutex&>::value == true));
        
         //lock internal before unlocking external to avoid race with a notifier
         scoped_lock<interprocess_mutex>     internal_lock(*this->mutex());
         lock_inverter<Lock> inverted_lock(lock);
         scoped_lock<lock_inverter<Lock> >   external_unlock(inverted_lock);

         //unlock internal first to avoid deadlock with near simultaneous waits
         scoped_lock<interprocess_mutex>     internal_unlock;
         internal_lock.swap(internal_unlock);
         this->condition()->wait(internal_unlock);
      }

      template <class Lock>
      bool do_timed_wait(Lock& lock, const boost::posix_time::ptime &abs_time)
      {
         //shm_named_condition only works with named_mutex
         BOOST_STATIC_ASSERT((is_convertible<typename Lock::mutex_type&, named_mutex&>::value == true));
         //lock internal before unlocking external to avoid race with a notifier 
         scoped_lock<interprocess_mutex>     internal_lock(*this->mutex(), abs_time); 
         if(!internal_lock) return false;
         lock_inverter<Lock> inverted_lock(lock); 
         scoped_lock<lock_inverter<Lock> >   external_unlock(inverted_lock); 

         //unlock internal first to avoid deadlock with near simultaneous waits 
         scoped_lock<interprocess_mutex>     internal_unlock; 
         internal_lock.swap(internal_unlock); 
         return this->condition()->timed_wait(internal_unlock, abs_time); 
      }
   #else //defined (BOOST_INTERPROCESS_NAMED_MUTEX_USES_POSIX_SEMAPHORES)
      template<class Lock>
      class lock_wrapper
      {
         typedef void (lock_wrapper::*unspecified_bool_type)();
         public:

         typedef interprocess_mutex mutex_type;

         lock_wrapper(Lock &l)
            : l_(l)
         {}

         mutex_type* mutex() const
         {  return l_.mutex()->mutex();  }

         void lock()    { l_.lock(); }

         void unlock()  { l_.unlock(); }

         operator unspecified_bool_type() const
         {  return l_ ? &lock_wrapper::lock : 0;  }

         private:
         Lock &l_;
      };
   #endif   //defined (BOOST_INTERPROCESS_NAMED_MUTEX_USES_POSIX_SEMAPHORES)

   friend class boost::interprocess::ipcdetail::interprocess_tester;
   void dont_close_on_destruction();

   managed_open_or_create_impl<shared_memory_object> m_shmem;

   template <class T, class Arg> friend class boost::interprocess::ipcdetail::named_creation_functor;
   typedef boost::interprocess::ipcdetail::named_creation_functor<condition_holder> construct_func_t;
   /// @endcond
};

/// @cond

inline shm_named_condition::~shm_named_condition()
{}

inline shm_named_condition::shm_named_condition(create_only_t, const char *name, const permissions &perm)
   :  m_shmem  (create_only
               ,name
               ,sizeof(condition_holder) +
                  managed_open_or_create_impl<shared_memory_object>::
                     ManagedOpenOrCreateUserOffset
               ,read_write
               ,0
               ,construct_func_t(DoCreate)
               ,perm)
{}

inline shm_named_condition::shm_named_condition(open_or_create_t, const char *name, const permissions &perm)
   :  m_shmem  (open_or_create
               ,name
               ,sizeof(condition_holder) +
                  managed_open_or_create_impl<shared_memory_object>::
                     ManagedOpenOrCreateUserOffset
               ,read_write
               ,0
               ,construct_func_t(DoOpenOrCreate)
               ,perm)
{}

inline shm_named_condition::shm_named_condition(open_only_t, const char *name)
   :  m_shmem  (open_only
               ,name
               ,read_write
               ,0
               ,construct_func_t(DoOpen))
{}

inline void shm_named_condition::dont_close_on_destruction()
{  interprocess_tester::dont_close_on_destruction(m_shmem);  }

#if defined(BOOST_INTERPROCESS_NAMED_MUTEX_USES_POSIX_SEMAPHORES)

inline void shm_named_condition::notify_one()
{
   scoped_lock<interprocess_mutex> internal_lock(*this->mutex());
   this->condition()->notify_one();
}

inline void shm_named_condition::notify_all()
{
   scoped_lock<interprocess_mutex> internal_lock(*this->mutex());
   this->condition()->notify_all();
}

template <typename L>
inline void shm_named_condition::wait(L& lock)
{
   if (!lock)
      throw lock_exception();
   this->do_wait(lock);
}

template <typename L, typename Pr>
inline void shm_named_condition::wait(L& lock, Pr pred)
{
   if (!lock)
      throw lock_exception();
   while (!pred())
      this->do_wait(lock);
}

template <typename L>
inline bool shm_named_condition::timed_wait
   (L& lock, const boost::posix_time::ptime &abs_time)
{
   if(abs_time == boost::posix_time::pos_infin){
      this->wait(lock);
      return true;
   }
   if (!lock)
      throw lock_exception();
   return this->do_timed_wait(lock, abs_time);
}

template <typename L, typename Pr>
inline bool shm_named_condition::timed_wait
   (L& lock, const boost::posix_time::ptime &abs_time, Pr pred)
{
   if(abs_time == boost::posix_time::pos_infin){
      this->wait(lock, pred);
      return true;
   }
   if (!lock)
      throw lock_exception();

   while (!pred()){
      if(!this->do_timed_wait(lock, abs_time)){
         return pred();
      }
   }
   return true;
}

#else

inline void shm_named_condition::notify_one()
{  this->condition()->notify_one();  }

inline void shm_named_condition::notify_all()
{  this->condition()->notify_all();  }

template <typename L>
inline void shm_named_condition::wait(L& lock)
{
   lock_wrapper<L> newlock(lock);
   this->condition()->wait(newlock);
}

template <typename L, typename Pr>
inline void shm_named_condition::wait(L& lock, Pr pred)
{
   lock_wrapper<L> newlock(lock);
   this->condition()->wait(newlock, pred);
}

template <typename L>
inline bool shm_named_condition::timed_wait
   (L& lock, const boost::posix_time::ptime &abs_time)
{
   lock_wrapper<L> newlock(lock);
   return this->condition()->timed_wait(newlock, abs_time);
}

template <typename L, typename Pr>
inline bool shm_named_condition::timed_wait
   (L& lock, const boost::posix_time::ptime &abs_time, Pr pred)
{
   lock_wrapper<L> newlock(lock);
   return this->condition()->timed_wait(newlock, abs_time, pred);
}

#endif

inline bool shm_named_condition::remove(const char *name)
{  return shared_memory_object::remove(name); }

/// @endcond

}  //namespace ipcdetail
}  //namespace interprocess
}  //namespace boost

#include <boost/interprocess/detail/config_end.hpp>

#endif // BOOST_INTERPROCESS_SHM_NAMED_CONDITION_HPP