1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
|
/* Copyright (c) 2011 The Native Client Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include <stdio.h>
#include "native_client/src/include/atomic_ops.h"
#include "native_client/src/shared/platform/nacl_threads.h"
#include "native_client/src/shared/platform/nacl_sync_checked.h"
#include "native_client/src/shared/platform/nacl_sync.h"
#define THREAD_STACK_SIZE (128*1024)
int32_t gIncrementsPerThread = 1000;
int32_t gNumThreads;
struct NaClMutex gMutex;
/* For atomic counter test */
Atomic32 gCounter = 0;
static void IncrementTest(void) {
/* Increment the counter, gIncrementsPerThread times,
* with the values 1...gIncrementsPerThread
*/
Atomic32 i;
for (i=1; i <= gIncrementsPerThread; i++) {
AtomicIncrement(&gCounter, i);
}
}
static void DecrementTest(void) {
/* Decrement the counter gIncrementsPerThread times,
* with the values 1...gIncrementsPerThread-1
*/
Atomic32 i;
for (i=1; i < gIncrementsPerThread; i++) {
AtomicIncrement(&gCounter, -i);
}
}
/* Atomic exchange test
* Each thread exchanges gExchange for its tid.
* It takes the returned value and adds it to gExchangeSum.
* When finished, gExchangeSum + gExchange will contain
* 1 + ... + gNumThreads
*/
Atomic32 gExchange = 0;
Atomic32 gExchangeSum = 0;
static void ExchangeTest(int32_t tid) {
Atomic32 i;
i = AtomicExchange(&gExchange, tid);
if (i == tid) {
fprintf(stderr,
"Error: AtomicExchange returned the new value instead of old.\n");
exit(EXIT_FAILURE);
}
AtomicIncrement(&gExchangeSum, i);
}
/*
* Atomic compare and swap test.
*
* Each thread spins until gSwap == tid, and then exchanges it for tid + 1.
*
* If the threads are scheduled in the order they are launched, the
* CompareAndSwap() loops will complete quickly. However, if they are
* scheduled out-of-order, these loops could take a long time to
* complete. Yielding the CPU here improves that significantly.
*/
Atomic32 gSwap = 1;
static void CompareAndSwapTest(int32_t tid) {
while (CompareAndSwap(&gSwap, tid, tid + 1) != tid) {
NaClThreadYield();
}
}
void WINAPI ThreadMain(void *state) {
int32_t tid = (int32_t) (intptr_t) state;
/* Wait for the signal to begin */
NaClXMutexLock(&gMutex);
NaClXMutexUnlock(&gMutex);
/* Swap the order to shake things up a bit */
if (tid % 2 == 0) {
IncrementTest();
DecrementTest();
} else {
DecrementTest();
IncrementTest();
}
ExchangeTest(tid);
CompareAndSwapTest(tid);
}
int main(int argc, const char *argv[]) {
struct NaClThread *threads;
int rv;
int32_t tid;
int32_t tmp;
if (argc != 2) {
fprintf(stderr, "Usage: %s <NumThreads>\n", argv[0]);
exit(EXIT_FAILURE);
}
gNumThreads = strtol(argv[1], NULL, 10);
threads = (struct NaClThread*)malloc(gNumThreads*sizeof(struct NaClThread));
if (threads == NULL) {
fprintf(stderr, "malloc returned NULL\n");
exit(EXIT_FAILURE);
}
if (!NaClMutexCtor(&gMutex)) {
fprintf(stderr, "NaClMutexCtor failed\n");
exit(EXIT_FAILURE);
}
NaClXMutexLock(&gMutex);
for (tid = 1; tid <= gNumThreads; ++tid) {
fprintf(stderr, "Creating thread %d\n", (int)tid);
rv = NaClThreadCreateJoinable(&threads[tid-1],
ThreadMain,
(void*) (intptr_t) tid,
THREAD_STACK_SIZE);
if (!rv) {
fprintf(stderr, "NaClThreadCtor failed\n");
exit(EXIT_FAILURE);
}
}
NaClXMutexUnlock(&gMutex);
for (tid = 1; tid <= gNumThreads; ++tid) {
NaClThreadJoin(&threads[tid-1]);
}
/* Check the results */
tmp = gIncrementsPerThread * gNumThreads;
if (gCounter != tmp) {
fprintf(stderr, "ERROR: gCounter is wrong. Expected %d, got %d\n",
(int)tmp, (int)gCounter);
exit(EXIT_FAILURE);
}
tmp = gNumThreads*(gNumThreads+1)/2;
if (gExchange + gExchangeSum != tmp) {
fprintf(stderr,
"ERROR: gExchange+gExchangeSum is wrong. Expected %d, got %d\n",
(int)tmp, (int)(gExchange + gExchangeSum));
exit(EXIT_FAILURE);
}
if (gSwap != gNumThreads+1) {
fprintf(stderr, "ERROR: gSwap is wrong. Expected %d, got %d\n",
(int)(gNumThreads+1), (int)gSwap);
exit(EXIT_FAILURE);
}
fprintf(stderr, "PASSED\n");
NaClMutexDtor(&gMutex);
free(threads);
return 0;
}
|