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) */
5 // ***** BEGIN LICENSE BLOCK *****
6 // Version: MPL 1.1/GPL 2.0/LGPL 2.1
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
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.
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 ***** */
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
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
{
62 ThreadTestBase(int iterations
) : m_iterations(iterations
), sharedCounter(0) {
64 virtual ~ThreadTestBase() {
68 WaitNotifyMonitor m_monitor
;
69 const int m_iterations
;
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
);
109 const int m_threadQty
;
110 ThreadRecord
* m_threads
;
114 class MutexTest
: public ThreadTestBase
{
116 MutexTest(int iterations
) : ThreadTestBase(iterations
) {}
117 virtual ~MutexTest() {}
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
) {
128 for (int i
= 0; i
< m_iterations
/2; i
++) {
129 SCOPE_LOCK_NAMED(locker
, m_monitor
) {
130 SCOPE_LOCK_NAMED(locker
, m_monitor
) {
137 class ConditionTest
: public ThreadTestBase
{
139 ConditionTest(int iterations
, int threadQty
) : ThreadTestBase(iterations
), m_threadQty(threadQty
) {}
140 virtual ~ConditionTest() {}
144 AvmAssert(m_threadQty
>= 2);
146 for (int i
= 0; i
< m_iterations
; i
++) {
147 SCOPE_LOCK_NAMED(locker
, m_monitor
) {
149 // If there's another thread still active then wait.
150 if (m_threadQty
> 1) {
154 // This thread has finished, so let's wake everyone else up
155 if (i
== m_iterations
- 1) {
169 class AtomicCounterTest
: public ThreadTestBase
{
171 AtomicCounterTest(int iterations
, int threadQty
) : ThreadTestBase(iterations
), m_threadQty(threadQty
) {}
172 virtual ~AtomicCounterTest() {}
176 AvmAssert(m_iterations
% 4 == 0);
178 for (int i
= 0; i
< m_iterations
/4; i
++) {
181 for (int i
= 0; i
< m_iterations
/4; i
++) {
184 for (int i
= 0; i
< m_iterations
/4; i
++) {
187 for (int i
= 0; i
< m_iterations
/4; i
++) {
191 SCOPE_LOCK(m_monitor
) {
192 if (--m_threadQty
== 0) {
193 sharedCounter
= m_counter
;
198 AtomicCounter32 m_counter
;
202 class CASTest
: public ThreadTestBase
{
204 CASTest(int iterations
, bool withBarrier
) : ThreadTestBase(iterations
), m_withBarrier(withBarrier
) {}
205 virtual ~CASTest() {}
209 for (int i
= 0; i
< m_iterations
; i
++) {
210 int32_t current
, next
;
212 current
= sharedCounter
;
214 } while (!AtomicOps::compareAndSwap32WithBarrier(current
, next
, &sharedCounter
));
217 for (int i
= 0; i
< m_iterations
; i
++) {
218 int32_t current
, next
;
220 current
= sharedCounter
;
222 } while (!AtomicOps::compareAndSwap32(current
, next
, &sharedCounter
));
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
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
{
249 MemoryBarrierTest(int iterations
) : ThreadTestBase(iterations
), m_thread0(0), m_thread1(0), m_turn(NULL
) {}
250 virtual ~MemoryBarrierTest() {}
256 volatile int* const counterp
= &sharedCounter
;
258 SCOPE_LOCK(m_monitor
) {
259 if (m_turn
== NULL
) {
269 for (int i
= 0; i
< m_iterations
; i
++) {
272 MemoryBarrier::readWrite();
273 while (*other
== 1) {
274 MemoryBarrier::readWrite();
275 if (m_turn
== other
) {
276 MemoryBarrier::readWrite();
278 MemoryBarrier::readWrite();
279 while (m_turn
== other
) {
280 MemoryBarrier::readWrite();
282 MemoryBarrier::readWrite();
284 MemoryBarrier::readWrite();
287 MemoryBarrier::readWrite();
289 MemoryBarrier::readWrite();
291 MemoryBarrier::readWrite();
293 MemoryBarrier::readWrite();
297 volatile int m_thread0
;
298 volatile int m_thread1
;
299 volatile int* volatile m_turn
;
302 class ConditionWithWaitTest
: public ThreadTestBase
{
304 ConditionWithWaitTest(int iterations
) : ThreadTestBase(iterations
) {}
305 virtual ~ConditionWithWaitTest() {}
308 for (int i
= 0; i
< m_iterations
; i
++) {
309 SCOPE_LOCK_NAMED(locker
, m_monitor
) {
317 class SleepTest
: public ThreadTestBase
{
319 SleepTest(int iterations
) : ThreadTestBase(iterations
) {}
320 virtual ~SleepTest() {}
323 for (int i
= 0; i
< m_iterations
; i
++) {
324 SCOPE_LOCK(m_monitor
) {
332 class VMThreadLocalTest
: public ThreadTestBase
{
334 VMThreadLocalTest(int iterations
) : ThreadTestBase(iterations
) {}
335 virtual ~VMThreadLocalTest() {}
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
;
346 VMThreadLocal
<uintptr_t> m_localCounter
;
350 // This needs to be at least 2 for ConditionTest
352 #define ITERATIONS 100000
354 using namespace selftestconcurrency
;
356 class ST_vmbase_concurrency
: public Selftest
{
358 ST_vmbase_concurrency(AvmCore
* core
);
359 virtual void run(int n
);
361 static const char* ST_names
[];
362 static const bool ST_explicits
[];
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
) {
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() {
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__
);
403 void ST_vmbase_concurrency::test1() {
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__
);
413 void ST_vmbase_concurrency::test2() {
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__
);
423 void ST_vmbase_concurrency::test3() {
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__
);
433 void ST_vmbase_concurrency::test4() {
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__
);
443 void ST_vmbase_concurrency::test5() {
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__
);
465 void ST_vmbase_concurrency::test6() {
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__
);
475 void ST_vmbase_concurrency::test7() {
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__
);
485 void ST_vmbase_concurrency::test8() {
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__
);
495 void ST_vmbase_concurrency::test9() {
497 // We should be able to run the dtor of a non-started 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__
);
514 void create_vmbase_concurrency(AvmCore
* core
) { new ST_vmbase_concurrency(core
); }