@@ -55,6 +55,244 @@ using ::v8::Value;
55
55
using ::v8::V8;
56
56
57
57
58
+ namespace {
59
+
60
+ class DeoptimizeCodeThread : public v8 ::base::Thread {
61
+ public:
62
+ DeoptimizeCodeThread (v8::Isolate* isolate, v8::Local<v8::Context> context,
63
+ const char * trigger)
64
+ : Thread(Options(" DeoptimizeCodeThread" )),
65
+ isolate_ (isolate),
66
+ context_(isolate, context),
67
+ source_(trigger) {}
68
+
69
+ void Run () {
70
+ v8::Locker locker (isolate_);
71
+ isolate_->Enter ();
72
+ v8::HandleScope handle_scope (isolate_);
73
+ v8::Local<v8::Context> context =
74
+ v8::Local<v8::Context>::New (isolate_, context_);
75
+ v8::Context::Scope context_scope (context);
76
+ CHECK_EQ (isolate_, v8::Isolate::GetCurrent ());
77
+ // This code triggers deoptimization of some function that will be
78
+ // used in a different thread.
79
+ CompileRun (source_);
80
+ isolate_->Exit ();
81
+ }
82
+
83
+ private:
84
+ v8::Isolate* isolate_;
85
+ Persistent<v8::Context> context_;
86
+ // The code that triggers the deoptimization.
87
+ const char * source_;
88
+ };
89
+
90
+ void UnlockForDeoptimization (const v8::FunctionCallbackInfo<v8::Value>& args) {
91
+ v8::Isolate* isolate = v8::Isolate::GetCurrent ();
92
+ // Gets the pointer to the thread that will trigger the deoptimization of the
93
+ // code.
94
+ DeoptimizeCodeThread* deoptimizer =
95
+ reinterpret_cast <DeoptimizeCodeThread*>(isolate->GetData (0 ));
96
+ {
97
+ // Exits and unlocks the isolate.
98
+ isolate->Exit ();
99
+ v8::Unlocker unlocker (isolate);
100
+ // Starts the deoptimizing thread.
101
+ deoptimizer->Start ();
102
+ // Waits for deoptimization to finish.
103
+ deoptimizer->Join ();
104
+ }
105
+ // The deoptimizing thread has finished its work, and the isolate
106
+ // will now be used by the current thread.
107
+ isolate->Enter ();
108
+ }
109
+
110
+ void UnlockForDeoptimizationIfReady (
111
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
112
+ v8::Isolate* isolate = v8::Isolate::GetCurrent ();
113
+ bool * ready_to_deoptimize = reinterpret_cast <bool *>(isolate->GetData (1 ));
114
+ if (*ready_to_deoptimize) {
115
+ // The test should enter here only once, so put the flag back to false.
116
+ *ready_to_deoptimize = false ;
117
+ // Gets the pointer to the thread that will trigger the deoptimization of
118
+ // the code.
119
+ DeoptimizeCodeThread* deoptimizer =
120
+ reinterpret_cast <DeoptimizeCodeThread*>(isolate->GetData (0 ));
121
+ {
122
+ // Exits and unlocks the thread.
123
+ isolate->Exit ();
124
+ v8::Unlocker unlocker (isolate);
125
+ // Starts the thread that deoptimizes the function.
126
+ deoptimizer->Start ();
127
+ // Waits for the deoptimizing thread to finish.
128
+ deoptimizer->Join ();
129
+ }
130
+ // The deoptimizing thread has finished its work, and the isolate
131
+ // will now be used by the current thread.
132
+ isolate->Enter ();
133
+ }
134
+ }
135
+ } // namespace
136
+
137
+ TEST (LazyDeoptimizationMultithread) {
138
+ i::FLAG_allow_natives_syntax = true ;
139
+ v8::Isolate::CreateParams create_params;
140
+ create_params.array_buffer_allocator = CcTest::array_buffer_allocator ();
141
+ v8::Isolate* isolate = v8::Isolate::New (create_params);
142
+ {
143
+ v8::Locker locker (isolate);
144
+ v8::Isolate::Scope isolate_scope (isolate);
145
+ v8::HandleScope scope (isolate);
146
+ v8::Local<v8::Context> context = v8::Context::New (isolate);
147
+ const char * trigger_deopt = " obj = { y: 0, x: 1 };" ;
148
+
149
+ // We use the isolate to pass arguments to the UnlockForDeoptimization
150
+ // function. Namely, we pass a pointer to the deoptimizing thread.
151
+ DeoptimizeCodeThread deoptimize_thread (isolate, context, trigger_deopt);
152
+ isolate->SetData (0 , &deoptimize_thread);
153
+ v8::Context::Scope context_scope (context);
154
+
155
+ // Create the function templace for C++ code that is invoked from
156
+ // JavaScript code.
157
+ Local<v8::FunctionTemplate> fun_templ =
158
+ v8::FunctionTemplate::New (isolate, UnlockForDeoptimization);
159
+ Local<Function> fun = fun_templ->GetFunction (context).ToLocalChecked ();
160
+ CHECK (context->Global ()
161
+ ->Set (context, v8_str (" unlock_for_deoptimization" ), fun)
162
+ .FromJust ());
163
+
164
+ // Optimizes a function f, which will be deoptimized in another
165
+ // thread.
166
+ CompileRun (
167
+ " var b = false; var obj = { x: 1 };"
168
+ " function f() { g(); return obj.x; }"
169
+ " function g() { if (b) { unlock_for_deoptimization(); } }"
170
+ " %NeverOptimizeFunction(g);"
171
+ " f(); f(); %OptimizeFunctionOnNextCall(f);"
172
+ " f();" );
173
+
174
+ // Trigger the unlocking.
175
+ Local<Value> v = CompileRun (" b = true; f();" );
176
+
177
+ // Once the isolate has been unlocked, the thread will wait for the
178
+ // other thread to finish its task. Once this happens, this thread
179
+ // continues with its execution, that is, with the execution of the
180
+ // function g, which then returns to f. The function f should have
181
+ // also been deoptimized. If the replacement did not happen on this
182
+ // thread's stack, then the test will fail here.
183
+ CHECK (v->IsNumber ());
184
+ CHECK_EQ (1 , static_cast <int >(v->NumberValue (context).FromJust ()));
185
+ }
186
+ isolate->Dispose ();
187
+ }
188
+
189
+ TEST (LazyDeoptimizationMultithreadWithNatives) {
190
+ i::FLAG_allow_natives_syntax = true ;
191
+ v8::Isolate::CreateParams create_params;
192
+ create_params.array_buffer_allocator = CcTest::array_buffer_allocator ();
193
+ v8::Isolate* isolate = v8::Isolate::New (create_params);
194
+ {
195
+ v8::Locker locker (isolate);
196
+ v8::Isolate::Scope isolate_scope (isolate);
197
+ v8::HandleScope scope (isolate);
198
+ v8::Local<v8::Context> context = v8::Context::New (isolate);
199
+ const char * trigger_deopt = " %DeoptimizeFunction(f);" ;
200
+
201
+ // We use the isolate to pass arguments to the UnlockForDeoptimization
202
+ // function. Namely, we pass a pointer to the deoptimizing thread.
203
+ DeoptimizeCodeThread deoptimize_thread (isolate, context, trigger_deopt);
204
+ isolate->SetData (0 , &deoptimize_thread);
205
+ bool ready_to_deopt = false ;
206
+ isolate->SetData (1 , &ready_to_deopt);
207
+ v8::Context::Scope context_scope (context);
208
+
209
+ // Create the function templace for C++ code that is invoked from
210
+ // JavaScript code.
211
+ Local<v8::FunctionTemplate> fun_templ =
212
+ v8::FunctionTemplate::New (isolate, UnlockForDeoptimizationIfReady);
213
+ Local<Function> fun = fun_templ->GetFunction (context).ToLocalChecked ();
214
+ CHECK (context->Global ()
215
+ ->Set (context, v8_str (" unlock_for_deoptimization" ), fun)
216
+ .FromJust ());
217
+
218
+ // Optimizes a function f, which will be deoptimized in another
219
+ // thread.
220
+ CompileRun (
221
+ " var obj = { x: 1 };"
222
+ " function f() { g(); return obj.x;}"
223
+ " function g() { "
224
+ " unlock_for_deoptimization(); }"
225
+ " %NeverOptimizeFunction(g);"
226
+ " f(); f(); %OptimizeFunctionOnNextCall(f);" );
227
+
228
+ // Trigger the unlocking.
229
+ ready_to_deopt = true ;
230
+ isolate->SetData (1 , &ready_to_deopt);
231
+ Local<Value> v = CompileRun (" f();" );
232
+
233
+ // Once the isolate has been unlocked, the thread will wait for the
234
+ // other thread to finish its task. Once this happens, this thread
235
+ // continues with its execution, that is, with the execution of the
236
+ // function g, which then returns to f. The function f should have
237
+ // also been deoptimized. Otherwise, the test will fail here.
238
+ CHECK (v->IsNumber ());
239
+ CHECK_EQ (1 , static_cast <int >(v->NumberValue (context).FromJust ()));
240
+ }
241
+ isolate->Dispose ();
242
+ }
243
+
244
+ TEST (EagerDeoptimizationMultithread) {
245
+ i::FLAG_allow_natives_syntax = true ;
246
+ v8::Isolate::CreateParams create_params;
247
+ create_params.array_buffer_allocator = CcTest::array_buffer_allocator ();
248
+ v8::Isolate* isolate = v8::Isolate::New (create_params);
249
+ {
250
+ v8::Locker locker (isolate);
251
+ v8::Isolate::Scope isolate_scope (isolate);
252
+ v8::HandleScope scope (isolate);
253
+ v8::Local<v8::Context> context = v8::Context::New (isolate);
254
+ const char * trigger_deopt = " f({y: 0, x: 1});" ;
255
+
256
+ // We use the isolate to pass arguments to the UnlockForDeoptimization
257
+ // function. Namely, we pass a pointer to the deoptimizing thread.
258
+ DeoptimizeCodeThread deoptimize_thread (isolate, context, trigger_deopt);
259
+ isolate->SetData (0 , &deoptimize_thread);
260
+ bool ready_to_deopt = false ;
261
+ isolate->SetData (1 , &ready_to_deopt);
262
+ v8::Context::Scope context_scope (context);
263
+
264
+ // Create the function templace for C++ code that is invoked from
265
+ // JavaScript code.
266
+ Local<v8::FunctionTemplate> fun_templ =
267
+ v8::FunctionTemplate::New (isolate, UnlockForDeoptimizationIfReady);
268
+ Local<Function> fun = fun_templ->GetFunction (context).ToLocalChecked ();
269
+ CHECK (context->Global ()
270
+ ->Set (context, v8_str (" unlock_for_deoptimization" ), fun)
271
+ .FromJust ());
272
+
273
+ // Optimizes a function f, which will be deoptimized by another thread.
274
+ CompileRun (
275
+ " function f(obj) { unlock_for_deoptimization(); return obj.x; }"
276
+ " f({x: 1}); f({x: 1});"
277
+ " %OptimizeFunctionOnNextCall(f);"
278
+ " f({x: 1});" );
279
+
280
+ // Trigger the unlocking.
281
+ ready_to_deopt = true ;
282
+ isolate->SetData (1 , &ready_to_deopt);
283
+ Local<Value> v = CompileRun (" f({x: 1});" );
284
+
285
+ // Once the isolate has been unlocked, the thread will wait for the
286
+ // other thread to finish its task. Once this happens, this thread
287
+ // continues with its execution, that is, with the execution of the
288
+ // function g, which then returns to f. The function f should have
289
+ // also been deoptimized. Otherwise, the test will fail here.
290
+ CHECK (v->IsNumber ());
291
+ CHECK_EQ (1 , static_cast <int >(v->NumberValue (context).FromJust ()));
292
+ }
293
+ isolate->Dispose ();
294
+ }
295
+
58
296
// Migrating an isolate
59
297
class KangarooThread : public v8 ::base::Thread {
60
298
public:
0 commit comments