1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
7 /*
8 *
9 * Test atomic stack operations
10 *
11 * Two stacks are created and threads add data items (each containing
12 * one of the first n integers) to the first stack, remove data items
13 * from the first stack and add them to the second stack. The primordial
14 * thread compares the sum of the first n integers to the sum of the
15 * integers in the data items in the second stack. The test succeeds if
16 * they are equal.
17 */
18
19 #include "nspr.h"
20 #include "plgetopt.h"
21
22 typedef struct _DataRecord {
23 PRInt32 data;
24 PRStackElem link;
25 } DataRecord;
26
27 #define RECORD_LINK_PTR(lp) ((DataRecord*) ((char*) (lp) - offsetof(DataRecord,link)))
28
29 #define MAX_THREAD_CNT 100
30 #define DEFAULT_THREAD_CNT 4
31 #define DEFAULT_DATA_CNT 100
32 #define DEFAULT_LOOP_CNT 10000
33
34 /*
35 * sum of the first n numbers using the formula n*(n+1)/2
36 */
37 #define SUM_OF_NUMBERS(n) ((n & 1) ? (((n + 1)/2) * n) : ((n/2) * (n+1)))
38
39 typedef struct stack_data {
40 PRStack *list1;
41 PRStack *list2;
42 PRInt32 initial_data_value;
43 PRInt32 data_cnt;
44 PRInt32 loops;
45 } stack_data;
46
47 static void stackop(void *arg);
48
49 static int _debug_on;
50
51 PRFileDesc *output;
52 PRFileDesc *errhandle;
53
main(int argc,char ** argv)54 int main(int argc, char **argv)
55 {
56 PRInt32 rv, cnt, sum;
57 DataRecord *Item;
58 PRStack *list1, *list2;
59 PRStackElem *node;
60 PRStatus rc;
61
62 PRInt32 thread_cnt = DEFAULT_THREAD_CNT;
63 PRInt32 data_cnt = DEFAULT_DATA_CNT;
64 PRInt32 loops = DEFAULT_LOOP_CNT;
65 PRThread **threads;
66 stack_data *thread_args;
67
68 PLOptStatus os;
69 PLOptState *opt = PL_CreateOptState(argc, argv, "dt:c:l:");
70
71 while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
72 {
73 if (PL_OPT_BAD == os) {
74 continue;
75 }
76 switch (opt->option)
77 {
78 case 'd': /* debug mode */
79 _debug_on = 1;
80 break;
81 case 't': /* thread count */
82 thread_cnt = atoi(opt->value);
83 break;
84 case 'c': /* data count */
85 data_cnt = atoi(opt->value);
86 break;
87 case 'l': /* loop count */
88 loops = atoi(opt->value);
89 break;
90 default:
91 break;
92 }
93 }
94 PL_DestroyOptState(opt);
95
96 PR_SetConcurrency(4);
97
98 output = PR_GetSpecialFD(PR_StandardOutput);
99 errhandle = PR_GetSpecialFD(PR_StandardError);
100 list1 = PR_CreateStack("Stack_1");
101 if (list1 == NULL) {
102 PR_fprintf(errhandle, "PR_CreateStack failed - error %d\n",
103 PR_GetError());
104 return 1;
105 }
106
107 list2 = PR_CreateStack("Stack_2");
108 if (list2 == NULL) {
109 PR_fprintf(errhandle, "PR_CreateStack failed - error %d\n",
110 PR_GetError());
111 return 1;
112 }
113
114
115 threads = (PRThread**) PR_CALLOC(sizeof(PRThread*) * thread_cnt);
116 thread_args = (stack_data *) PR_CALLOC(sizeof(stack_data) * thread_cnt);
117
118 if (_debug_on)
119 PR_fprintf(output,"%s: thread_cnt = %d data_cnt = %d\n", argv[0],
120 thread_cnt, data_cnt);
121 for(cnt = 0; cnt < thread_cnt; cnt++) {
122 PRThreadScope scope;
123
124 thread_args[cnt].list1 = list1;
125 thread_args[cnt].list2 = list2;
126 thread_args[cnt].loops = loops;
127 thread_args[cnt].data_cnt = data_cnt;
128 thread_args[cnt].initial_data_value = 1 + cnt * data_cnt;
129
130 if (cnt & 1) {
131 scope = PR_GLOBAL_THREAD;
132 }
133 else {
134 scope = PR_LOCAL_THREAD;
135 }
136
137
138 threads[cnt] = PR_CreateThread(PR_USER_THREAD,
139 stackop, &thread_args[cnt],
140 PR_PRIORITY_NORMAL,
141 scope,
142 PR_JOINABLE_THREAD,
143 0);
144 if (threads[cnt] == NULL) {
145 PR_fprintf(errhandle, "PR_CreateThread failed - error %d\n",
146 PR_GetError());
147 PR_ProcessExit(2);
148 }
149 if (_debug_on)
150 PR_fprintf(output,"%s: created thread = 0x%x\n", argv[0],
151 threads[cnt]);
152 }
153
154 for(cnt = 0; cnt < thread_cnt; cnt++) {
155 rc = PR_JoinThread(threads[cnt]);
156 PR_ASSERT(rc == PR_SUCCESS);
157 }
158
159 node = PR_StackPop(list1);
160 /*
161 * list1 should be empty
162 */
163 if (node != NULL) {
164 PR_fprintf(errhandle, "Error - Stack 1 not empty\n");
165 PR_ASSERT(node == NULL);
166 PR_ProcessExit(4);
167 }
168
169 cnt = data_cnt * thread_cnt;
170 sum = 0;
171 while (cnt-- > 0) {
172 node = PR_StackPop(list2);
173 /*
174 * There should be at least 'cnt' number of records
175 */
176 if (node == NULL) {
177 PR_fprintf(errhandle, "Error - PR_StackPop returned NULL\n");
178 PR_ProcessExit(3);
179 }
180 Item = RECORD_LINK_PTR(node);
181 sum += Item->data;
182 }
183 node = PR_StackPop(list2);
184 /*
185 * there should be exactly 'cnt' number of records
186 */
187 if (node != NULL) {
188 PR_fprintf(errhandle, "Error - Stack 2 not empty\n");
189 PR_ASSERT(node == NULL);
190 PR_ProcessExit(4);
191 }
192 PR_DELETE(threads);
193 PR_DELETE(thread_args);
194
195 PR_DestroyStack(list1);
196 PR_DestroyStack(list2);
197
198 if (sum == SUM_OF_NUMBERS(data_cnt * thread_cnt)) {
199 PR_fprintf(output, "%s successful\n", argv[0]);
200 PR_fprintf(output, "\t\tsum = 0x%x, expected = 0x%x\n", sum,
201 SUM_OF_NUMBERS(thread_cnt * data_cnt));
202 return 0;
203 } else {
204 PR_fprintf(output, "%s failed: sum = 0x%x, expected = 0x%x\n",
205 argv[0], sum,
206 SUM_OF_NUMBERS(data_cnt * thread_cnt));
207 return 2;
208 }
209 }
210
stackop(void * thread_arg)211 static void stackop(void *thread_arg)
212 {
213 PRInt32 val, cnt, index, loops;
214 DataRecord *Items, *Item;
215 PRStack *list1, *list2;
216 PRStackElem *node;
217 stack_data *arg = (stack_data *) thread_arg;
218
219 val = arg->initial_data_value;
220 cnt = arg->data_cnt;
221 loops = arg->loops;
222 list1 = arg->list1;
223 list2 = arg->list2;
224
225 /*
226 * allocate memory for the data records
227 */
228 Items = (DataRecord *) PR_CALLOC(sizeof(DataRecord) * cnt);
229 PR_ASSERT(Items != NULL);
230 index = 0;
231
232 if (_debug_on)
233 PR_fprintf(output,
234 "Thread[0x%x] init_val = %d cnt = %d data1 = 0x%x datan = 0x%x\n",
235 PR_GetCurrentThread(), val, cnt, &Items[0], &Items[cnt-1]);
236
237
238 /*
239 * add the data records to list1
240 */
241 while (cnt-- > 0) {
242 Items[index].data = val++;
243 PR_StackPush(list1, &Items[index].link);
244 index++;
245 }
246
247 /*
248 * pop data records from list1 and add them back to list1
249 * generates contention for the stack accesses
250 */
251 while (loops-- > 0) {
252 cnt = arg->data_cnt;
253 while (cnt-- > 0) {
254 node = PR_StackPop(list1);
255 if (node == NULL) {
256 PR_fprintf(errhandle, "Error - PR_StackPop returned NULL\n");
257 PR_ASSERT(node != NULL);
258 PR_ProcessExit(3);
259 }
260 PR_StackPush(list1, node);
261 }
262 }
263 /*
264 * remove the data records from list1 and add them to list2
265 */
266 cnt = arg->data_cnt;
267 while (cnt-- > 0) {
268 node = PR_StackPop(list1);
269 if (node == NULL) {
270 PR_fprintf(errhandle, "Error - PR_StackPop returned NULL\n");
271 PR_ASSERT(node != NULL);
272 PR_ProcessExit(3);
273 }
274 PR_StackPush(list2, node);
275 }
276 if (_debug_on)
277 PR_fprintf(output,
278 "Thread[0x%x] init_val = %d cnt = %d exiting\n",
279 PR_GetCurrentThread(), val, cnt);
280
281 }
282
283