Harden failure signal handler in the face of memory corruptions
[folly.git] / folly / SmallLocks.h
1 /*
2  * Copyright 2014 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #ifndef FOLLY_SMALLLOCKS_H_
18 #define FOLLY_SMALLLOCKS_H_
19
20 /*
21  * This header defines a few very small mutex types.  These are useful
22  * in highly memory-constrained environments where contention is
23  * unlikely.
24  *
25  * Note: these locks are for use when you aren't likely to contend on
26  * the critical section, or when the critical section is incredibly
27  * small.  Given that, both of the locks defined in this header are
28  * inherently unfair: that is, the longer a thread is waiting, the
29  * longer it waits between attempts to acquire, so newer waiters are
30  * more likely to get the mutex.  For the intended use-case this is
31  * fine.
32  *
33  * @author Keith Adams <kma@fb.com>
34  * @author Jordan DeLong <delong.j@fb.com>
35  */
36
37 #include <array>
38 #include <cinttypes>
39 #include <type_traits>
40 #include <ctime>
41 #include <boost/noncopyable.hpp>
42 #include <cstdlib>
43 #include <pthread.h>
44 #include <mutex>
45
46 #include <glog/logging.h>
47
48 #ifndef __x86_64__
49 # error "SmallLocks.h is currently x64-only."
50 #endif
51
52 #include "folly/Portability.h"
53
54 namespace folly {
55
56 //////////////////////////////////////////////////////////////////////
57
58 namespace detail {
59
60   /*
61    * A helper object for the condended case. Starts off with eager
62    * spinning, and falls back to sleeping for small quantums.
63    */
64   class Sleeper {
65     static const uint32_t kMaxActiveSpin = 4000;
66
67     uint32_t spinCount;
68
69   public:
70     Sleeper() : spinCount(0) {}
71
72     void wait() {
73       if (spinCount < kMaxActiveSpin) {
74         ++spinCount;
75         asm volatile("pause");
76       } else {
77         /*
78          * Always sleep 0.5ms, assuming this will make the kernel put
79          * us down for whatever its minimum timer resolution is (in
80          * linux this varies by kernel version from 1ms to 10ms).
81          */
82         struct timespec ts = { 0, 500000 };
83         nanosleep(&ts, NULL);
84       }
85     }
86   };
87
88 }
89
90 //////////////////////////////////////////////////////////////////////
91
92 /*
93  * A really, *really* small spinlock for fine-grained locking of lots
94  * of teeny-tiny data.
95  *
96  * Zero initializing these is guaranteed to be as good as calling
97  * init(), since the free state is guaranteed to be all-bits zero.
98  *
99  * This class should be kept a POD, so we can used it in other packed
100  * structs (gcc does not allow __attribute__((packed)) on structs that
101  * contain non-POD data).  This means avoid adding a constructor, or
102  * making some members private, etc.
103  */
104 struct MicroSpinLock {
105   enum { FREE = 0, LOCKED = 1 };
106   uint8_t lock_;
107
108   /*
109    * Atomically move lock_ from "compare" to "newval". Return boolean
110    * success. Do not play on or around.
111    */
112   bool cas(uint8_t compare, uint8_t newVal) {
113     bool out;
114     bool memVal; // only set if the cmpxchg fails
115     asm volatile("lock; cmpxchgb %[newVal], (%[lockPtr]);"
116                  "setz %[output];"
117                  : [output] "=r" (out), "=a" (memVal)
118                  : "a" (compare), // cmpxchgb constrains this to be in %al
119                    [newVal] "q" (newVal),  // Needs to be byte-accessible
120                    [lockPtr] "r" (&lock_)
121                  : "memory", "flags");
122     return out;
123   }
124
125   // Initialize this MSL.  It is unnecessary to call this if you
126   // zero-initialize the MicroSpinLock.
127   void init() {
128     lock_ = FREE;
129   }
130
131   bool try_lock() {
132     return cas(FREE, LOCKED);
133   }
134
135   void lock() {
136     detail::Sleeper sleeper;
137     do {
138       while (lock_ != FREE) {
139         asm volatile("" : : : "memory");
140         sleeper.wait();
141       }
142     } while (!try_lock());
143     DCHECK(lock_ == LOCKED);
144   }
145
146   void unlock() {
147     CHECK(lock_ == LOCKED);
148     asm volatile("" : : : "memory");
149     lock_ = FREE; // release barrier on x86
150   }
151 };
152
153 //////////////////////////////////////////////////////////////////////
154
155 /*
156  * Spin lock on a single bit in an integral type.  You can use this
157  * with 16, 32, or 64-bit integral types.
158  *
159  * This is useful if you want a small lock and already have an int
160  * with a bit in it that you aren't using.  But note that it can't be
161  * as small as MicroSpinLock (1 byte), if you don't already have a
162  * convenient int with an unused bit lying around to put it on.
163  *
164  * To construct these, either use init() or zero initialize.  We don't
165  * have a real constructor because we want this to be a POD type so we
166  * can put it into packed structs.
167  */
168 template<class IntType, int Bit = sizeof(IntType) * 8 - 1>
169 struct PicoSpinLock {
170   // Internally we deal with the unsigned version of the type.
171   typedef typename std::make_unsigned<IntType>::type UIntType;
172
173   static_assert(std::is_integral<IntType>::value,
174                 "PicoSpinLock needs an integral type");
175   static_assert(sizeof(IntType) == 2 || sizeof(IntType) == 4 ||
176                   sizeof(IntType) == 8,
177                 "PicoSpinLock can't work on integers smaller than 2 bytes");
178
179  public:
180   static const UIntType kLockBitMask_ = UIntType(1) << Bit;
181   UIntType lock_;
182
183   /*
184    * You must call this function before using this class, if you
185    * default constructed it.  If you zero-initialized it you can
186    * assume the PicoSpinLock is in a valid unlocked state with
187    * getData() == 0.
188    *
189    * (This doesn't use a constructor because we want to be a POD.)
190    */
191   void init(IntType initialValue = 0) {
192     CHECK(!(initialValue & kLockBitMask_));
193     lock_ = initialValue;
194   }
195
196   /*
197    * Returns the value of the integer we using for our lock, except
198    * with the bit we are using as a lock cleared, regardless of
199    * whether the lock is held.
200    *
201    * It is 'safe' to call this without holding the lock.  (As in: you
202    * get the same guarantees for simultaneous accesses to an integer
203    * as you normally get.)
204    */
205   IntType getData() const {
206     return static_cast<IntType>(lock_ & ~kLockBitMask_);
207   }
208
209   /*
210    * Set the value of the other bits in our integer.
211    *
212    * Don't use this when you aren't holding the lock, unless it can be
213    * guaranteed that no other threads may be trying to use this.
214    */
215   void setData(IntType w) {
216     CHECK(!(w & kLockBitMask_));
217     lock_ = (lock_ & kLockBitMask_) | w;
218   }
219
220   /*
221    * Try to get the lock without blocking: returns whether or not we
222    * got it.
223    */
224   bool try_lock() const {
225     bool ret = false;
226
227 #define FB_DOBTS(size)                                  \
228   asm volatile("lock; bts" #size " %1, (%2); setnc %0"  \
229                : "=r" (ret)                             \
230                : "i" (Bit),                             \
231                  "r" (&lock_)                           \
232                : "memory", "flags")
233
234     switch (sizeof(IntType)) {
235     case 2: FB_DOBTS(w); break;
236     case 4: FB_DOBTS(l); break;
237     case 8: FB_DOBTS(q); break;
238     }
239
240 #undef FB_DOBTS
241
242     return ret;
243   }
244
245   /*
246    * Block until we can acquire the lock.  Uses Sleeper to wait.
247    */
248   void lock() const {
249     detail::Sleeper sleeper;
250     while (!try_lock()) {
251       sleeper.wait();
252     }
253   }
254
255   /*
256    * Release the lock, without changing the value of the rest of the
257    * integer.
258    */
259   void unlock() const {
260 #define FB_DOBTR(size)                          \
261   asm volatile("lock; btr" #size " %0, (%1)"    \
262                :                                \
263                : "i" (Bit),                     \
264                  "r" (&lock_)                   \
265                : "memory", "flags")
266
267
268     // Reads and writes can not be reordered wrt locked instructions,
269     // so we don't need a memory fence here.
270     switch (sizeof(IntType)) {
271     case 2: FB_DOBTR(w); break;
272     case 4: FB_DOBTR(l); break;
273     case 8: FB_DOBTR(q); break;
274     }
275
276 #undef FB_DOBTR
277   }
278 };
279
280 //////////////////////////////////////////////////////////////////////
281
282 /**
283  * Array of spinlocks where each one is padded to prevent false sharing.
284  * Useful for shard-based locking implementations in environments where
285  * contention is unlikely.
286  */
287
288 // TODO: generate it from configure (`getconf LEVEL1_DCACHE_LINESIZE`)
289 #define FOLLY_CACHE_LINE_SIZE 64
290
291 template <class T, size_t N>
292 struct SpinLockArray {
293   T& operator[](size_t i) {
294     return data_[i].lock;
295   }
296
297   const T& operator[](size_t i) const {
298     return data_[i].lock;
299   }
300
301   constexpr size_t size() const { return N; }
302
303  private:
304   struct PaddedSpinLock {
305     PaddedSpinLock() : lock() { }
306     T lock;
307     char padding[FOLLY_CACHE_LINE_SIZE - sizeof(T)];
308   };
309   static_assert(sizeof(PaddedSpinLock) == FOLLY_CACHE_LINE_SIZE,
310                 "Invalid size of PaddedSpinLock");
311
312   // Check if T can theoretically cross a cache line.
313   // NOTE: It should be alignof(std::max_align_t), but max_align_t
314   // isn't supported by gcc 4.6.2.
315   static_assert(alignof(MaxAlign) > 0 &&
316                 FOLLY_CACHE_LINE_SIZE % alignof(MaxAlign) == 0 &&
317                 sizeof(T) <= alignof(MaxAlign),
318                 "T can cross cache line boundaries");
319
320   char padding_[FOLLY_CACHE_LINE_SIZE];
321   std::array<PaddedSpinLock, N> data_;
322 } __attribute__((aligned));
323
324 //////////////////////////////////////////////////////////////////////
325
326 typedef std::lock_guard<MicroSpinLock> MSLGuard;
327
328 //////////////////////////////////////////////////////////////////////
329
330 }
331
332 #endif