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 +----------------------------------------------------------------------+
17 #ifndef incl_HPHP_RESOURCE_DATA_H_
18 #define incl_HPHP_RESOURCE_DATA_H_
21 #include "hphp/runtime/base/countable.h"
22 #include "hphp/runtime/base/sweepable.h"
23 #include "hphp/runtime/base/classname-is.h"
24 #include "hphp/runtime/base/req-ptr.h"
25 #include "hphp/runtime/base/memory-manager.h"
26 #include "hphp/util/thread-local.h"
35 template<class T
, class... Args
>
36 typename
std::enable_if
<std::is_convertible
<T
*,ResourceData
*>::value
,
42 * De-virtualized header for Resource objects. The memory layout is:
44 * [ResourceHdr] { HeapObject, m_id; }
45 * [ResourceData] { vtbl, subclass fields; }
47 * Historically, we only had ResourceData. To ease refactoring, we have
48 * pointer conversion utilities:
49 * ResourceHdr* ResourceData::hdr()
50 * ResourceData* ResourceHdr::data()
52 * ResourceData explicitly declares inc/decref functions that
53 * delegate to ResourceHdr, which allows req::ptr<T> in user code to
54 * continue doing transparent refcounting.
56 * Type-agnostic header access requires TypedValue (and Variant) to have a
57 * ResourceHdr* ptr in the m_data union. We also still need to cast &m_data
58 * to a Resource**, so Resource owns a req::ptr<Resourcebase>.
60 * Runtime and extension code typically will use req::ptr<T> where T extends
61 * ResourceData; these are interior pointers, but allow code to continue
62 * using ResourceData as the base of the virtual class hierarchy.
64 * In the JIT, SSATmps of type Res are ResourceHdr pointers.
66 struct ResourceHdr final
: Countable
, // aux stores heap size
67 type_scan::MarkCollectable
<ResourceHdr
> {
68 static void resetMaxId();
70 ALWAYS_INLINE
void decRefAndRelease() {
71 assert(kindIsValid());
72 if (decReleaseCheck()) release();
74 bool kindIsValid() const { return m_kind
== HeaderKind::Resource
; }
75 void release() noexcept
;
77 void init(uint16_t size
, type_scan::Index tyindex
) {
78 assert(type_scan::isKnownType(tyindex
));
79 initHeader_16(HeaderKind::Resource
, OneReference
, size
);
80 m_type_index
= tyindex
;
83 ResourceData
* data() {
84 assert(kindIsValid());
85 return reinterpret_cast<ResourceData
*>(this + 1);
87 const ResourceData
* data() const {
88 assert(kindIsValid());
89 return reinterpret_cast<const ResourceData
*>(this + 1);
92 size_t heapSize() const {
93 assert(kindIsValid());
98 type_scan::Index
typeIndex() const { return m_type_index
; }
100 int32_t getId() const { return m_id
; }
101 void setRawId(int32_t id
) { m_id
= id
; }
102 void setId(int32_t id
); // only for BuiltinFiles
105 static_assert(sizeof(type_scan::Index
) <= 4,
106 "type_scan::Index cannot be greater than 32-bits");
109 type_scan::Index m_type_index
;
113 * Base class of all PHP resources.
115 struct ResourceData
: type_scan::MarkCollectable
<ResourceData
> {
118 ResourceData(const ResourceData
&) = delete;
119 ResourceData
& operator=(const ResourceData
&) = delete;
121 const ResourceHdr
* hdr() const {
122 auto h
= reinterpret_cast<const ResourceHdr
*>(this) - 1;
123 assert(h
->kindIsValid());
127 auto h
= reinterpret_cast<ResourceHdr
*>(this) - 1;
128 assert(h
->kindIsValid());
132 // delegate refcount operations to base.
133 void incRefCount() const { hdr()->incRefCount(); }
134 void decRefAndRelease() { hdr()->decRefAndRelease(); }
135 bool hasExactlyOneRef() const { return hdr()->hasExactlyOneRef(); }
136 bool hasMultipleRefs() const { return hdr()->hasMultipleRefs(); }
137 int32_t getId() const { return hdr()->getId(); }
138 void setId(int32_t id
) { hdr()->setId(id
); }
140 virtual ~ResourceData(); // all PHP resources need vtables
142 void operator delete(void* /*p*/) { always_assert(false); }
144 const String
& o_getClassName() const;
145 virtual const String
& o_getClassNameHook() const;
146 virtual const String
& o_getResourceName() const;
147 virtual bool isInvalid() const { return false; }
149 template <typename T
>
150 bool instanceof() const { return dynamic_cast<const T
*>(this) != nullptr; }
152 bool o_toBoolean() const { return true; }
153 int64_t o_toInt64() const { return hdr()->getId(); }
154 double o_toDouble() const { return hdr()->getId(); }
155 String
o_toString() const;
156 Array
o_toArray() const;
159 template<class T
, class... Args
> friend
160 typename
std::enable_if
<std::is_convertible
<T
*,ResourceData
*>::value
,
161 req::ptr
<T
>>::type
req::make(Args
&&... args
);
164 inline void ResourceHdr::release() noexcept
{
165 assert(kindIsValid());
167 AARCH64_WALKABLE_FRAME();
170 inline ResourceData
* safedata(ResourceHdr
* hdr
) {
171 return hdr
? hdr
->data() : nullptr;
173 inline const ResourceData
* safedata(const ResourceHdr
* hdr
) {
174 return hdr
? hdr
->data() : nullptr;
176 inline ResourceHdr
* safehdr(ResourceData
* data
) {
177 return data
? data
->hdr() : nullptr;
179 inline const ResourceHdr
* safehdr(const ResourceData
* data
) {
180 return data
? data
->hdr() : nullptr;
184 * Rules to avoid memory problems/leaks from ResourceData classes
185 * ==============================================================
187 * 1. If a ResourceData is entirely request-allocated, for example,
189 * struct EntirelyRequestAllocated : ResourceData {
190 * int number; // primitives are allocated together with "this"
191 * String str; // request-allocated objects are fine
194 * Then, the best choice is to use this macro to make sure the object
195 * is always collected during request shutdown time:
197 * DECLARE_RESOURCE_ALLOCATION_NO_SWEEP(T);
199 * This object doesn't participate in sweep(), as object allocator doesn't
200 * have any callback installed.
202 * 2. If a ResourceData is entirely not request allocated, for example,
204 * struct NonRequestAllocated : SweepableResourceData {
205 * int number; // primitives are always not in consideration
206 * std::string str; // this has malloc() in its own
207 * std::vector<int> vec; // all STL collection classes belong here
208 * HANDLE ptr; // raw pointers that need to be free-d somehow
211 * Then it has to derive from SweepableResourceData, so sweep() will be
212 * called. By default, it will call this object's destructor automatically,
213 * so everything will be free-d.
215 * When deriving from SweepableResourceData, either "new" or "NEW" can
216 * be used, but we prefer people use NEW with these macros:
218 * DECLARE_RESOURCE_ALLOCATION(T);
219 * IMPLEMENT_RESOURCE_ALLOCATION(T);
221 * 3. If a ResourceData is a mix of request allocated data members and globally
222 * allocated data members, sweep() has to be overwritten to only free
223 * the globally allocated members. This is because request-allocated
224 * data members may have their own sweep() defined to destruct, and another
225 * destruction from this ResourceData's default sweep() will cause double-
226 * free problems on these request-allocated data members.
228 * This means, std::vector<String> is almost always wrong, because there is
229 * no way to free up vector's memory without touching String, which is
232 * struct MixedRequestAllocated : SweepableResourceData {
233 * int number; // primitives are always not in consideration
235 * // STL classes need to new/delete to have clean sweep
236 * std::string *stdstr;
237 * std::vector<int> *vec;
239 * HANDLE ptr; // raw pointers that need to be free-d somehow
240 * String str; // request-allocated objects are fine
242 * DECLARE_RESOURCE_ALLOCATION(T);
244 * void MixedRequestAllocated::sweep() {
248 * // without doing anything with Strings, Arrays, or Objects
252 struct SweepableResourceData
: ResourceData
, Sweepable
{
254 void* owner() override
{
255 return static_cast<ResourceData
*>(this)->hdr();
259 ///////////////////////////////////////////////////////////////////////////////
261 ALWAYS_INLINE
void decRefRes(ResourceData
* res
) {
262 res
->hdr()->decRefAndRelease();
264 ALWAYS_INLINE
void decRefRes(ResourceHdr
* res
) {
265 res
->decRefAndRelease();
268 #define DECLARE_RESOURCE_ALLOCATION_NO_SWEEP(T) \
270 ALWAYS_INLINE void operator delete(void* p) { \
271 static_assert(std::is_base_of<ResourceData,T>::value, ""); \
272 constexpr auto size = sizeof(ResourceHdr) + sizeof(T); \
273 auto h = static_cast<ResourceData*>(p)->hdr(); \
274 assert(h->heapSize() == size); \
275 tl_heap->objFree(h, size); \
278 #define DECLARE_RESOURCE_ALLOCATION(T) \
279 DECLARE_RESOURCE_ALLOCATION_NO_SWEEP(T) \
280 void sweep() override;
282 #define IMPLEMENT_RESOURCE_ALLOCATION_NS(NS, T) \
283 static_assert(std::is_base_of<HPHP::ResourceData,NS::T>::value, ""); \
284 void NS::T::sweep() { this->~T(); }
286 #define IMPLEMENT_RESOURCE_ALLOCATION(T) \
287 IMPLEMENT_RESOURCE_ALLOCATION_NS(HPHP, T)
290 // allocate and construct a resource subclass type T,
291 // wrapped in a req::ptr<T>
292 template<class T
, class... Args
>
293 typename
std::enable_if
<
294 std::is_convertible
<T
*, ResourceData
*>::value
,
296 >::type
make(Args
&&... args
) {
297 constexpr auto size
= sizeof(ResourceHdr
) + sizeof(T
);
298 static_assert(uint16_t(size
) == size
, "size must fit in 16 bits");
299 static_assert(std::is_convertible
<T
*,ResourceData
*>::value
, "");
300 auto const b
= static_cast<ResourceHdr
*>(tl_heap
->objMalloc(size
));
301 // initialize HeapObject
302 b
->init(size
, type_scan::getIndexForMalloc
<T
>());
304 auto r
= new (b
->data()) T(std::forward
<Args
>(args
)...);
305 assert(r
->hasExactlyOneRef());
306 return req::ptr
<T
>::attach(r
);
308 tl_heap
->objFree(b
, size
);
314 ///////////////////////////////////////////////////////////////////////////////
317 #endif // incl_HPHP_RESOURCE_DATA_H_