Merge remote-tracking branch 'redux/master' into sh4-pool
[tamarin-stm.git] / extensions / ST_vmbase_concurrency.cpp
blob7c0b6a601ad3d3df38066172ce3018523eab38f5
1 // Generated from ST_vmbase_concurrency.st
2 // -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*-
3 // vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
4 //
5 // ***** BEGIN LICENSE BLOCK *****
6 // Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 //
8 // The contents of this file are subject to the Mozilla Public License Version
9 // 1.1 (the "License"); you may not use this file except in compliance with
10 // the License. You may obtain a copy of the License at
11 // http://www.mozilla.org/MPL/
13 // Software distributed under the License is distributed on an "AS IS" basis,
14 // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
15 // for the specific language governing rights and limitations under the
16 // License.
18 // The Original Code is [Open Source Virtual Machine.].
20 // The Initial Developer of the Original Code is
21 // Adobe System Incorporated.
22 // Portions created by the Initial Developer are Copyright (C) 2004-2006
23 // the Initial Developer. All Rights Reserved.
25 // Contributor(s):
26 // Adobe AS3 Team
28 // Alternatively, the contents of this file may be used under the terms of
29 // either the GNU General Public License Version 2 or later (the "GPL"), or
30 // the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 // in which case the provisions of the GPL or the LGPL are applicable instead
32 // of those above. If you wish to allow use of your version of this file only
33 // under the terms of either the GPL or the LGPL, and not to allow others to
34 // use your version of this file under the terms of the MPL, indicate your
35 // decision by deleting the provisions above and replace them with the notice
36 // and other provisions required by the GPL or the LGPL. If you do not delete
37 // the provisions above, a recipient may use your version of this file under
38 // the terms of any one of the MPL, the GPL or the LGPL.
40 // ***** END LICENSE BLOCK ***** */
42 #include "avmshell.h"
43 #ifdef VMCFG_SELFTEST
44 namespace avmplus {
45 using namespace MMgc;
46 using namespace vmbase;
48 // We use the same testing method as that for ST_vmpi_threads:
49 // Each construct is tested by (1) using it in the implementation of a mutator that
50 // modifies a counter for a fixed number of iterations, and then (2) running
51 // duplicates of that mutator in parallel. The final counter value ends up in
52 // sharedCounter, which is guarded by m_monitor (except for in CASTest).
53 // Each test checks that the sharedCounter ends up with a statically determined
54 // end value.
55 // (Description courtesy of Felix)
57 // We need a specific namespace as ST_vmpi_threads uses the same test class names
58 namespace selftestconcurrency {
60 class ThreadTestBase : public Runnable {
61 public:
62 ThreadTestBase(int iterations) : m_iterations(iterations), sharedCounter(0) {
64 virtual ~ThreadTestBase() {
67 protected:
68 WaitNotifyMonitor m_monitor;
69 const int m_iterations;
71 public:
72 int sharedCounter;
75 class TestRunner {
76 public:
77 struct ThreadRecord {
78 VMThread* thread;
79 bool startupOk;
82 TestRunner(int threadQty, bool doJoin = true) : m_threadQty(threadQty), m_doJoin(doJoin) {
84 virtual ~TestRunner() {
87 void runTest(ThreadTestBase& test) {
89 m_threads = mmfx_new_array(ThreadRecord, m_threadQty);
91 // Start up the threads
92 for (int i = 0; i < m_threadQty; i++) {
93 m_threads[i].thread = mmfx_new(VMThread(&test));
94 m_threads[i].startupOk = m_threads[i].thread->start();
97 // ...then block until they all terminate
98 for (int i = 0; i < m_threadQty; i++) {
99 if (m_doJoin && m_threads[i].startupOk) {
100 m_threads[i].thread->join();
102 mmfx_delete(m_threads[i].thread);
105 mmfx_delete_array(m_threads);
108 private:
109 const int m_threadQty;
110 ThreadRecord* m_threads;
111 bool m_doJoin;
114 class MutexTest : public ThreadTestBase {
115 public:
116 MutexTest(int iterations) : ThreadTestBase(iterations) {}
117 virtual ~MutexTest() {}
119 virtual void run() {
120 AvmAssert(m_iterations % 2 == 0);
121 for (int i = 0; i < m_iterations/2; i++) {
122 SCOPE_LOCK(m_monitor) {
123 SCOPE_LOCK(m_monitor) {
124 sharedCounter++;
128 for (int i = 0; i < m_iterations/2; i++) {
129 SCOPE_LOCK_NAMED(locker, m_monitor) {
130 SCOPE_LOCK_NAMED(locker, m_monitor) {
131 sharedCounter++;
137 class ConditionTest : public ThreadTestBase {
138 public:
139 ConditionTest(int iterations, int threadQty) : ThreadTestBase(iterations), m_threadQty(threadQty) {}
140 virtual ~ConditionTest() {}
142 virtual void run() {
144 AvmAssert(m_threadQty >= 2);
146 for (int i = 0; i < m_iterations; i++) {
147 SCOPE_LOCK_NAMED(locker, m_monitor) {
148 sharedCounter++;
149 // If there's another thread still active then wait.
150 if (m_threadQty > 1) {
151 locker.notify();
152 locker.wait();
154 // This thread has finished, so let's wake everyone else up
155 if (i == m_iterations - 1) {
156 --m_threadQty;
157 locker.notifyAll();
165 private:
166 int m_threadQty;
169 class AtomicCounterTest : public ThreadTestBase {
170 public:
171 AtomicCounterTest(int iterations, int threadQty) : ThreadTestBase(iterations), m_threadQty(threadQty) {}
172 virtual ~AtomicCounterTest() {}
174 virtual void run() {
176 AvmAssert(m_iterations % 4 == 0);
178 for (int i = 0; i < m_iterations/4; i++) {
179 m_counter++;
181 for (int i = 0; i < m_iterations/4; i++) {
182 m_counter--;
184 for (int i = 0; i < m_iterations/4; i++) {
185 ++m_counter;
187 for (int i = 0; i < m_iterations/4; i++) {
188 --m_counter;
191 SCOPE_LOCK(m_monitor) {
192 if (--m_threadQty == 0) {
193 sharedCounter = m_counter;
197 private:
198 AtomicCounter32 m_counter;
199 int m_threadQty;
202 class CASTest : public ThreadTestBase {
203 public:
204 CASTest(int iterations, bool withBarrier) : ThreadTestBase(iterations), m_withBarrier(withBarrier) {}
205 virtual ~CASTest() {}
207 virtual void run() {
208 if (m_withBarrier) {
209 for (int i = 0; i < m_iterations; i++) {
210 int32_t current, next;
211 do {
212 current = sharedCounter;
213 next = current + 1;
214 } while (!AtomicOps::compareAndSwap32WithBarrier(current, next, &sharedCounter));
216 } else {
217 for (int i = 0; i < m_iterations; i++) {
218 int32_t current, next;
219 do {
220 current = sharedCounter;
221 next = current + 1;
222 } while (!AtomicOps::compareAndSwap32(current, next, &sharedCounter));
226 private:
227 bool m_withBarrier;
231 * We protect a shared counter with a Dekker-style lock that has been made
232 * sequentially consistent with memory barriers.
234 * The idea is that if the barriers are correct, then two threads can compete
235 * to update the counter n times each, so that the final counter value is 2n. If
236 * the final value is not 2n, then the barriers have failed to ensure sequential
237 * consistency.
239 * FIXME: bug 609943
240 * This seems way too complicated. We have to be confident in the algorithm
241 * before considering the barrier implementations, and I'm not convinced as yet.
242 * Is there something simpler?
243 * Note that the barriers below are extremely conservative.
245 * This is test is not actually run. The verifyPass below just returns true.
247 class MemoryBarrierTest : public ThreadTestBase {
248 public:
249 MemoryBarrierTest(int iterations) : ThreadTestBase(iterations), m_thread0(0), m_thread1(0), m_turn(NULL) {}
250 virtual ~MemoryBarrierTest() {}
252 virtual void run() {
254 volatile int* me;
255 volatile int* other;
256 volatile int* const counterp = &sharedCounter;
258 SCOPE_LOCK(m_monitor) {
259 if (m_turn == NULL) {
260 me = &m_thread0;
261 other = &m_thread1;
262 m_turn = me;
263 } else {
264 me = &m_thread1;
265 other = &m_thread0;
269 for (int i = 0; i < m_iterations; i++) {
270 // Dekker lock
271 *me = 1;
272 MemoryBarrier::readWrite();
273 while (*other == 1) {
274 MemoryBarrier::readWrite();
275 if (m_turn == other) {
276 MemoryBarrier::readWrite();
277 *me = 0;
278 MemoryBarrier::readWrite();
279 while (m_turn == other) {
280 MemoryBarrier::readWrite();
282 MemoryBarrier::readWrite();
283 *me = 1;
284 MemoryBarrier::readWrite();
287 MemoryBarrier::readWrite();
288 (*counterp)++;
289 MemoryBarrier::readWrite();
290 m_turn = other;
291 MemoryBarrier::readWrite();
292 *me = 0;
293 MemoryBarrier::readWrite();
296 private:
297 volatile int m_thread0;
298 volatile int m_thread1;
299 volatile int* volatile m_turn;
302 class ConditionWithWaitTest : public ThreadTestBase {
303 public:
304 ConditionWithWaitTest(int iterations) : ThreadTestBase(iterations) {}
305 virtual ~ConditionWithWaitTest() {}
307 virtual void run() {
308 for (int i = 0; i < m_iterations; i++) {
309 SCOPE_LOCK_NAMED(locker, m_monitor) {
310 sharedCounter++;
311 locker.wait(1);
317 class SleepTest : public ThreadTestBase {
318 public:
319 SleepTest(int iterations) : ThreadTestBase(iterations) {}
320 virtual ~SleepTest() {}
322 virtual void run() {
323 for (int i = 0; i < m_iterations; i++) {
324 SCOPE_LOCK(m_monitor) {
325 sharedCounter++;
327 VMThread::sleep(1);
332 class VMThreadLocalTest : public ThreadTestBase {
333 public:
334 VMThreadLocalTest(int iterations) : ThreadTestBase(iterations) {}
335 virtual ~VMThreadLocalTest() {}
337 virtual void run() {
338 for (int i = 0; i < m_iterations; i++) {
339 m_localCounter.set(m_localCounter.get() + 1);
341 SCOPE_LOCK(m_monitor) {
342 sharedCounter += (int)m_localCounter;
345 private:
346 VMThreadLocal<uintptr_t> m_localCounter;
350 // This needs to be at least 2 for ConditionTest
351 #define THREAD_QTY 4
352 #define ITERATIONS 100000
354 using namespace selftestconcurrency;
356 class ST_vmbase_concurrency : public Selftest {
357 public:
358 ST_vmbase_concurrency(AvmCore* core);
359 virtual void run(int n);
360 private:
361 static const char* ST_names[];
362 static const bool ST_explicits[];
363 void test0();
364 void test1();
365 void test2();
366 void test3();
367 void test4();
368 void test5();
369 void test6();
370 void test7();
371 void test8();
372 void test9();
374 ST_vmbase_concurrency::ST_vmbase_concurrency(AvmCore* core)
375 : Selftest(core, "vmbase", "concurrency", ST_vmbase_concurrency::ST_names,ST_vmbase_concurrency::ST_explicits)
377 const char* ST_vmbase_concurrency::ST_names[] = {"mutexes","conditions","atomic_counter","compare_and_swap_without_barrier","compare_and_swap_with_barrier","memory_barrier","condition_with_wait","sleep","vmthreadlocal","join", NULL };
378 const bool ST_vmbase_concurrency::ST_explicits[] = {false,false,false,false,false,false,false,false,false,false, false };
379 void ST_vmbase_concurrency::run(int n) {
380 switch(n) {
381 case 0: test0(); return;
382 case 1: test1(); return;
383 case 2: test2(); return;
384 case 3: test3(); return;
385 case 4: test4(); return;
386 case 5: test5(); return;
387 case 6: test6(); return;
388 case 7: test7(); return;
389 case 8: test8(); return;
390 case 9: test9(); return;
393 void ST_vmbase_concurrency::test0() {
394 #ifndef UNDER_CE
395 TestRunner runner(THREAD_QTY);
396 MutexTest test(ITERATIONS);
397 runner.runTest(test);
398 #line 361 "ST_vmbase_concurrency.st"
399 verifyPass(test.sharedCounter == THREAD_QTY * ITERATIONS, "test.sharedCounter == THREAD_QTY * ITERATIONS", __FILE__, __LINE__);
400 #endif
403 void ST_vmbase_concurrency::test1() {
404 #ifndef UNDER_CE
405 TestRunner runner(THREAD_QTY);
406 ConditionTest test(ITERATIONS, THREAD_QTY);
407 runner.runTest(test);
408 #line 369 "ST_vmbase_concurrency.st"
409 verifyPass(test.sharedCounter == THREAD_QTY * ITERATIONS, "test.sharedCounter == THREAD_QTY * ITERATIONS", __FILE__, __LINE__);
410 #endif
413 void ST_vmbase_concurrency::test2() {
414 #ifndef UNDER_CE
415 TestRunner runner(THREAD_QTY);
416 AtomicCounterTest test(ITERATIONS, THREAD_QTY);
417 runner.runTest(test);
418 #line 377 "ST_vmbase_concurrency.st"
419 verifyPass(test.sharedCounter == 0, "test.sharedCounter == 0", __FILE__, __LINE__);
420 #endif
423 void ST_vmbase_concurrency::test3() {
424 #ifndef UNDER_CE
425 TestRunner runner(THREAD_QTY);
426 CASTest test(ITERATIONS, false);
427 runner.runTest(test);
428 #line 385 "ST_vmbase_concurrency.st"
429 verifyPass(test.sharedCounter == THREAD_QTY * ITERATIONS, "test.sharedCounter == THREAD_QTY * ITERATIONS", __FILE__, __LINE__);
430 #endif
433 void ST_vmbase_concurrency::test4() {
434 #ifndef UNDER_CE
435 TestRunner runner(THREAD_QTY);
436 CASTest test(ITERATIONS, true);
437 runner.runTest(test);
438 #line 393 "ST_vmbase_concurrency.st"
439 verifyPass(test.sharedCounter == THREAD_QTY * ITERATIONS, "test.sharedCounter == THREAD_QTY * ITERATIONS", __FILE__, __LINE__);
440 #endif
443 void ST_vmbase_concurrency::test5() {
444 #ifndef UNDER_CE
445 /* This test is failing on Windows and Mac OSX 10.4.
446 * For Windows, see bug 609820.
447 * For Mac, are the 10.4 APIs not reliable?
448 * It could also be the test, or the compiler!
449 * FIXME: bug 609943 Selftests to stress memory barriers (fences)
451 // Note that the memory barrier test is based on a Dekker lock, so we
452 // only ever use 2 threads.
453 TestRunner runner(2);
454 MemoryBarrierTest test(ITERATIONS);
455 runner.runTest(test);
456 #line 409 "ST_vmbase_concurrency.st"
457 verifyPass(test.sharedCounter == 2 * ITERATIONS, "test.sharedCounter == 2 * ITERATIONS", __FILE__, __LINE__);
460 #line 412 "ST_vmbase_concurrency.st"
461 verifyPass(true, "true", __FILE__, __LINE__);
462 #endif
465 void ST_vmbase_concurrency::test6() {
466 #ifndef UNDER_CE
467 TestRunner runner(THREAD_QTY);
468 ConditionWithWaitTest test(2000); // Use 2000 iterations with a 1 ms wait
469 runner.runTest(test);
470 #line 420 "ST_vmbase_concurrency.st"
471 verifyPass(test.sharedCounter == THREAD_QTY * 2000, "test.sharedCounter == THREAD_QTY * 2000", __FILE__, __LINE__);
472 #endif
475 void ST_vmbase_concurrency::test7() {
476 #ifndef UNDER_CE
477 TestRunner runner(THREAD_QTY);
478 SleepTest test(2000); // Use 2000 iterations with a 1 ms sleep
479 runner.runTest(test);
480 #line 428 "ST_vmbase_concurrency.st"
481 verifyPass(test.sharedCounter == THREAD_QTY * 2000, "test.sharedCounter == THREAD_QTY * 2000", __FILE__, __LINE__);
482 #endif
485 void ST_vmbase_concurrency::test8() {
486 #ifndef UNDER_CE
487 TestRunner runner(THREAD_QTY);
488 VMThreadLocalTest test(ITERATIONS);
489 runner.runTest(test);
490 #line 436 "ST_vmbase_concurrency.st"
491 verifyPass(test.sharedCounter == THREAD_QTY * ITERATIONS, "test.sharedCounter == THREAD_QTY * ITERATIONS", __FILE__, __LINE__);
492 #endif
495 void ST_vmbase_concurrency::test9() {
496 #ifndef UNDER_CE
497 // We should be able to run the dtor of a non-started VMThread.
499 VMThread vmthread;
501 // Run the mutex test but call the VMThread dtors without joining first
502 TestRunner runner(THREAD_QTY, false);
503 MutexTest test(ITERATIONS);
504 runner.runTest(test);
505 #line 449 "ST_vmbase_concurrency.st"
506 verifyPass(test.sharedCounter == THREAD_QTY * ITERATIONS, "test.sharedCounter == THREAD_QTY * ITERATIONS", __FILE__, __LINE__);
507 #endif
514 void create_vmbase_concurrency(AvmCore* core) { new ST_vmbase_concurrency(core); }
516 #endif