@@ -55,6 +55,244 @@ using ::v8::Value;
5555using ::v8::V8;
5656
5757
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+
58296// Migrating an isolate
59297class KangarooThread : public v8 ::base::Thread {
60298 public:
0 commit comments