Home | History | Annotate | Download | only in default
      1 /* Copyright 2015 The TensorFlow Authors. All Rights Reserved.
      2 
      3 Licensed under the Apache License, Version 2.0 (the "License");
      4 you may not use this file except in compliance with the License.
      5 You may obtain a copy of the License at
      6 
      7     http://www.apache.org/licenses/LICENSE-2.0
      8 
      9 Unless required by applicable law or agreed to in writing, software
     10 distributed under the License is distributed on an "AS IS" BASIS,
     11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 See the License for the specific language governing permissions and
     13 limitations under the License.
     14 ==============================================================================*/
     15 
     16 #ifndef TENSORFLOW_PLATFORM_DEFAULT_MUTEX_H_
     17 #define TENSORFLOW_PLATFORM_DEFAULT_MUTEX_H_
     18 
     19 // IWYU pragma: private, include "third_party/tensorflow/core/platform/mutex.h"
     20 // IWYU pragma: friend third_party/tensorflow/core/platform/mutex.h
     21 
     22 #include <chrono>
     23 #include <condition_variable>
     24 #include <mutex>
     25 #include "nsync_cv.h"
     26 #include "nsync_mu.h"
     27 #include "tensorflow/core/platform/thread_annotations.h"
     28 namespace tensorflow {
     29 
     30 #undef mutex_lock
     31 
     32 enum LinkerInitialized { LINKER_INITIALIZED };
     33 
     34 class condition_variable;
     35 
     36 // Mimic std::mutex + C++17's shared_mutex, adding a LinkerInitialized
     37 // constructor interface.  This type is as fast as mutex, but is also a shared
     38 // lock.
     39 class LOCKABLE mutex {
     40  public:
     41   mutex() { nsync::nsync_mu_init(&mu_); }
     42   // The default implementation of nsync_mutex is safe to use after the linker
     43   // initializations
     44   explicit mutex(LinkerInitialized x) {}
     45 
     46   void lock() EXCLUSIVE_LOCK_FUNCTION() { nsync::nsync_mu_lock(&mu_); }
     47   bool try_lock() EXCLUSIVE_TRYLOCK_FUNCTION(true) {
     48     return nsync::nsync_mu_trylock(&mu_) != 0;
     49   };
     50   void unlock() UNLOCK_FUNCTION() { nsync::nsync_mu_unlock(&mu_); }
     51 
     52   void lock_shared() SHARED_LOCK_FUNCTION() { nsync::nsync_mu_rlock(&mu_); }
     53   bool try_lock_shared() SHARED_TRYLOCK_FUNCTION(true) {
     54     return nsync::nsync_mu_rtrylock(&mu_) != 0;
     55   };
     56   void unlock_shared() UNLOCK_FUNCTION() { nsync::nsync_mu_runlock(&mu_); }
     57 
     58  private:
     59   friend class condition_variable;
     60   nsync::nsync_mu mu_;
     61 };
     62 
     63 // Mimic a subset of the std::unique_lock<tensorflow::mutex> functionality.
     64 class SCOPED_LOCKABLE mutex_lock {
     65  public:
     66   typedef ::tensorflow::mutex mutex_type;
     67 
     68   explicit mutex_lock(mutex_type& mu) EXCLUSIVE_LOCK_FUNCTION(mu) : mu_(&mu) {
     69     mu_->lock();
     70   }
     71 
     72   mutex_lock(mutex_type& mu, std::try_to_lock_t) EXCLUSIVE_LOCK_FUNCTION(mu)
     73       : mu_(&mu) {
     74     if (!mu.try_lock()) {
     75       mu_ = nullptr;
     76     }
     77   }
     78 
     79   // Manually nulls out the source to prevent double-free.
     80   // (std::move does not null the source pointer by default.)
     81   explicit mutex_lock(mutex_lock&& ml) noexcept : mu_(ml.mu_) {
     82     ml.mu_ = nullptr;
     83   }
     84   ~mutex_lock() UNLOCK_FUNCTION() {
     85     if (mu_ != nullptr) {
     86       mu_->unlock();
     87     }
     88   }
     89   mutex_type* mutex() { return mu_; }
     90 
     91   operator bool() const { return mu_ != nullptr; }
     92 
     93  private:
     94   mutex_type* mu_;
     95 };
     96 
     97 // Catch bug where variable name is omitted, e.g. mutex_lock (mu);
     98 #define mutex_lock(x) static_assert(0, "mutex_lock_decl_missing_var_name");
     99 
    100 // Mimic a subset of the std::shared_lock<tensorflow::mutex> functionality.
    101 // Name chosen to minimise conflicts with the tf_shared_lock macro, below.
    102 class SCOPED_LOCKABLE tf_shared_lock {
    103  public:
    104   typedef ::tensorflow::mutex mutex_type;
    105 
    106   explicit tf_shared_lock(mutex_type& mu) SHARED_LOCK_FUNCTION(mu) : mu_(&mu) {
    107     mu_->lock_shared();
    108   }
    109 
    110   tf_shared_lock(mutex_type& mu, std::try_to_lock_t) SHARED_LOCK_FUNCTION(mu)
    111       : mu_(&mu) {
    112     if (!mu.try_lock_shared()) {
    113       mu_ = nullptr;
    114     }
    115   }
    116 
    117   // Manually nulls out the source to prevent double-free.
    118   // (std::move does not null the source pointer by default.)
    119   explicit tf_shared_lock(tf_shared_lock&& ml) noexcept : mu_(ml.mu_) {
    120     ml.mu_ = nullptr;
    121   }
    122   ~tf_shared_lock() UNLOCK_FUNCTION() {
    123     if (mu_ != nullptr) {
    124       mu_->unlock_shared();
    125     }
    126   }
    127   mutex_type* mutex() { return mu_; }
    128 
    129   operator bool() const { return mu_ != nullptr; }
    130 
    131  private:
    132   mutex_type* mu_;
    133 };
    134 
    135 // Catch bug where variable name is omitted, e.g. tf_shared_lock (mu);
    136 #define tf_shared_lock(x) \
    137   static_assert(0, "tf_shared_lock_decl_missing_var_name");
    138 
    139 // Mimic std::condition_variable.
    140 class condition_variable {
    141  public:
    142   condition_variable() { nsync::nsync_cv_init(&cv_); }
    143 
    144   void wait(mutex_lock& lock) {
    145     nsync::nsync_cv_wait(&cv_, &lock.mutex()->mu_);
    146   }
    147   template <class Rep, class Period>
    148   std::cv_status wait_for(mutex_lock& lock,
    149                           std::chrono::duration<Rep, Period> dur) {
    150     int r = nsync::nsync_cv_wait_with_deadline(
    151         &cv_, &lock.mutex()->mu_, std::chrono::system_clock::now() + dur,
    152         nullptr);
    153     return r ? std::cv_status::timeout : std::cv_status::no_timeout;
    154   }
    155   void notify_one() { nsync::nsync_cv_signal(&cv_); }
    156   void notify_all() { nsync::nsync_cv_broadcast(&cv_); }
    157 
    158  private:
    159   friend ConditionResult WaitForMilliseconds(mutex_lock* mu,
    160                                              condition_variable* cv, int64 ms);
    161   nsync::nsync_cv cv_;
    162 };
    163 
    164 inline ConditionResult WaitForMilliseconds(mutex_lock* mu,
    165                                            condition_variable* cv, int64 ms) {
    166   std::cv_status s = cv->wait_for(*mu, std::chrono::milliseconds(ms));
    167   return (s == std::cv_status::timeout) ? kCond_Timeout : kCond_MaybeNotified;
    168 }
    169 
    170 }  // namespace tensorflow
    171 
    172 #endif  // TENSORFLOW_PLATFORM_DEFAULT_MUTEX_H_
    173