proxygen
PackedSyncPtr.h
Go to the documentation of this file.
1 /*
2  * Copyright 2011-present 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 <type_traits>
20 
21 #include <glog/logging.h>
22 
23 #include <folly/Portability.h>
25 
26 #if !FOLLY_X64 && !FOLLY_PPC64 && !FOLLY_AARCH64
27 #error "PackedSyncPtr is x64, ppc64 or aarch64 specific code."
28 #endif
29 
30 /*
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).
34  *
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:
40  *
41  * http://en.wikipedia.org/wiki/X86-64#Canonical_form_addresses
42  *
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().
47  *
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
50  * using this class.
51  *
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.)
55  *
56  * @author Spencer Ahrens <sahrens@fb.com>
57  * @author Jordan DeLong <delong.j@fb.com>
58  */
59 
60 namespace folly {
61 
62 template <class T>
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.
68 
69  public:
70  /*
71  * If you default construct one of these, you must call this init()
72  * function before using it.
73  *
74  * (We are avoiding a constructor to ensure gcc allows us to put
75  * this class in packed structures.)
76  */
77  void init(T* initialPtr = nullptr, uint16_t initialExtra = 0) {
78  auto intPtr = reinterpret_cast<uintptr_t>(initialPtr);
79  CHECK(!(intPtr >> 48));
80  data_.init(intPtr);
81  setExtra(initialExtra);
82  }
83 
84  /*
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<>.
88  */
89  void set(T* t) {
90  auto intPtr = reinterpret_cast<uintptr_t>(t);
91  auto shiftedExtra = uintptr_t(extra()) << 48;
92  CHECK(!(intPtr >> 48));
93  data_.setData(intPtr | shiftedExtra);
94  }
95 
96  /*
97  * Get the pointer.
98  *
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
101  * without locking.
102  */
103  T* get() const {
104  return reinterpret_cast<T*>(data_.getData() & (-1ull >> 16));
105  }
106  T* operator->() const {
107  return get();
108  }
109  reference operator*() const {
110  return *get();
111  }
112  reference operator[](std::ptrdiff_t i) const {
113  return get()[i];
114  }
115 
116  // Synchronization (logically const, even though this mutates our
117  // locked state: you can lock a const PackedSyncPtr<T> to read it).
118  void lock() const {
119  data_.lock();
120  }
121  void unlock() const {
122  data_.unlock();
123  }
124  bool try_lock() const {
125  return data_.try_lock();
126  }
127 
128  /*
129  * Access extra data stored in unused bytes of the pointer.
130  *
131  * It is ok to call this without holding the lock.
132  */
133  uint16_t extra() const {
134  return data_.getData() >> 48;
135  }
136 
137  /*
138  * Don't try to put anything into this that has the high bit set:
139  * that's what we're using for the mutex.
140  *
141  * Don't call this without holding the lock.
142  */
144  CHECK(!(extra & 0x8000));
145  auto ptr = data_.getData() & (-1ull >> 16);
146  data_.setData((uintptr_t(extra) << 48) | ptr);
147  }
148 
149  private:
152 
153 static_assert(
154  std::is_pod<PackedSyncPtr<void>>::value,
155  "PackedSyncPtr must be kept a POD type.");
156 static_assert(
157  sizeof(PackedSyncPtr<void>) == 8,
158  "PackedSyncPtr should be only 8 bytes---something is "
159  "messed up");
160 
161 template <typename T>
162 std::ostream& operator<<(std::ostream& os, const PackedSyncPtr<T>& ptr) {
163  os << "PackedSyncPtr(" << ptr.get() << ", " << ptr.extra() << ")";
164  return os;
165 }
166 } // namespace folly
void * ptr
uint16_t extra() const
FOLLY_PACK_PUSH struct folly::Unaligned< T, typename std::enable_if< std::is_pod< T >::value >::type > FOLLY_PACK_ATTR
IntType getData() const
Definition: PicoSpinLock.h:111
void setExtra(uint16_t extra)
PskType type
void unlock() const
folly::std T
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
void init(IntType initialValue=0)
Definition: PicoSpinLock.h:96
reference operator[](std::ptrdiff_t i) const
void init(T *initialPtr=nullptr, uint16_t initialExtra=0)
Definition: PackedSyncPtr.h:77
PicoSpinLock< uintptr_t > data_
void unlock() const
Definition: PicoSpinLock.h:240
bool try_lock() const
Definition: PicoSpinLock.h:136
std::add_lvalue_reference< T >::type reference
Definition: PackedSyncPtr.h:67
reference operator*() const
void lock() const
Definition: PicoSpinLock.h:229
T * operator->() const
uint64_t value(const typename LockFreeRingBuffer< T, Atom >::Cursor &rbcursor)
void lock() const
bool try_lock() const
void setData(IntType w)
Definition: PicoSpinLock.h:124