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 ** File:        foreign.c
8 ** Description: Testing various functions w/ foreign threads
9 **
10 **      We create a thread and get it to call exactly one runtime function.
11 **      The thread is allowed to be created by some other environment that
12 **      NSPR, but it does not announce itself to the runtime prior to calling
13 **      in.
14 **
15 **      The goal: try to survive.
16 **
17 */
18 
19 #include "prcvar.h"
20 #include "prenv.h"
21 #include "prerror.h"
22 #include "prinit.h"
23 #include "prinrval.h"
24 #include "prio.h"
25 #include "prlock.h"
26 #include "prlog.h"
27 #include "prmem.h"
28 #include "prthread.h"
29 #include "prtypes.h"
30 #include "prprf.h"
31 #include "plgetopt.h"
32 
33 #include <stdio.h>
34 #include <stdlib.h>
35 
36 static enum {
37     thread_nspr, thread_pthread, thread_sproc, thread_win32
38 } thread_provider;
39 
40 typedef void (*StartFn)(void*);
41 typedef struct StartObject
42 {
43     StartFn start;
44     void *arg;
45 } StartObject;
46 
47 static PRFileDesc *output;
48 
49 static int _debug_on = 0;
50 
51 #define DEFAULT_THREAD_COUNT    10
52 
53 #define DPRINTF(arg)    if (_debug_on) PR_fprintf arg
54 
55 #if defined(_PR_PTHREADS)
56 #include <pthread.h>
57 #include "md/_pth.h"
pthread_start(void * arg)58 static void *pthread_start(void *arg)
59 {
60     StartFn start = ((StartObject*)arg)->start;
61     void *data = ((StartObject*)arg)->arg;
62     PR_Free(arg);
63     start(data);
64     return NULL;
65 }  /* pthread_start */
66 #endif /* defined(_PR_PTHREADS) */
67 
68 #if defined(WIN32)
69 #include <windows.h>
70 #include <process.h>  /* for _beginthreadex() */
71 
windows_start(void * arg)72 static PRUintn __stdcall windows_start(void *arg)
73 {
74     StartObject *so = (StartObject*)arg;
75     StartFn start = so->start;
76     void *data = so->arg;
77     PR_Free(so);
78     start(data);
79     return 0;
80 }  /* windows_start */
81 #endif /* defined(WIN32) */
82 
NSPRPUB_TESTS_CreateThread(StartFn start,void * arg)83 static PRStatus NSPRPUB_TESTS_CreateThread(StartFn start, void *arg)
84 {
85     PRStatus rv;
86 
87     switch (thread_provider)
88     {
89         case thread_nspr:
90         {
91             PRThread *thread = PR_CreateThread(
92                                    PR_USER_THREAD, start, arg,
93                                    PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
94                                    PR_UNJOINABLE_THREAD, 0);
95             rv = (NULL == thread) ? PR_FAILURE : PR_SUCCESS;
96         }
97         break;
98         case thread_pthread:
99 #if defined(_PR_PTHREADS)
100         {
101             int rv;
102             pthread_t id;
103             pthread_attr_t tattr;
104             StartObject *start_object;
105             start_object = PR_NEW(StartObject);
106             PR_ASSERT(NULL != start_object);
107             start_object->start = start;
108             start_object->arg = arg;
109 
110             rv = _PT_PTHREAD_ATTR_INIT(&tattr);
111             PR_ASSERT(0 == rv);
112 
113             rv = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
114             PR_ASSERT(0 == rv);
115 
116             rv = pthread_attr_setstacksize(&tattr, 64 * 1024);
117             PR_ASSERT(0 == rv);
118 
119             rv = _PT_PTHREAD_CREATE(&id, tattr, pthread_start, start_object);
120             (void)_PT_PTHREAD_ATTR_DESTROY(&tattr);
121             return (0 == rv) ? PR_SUCCESS : PR_FAILURE;
122         }
123 #else
124         PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
125         rv = PR_FAILURE;
126         break;
127 #endif /* defined(_PR_PTHREADS) */
128 
129         case thread_sproc:
130             PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
131             rv = PR_FAILURE;
132             break;
133         case thread_win32:
134 #if defined(WIN32)
135         {
136             void *th;
137             PRUintn id;
138             StartObject *start_object;
139             start_object = PR_NEW(StartObject);
140             PR_ASSERT(NULL != start_object);
141             start_object->start = start;
142             start_object->arg = arg;
143             th = (void*)_beginthreadex(
144                      NULL, /* LPSECURITY_ATTRIBUTES - pointer to thread security attributes */
145                      0U, /* DWORD - initial thread stack size, in bytes */
146                      windows_start, /* LPTHREAD_START_ROUTINE - pointer to thread function */
147                      start_object, /* LPVOID - argument for new thread */
148                      STACK_SIZE_PARAM_IS_A_RESERVATION, /*DWORD dwCreationFlags - creation flags */
149                      &id /* LPDWORD - pointer to returned thread identifier */ );
150 
151             rv = (NULL == th) ? PR_FAILURE : PR_SUCCESS;
152         }
153 #else
154         PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
155         rv = PR_FAILURE;
156 #endif
157         break;
158         default:
159             PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
160             rv = PR_FAILURE;
161     }
162     return rv;
163 }  /* NSPRPUB_TESTS_CreateThread */
164 
lazyEntry(void * arg)165 static void PR_CALLBACK lazyEntry(void *arg)
166 {
167     PR_ASSERT(NULL == arg);
168 }  /* lazyEntry */
169 
170 
OneShot(void * arg)171 static void OneShot(void *arg)
172 {
173     PRUintn pdkey;
174     PRLock *lock;
175     PRFileDesc *fd;
176     PRDir *dir;
177     PRFileDesc *pair[2];
178     PRIntn test = (PRIntn)arg;
179 
180     for (test = 0; test < 12; ++test) {
181 
182         switch (test)
183         {
184             case 0:
185                 lock = PR_NewLock();
186                 DPRINTF((output,"Thread[0x%x] called PR_NewLock\n",
187                          PR_GetCurrentThread()));
188                 PR_DestroyLock(lock);
189                 break;
190 
191             case 1:
192                 (void)PR_SecondsToInterval(1);
193                 DPRINTF((output,"Thread[0x%x] called PR_SecondsToInterval\n",
194                          PR_GetCurrentThread()));
195                 break;
196 
197             case 2: (void)PR_CreateThread(
198                     PR_USER_THREAD, lazyEntry, NULL, PR_PRIORITY_NORMAL,
199                     PR_LOCAL_THREAD, PR_UNJOINABLE_THREAD, 0);
200                 DPRINTF((output,"Thread[0x%x] called PR_CreateThread\n",
201                          PR_GetCurrentThread()));
202                 break;
203 
204             case 3:
205                 fd = PR_Open("foreign.tmp", PR_CREATE_FILE | PR_RDWR, 0666);
206                 DPRINTF((output,"Thread[0x%x] called PR_Open\n",
207                          PR_GetCurrentThread()));
208                 PR_Close(fd);
209                 break;
210 
211             case 4:
212                 fd = PR_NewUDPSocket();
213                 DPRINTF((output,"Thread[0x%x] called PR_NewUDPSocket\n",
214                          PR_GetCurrentThread()));
215                 PR_Close(fd);
216                 break;
217 
218             case 5:
219                 fd = PR_NewTCPSocket();
220                 DPRINTF((output,"Thread[0x%x] called PR_NewTCPSocket\n",
221                          PR_GetCurrentThread()));
222                 PR_Close(fd);
223                 break;
224 
225             case 6:
226 #define TEMP_DIR "./tmp"
227                 PR_MkDir(TEMP_DIR, 0700);
228                 dir = PR_OpenDir(TEMP_DIR);
229                 DPRINTF((output,"Thread[0x%x] called PR_OpenDir\n",
230                          PR_GetCurrentThread()));
231                 PR_CloseDir(dir);
232                 break;
233 
234             case 7:
235                 (void)PR_NewThreadPrivateIndex(&pdkey, NULL);
236                 DPRINTF((output,"Thread[0x%x] called PR_NewThreadPrivateIndex\n",
237                          PR_GetCurrentThread()));
238                 break;
239 
240             case 8:
241                 (void)PR_GetEnv("PATH");
242                 DPRINTF((output,"Thread[0x%x] called PR_GetEnv\n",
243                          PR_GetCurrentThread()));
244                 break;
245 
246             case 9:
247                 (void)PR_NewTCPSocketPair(pair);
248                 DPRINTF((output,"Thread[0x%x] called PR_NewTCPSocketPair\n",
249                          PR_GetCurrentThread()));
250                 PR_Close(pair[0]);
251                 PR_Close(pair[1]);
252                 break;
253 
254             case 10:
255                 PR_SetConcurrency(2);
256                 DPRINTF((output,"Thread[0x%x] called PR_SetConcurrency\n",
257                          PR_GetCurrentThread()));
258                 break;
259 
260             case 11:
261                 PR_SetThreadPriority(PR_GetCurrentThread(), PR_PRIORITY_HIGH);
262                 DPRINTF((output,"Thread[0x%x] called PR_SetThreadPriority\n",
263                          PR_GetCurrentThread()));
264                 break;
265 
266             default:
267                 break;
268         } /* switch() */
269     }
270 }  /* OneShot */
271 
main(int argc,char ** argv)272 int main(int argc, char **argv)
273 {
274     PRStatus rv;
275     PRInt32 thread_cnt = DEFAULT_THREAD_COUNT;
276     PLOptStatus os;
277     PLOptState *opt = PL_CreateOptState(argc, argv, "dt:");
278 
279 #if defined(WIN32)
280     thread_provider = thread_win32;
281 #elif defined(_PR_PTHREADS)
282     thread_provider = thread_pthread;
283 #else
284     thread_provider = thread_nspr;
285 #endif
286 
287 
288     while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
289     {
290         if (PL_OPT_BAD == os) {
291             continue;
292         }
293         switch (opt->option)
294         {
295             case 'd':  /* debug mode */
296                 _debug_on = 1;
297                 break;
298             case 't':  /* thread count */
299                 thread_cnt = atoi(opt->value);
300                 break;
301             default:
302                 break;
303         }
304     }
305     PL_DestroyOptState(opt);
306 
307     PR_SetConcurrency(2);
308 
309     output = PR_GetSpecialFD(PR_StandardOutput);
310 
311     while (thread_cnt-- > 0)
312     {
313         rv = NSPRPUB_TESTS_CreateThread(OneShot, (void*)thread_cnt);
314         PR_ASSERT(PR_SUCCESS == rv);
315         PR_Sleep(PR_MillisecondsToInterval(5));
316     }
317     PR_Sleep(PR_SecondsToInterval(3));
318     return (PR_SUCCESS == PR_Cleanup()) ? 0 : 1;
319 }  /* main */
320 
321 /* foreign.c */
322