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