|
| 1 | +// Copyright 2015 The Emscripten Authors. All rights reserved. |
| 2 | +// Emscripten is available under two separate licenses, the MIT license and the |
| 3 | +// University of Illinois/NCSA Open Source License. Both these licenses can be |
| 4 | +// found in the LICENSE file. |
| 5 | + |
| 6 | +#include <stdio.h> |
| 7 | +#include <stdlib.h> |
| 8 | +#include <memory.h> |
| 9 | +#include <pthread.h> |
| 10 | +#include <emscripten.h> |
| 11 | +#include <emscripten/threading.h> |
| 12 | +#include <emscripten/console.h> |
| 13 | +#include <assert.h> |
| 14 | + |
| 15 | +#define NUM_THREADS 8 |
| 16 | +#define DATA_COUNT 10 |
| 17 | + |
| 18 | +volatile double globalDouble = 0.0; |
| 19 | +volatile uint64_t globalU64 = 0; |
| 20 | + |
| 21 | +uint64_t sharedData[DATA_COUNT] = {}; |
| 22 | + |
| 23 | +struct Test { |
| 24 | + int op; |
| 25 | + int threadId; |
| 26 | +}; |
| 27 | + |
| 28 | +uint64_t threadCasAccumulatedWrittenData[NUM_THREADS] = {}; |
| 29 | +uint64_t threadCasAccumulatedReadData[NUM_THREADS] = {}; |
| 30 | + |
| 31 | +int64_t rand_60() { |
| 32 | + return (int64_t)(emscripten_random() * (float)0x3FFFFFFF) | ((int64_t)(emscripten_random() * (float)0x3FFFFFFF) << 30); |
| 33 | +} |
| 34 | + |
| 35 | +void *ThreadMain(void *arg) { |
| 36 | + assert(pthread_self() != 0); |
| 37 | + assert(globalDouble == 5.0); |
| 38 | + assert(globalU64 == 5); |
| 39 | + struct Test *t = (struct Test*)arg; |
| 40 | + emscripten_outf("Thread %d for test %d: starting computation", t->threadId, t->op); |
| 41 | + |
| 42 | + for (int i = 0; i < 99999; ++i) { |
| 43 | + for (int j = 0; j < DATA_COUNT; ++j) { |
| 44 | + switch (t->op) { |
| 45 | + case 0: emscripten_atomic_add_u64(&sharedData[j], 1); break; |
| 46 | + case 1: emscripten_atomic_sub_u64(&sharedData[j], 1); break; |
| 47 | + case 2: emscripten_atomic_and_u64(&sharedData[j], ~(1UL << t->threadId)); break; |
| 48 | + case 3: emscripten_atomic_or_u64(&sharedData[j], 1UL << t->threadId); break; |
| 49 | + case 4: emscripten_atomic_xor_u64(&sharedData[j], 1UL << t->threadId); break; |
| 50 | + case 5: { |
| 51 | + // Atomically load and store data, and test that each individual u8 is the same. |
| 52 | + uint64_t data = emscripten_atomic_load_u64(&sharedData[j]); |
| 53 | + uint8_t dataU8[8]; |
| 54 | + memcpy(dataU8, &data, 8); |
| 55 | + assert(dataU8[0] >= 10 && dataU8[0] < 10+NUM_THREADS); |
| 56 | + assert(dataU8[0] == dataU8[1] && dataU8[0] == dataU8[2] && dataU8[0] == dataU8[3]); |
| 57 | + assert(dataU8[0] == dataU8[4] && dataU8[0] == dataU8[5] && dataU8[0] == dataU8[6] && dataU8[0] == dataU8[7]); |
| 58 | + dataU8[0] = dataU8[1] = dataU8[2] = dataU8[3] = dataU8[4] = dataU8[5] = dataU8[6] = dataU8[7] = 10 + t->threadId; |
| 59 | + memcpy(&data, dataU8, 8); |
| 60 | + emscripten_atomic_store_u64(&sharedData[j], data); |
| 61 | + } |
| 62 | + break; |
| 63 | + case 6: { |
| 64 | + uint64_t newData = rand_60(); |
| 65 | + uint64_t data; |
| 66 | + uint64_t prevData; |
| 67 | + do { |
| 68 | + data = emscripten_atomic_load_u64(&sharedData[j]); |
| 69 | + prevData = emscripten_atomic_cas_u64(&sharedData[j], data, newData); |
| 70 | + } while(prevData != data); |
| 71 | + threadCasAccumulatedReadData[t->threadId] += data; |
| 72 | + threadCasAccumulatedWrittenData[t->threadId] += newData; |
| 73 | + } |
| 74 | + break; |
| 75 | + } |
| 76 | + } |
| 77 | + } |
| 78 | + emscripten_outf("Thread %d for test %d: finished, exit()ing", t->threadId, t->op); |
| 79 | + pthread_exit(0); |
| 80 | +} |
| 81 | + |
| 82 | +struct Test t[NUM_THREADS] = {}; |
| 83 | +pthread_t thread[NUM_THREADS]; |
| 84 | + |
| 85 | +void RunTest(int test) { |
| 86 | + pthread_attr_t attr; |
| 87 | + pthread_attr_init(&attr); |
| 88 | + pthread_attr_setstacksize(&attr, 4*1024); |
| 89 | + |
| 90 | + emscripten_outf("Main thread has thread ID %p\n", pthread_self()); |
| 91 | + assert(pthread_self() != 0); |
| 92 | + |
| 93 | + switch(test) { |
| 94 | + case 2: memset(sharedData, 0xFF, sizeof(sharedData)); break; |
| 95 | + case 5: memset(sharedData, 0x10, sizeof(sharedData)); break; |
| 96 | + default: memset(sharedData, 0, sizeof(sharedData)); break; |
| 97 | + } |
| 98 | + |
| 99 | + emscripten_outf("Main: Starting test %d", test); |
| 100 | + |
| 101 | + for (int i = 0; i < NUM_THREADS; ++i) { |
| 102 | + t[i].op = test; |
| 103 | + t[i].threadId = i; |
| 104 | + int rc = pthread_create(&thread[i], &attr, ThreadMain, &t[i]); |
| 105 | + assert(rc == 0); |
| 106 | + } |
| 107 | + |
| 108 | + pthread_attr_destroy(&attr); |
| 109 | + |
| 110 | + for (int i = 0; i < NUM_THREADS; ++i) { |
| 111 | + int status = 1; |
| 112 | + int rc = pthread_join(thread[i], (void**)&status); |
| 113 | + assert(rc == 0); |
| 114 | + assert(status == 0); |
| 115 | + } |
| 116 | + |
| 117 | + int val = sharedData[0]; |
| 118 | + emscripten_outf("Main: Test %d finished. Result: %d", test, val); |
| 119 | + if (test != 6) { |
| 120 | + for (int i = 1; i < DATA_COUNT; ++i) { |
| 121 | + assert(sharedData[i] == sharedData[0]); |
| 122 | + } |
| 123 | + } |
| 124 | +} |
| 125 | + |
| 126 | +int main() { |
| 127 | + globalDouble = 5.0; |
| 128 | + globalU64 = 4; |
| 129 | + |
| 130 | + uint64_t prevU64 = emscripten_atomic_add_u64((void*)&globalU64, 1); |
| 131 | + assert(prevU64 == 4); |
| 132 | + |
| 133 | + for (int i = 0; i < 7; ++i) { |
| 134 | + RunTest(i); |
| 135 | + } |
| 136 | + |
| 137 | + uint64_t totalRead = 0; |
| 138 | + uint64_t totalWritten = 0; |
| 139 | + for (int i = 0; i < NUM_THREADS; ++i) { |
| 140 | + totalRead += threadCasAccumulatedReadData[i]; |
| 141 | + totalWritten += threadCasAccumulatedWrittenData[i]; |
| 142 | + } |
| 143 | + for (int i = 0; i < DATA_COUNT; ++i) { |
| 144 | + totalRead += sharedData[i]; |
| 145 | + } |
| 146 | + |
| 147 | + if (totalRead == totalWritten) { |
| 148 | + emscripten_outf("totalRead: %llu, totalWritten: %llu\n", totalRead, totalWritten); |
| 149 | + } else { |
| 150 | + emscripten_outf("64-bit CAS test failed! totalRead != totalWritten (%llu != %llu)\n", totalRead, totalWritten); |
| 151 | + } |
| 152 | + assert(totalRead == totalWritten); |
| 153 | + emscripten_outf("Main: Test successfully finished"); |
| 154 | + return 0; |
| 155 | +} |
0 commit comments