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