libstdc++: Add POSIX variant of shared_timed_mutex.
authorTorvald Riegel <triegel@redhat.com>
Fri, 16 Jan 2015 13:22:00 +0000 (13:22 +0000)
committerTorvald Riegel <torvald@gcc.gnu.org>
Fri, 16 Jan 2015 13:22:00 +0000 (13:22 +0000)
* include/std/shared_mutex (shared_timed_mutex): Add POSIX-based
implementation.

From-SVN: r219737

libstdc++-v3/ChangeLog
libstdc++-v3/include/std/shared_mutex

index df009842437eef12b85e1ef9fa60757d130a8bc5..98b958961551755ab594bb3da223027d6f47bc42 100644 (file)
@@ -1,3 +1,8 @@
+2015-01-16  Torvald Riegel  <triegel@redhat.com>
+
+       * include/std/shared_mutex (shared_timed_mutex): Add POSIX-based
+       implementation.
+
 2015-01-13  Jonathan Wakely  <jwakely@redhat.com>
 
        PR libstdc++/64571
index 8bfede39878d984664a28e9c0b76149061c3764f..643768c6f8b27d57abac60127fcc2acc6d56ffa6 100644 (file)
@@ -57,6 +57,188 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   /// shared_timed_mutex
   class shared_timed_mutex
   {
+#if defined(__GTHREADS_CXX0X)
+    typedef chrono::system_clock       __clock_t;
+
+    pthread_rwlock_t                   _M_rwlock;
+
+  public:
+    shared_timed_mutex()
+    {
+      int __ret = pthread_rwlock_init(&_M_rwlock, NULL);
+      if (__ret == ENOMEM)
+       throw bad_alloc();
+      else if (__ret == EAGAIN)
+       __throw_system_error(int(errc::resource_unavailable_try_again));
+      else if (__ret == EPERM)
+       __throw_system_error(int(errc::operation_not_permitted));
+      // Errors not handled: EBUSY, EINVAL
+      _GLIBCXX_DEBUG_ASSERT(__ret == 0);
+    }
+
+    ~shared_timed_mutex()
+    {
+      int __ret __attribute((unused)) = pthread_rwlock_destroy(&_M_rwlock);
+      // Errors not handled: EBUSY, EINVAL
+      _GLIBCXX_DEBUG_ASSERT(__ret == 0);
+    }
+
+    shared_timed_mutex(const shared_timed_mutex&) = delete;
+    shared_timed_mutex& operator=(const shared_timed_mutex&) = delete;
+
+    // Exclusive ownership
+
+    void
+    lock()
+    {
+      int __ret = pthread_rwlock_wrlock(&_M_rwlock);
+      if (__ret == EDEADLK)
+       __throw_system_error(int(errc::resource_deadlock_would_occur));
+      // Errors not handled: EINVAL
+      _GLIBCXX_DEBUG_ASSERT(__ret == 0);
+    }
+
+    bool
+    try_lock()
+    {
+      int __ret = pthread_rwlock_trywrlock(&_M_rwlock);
+      if (__ret == EBUSY) return false;
+      // Errors not handled: EINVAL
+      _GLIBCXX_DEBUG_ASSERT(__ret == 0);
+      return true;
+    }
+
+    template<typename _Rep, typename _Period>
+      bool
+      try_lock_for(const chrono::duration<_Rep, _Period>& __rel_time)
+      {
+       return try_lock_until(__clock_t::now() + __rel_time);
+      }
+
+    template<typename _Duration>
+      bool
+      try_lock_until(const chrono::time_point<__clock_t, _Duration>& __atime)
+      {
+       auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
+       auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
+
+       __gthread_time_t __ts =
+         {
+           static_cast<std::time_t>(__s.time_since_epoch().count()),
+           static_cast<long>(__ns.count())
+         };
+
+       int __ret = pthread_rwlock_timedwrlock(&_M_rwlock, &__ts);
+       // On self-deadlock, we just fail to acquire the lock.  Technically,
+       // the program violated the precondition.
+       if (__ret == ETIMEDOUT || __ret == EDEADLK)
+         return false;
+       // Errors not handled: EINVAL
+       _GLIBCXX_DEBUG_ASSERT(__ret == 0);
+       return true;
+      }
+
+    template<typename _Clock, typename _Duration>
+      bool
+      try_lock_until(const chrono::time_point<_Clock, _Duration>& __abs_time)
+      {
+       // DR 887 - Sync unknown clock to known clock.
+       const typename _Clock::time_point __c_entry = _Clock::now();
+       const __clock_t::time_point __s_entry = __clock_t::now();
+       const auto __delta = __abs_time - __c_entry;
+       const auto __s_atime = __s_entry + __delta;
+       return try_lock_until(__s_atime);
+      }
+
+    void
+    unlock()
+    {
+      int __ret __attribute((unused)) = pthread_rwlock_unlock(&_M_rwlock);
+      // Errors not handled: EPERM, EBUSY, EINVAL
+      _GLIBCXX_DEBUG_ASSERT(__ret == 0);
+    }
+
+    // Shared ownership
+
+    void
+    lock_shared()
+    {
+      int __ret = pthread_rwlock_rdlock(&_M_rwlock);
+      if (__ret == EDEADLK)
+       __throw_system_error(int(errc::resource_deadlock_would_occur));
+      if (__ret == EAGAIN)
+       // Maximum number of read locks has been exceeded.
+       __throw_system_error(int(errc::device_or_resource_busy));
+      // Errors not handled: EINVAL
+      _GLIBCXX_DEBUG_ASSERT(__ret == 0);
+    }
+
+    bool
+    try_lock_shared()
+    {
+      int __ret = pthread_rwlock_tryrdlock(&_M_rwlock);
+      // If the maximum number of read locks has been exceeded, we just fail
+      // to acquire the lock.  Unlike for lock(), we are not allowed to throw
+      // an exception.
+      if (__ret == EBUSY || __ret == EAGAIN) return false;
+      // Errors not handled: EINVAL
+      _GLIBCXX_DEBUG_ASSERT(__ret == 0);
+      return true;
+    }
+
+    template<typename _Rep, typename _Period>
+      bool
+      try_lock_shared_for(const chrono::duration<_Rep, _Period>& __rel_time)
+      {
+       return try_lock_shared_until(__clock_t::now() + __rel_time);
+      }
+
+    template<typename _Duration>
+      bool
+      try_lock_shared_until(const chrono::time_point<__clock_t,
+                           _Duration>& __atime)
+      {
+       auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
+       auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
+
+       __gthread_time_t __ts =
+         {
+           static_cast<std::time_t>(__s.time_since_epoch().count()),
+           static_cast<long>(__ns.count())
+         };
+
+       int __ret = pthread_rwlock_timedrdlock(&_M_rwlock, &__ts);
+       // If the maximum number of read locks has been exceeded, or we would
+       // deadlock, we just fail to acquire the lock.  Unlike for lock(),
+       // we are not allowed to throw an exception.
+       if (__ret == ETIMEDOUT || __ret == EAGAIN || __ret == EDEADLK)
+         return false;
+       // Errors not handled: EINVAL
+       _GLIBCXX_DEBUG_ASSERT(__ret == 0);
+       return true;
+      }
+
+    template<typename _Clock, typename _Duration>
+      bool
+      try_lock_shared_until(const chrono::time_point<_Clock,
+                           _Duration>& __abs_time)
+      {
+       // DR 887 - Sync unknown clock to known clock.
+       const typename _Clock::time_point __c_entry = _Clock::now();
+       const __clock_t::time_point __s_entry = __clock_t::now();
+       const auto __delta = __abs_time - __c_entry;
+       const auto __s_atime = __s_entry + __delta;
+       return try_lock_shared_until(__s_atime);
+      }
+
+    void
+    unlock_shared()
+    {
+      unlock();
+    }
+
+#else // defined(__GTHREADS_CXX0X)
+
 #if _GTHREAD_USE_MUTEX_TIMEDLOCK
     struct _Mutex : mutex, __timed_mutex_impl<_Mutex>
     {
@@ -252,6 +434,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
            _M_gate1.notify_one();
        }
     }
+#endif // !defined(__GTHREADS_CXX0X)
   };
 #endif // _GLIBCXX_HAS_GTHREADS