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