1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is the Netscape Portable Runtime (NSPR).
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998-2000
20 * the Initial Developer. All Rights Reserved.
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
41 * Test atomic stack operations
43 * Two stacks are created and threads add data items (each containing
44 * one of the first n integers) to the first stack, remove data items
45 * from the first stack and add them to the second stack. The primordial
46 * thread compares the sum of the first n integers to the sum of the
47 * integers in the data items in the second stack. The test succeeds if
54 typedef struct _DataRecord
{
59 #define RECORD_LINK_PTR(lp) ((DataRecord*) ((char*) (lp) - offsetof(DataRecord,link)))
61 #define MAX_THREAD_CNT 100
62 #define DEFAULT_THREAD_CNT 4
63 #define DEFAULT_DATA_CNT 100
64 #define DEFAULT_LOOP_CNT 10000
67 * sum of the first n numbers using the formula n*(n+1)/2
69 #define SUM_OF_NUMBERS(n) ((n & 1) ? (((n + 1)/2) * n) : ((n/2) * (n+1)))
71 typedef struct stack_data
{
74 PRInt32 initial_data_value
;
79 static void stackop(void *arg
);
84 PRFileDesc
*errhandle
;
86 PRIntn
main(PRIntn argc
, char **argv
)
90 PRStack
*list1
, *list2
;
94 PRInt32 thread_cnt
= DEFAULT_THREAD_CNT
;
95 PRInt32 data_cnt
= DEFAULT_DATA_CNT
;
96 PRInt32 loops
= DEFAULT_LOOP_CNT
;
98 stack_data
*thread_args
;
101 PLOptState
*opt
= PL_CreateOptState(argc
, argv
, "dt:c:l:");
103 while (PL_OPT_EOL
!= (os
= PL_GetNextOpt(opt
)))
105 if (PL_OPT_BAD
== os
) continue;
108 case 'd': /* debug mode */
111 case 't': /* thread count */
112 thread_cnt
= atoi(opt
->value
);
114 case 'c': /* data count */
115 data_cnt
= atoi(opt
->value
);
117 case 'l': /* loop count */
118 loops
= atoi(opt
->value
);
124 PL_DestroyOptState(opt
);
126 PR_SetConcurrency(4);
128 output
= PR_GetSpecialFD(PR_StandardOutput
);
129 errhandle
= PR_GetSpecialFD(PR_StandardError
);
130 list1
= PR_CreateStack("Stack_1");
132 PR_fprintf(errhandle
, "PR_CreateStack failed - error %d\n",
137 list2
= PR_CreateStack("Stack_2");
139 PR_fprintf(errhandle
, "PR_CreateStack failed - error %d\n",
145 threads
= (PRThread
**) PR_CALLOC(sizeof(PRThread
*) * thread_cnt
);
146 thread_args
= (stack_data
*) PR_CALLOC(sizeof(stack_data
) * thread_cnt
);
149 PR_fprintf(output
,"%s: thread_cnt = %d data_cnt = %d\n", argv
[0],
150 thread_cnt
, data_cnt
);
151 for(cnt
= 0; cnt
< thread_cnt
; cnt
++) {
154 thread_args
[cnt
].list1
= list1
;
155 thread_args
[cnt
].list2
= list2
;
156 thread_args
[cnt
].loops
= loops
;
157 thread_args
[cnt
].data_cnt
= data_cnt
;
158 thread_args
[cnt
].initial_data_value
= 1 + cnt
* data_cnt
;
161 scope
= PR_GLOBAL_THREAD
;
163 scope
= PR_LOCAL_THREAD
;
166 threads
[cnt
] = PR_CreateThread(PR_USER_THREAD
,
167 stackop
, &thread_args
[cnt
],
172 if (threads
[cnt
] == NULL
) {
173 PR_fprintf(errhandle
, "PR_CreateThread failed - error %d\n",
178 PR_fprintf(output
,"%s: created thread = 0x%x\n", argv
[0],
182 for(cnt
= 0; cnt
< thread_cnt
; cnt
++) {
183 rc
= PR_JoinThread(threads
[cnt
]);
184 PR_ASSERT(rc
== PR_SUCCESS
);
187 node
= PR_StackPop(list1
);
189 * list1 should be empty
192 PR_fprintf(errhandle
, "Error - Stack 1 not empty\n");
193 PR_ASSERT(node
== NULL
);
197 cnt
= data_cnt
* thread_cnt
;
200 node
= PR_StackPop(list2
);
202 * There should be at least 'cnt' number of records
205 PR_fprintf(errhandle
, "Error - PR_StackPop returned NULL\n");
208 Item
= RECORD_LINK_PTR(node
);
211 node
= PR_StackPop(list2
);
213 * there should be exactly 'cnt' number of records
216 PR_fprintf(errhandle
, "Error - Stack 2 not empty\n");
217 PR_ASSERT(node
== NULL
);
221 PR_DELETE(thread_args
);
223 PR_DestroyStack(list1
);
224 PR_DestroyStack(list2
);
226 if (sum
== SUM_OF_NUMBERS(data_cnt
* thread_cnt
)) {
227 PR_fprintf(output
, "%s successful\n", argv
[0]);
228 PR_fprintf(output
, "\t\tsum = 0x%x, expected = 0x%x\n", sum
,
229 SUM_OF_NUMBERS(thread_cnt
* data_cnt
));
232 PR_fprintf(output
, "%s failed: sum = 0x%x, expected = 0x%x\n",
234 SUM_OF_NUMBERS(data_cnt
* thread_cnt
));
239 static void stackop(void *thread_arg
)
241 PRInt32 val
, cnt
, index
, loops
;
242 DataRecord
*Items
, *Item
;
243 PRStack
*list1
, *list2
;
245 stack_data
*arg
= (stack_data
*) thread_arg
;
247 val
= arg
->initial_data_value
;
254 * allocate memory for the data records
256 Items
= (DataRecord
*) PR_CALLOC(sizeof(DataRecord
) * cnt
);
257 PR_ASSERT(Items
!= NULL
);
262 "Thread[0x%x] init_val = %d cnt = %d data1 = 0x%x datan = 0x%x\n",
263 PR_GetCurrentThread(), val
, cnt
, &Items
[0], &Items
[cnt
-1]);
267 * add the data records to list1
270 Items
[index
].data
= val
++;
271 PR_StackPush(list1
, &Items
[index
].link
);
276 * pop data records from list1 and add them back to list1
277 * generates contention for the stack accesses
279 while (loops
-- > 0) {
282 node
= PR_StackPop(list1
);
284 PR_fprintf(errhandle
, "Error - PR_StackPop returned NULL\n");
285 PR_ASSERT(node
!= NULL
);
288 PR_StackPush(list1
, node
);
292 * remove the data records from list1 and add them to list2
296 node
= PR_StackPop(list1
);
298 PR_fprintf(errhandle
, "Error - PR_StackPop returned NULL\n");
299 PR_ASSERT(node
!= NULL
);
302 PR_StackPush(list2
, node
);
306 "Thread[0x%x] init_val = %d cnt = %d exiting\n",
307 PR_GetCurrentThread(), val
, cnt
);