logging: add new RateLimiter helper class
[folly.git] / folly / PackedSyncPtr.h
1 /*
2  * Copyright 2017 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 #pragma once
18
19 #include <folly/Portability.h>
20
21 #if !FOLLY_X64 && !FOLLY_PPC64 && !FOLLY_A64
22 # error "PackedSyncPtr is x64, ppc64 or aarch64 specific code."
23 #endif
24
25 /*
26  * An 8-byte pointer with an integrated spin lock and 15-bit integer
27  * (you can use this for a size of the allocation, if you want, or
28  * something else, or nothing).
29  *
30  * This is using an x64-specific detail about the effective virtual
31  * address space.  Long story short: the upper two bytes of all our
32  * pointers will be zero in reality---and if you have a couple billion
33  * such pointers in core, it makes pretty good sense to try to make
34  * use of that memory.  The exact details can be perused here:
35  *
36  *    http://en.wikipedia.org/wiki/X86-64#Canonical_form_addresses
37  *
38  * This is not a "smart" pointer: nothing automagical is going on
39  * here.  Locking is up to the user.  Resource deallocation is up to
40  * the user.  Locks are never acquired or released outside explicit
41  * calls to lock() and unlock().
42  *
43  * Change the value of the raw pointer with set(), but you must hold
44  * the lock when calling this function if multiple threads could be
45  * using this class.
46  *
47  * TODO(jdelong): should we use the low order bit for the lock, so we
48  * get a whole 16-bits for our integer?  (There's also 2 more bits
49  * down there if the pointer comes from malloc.)
50  *
51  * @author Spencer Ahrens <sahrens@fb.com>
52  * @author Jordan DeLong <delong.j@fb.com>
53  */
54
55 #include <folly/SmallLocks.h>
56 #include <type_traits>
57 #include <glog/logging.h>
58
59 namespace folly {
60
61 template<class T>
62 class PackedSyncPtr {
63   // This just allows using this class even with T=void.  Attempting
64   // to use the operator* or operator[] on a PackedSyncPtr<void> will
65   // still properly result in a compile error.
66   typedef typename std::add_lvalue_reference<T>::type reference;
67
68 public:
69   /*
70    * If you default construct one of these, you must call this init()
71    * function before using it.
72    *
73    * (We are avoiding a constructor to ensure gcc allows us to put
74    * this class in packed structures.)
75    */
76   void init(T* initialPtr = 0, uint16_t initialExtra = 0) {
77     auto intPtr = reinterpret_cast<uintptr_t>(initialPtr);
78     CHECK(!(intPtr >> 48));
79     data_.init(intPtr);
80     setExtra(initialExtra);
81   }
82
83   /*
84    * Sets a new pointer.  You must hold the lock when calling this
85    * function, or else be able to guarantee no other threads could be
86    * using this PackedSyncPtr<>.
87    */
88   void set(T* t) {
89     auto intPtr = reinterpret_cast<uintptr_t>(t);
90     auto shiftedExtra = uintptr_t(extra()) << 48;
91     CHECK(!(intPtr >> 48));
92     data_.setData(intPtr | shiftedExtra);
93   }
94
95   /*
96    * Get the pointer.
97    *
98    * You can call any of these without holding the lock, with the
99    * normal types of behavior you'll get on x64 from reading a pointer
100    * without locking.
101    */
102   T* get() const {
103     return reinterpret_cast<T*>(data_.getData() & (-1ull >> 16));
104   }
105   T* operator->() const { return get(); }
106   reference operator*() const { return *get(); }
107   reference operator[](std::ptrdiff_t i) const { return get()[i]; }
108
109   // Synchronization (logically const, even though this mutates our
110   // locked state: you can lock a const PackedSyncPtr<T> to read it).
111   void lock() const { data_.lock(); }
112   void unlock() const { data_.unlock(); }
113   bool try_lock() const { return data_.try_lock(); }
114
115   /*
116    * Access extra data stored in unused bytes of the pointer.
117    *
118    * It is ok to call this without holding the lock.
119    */
120   uint16_t extra() const {
121     return data_.getData() >> 48;
122   }
123
124   /*
125    * Don't try to put anything into this that has the high bit set:
126    * that's what we're using for the mutex.
127    *
128    * Don't call this without holding the lock.
129    */
130   void setExtra(uint16_t extra) {
131     CHECK(!(extra & 0x8000));
132     auto ptr = data_.getData() & (-1ull >> 16);
133     data_.setData((uintptr_t(extra) << 48) | ptr);
134   }
135
136  private:
137   PicoSpinLock<uintptr_t> data_;
138 } FOLLY_PACK_ATTR;
139
140 static_assert(
141     std::is_pod<PackedSyncPtr<void>>::value,
142     "PackedSyncPtr must be kept a POD type.");
143 static_assert(sizeof(PackedSyncPtr<void>) == 8,
144               "PackedSyncPtr should be only 8 bytes---something is "
145               "messed up");
146
147 template <typename T>
148 std::ostream& operator<<(std::ostream& os, const PackedSyncPtr<T>& ptr) {
149   os << "PackedSyncPtr(" << ptr.get() << ", " << ptr.extra() << ")";
150   return os;
151 }
152 }