1aa57e3081a5d779addf9fe141b77edc7a9ff623
[profile/ivi/qtjsbackend.git] / src / 3rdparty / v8 / test / cctest / test-thread-termination.cc
1 // Copyright 2009 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 //     * Redistributions of source code must retain the above copyright
7 //       notice, this list of conditions and the following disclaimer.
8 //     * Redistributions in binary form must reproduce the above
9 //       copyright notice, this list of conditions and the following
10 //       disclaimer in the documentation and/or other materials provided
11 //       with the distribution.
12 //     * Neither the name of Google Inc. nor the names of its
13 //       contributors may be used to endorse or promote products derived
14 //       from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28 #include "v8.h"
29 #include "platform.h"
30 #include "cctest.h"
31
32
33 v8::internal::Semaphore* semaphore = NULL;
34
35
36 v8::Handle<v8::Value> Signal(const v8::Arguments& args) {
37   semaphore->Signal();
38   return v8::Undefined();
39 }
40
41
42 v8::Handle<v8::Value> TerminateCurrentThread(const v8::Arguments& args) {
43   CHECK(!v8::V8::IsExecutionTerminating());
44   v8::V8::TerminateExecution();
45   return v8::Undefined();
46 }
47
48
49 v8::Handle<v8::Value> Fail(const v8::Arguments& args) {
50   CHECK(false);
51   return v8::Undefined();
52 }
53
54
55 v8::Handle<v8::Value> Loop(const v8::Arguments& args) {
56   CHECK(!v8::V8::IsExecutionTerminating());
57   v8::Handle<v8::String> source =
58       v8::String::New("try { doloop(); fail(); } catch(e) { fail(); }");
59   v8::Handle<v8::Value> result = v8::Script::Compile(source)->Run();
60   CHECK(result.IsEmpty());
61   CHECK(v8::V8::IsExecutionTerminating());
62   return v8::Undefined();
63 }
64
65
66 v8::Handle<v8::Value> DoLoop(const v8::Arguments& args) {
67   v8::TryCatch try_catch;
68   CHECK(!v8::V8::IsExecutionTerminating());
69   v8::Script::Compile(v8::String::New("function f() {"
70                                       "  var term = true;"
71                                       "  try {"
72                                       "    while(true) {"
73                                       "      if (term) terminate();"
74                                       "      term = false;"
75                                       "    }"
76                                       "    fail();"
77                                       "  } catch(e) {"
78                                       "    fail();"
79                                       "  }"
80                                       "}"
81                                       "f()"))->Run();
82   CHECK(try_catch.HasCaught());
83   CHECK(try_catch.Exception()->IsNull());
84   CHECK(try_catch.Message().IsEmpty());
85   CHECK(!try_catch.CanContinue());
86   CHECK(v8::V8::IsExecutionTerminating());
87   return v8::Undefined();
88 }
89
90
91 v8::Handle<v8::Value> DoLoopNoCall(const v8::Arguments& args) {
92   v8::TryCatch try_catch;
93   CHECK(!v8::V8::IsExecutionTerminating());
94   v8::Script::Compile(v8::String::New("var term = true;"
95                                       "while(true) {"
96                                       "  if (term) terminate();"
97                                       "  term = false;"
98                                       "}"))->Run();
99   CHECK(try_catch.HasCaught());
100   CHECK(try_catch.Exception()->IsNull());
101   CHECK(try_catch.Message().IsEmpty());
102   CHECK(!try_catch.CanContinue());
103   CHECK(v8::V8::IsExecutionTerminating());
104   return v8::Undefined();
105 }
106
107
108 v8::Handle<v8::ObjectTemplate> CreateGlobalTemplate(
109     v8::InvocationCallback terminate,
110     v8::InvocationCallback doloop) {
111   v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
112   global->Set(v8::String::New("terminate"),
113               v8::FunctionTemplate::New(terminate));
114   global->Set(v8::String::New("fail"), v8::FunctionTemplate::New(Fail));
115   global->Set(v8::String::New("loop"), v8::FunctionTemplate::New(Loop));
116   global->Set(v8::String::New("doloop"), v8::FunctionTemplate::New(doloop));
117   return global;
118 }
119
120
121 // Test that a single thread of JavaScript execution can terminate
122 // itself.
123 TEST(TerminateOnlyV8ThreadFromThreadItself) {
124   v8::HandleScope scope;
125   v8::Handle<v8::ObjectTemplate> global =
126       CreateGlobalTemplate(TerminateCurrentThread, DoLoop);
127   v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
128   v8::Context::Scope context_scope(context);
129   CHECK(!v8::V8::IsExecutionTerminating());
130   // Run a loop that will be infinite if thread termination does not work.
131   v8::Handle<v8::String> source =
132       v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
133   v8::Script::Compile(source)->Run();
134   // Test that we can run the code again after thread termination.
135   CHECK(!v8::V8::IsExecutionTerminating());
136   v8::Script::Compile(source)->Run();
137   context.Dispose();
138 }
139
140
141 // Test that a single thread of JavaScript execution can terminate
142 // itself in a loop that performs no calls.
143 TEST(TerminateOnlyV8ThreadFromThreadItselfNoLoop) {
144   v8::HandleScope scope;
145   v8::Handle<v8::ObjectTemplate> global =
146       CreateGlobalTemplate(TerminateCurrentThread, DoLoopNoCall);
147   v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
148   v8::Context::Scope context_scope(context);
149   CHECK(!v8::V8::IsExecutionTerminating());
150   // Run a loop that will be infinite if thread termination does not work.
151   v8::Handle<v8::String> source =
152       v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
153   v8::Script::Compile(source)->Run();
154   CHECK(!v8::V8::IsExecutionTerminating());
155   // Test that we can run the code again after thread termination.
156   v8::Script::Compile(source)->Run();
157   context.Dispose();
158 }
159
160
161 class TerminatorThread : public v8::internal::Thread {
162  public:
163   explicit TerminatorThread(i::Isolate* isolate)
164       : Thread("TerminatorThread"),
165         isolate_(reinterpret_cast<v8::Isolate*>(isolate)) { }
166   void Run() {
167     semaphore->Wait();
168     CHECK(!v8::V8::IsExecutionTerminating(isolate_));
169     v8::V8::TerminateExecution(isolate_);
170   }
171
172  private:
173   v8::Isolate* isolate_;
174 };
175
176
177 // Test that a single thread of JavaScript execution can be terminated
178 // from the side by another thread.
179 TEST(TerminateOnlyV8ThreadFromOtherThread) {
180   semaphore = v8::internal::OS::CreateSemaphore(0);
181   TerminatorThread thread(i::Isolate::Current());
182   thread.Start();
183
184   v8::HandleScope scope;
185   v8::Handle<v8::ObjectTemplate> global = CreateGlobalTemplate(Signal, DoLoop);
186   v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
187   v8::Context::Scope context_scope(context);
188   CHECK(!v8::V8::IsExecutionTerminating());
189   // Run a loop that will be infinite if thread termination does not work.
190   v8::Handle<v8::String> source =
191       v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
192   v8::Script::Compile(source)->Run();
193
194   thread.Join();
195   delete semaphore;
196   semaphore = NULL;
197   context.Dispose();
198 }
199
200
201 class LoopingThread : public v8::internal::Thread {
202  public:
203   LoopingThread() : Thread("LoopingThread") { }
204   void Run() {
205     v8::Locker locker;
206     v8::HandleScope scope;
207     v8_thread_id_ = v8::V8::GetCurrentThreadId();
208     v8::Handle<v8::ObjectTemplate> global =
209         CreateGlobalTemplate(Signal, DoLoop);
210     v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
211     v8::Context::Scope context_scope(context);
212     CHECK(!v8::V8::IsExecutionTerminating());
213     // Run a loop that will be infinite if thread termination does not work.
214     v8::Handle<v8::String> source =
215         v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
216     v8::Script::Compile(source)->Run();
217     context.Dispose();
218   }
219
220   int GetV8ThreadId() { return v8_thread_id_; }
221
222  private:
223   int v8_thread_id_;
224 };
225
226
227 // Test that multiple threads using default isolate can be terminated
228 // from another thread when using Lockers and preemption.
229 TEST(TerminateMultipleV8ThreadsDefaultIsolate) {
230   {
231     v8::Locker locker;
232     v8::V8::Initialize();
233     v8::Locker::StartPreemption(1);
234     semaphore = v8::internal::OS::CreateSemaphore(0);
235   }
236   const int kThreads = 2;
237   i::List<LoopingThread*> threads(kThreads);
238   for (int i = 0; i < kThreads; i++) {
239     threads.Add(new LoopingThread());
240   }
241   for (int i = 0; i < kThreads; i++) {
242     threads[i]->Start();
243   }
244   // Wait until all threads have signaled the semaphore.
245   for (int i = 0; i < kThreads; i++) {
246     semaphore->Wait();
247   }
248   {
249     v8::Locker locker;
250     for (int i = 0; i < kThreads; i++) {
251       v8::V8::TerminateExecution(threads[i]->GetV8ThreadId());
252     }
253   }
254   for (int i = 0; i < kThreads; i++) {
255     threads[i]->Join();
256     delete threads[i];
257   }
258
259   delete semaphore;
260   semaphore = NULL;
261 }
262
263
264 int call_count = 0;
265
266
267 v8::Handle<v8::Value> TerminateOrReturnObject(const v8::Arguments& args) {
268   if (++call_count == 10) {
269     CHECK(!v8::V8::IsExecutionTerminating());
270     v8::V8::TerminateExecution();
271     return v8::Undefined();
272   }
273   v8::Local<v8::Object> result = v8::Object::New();
274   result->Set(v8::String::New("x"), v8::Integer::New(42));
275   return result;
276 }
277
278
279 v8::Handle<v8::Value> LoopGetProperty(const v8::Arguments& args) {
280   v8::TryCatch try_catch;
281   CHECK(!v8::V8::IsExecutionTerminating());
282   v8::Script::Compile(v8::String::New("function f() {"
283                                       "  try {"
284                                       "    while(true) {"
285                                       "      terminate_or_return_object().x;"
286                                       "    }"
287                                       "    fail();"
288                                       "  } catch(e) {"
289                                       "    fail();"
290                                       "  }"
291                                       "}"
292                                       "f()"))->Run();
293   CHECK(try_catch.HasCaught());
294   CHECK(try_catch.Exception()->IsNull());
295   CHECK(try_catch.Message().IsEmpty());
296   CHECK(!try_catch.CanContinue());
297   CHECK(v8::V8::IsExecutionTerminating());
298   return v8::Undefined();
299 }
300
301
302 // Test that we correctly handle termination exceptions if they are
303 // triggered by the creation of error objects in connection with ICs.
304 TEST(TerminateLoadICException) {
305   v8::HandleScope scope;
306   v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
307   global->Set(v8::String::New("terminate_or_return_object"),
308               v8::FunctionTemplate::New(TerminateOrReturnObject));
309   global->Set(v8::String::New("fail"), v8::FunctionTemplate::New(Fail));
310   global->Set(v8::String::New("loop"),
311               v8::FunctionTemplate::New(LoopGetProperty));
312
313   v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
314   v8::Context::Scope context_scope(context);
315   CHECK(!v8::V8::IsExecutionTerminating());
316   // Run a loop that will be infinite if thread termination does not work.
317   v8::Handle<v8::String> source =
318       v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
319   call_count = 0;
320   v8::Script::Compile(source)->Run();
321   // Test that we can run the code again after thread termination.
322   CHECK(!v8::V8::IsExecutionTerminating());
323   call_count = 0;
324   v8::Script::Compile(source)->Run();
325   context.Dispose();
326 }
327
328 v8::Handle<v8::Value> ReenterAfterTermination(const v8::Arguments& args) {
329   v8::TryCatch try_catch;
330   CHECK(!v8::V8::IsExecutionTerminating());
331   v8::Script::Compile(v8::String::New("function f() {"
332                                       "  var term = true;"
333                                       "  try {"
334                                       "    while(true) {"
335                                       "      if (term) terminate();"
336                                       "      term = false;"
337                                       "    }"
338                                       "    fail();"
339                                       "  } catch(e) {"
340                                       "    fail();"
341                                       "  }"
342                                       "}"
343                                       "f()"))->Run();
344   CHECK(try_catch.HasCaught());
345   CHECK(try_catch.Exception()->IsNull());
346   CHECK(try_catch.Message().IsEmpty());
347   CHECK(!try_catch.CanContinue());
348   CHECK(v8::V8::IsExecutionTerminating());
349   v8::Script::Compile(v8::String::New("function f() { fail(); } f()"))->Run();
350   return v8::Undefined();
351 }
352
353 // Test that reentry into V8 while the termination exception is still pending
354 // (has not yet unwound the 0-level JS frame) does not crash.
355 TEST(TerminateAndReenterFromThreadItself) {
356   v8::HandleScope scope;
357   v8::Handle<v8::ObjectTemplate> global =
358       CreateGlobalTemplate(TerminateCurrentThread, ReenterAfterTermination);
359   v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
360   v8::Context::Scope context_scope(context);
361   CHECK(!v8::V8::IsExecutionTerminating());
362   v8::Handle<v8::String> source =
363       v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
364   v8::Script::Compile(source)->Run();
365   CHECK(!v8::V8::IsExecutionTerminating());
366   // Check we can run JS again after termination.
367   CHECK(v8::Script::Compile(v8::String::New("function f() { return true; }"
368                                             "f()"))->Run()->IsTrue());
369   context.Dispose();
370 }
371