1 : /*
2 : * Copyright (c) 2011 The Native Client Authors. All rights reserved.
3 : * Use of this source code is governed by a BSD-style license that can be
4 : * found in the LICENSE file.
5 : */
6 :
7 : #include <stdio.h>
8 :
9 : #include "native_client/src/include/portability.h"
10 : #include "native_client/src/include/nacl_macros.h"
11 :
12 : #include "native_client/src/shared/platform/nacl_semaphore.h"
13 :
14 : #include "native_client/src/shared/platform/nacl_sync.h"
15 : #include "native_client/src/shared/platform/nacl_sync_checked.h"
16 : #include "native_client/src/shared/platform/nacl_threads.h"
17 : #include "native_client/src/shared/platform/nacl_time.h"
18 : #include "native_client/src/shared/platform/platform_init.h"
19 :
20 : #include "native_client/src/trusted/service_runtime/include/sys/time.h"
21 :
22 : #define STACK_SIZE_BYTES (4 * 4096)
23 : #define NUM_TRIES_SUFFICIENT (5)
24 :
25 1 : void ThreadSleepMs(uint64_t msec) {
26 : struct nacl_abi_timespec nap_duration;
27 :
28 1 : nap_duration.tv_sec = (nacl_abi_time_t) (msec / 1000);
29 1 : nap_duration.tv_nsec = (long) (msec % 1000);
30 1 : NaClNanosleep(&nap_duration, (struct nacl_abi_timespec *) NULL);
31 1 : }
32 :
33 1 : void PauseSpinningThread(void) {
34 1 : ThreadSleepMs(10);
35 1 : }
36 :
37 : size_t gNumTriesSufficient = NUM_TRIES_SUFFICIENT;
38 : struct NaClSemaphore gSem;
39 : struct NaClMutex gMu;
40 : struct NaClCondVar gCv;
41 : size_t gNumThreadsDone = 0;
42 : size_t gNumThreadsTried = 0;
43 : int gFailure = 0;
44 :
45 1 : void WINAPI ThreadMain(void *personality) {
46 1 : int thread_num = (int) (uintptr_t) personality;
47 : uint64_t sleep_count;
48 1 : int got_sem = 0;
49 1 : int failed = 0;
50 :
51 1 : for (sleep_count = 0; sleep_count < gNumTriesSufficient; ++sleep_count) {
52 : /* the sem_trywait should not succeed the first time through */
53 1 : if (NACL_SYNC_BUSY != NaClSemTryWait(&gSem)) {
54 1 : got_sem = 1;
55 1 : break;
56 : }
57 1 : if (0 == sleep_count) {
58 1 : NaClXMutexLock(&gMu);
59 1 : ++gNumThreadsTried;
60 1 : NaClXCondVarSignal(&gCv);
61 1 : NaClXMutexUnlock(&gMu);
62 : }
63 1 : PauseSpinningThread();
64 1 : }
65 :
66 1 : if (got_sem) {
67 : printf("Thread %d: NaClSemTryWait succeeded at %"NACL_PRId64"\n",
68 : thread_num,
69 1 : sleep_count);
70 1 : } else {
71 : /* gNumThreadsTried == sleep_count */
72 0 : printf("Thread %d: NaClSemWait\n", thread_num);
73 0 : if (NACL_SYNC_OK != NaClSemWait(&gSem)) {
74 0 : printf("FAILED\n");
75 0 : printf("NaClSemWait failed!?!\n");
76 0 : failed = 1;
77 : }
78 : }
79 :
80 1 : if (0 == sleep_count) {
81 0 : printf("FAILED\n");
82 0 : printf("Thread %d never actually waited at NaClSemTryWait\n", thread_num);
83 0 : failed = 1;
84 0 : } else {
85 1 : printf("OK -- thread %d\n", thread_num);
86 : }
87 :
88 1 : NaClXMutexLock(&gMu);
89 1 : gFailure += failed;
90 1 : ++gNumThreadsDone;
91 1 : NaClXCondVarSignal(&gCv);
92 1 : NaClXMutexUnlock(&gMu);
93 1 : }
94 :
95 1 : int main(int ac, char **av) {
96 1 : int exit_status = -1;
97 : int opt;
98 1 : size_t num_threads = 16;
99 : size_t n;
100 : struct NaClThread thr;
101 :
102 1 : while (EOF != (opt = getopt(ac, av, "n:s:t:"))) {
103 0 : switch (opt) {
104 : case 'n':
105 0 : num_threads = strtoul(optarg, (char **) NULL, 0);
106 0 : break;
107 : case 't':
108 0 : gNumTriesSufficient = strtoul(optarg, (char **) NULL, 0);
109 0 : break;
110 : default:
111 : fprintf(stderr,
112 : "Usage: nacl_semaphore_test [args]\n"
113 : " -n n number of threads used to test semaphore\n"
114 0 : " -t n number of TryWait operations before blocking Try\n");
115 0 : goto cleanup0;
116 : }
117 0 : }
118 :
119 1 : NaClPlatformInit();
120 :
121 1 : if (!NaClSemCtor(&gSem, 0)) {
122 0 : fprintf(stderr, "nacl_semaphore_test: NaClSemCtor failed!\n");
123 0 : goto cleanup1;
124 : }
125 1 : if (!NaClMutexCtor(&gMu)) {
126 0 : fprintf(stderr, "nacl_semaphore_test: NaClMutexCtor failed!\n");
127 0 : goto cleanup2;
128 : }
129 1 : if (!NaClCondVarCtor(&gCv)) {
130 0 : fprintf(stderr, "nacl_semaphore_test: NaClCondVarCtor failed!\n");
131 0 : goto cleanup3;
132 : }
133 :
134 1 : for (n = 0; n < num_threads; ++n) {
135 : if (!NaClThreadCtor(&thr, ThreadMain, (void *) (uintptr_t) n,
136 1 : STACK_SIZE_BYTES)) {
137 : fprintf(stderr,
138 : "nacl_semaphore_test: could not create thread %"NACL_PRIdS"\n",
139 0 : n);
140 0 : goto cleanup4; /* osx leak semaphore otherwise */
141 : }
142 1 : }
143 :
144 1 : NaClXMutexLock(&gMu);
145 1 : while (gNumThreadsTried != num_threads) {
146 1 : NaClXCondVarWait(&gCv, &gMu);
147 1 : }
148 1 : NaClXMutexUnlock(&gMu);
149 :
150 1 : for (n = 0; n < num_threads; ++n) {
151 1 : NaClSemPost(&gSem); /* let a thread go */
152 1 : }
153 :
154 1 : NaClXMutexLock(&gMu);
155 1 : while (gNumThreadsDone != num_threads) {
156 1 : NaClXCondVarWait(&gCv, &gMu);
157 1 : }
158 1 : exit_status = gFailure;
159 1 : NaClXMutexUnlock(&gMu);
160 :
161 1 : if (0 == exit_status) {
162 1 : printf("SUCCESS\n");
163 : }
164 : cleanup4:
165 : /* single exit with (ah hem) simulation of RAII via cleanup sled */
166 1 : NaClCondVarDtor(&gCv);
167 : cleanup3:
168 1 : NaClMutexDtor(&gMu);
169 : cleanup2:
170 1 : NaClSemDtor(&gSem);
171 : cleanup1:
172 1 : NaClPlatformFini();
173 : cleanup0:
174 1 : return exit_status;
175 1 : }
|