2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
16 #ifndef incl_HPHP_TLS_POD_BAG_H_
17 #define incl_HPHP_TLS_POD_BAG_H_
21 #include <type_traits>
24 #include "hphp/util/assertions.h"
28 //////////////////////////////////////////////////////////////////////
31 * This class implements a small vector that is suitable for direct
32 * placement into thread local storage, to maintain small sets of POD
35 * The motivating case for this was the strong iterator association in
36 * tl_miter_table, so it's got a fairly specialized API. It has a
37 * concept of "unpopulated" slots of the vector, and has methods for
38 * finding slots that are currently unpopulated. This was pulled out
39 * in a separate class to it testable, not really to make it reusable.
41 * To work with this class, T must be at least 8 bytes, and whatever
42 * state the user of this class wants to put into T must involve
43 * having a non-zero initial 8 bytes, since this is how we decide
44 * whether a slot is populated.
46 template<class T
, class Allocator
>
49 sizeof(T
) >= sizeof(uint64_t),
50 "TlsPodBag expects at least 8 bytes per entry"
53 static constexpr uint32_t kInitialCap
= 4;
56 * No constructor or destructor. This is a POD, intended to be
57 * allocated by value in TLS. You have to destruct its elements
60 * Zeroing memory and then casting it to a TlsPodBag is guaranteed
61 * to produce a TLS vec where empty() is true.
65 * Query the population of the TlsPodBag.
67 bool empty() const { return !m_population
; }
68 uint32_t population() const { return m_population
; }
71 * Return a pointer to an unpopulated slot, growing if necessary.
72 * Before returning the pointer, this increments the population
73 * count---the caller must make some change to this element to cause
74 * its first uint64_t bytes to be non-zero.
76 T
* find_unpopulated() {
77 if (m_population
== m_capacity
) {
78 return realloc_find();
80 for (auto i
= uint32_t{0}; i
< m_capacity
; ++i
) {
81 if (unpopulated(&m_data
[i
])) {
90 * Find a populated slot, call a function on it, and then remove it.
93 void visit_to_remove(Fn fn
) {
95 for (auto i
= uint32_t{0}; i
< m_capacity
; ++i
) {
96 if (!unpopulated(&m_data
[i
])) {
98 std::memset(&m_data
[i
], 0, sizeof m_data
[i
]);
100 if (!m_population
) deallocate();
108 * Call a function for each slot in the container, regardless of
109 * whether it is populated or not.
112 void for_each(Fn fn
) {
113 for (auto i
= uint32_t{0}; i
< m_capacity
; ++i
) {
119 * Mark all slots that match a predicate as unpopulated. If the
120 * population count goes to zero after this, free the memory.
122 * Cond must not return true for an unpopulated slot, but it is
123 * called for unpopulated slots.
126 void release_if(Cond cond
) {
127 for (auto i
= uint32_t{0}; i
< m_capacity
; ++i
) {
128 if (cond(m_data
[i
])) {
129 std::memset(&m_data
[i
], 0, sizeof m_data
[i
]);
134 if (!m_population
) deallocate();
138 bool unpopulated(const T
* t
) const {
139 return *reinterpret_cast<const uint64_t*>(t
) == 0;
144 m_data
= Allocator
{}.allocate(kInitialCap
);
145 std::memset(m_data
, 0, sizeof *m_data
* kInitialCap
);
146 m_capacity
= kInitialCap
;
152 assert(m_population
== 0 && m_data
!= 0);
153 Allocator
{}.deallocate(m_data
, m_capacity
);
159 if (!m_capacity
) return allocate();
161 auto const oldcap
= m_capacity
;
162 auto const newcap
= oldcap
* 2;
163 auto const newdat
= Allocator
{}.allocate(newcap
);
164 std::copy(m_data
, m_data
+ oldcap
, newdat
);
165 std::memset(newdat
+ oldcap
, 0, oldcap
* sizeof *newdat
);
166 Allocator
{}.deallocate(m_data
, oldcap
);
170 return newdat
+ oldcap
;
175 uint32_t m_population
;
179 //////////////////////////////////////////////////////////////////////