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_RUNTIME_VM_RESUMABLE_H_
18 #define incl_HPHP_RUNTIME_VM_RESUMABLE_H_
20 #include "hphp/runtime/vm/act-rec.h"
21 #include "hphp/runtime/vm/bytecode.h"
22 #include "hphp/runtime/vm/func.h"
23 #include "hphp/runtime/vm/native-data.h"
24 #include "hphp/runtime/vm/jit/types.h"
28 //////////////////////////////////////////////////////////////////////
31 * Indicates how a function got resumed.
33 enum class ResumeMode
: uint8_t {
34 // The function was regularly called and its frame is located on the stack.
35 // This is the only valid mode for regular (non-async, non-generator)
36 // functions. Async functions are executed in this mode (also called eager
37 // execution) until reaching the first blocking await statement. Generators
38 // and async generators run in this mode only during argument type enforcement
39 // and suspend their execution immediately afterwards using the CreateCont
43 // Execution of the function was resumed by the asio scheduler upon completion
44 // of the awaited WaitHandle. Frame of the function is stored on the heap
45 // colocated with the Resumable structure. Valid only for async functions and
46 // async generators. An AsyncGeneratorWaitHandle is associated with async
50 // Execution of the function was resumed by generator iteration (e.g. by
51 // calling next() or send()). Frame of the function is stored on the heap
52 // colocated with the Resumable structure. Valid only for generators and
53 // async generators. Async generators are considered to be eagerly executed
54 // in this mode and don't have any associated AsyncGeneratorWaitHandle.
58 char* resumeModeShortName(ResumeMode resumeMode
);
60 ResumeMode
resumeModeFromActRecImpl(ActRec
* ar
);
61 ALWAYS_INLINE ResumeMode
resumeModeFromActRec(ActRec
* ar
) {
62 if (LIKELY(!ar
->resumed())) return ResumeMode::None
;
63 return resumeModeFromActRecImpl(ar
);
67 * Header of the resumable frame used by async functions:
69 * NativeNode* -> +--------------------------------+ low address
70 * | kind=AsyncFuncFrame |
71 * +--------------------------------+
72 * | Function locals and iterators |
73 * Resumable* -> +--------------------------------+
74 * | ActRec in Resumable |
75 * +--------------------------------+
76 * | Rest of Resumable |
77 * ObjectData* -> +--------------------------------+
78 * | c_AsyncFuncWaitHandle |
79 * +--------------------------------+ high address
81 * Header of the native frame used by generators:
83 * NativeNode* -> +--------------------------------+ low address
85 * +--------------------------------+
86 * | Function locals and iterators |
87 * BaseGenerator* -> +--------------------------------+
88 * < NativeData > | ActRec in Resumable |
89 * +--------------------------------+
90 * | Rest of Resumable |
91 * +--------------------------------+
92 * | Rest of [Async]Generator |
93 * ObjectData* -> +--------------------------------+
95 * +--------------------------------+ high address
97 struct alignas(16) Resumable
{
98 // This function is used only by AFWH, temporary till AFWH is converted to HNI
99 static Resumable
* FromObj(ObjectData
* obj
) {
100 return reinterpret_cast<Resumable
*>(obj
) - 1;
102 static const Resumable
* FromObj(const ObjectData
* obj
) {
103 return reinterpret_cast<const Resumable
*>(obj
) - 1;
105 static constexpr ptrdiff_t arOff() {
106 return offsetof(Resumable
, m_actRec
);
108 static constexpr ptrdiff_t resumeAddrOff() {
109 return offsetof(Resumable
, m_resumeAddr
);
111 static constexpr ptrdiff_t resumeOffsetOff() {
112 return offsetof(Resumable
, m_resumeOffset
);
114 static constexpr ptrdiff_t dataOff() {
115 return sizeof(Resumable
);
117 static constexpr size_t getFrameSize(size_t numSlots
) {
118 return numSlots
* sizeof(TypedValue
);
121 // This function is temporary till we move AFWH to HNI
122 static Resumable
* Create(size_t frameSize
, size_t totalSize
) {
124 (void)type_scan::getIndexForMalloc
<ActRec
>();
125 auto node
= new (tl_heap
->objMalloc(totalSize
))
126 NativeNode(HeaderKind::AsyncFuncFrame
,
127 sizeof(NativeNode
) + frameSize
+ sizeof(Resumable
));
128 auto frame
= reinterpret_cast<char*>(node
+ 1);
129 return reinterpret_cast<Resumable
*>(frame
+ frameSize
);
133 bool mayUseVV
= true>
134 void initialize(const ActRec
* fp
, jit::TCA resumeAddr
,
135 Offset resumeOffset
, size_t frameSize
, size_t totalSize
) {
137 assertx(fp
->resumed() == clone
);
138 auto const func
= fp
->func();
140 assertx(func
->isResumable());
141 assertx(func
->contains(resumeOffset
));
142 // Check memory alignment
143 assertx((((uintptr_t) actRec()) & (sizeof(Cell
) - 1)) == 0);
146 // Copy ActRec, locals and iterators
147 auto src
= reinterpret_cast<const char*>(fp
) - frameSize
;
148 auto dst
= reinterpret_cast<char*>(actRec()) - frameSize
;
149 wordcpy(dst
, src
, frameSize
+ sizeof(ActRec
));
152 actRec()->setResumed();
154 // Suspend VarEnv if needed
155 assertx(mayUseVV
|| !(func
->attrs() & AttrMayUseVV
));
157 UNLIKELY(func
->attrs() & AttrMayUseVV
) &&
158 UNLIKELY(fp
->hasVarEnv())) {
159 fp
->getVarEnv()->suspend(fp
, actRec());
162 // If we are cloning a Resumable, only copy the ActRec. The
163 // caller will take care of copying locals, setting the VarEnv, etc.
164 // When called from AFWH::Create or Generator::Create we know we are
165 // going to overwrite m_sfp and m_savedRip, so don't copy them here.
166 auto src
= reinterpret_cast<const char*>(fp
);
167 auto dst
= reinterpret_cast<char*>(actRec());
168 const size_t offset
= offsetof(ActRec
, m_func
);
169 wordcpy(dst
+ offset
, src
+ offset
, sizeof(ActRec
) - offset
);
172 // Populate Resumable.
173 m_resumeAddr
= resumeAddr
;
174 m_offsetAndSize
= (totalSize
<< 32 | resumeOffset
);
177 template<class T
> static void Destroy(size_t size
, T
* obj
) {
178 auto const base
= reinterpret_cast<char*>(obj
+ 1) - size
;
180 tl_heap
->objFree(base
, size
);
183 ActRec
* actRec() { return &m_actRec
; }
184 const ActRec
* actRec() const { return &m_actRec
; }
185 jit::TCA
resumeAddr() const { return m_resumeAddr
; }
186 Offset
resumeOffset() const {
187 assertx(m_actRec
.func()->contains(m_resumeOffset
));
188 return m_resumeOffset
;
190 size_t size() const { return m_size
; }
192 void setResumeAddr(jit::TCA resumeAddr
, Offset resumeOffset
) {
193 assertx(m_actRec
.func()->contains(resumeOffset
));
194 m_resumeAddr
= resumeAddr
;
195 m_resumeOffset
= resumeOffset
;
199 // ActRec of the resumed frame.
203 jit::TCA m_resumeAddr
;
205 // Resume offset: bytecode offset from start of Unit's bytecode.
208 Offset m_resumeOffset
;
210 // Size of the memory block that includes this resumable.
213 uint64_t m_offsetAndSize
;
217 static_assert(Resumable::arOff() == 0,
218 "ActRec must be in the beginning of Resumable");
220 //////////////////////////////////////////////////////////////////////