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