backout 29799f914cab, Bug 917642 - [Helix] Please update the helix blobs
[gecko.git] / nsprpub / pr / tests / stack.c
blob182191ad5d2c7ba0b50f5f52aa44c2f3a6968bdc
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/. */
7 /*
9 * Test atomic stack operations
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.
19 #include "nspr.h"
20 #include "plgetopt.h"
22 typedef struct _DataRecord {
23 PRInt32 data;
24 PRStackElem link;
25 } DataRecord;
27 #define RECORD_LINK_PTR(lp) ((DataRecord*) ((char*) (lp) - offsetof(DataRecord,link)))
29 #define MAX_THREAD_CNT 100
30 #define DEFAULT_THREAD_CNT 4
31 #define DEFAULT_DATA_CNT 100
32 #define DEFAULT_LOOP_CNT 10000
35 * sum of the first n numbers using the formula n*(n+1)/2
37 #define SUM_OF_NUMBERS(n) ((n & 1) ? (((n + 1)/2) * n) : ((n/2) * (n+1)))
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;
47 static void stackop(void *arg);
49 static int _debug_on;
51 PRFileDesc *output;
52 PRFileDesc *errhandle;
54 int main(int argc, char **argv)
56 #if !(defined(SYMBIAN) && defined(__WINS__))
57 PRInt32 rv, cnt, sum;
58 DataRecord *Item;
59 PRStack *list1, *list2;
60 PRStackElem *node;
61 PRStatus rc;
63 PRInt32 thread_cnt = DEFAULT_THREAD_CNT;
64 PRInt32 data_cnt = DEFAULT_DATA_CNT;
65 PRInt32 loops = DEFAULT_LOOP_CNT;
66 PRThread **threads;
67 stack_data *thread_args;
69 PLOptStatus os;
70 PLOptState *opt = PL_CreateOptState(argc, argv, "dt:c:l:");
72 while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
74 if (PL_OPT_BAD == os) continue;
75 switch (opt->option)
77 case 'd': /* debug mode */
78 _debug_on = 1;
79 break;
80 case 't': /* thread count */
81 thread_cnt = atoi(opt->value);
82 break;
83 case 'c': /* data count */
84 data_cnt = atoi(opt->value);
85 break;
86 case 'l': /* loop count */
87 loops = atoi(opt->value);
88 break;
89 default:
90 break;
93 PL_DestroyOptState(opt);
95 PR_SetConcurrency(4);
97 output = PR_GetSpecialFD(PR_StandardOutput);
98 errhandle = PR_GetSpecialFD(PR_StandardError);
99 list1 = PR_CreateStack("Stack_1");
100 if (list1 == NULL) {
101 PR_fprintf(errhandle, "PR_CreateStack failed - error %d\n",
102 PR_GetError());
103 return 1;
106 list2 = PR_CreateStack("Stack_2");
107 if (list2 == NULL) {
108 PR_fprintf(errhandle, "PR_CreateStack failed - error %d\n",
109 PR_GetError());
110 return 1;
114 threads = (PRThread**) PR_CALLOC(sizeof(PRThread*) * thread_cnt);
115 thread_args = (stack_data *) PR_CALLOC(sizeof(stack_data) * thread_cnt);
117 if (_debug_on)
118 PR_fprintf(output,"%s: thread_cnt = %d data_cnt = %d\n", argv[0],
119 thread_cnt, data_cnt);
120 for(cnt = 0; cnt < thread_cnt; cnt++) {
121 PRThreadScope scope;
123 thread_args[cnt].list1 = list1;
124 thread_args[cnt].list2 = list2;
125 thread_args[cnt].loops = loops;
126 thread_args[cnt].data_cnt = data_cnt;
127 thread_args[cnt].initial_data_value = 1 + cnt * data_cnt;
129 if (cnt & 1)
130 scope = PR_GLOBAL_THREAD;
131 else
132 scope = PR_LOCAL_THREAD;
135 threads[cnt] = PR_CreateThread(PR_USER_THREAD,
136 stackop, &thread_args[cnt],
137 PR_PRIORITY_NORMAL,
138 scope,
139 PR_JOINABLE_THREAD,
141 if (threads[cnt] == NULL) {
142 PR_fprintf(errhandle, "PR_CreateThread failed - error %d\n",
143 PR_GetError());
144 PR_ProcessExit(2);
146 if (_debug_on)
147 PR_fprintf(output,"%s: created thread = 0x%x\n", argv[0],
148 threads[cnt]);
151 for(cnt = 0; cnt < thread_cnt; cnt++) {
152 rc = PR_JoinThread(threads[cnt]);
153 PR_ASSERT(rc == PR_SUCCESS);
156 node = PR_StackPop(list1);
158 * list1 should be empty
160 if (node != NULL) {
161 PR_fprintf(errhandle, "Error - Stack 1 not empty\n");
162 PR_ASSERT(node == NULL);
163 PR_ProcessExit(4);
166 cnt = data_cnt * thread_cnt;
167 sum = 0;
168 while (cnt-- > 0) {
169 node = PR_StackPop(list2);
171 * There should be at least 'cnt' number of records
173 if (node == NULL) {
174 PR_fprintf(errhandle, "Error - PR_StackPop returned NULL\n");
175 PR_ProcessExit(3);
177 Item = RECORD_LINK_PTR(node);
178 sum += Item->data;
180 node = PR_StackPop(list2);
182 * there should be exactly 'cnt' number of records
184 if (node != NULL) {
185 PR_fprintf(errhandle, "Error - Stack 2 not empty\n");
186 PR_ASSERT(node == NULL);
187 PR_ProcessExit(4);
189 PR_DELETE(threads);
190 PR_DELETE(thread_args);
192 PR_DestroyStack(list1);
193 PR_DestroyStack(list2);
195 if (sum == SUM_OF_NUMBERS(data_cnt * thread_cnt)) {
196 PR_fprintf(output, "%s successful\n", argv[0]);
197 PR_fprintf(output, "\t\tsum = 0x%x, expected = 0x%x\n", sum,
198 SUM_OF_NUMBERS(thread_cnt * data_cnt));
199 return 0;
200 } else {
201 PR_fprintf(output, "%s failed: sum = 0x%x, expected = 0x%x\n",
202 argv[0], sum,
203 SUM_OF_NUMBERS(data_cnt * thread_cnt));
204 return 2;
206 #endif
209 static void stackop(void *thread_arg)
211 PRInt32 val, cnt, index, loops;
212 DataRecord *Items, *Item;
213 PRStack *list1, *list2;
214 PRStackElem *node;
215 stack_data *arg = (stack_data *) thread_arg;
217 val = arg->initial_data_value;
218 cnt = arg->data_cnt;
219 loops = arg->loops;
220 list1 = arg->list1;
221 list2 = arg->list2;
224 * allocate memory for the data records
226 Items = (DataRecord *) PR_CALLOC(sizeof(DataRecord) * cnt);
227 PR_ASSERT(Items != NULL);
228 index = 0;
230 if (_debug_on)
231 PR_fprintf(output,
232 "Thread[0x%x] init_val = %d cnt = %d data1 = 0x%x datan = 0x%x\n",
233 PR_GetCurrentThread(), val, cnt, &Items[0], &Items[cnt-1]);
237 * add the data records to list1
239 while (cnt-- > 0) {
240 Items[index].data = val++;
241 PR_StackPush(list1, &Items[index].link);
242 index++;
246 * pop data records from list1 and add them back to list1
247 * generates contention for the stack accesses
249 while (loops-- > 0) {
250 cnt = arg->data_cnt;
251 while (cnt-- > 0) {
252 node = PR_StackPop(list1);
253 if (node == NULL) {
254 PR_fprintf(errhandle, "Error - PR_StackPop returned NULL\n");
255 PR_ASSERT(node != NULL);
256 PR_ProcessExit(3);
258 PR_StackPush(list1, node);
262 * remove the data records from list1 and add them to list2
264 cnt = arg->data_cnt;
265 while (cnt-- > 0) {
266 node = PR_StackPop(list1);
267 if (node == NULL) {
268 PR_fprintf(errhandle, "Error - PR_StackPop returned NULL\n");
269 PR_ASSERT(node != NULL);
270 PR_ProcessExit(3);
272 PR_StackPush(list2, node);
274 if (_debug_on)
275 PR_fprintf(output,
276 "Thread[0x%x] init_val = %d cnt = %d exiting\n",
277 PR_GetCurrentThread(), val, cnt);