1 /**
2  * @file   rwlock.h
3  *
4  * @section LICENSE
5  *
6  * The MIT License
7  *
8  * @copyright Copyright (c) 2021 TileDB, Inc.
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a copy
11  * of this software and associated documentation files (the "Software"), to deal
12  * in the Software without restriction, including without limitation the rights
13  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14  * copies of the Software, and to permit persons to whom the Software is
15  * furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included in
18  * all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26  * THE SOFTWARE.
27  *
28  * @section DESCRIPTION
29  *
30  * This file defines and implements a write-preferring read-write lock.
31  */
32 
33 #ifndef TILEDB_RWLOCK_H
34 #define TILEDB_RWLOCK_H
35 
36 #include <condition_variable>
37 #include <mutex>
38 
39 #include "tiledb/common/macros.h"
40 
41 namespace tiledb {
42 namespace common {
43 
44 class RWLock {
45  public:
46   /* ********************************* */
47   /*     CONSTRUCTORS & DESTRUCTORS    */
48   /* ********************************* */
49 
50   /** Default constructor. */
RWLock()51   RWLock()
52       : writer_(false)
53       , waiting_writers_(0)
54       , readers_(0) {
55   }
56 
57   /** Default destructor. */
58   ~RWLock() = default;
59 
60   DISABLE_COPY_AND_COPY_ASSIGN(RWLock);
61   DISABLE_MOVE_AND_MOVE_ASSIGN(RWLock);
62 
63   /* ********************************* */
64   /*                API                */
65   /* ********************************* */
66 
67   /** Blocks until acquiring a read lock. */
read_lock()68   void read_lock() {
69     std::unique_lock<std::mutex> ul(mutex_);
70     while (waiting_writers_ > 0 || writer_)
71       cv_.wait(ul);
72     ++readers_;
73   }
74 
75   /**
76    * Releases a read lock, must be called after
77    * `read_lock`.
78    */
read_unlock()79   void read_unlock() {
80     std::unique_lock<std::mutex> ul(mutex_);
81     if (--readers_ == 0)
82       cv_.notify_all();
83   }
84 
85   /** Blocks until acquiring a write lock. */
write_lock()86   void write_lock() {
87     std::unique_lock<std::mutex> ul(mutex_);
88     ++waiting_writers_;
89     while (writer_ || readers_ > 0)
90       cv_.wait(ul);
91     --waiting_writers_;
92     writer_ = true;
93   }
94 
95   /**
96    * Releases a write lock, must be called after
97    * `write_lock`.
98    */
write_unlock()99   void write_unlock() {
100     std::unique_lock<std::mutex> ul(mutex_);
101     writer_ = false;
102     cv_.notify_all();
103   }
104 
105   /** Synchronously downgrades a write lock to a read lock. */
write_downgrade()106   void write_downgrade() {
107     std::unique_lock<std::mutex> ul(mutex_);
108     ++readers_;
109     writer_ = false;
110     cv_.notify_all();
111   }
112 
113  private:
114   /* ********************************* */
115   /*         PRIVATE ATTRIBUTES        */
116   /* ********************************* */
117 
118   /** Protects all member variables. */
119   std::mutex mutex_;
120 
121   /** Conditional variable to signal lock activity. */
122   std::condition_variable cv_;
123 
124   /** True if a write lock is held. */
125   bool writer_;
126 
127   /** The number of writers waiting in `write_lock`. */
128   uint64_t waiting_writers_;
129 
130   /** The number of outstanding read locks. */
131   uint64_t readers_;
132 };
133 
134 }  // namespace common
135 }  // namespace tiledb
136 
137 #endif  // TILEDB_RWLOCK_H
138