1 // -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*-
2 // vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
4 // ***** BEGIN LICENSE BLOCK *****
5 // Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 // The contents of this file are subject to the Mozilla Public License Version
8 // 1.1 (the "License"); you may not use this file except in compliance with
9 // the License. You may obtain a copy of the License at
10 // http://www.mozilla.org/MPL/
12 // Software distributed under the License is distributed on an "AS IS" basis,
13 // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 // for the specific language governing rights and limitations under the
17 // The Original Code is [Open Source Virtual Machine.].
19 // The Initial Developer of the Original Code is
20 // Adobe System Incorporated.
21 // Portions created by the Initial Developer are Copyright (C) 2004-2006
22 // the Initial Developer. All Rights Reserved.
27 // Alternatively, the contents of this file may be used under the terms of
28 // either the GNU General Public License Version 2 or later (the "GPL"), or
29 // the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 // in which case the provisions of the GPL or the LGPL are applicable instead
31 // of those above. If you wish to allow use of your version of this file only
32 // under the terms of either the GPL or the LGPL, and not to allow others to
33 // use your version of this file under the terms of the MPL, indicate your
34 // decision by deleting the provisions above and replace them with the notice
35 // and other provisions required by the GPL or the LGPL. If you do not delete
36 // the provisions above, a recipient may use your version of this file under
37 // the terms of any one of the MPL, the GPL or the LGPL.
39 // ***** END LICENSE BLOCK ***** */
42 %%category concurrency
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;
358 TestRunner runner(THREAD_QTY);
359 MutexTest test(ITERATIONS);
360 runner.runTest(test);
361 %%verify test.sharedCounter == THREAD_QTY * ITERATIONS
366 TestRunner runner(THREAD_QTY);
367 ConditionTest test(ITERATIONS, THREAD_QTY);
368 runner.runTest(test);
369 %%verify test.sharedCounter == THREAD_QTY * ITERATIONS
372 %%test atomic_counter
374 TestRunner runner(THREAD_QTY);
375 AtomicCounterTest test(ITERATIONS, THREAD_QTY);
376 runner.runTest(test);
377 %%verify test.sharedCounter == 0
380 %%test compare_and_swap_without_barrier
382 TestRunner runner(THREAD_QTY);
383 CASTest test(ITERATIONS, false);
384 runner.runTest(test);
385 %%verify test.sharedCounter == THREAD_QTY * ITERATIONS
388 %%test compare_and_swap_with_barrier
390 TestRunner runner(THREAD_QTY);
391 CASTest test(ITERATIONS, true);
392 runner.runTest(test);
393 %%verify test.sharedCounter == THREAD_QTY * ITERATIONS
396 %%test memory_barrier
398 /* This test is failing on Windows and Mac OSX 10.4.
399 * For Windows, see bug 609820.
400 * For Mac, are the 10.4 APIs not reliable?
401 * It could also be the test, or the compiler!
402 * FIXME: bug 609943 Selftests to stress memory barriers (fences)
404 // Note that the memory barrier test is based on a Dekker lock, so we
405 // only ever use 2 threads.
406 TestRunner runner(2);
407 MemoryBarrierTest test(ITERATIONS);
408 runner.runTest(test);
409 %%verify test.sharedCounter == 2 * ITERATIONS
415 %%test condition_with_wait
417 TestRunner runner(THREAD_QTY);
418 ConditionWithWaitTest test(2000); // Use 2000 iterations with a 1 ms wait
419 runner.runTest(test);
420 %%verify test.sharedCounter == THREAD_QTY * 2000
425 TestRunner runner(THREAD_QTY);
426 SleepTest test(2000); // Use 2000 iterations with a 1 ms sleep
427 runner.runTest(test);
428 %%verify test.sharedCounter == THREAD_QTY * 2000
433 TestRunner runner(THREAD_QTY);
434 VMThreadLocalTest test(ITERATIONS);
435 runner.runTest(test);
436 %%verify test.sharedCounter == THREAD_QTY * ITERATIONS
441 // We should be able to run the dtor of a non-started VMThread.
445 // Run the mutex test but call the VMThread dtors without joining first
446 TestRunner runner(THREAD_QTY, false);
447 MutexTest test(ITERATIONS);
448 runner.runTest(test);
449 %%verify test.sharedCounter == THREAD_QTY * ITERATIONS