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: multiacc.c
8  *
9  * Description:
10  * This test creates multiple threads that accept on the
11  * same listening socket.
12  */
13 
14 #include "nspr.h"
15 
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 
20 #define NUM_SERVER_THREADS 10
21 
22 static int num_server_threads = NUM_SERVER_THREADS;
23 static PRThreadScope thread_scope = PR_GLOBAL_THREAD;
24 static PRBool exit_flag = PR_FALSE;
25 
ServerThreadFunc(void * arg)26 static void ServerThreadFunc(void *arg)
27 {
28     PRFileDesc *listenSock = (PRFileDesc *) arg;
29     PRFileDesc *acceptSock;
30     PRErrorCode err;
31     PRStatus status;
32 
33     while (!exit_flag) {
34         acceptSock = PR_Accept(listenSock, NULL, PR_INTERVAL_NO_TIMEOUT);
35         if (NULL == acceptSock) {
36             err = PR_GetError();
37             if (PR_PENDING_INTERRUPT_ERROR == err) {
38                 printf("server thread is interrupted\n");
39                 fflush(stdout);
40                 continue;
41             }
42             fprintf(stderr, "PR_Accept failed: %d\n", err);
43             exit(1);
44         }
45         status = PR_Close(acceptSock);
46         if (PR_FAILURE == status) {
47             fprintf(stderr, "PR_Close failed\n");
48             exit(1);
49         }
50     }
51 }
52 
main(int argc,char ** argv)53 int main(int argc, char **argv)
54 {
55     PRNetAddr serverAddr;
56     PRFileDesc *dummySock;
57     PRFileDesc *listenSock;
58     PRFileDesc *clientSock;
59     PRThread *dummyThread;
60     PRThread **serverThreads;
61     PRStatus status;
62     PRUint16 port;
63     int idx;
64     PRInt32 nbytes;
65     char buf[1024];
66 
67     serverThreads = (PRThread **)
68                     PR_Malloc(num_server_threads * sizeof(PRThread *));
69     if (NULL == serverThreads) {
70         fprintf(stderr, "PR_Malloc failed\n");
71         exit(1);
72     }
73 
74     /*
75      * Create a dummy listening socket and have the first
76      * (dummy) thread listen on it.  This is to ensure that
77      * the first thread becomes the I/O continuation thread
78      * in the pthreads implementation (see ptio.c) and remains
79      * so throughout the test, so that we never have to
80      * recycle the I/O continuation thread.
81      */
82     dummySock = PR_NewTCPSocket();
83     if (NULL == dummySock) {
84         fprintf(stderr, "PR_NewTCPSocket failed\n");
85         exit(1);
86     }
87     memset(&serverAddr, 0, sizeof(serverAddr));
88     status = PR_InitializeNetAddr(PR_IpAddrAny, 0, &serverAddr);
89     if (PR_FAILURE == status) {
90         fprintf(stderr, "PR_InitializeNetAddr failed\n");
91         exit(1);
92     }
93     status = PR_Bind(dummySock, &serverAddr);
94     if (PR_FAILURE == status) {
95         fprintf(stderr, "PR_Bind failed\n");
96         exit(1);
97     }
98     status = PR_Listen(dummySock, 5);
99     if (PR_FAILURE == status) {
100         fprintf(stderr, "PR_Listen failed\n");
101         exit(1);
102     }
103 
104     listenSock = PR_NewTCPSocket();
105     if (NULL == listenSock) {
106         fprintf(stderr, "PR_NewTCPSocket failed\n");
107         exit(1);
108     }
109     memset(&serverAddr, 0, sizeof(serverAddr));
110     status = PR_InitializeNetAddr(PR_IpAddrAny, 0, &serverAddr);
111     if (PR_FAILURE == status) {
112         fprintf(stderr, "PR_InitializeNetAddr failed\n");
113         exit(1);
114     }
115     status = PR_Bind(listenSock, &serverAddr);
116     if (PR_FAILURE == status) {
117         fprintf(stderr, "PR_Bind failed\n");
118         exit(1);
119     }
120     status = PR_GetSockName(listenSock, &serverAddr);
121     if (PR_FAILURE == status) {
122         fprintf(stderr, "PR_GetSockName failed\n");
123         exit(1);
124     }
125     port = PR_ntohs(serverAddr.inet.port);
126     status = PR_Listen(listenSock, 5);
127     if (PR_FAILURE == status) {
128         fprintf(stderr, "PR_Listen failed\n");
129         exit(1);
130     }
131 
132     printf("creating dummy thread\n");
133     fflush(stdout);
134     dummyThread = PR_CreateThread(PR_USER_THREAD,
135                                   ServerThreadFunc, dummySock, PR_PRIORITY_NORMAL,
136                                   thread_scope, PR_JOINABLE_THREAD, 0);
137     if (NULL == dummyThread) {
138         fprintf(stderr, "PR_CreateThread failed\n");
139         exit(1);
140     }
141     printf("sleeping one second before creating server threads\n");
142     fflush(stdout);
143     PR_Sleep(PR_SecondsToInterval(1));
144     for (idx = 0; idx < num_server_threads; idx++) {
145         serverThreads[idx] = PR_CreateThread(PR_USER_THREAD,
146                                              ServerThreadFunc, listenSock, PR_PRIORITY_NORMAL,
147                                              thread_scope, PR_JOINABLE_THREAD, 0);
148         if (NULL == serverThreads[idx]) {
149             fprintf(stderr, "PR_CreateThread failed\n");
150             exit(1);
151         }
152     }
153 
154     memset(&serverAddr, 0, sizeof(serverAddr));
155     PR_InitializeNetAddr(PR_IpAddrLoopback, port, &serverAddr);
156     clientSock = PR_NewTCPSocket();
157     if (NULL == clientSock) {
158         fprintf(stderr, "PR_NewTCPSocket failed\n");
159         exit(1);
160     }
161     printf("sleeping one second before connecting\n");
162     fflush(stdout);
163     PR_Sleep(PR_SecondsToInterval(1));
164     status = PR_Connect(clientSock, &serverAddr, PR_INTERVAL_NO_TIMEOUT);
165     if (PR_FAILURE == status) {
166         fprintf(stderr, "PR_Connect failed\n");
167         exit(1);
168     }
169     nbytes = PR_Read(clientSock, buf, sizeof(buf));
170     if (nbytes != 0) {
171         fprintf(stderr, "expected 0 bytes but got %d bytes\n", nbytes);
172         exit(1);
173     }
174     status = PR_Close(clientSock);
175     if (PR_FAILURE == status) {
176         fprintf(stderr, "PR_Close failed\n");
177         exit(1);
178     }
179     printf("sleeping one second before shutting down server threads\n");
180     fflush(stdout);
181     PR_Sleep(PR_SecondsToInterval(1));
182 
183     exit_flag = PR_TRUE;
184     status = PR_Interrupt(dummyThread);
185     if (PR_FAILURE == status) {
186         fprintf(stderr, "PR_Interrupt failed\n");
187         exit(1);
188     }
189     status = PR_JoinThread(dummyThread);
190     if (PR_FAILURE == status) {
191         fprintf(stderr, "PR_JoinThread failed\n");
192         exit(1);
193     }
194     for (idx = 0; idx < num_server_threads; idx++) {
195         status = PR_Interrupt(serverThreads[idx]);
196         if (PR_FAILURE == status) {
197             fprintf(stderr, "PR_Interrupt failed\n");
198             exit(1);
199         }
200         status = PR_JoinThread(serverThreads[idx]);
201         if (PR_FAILURE == status) {
202             fprintf(stderr, "PR_JoinThread failed\n");
203             exit(1);
204         }
205     }
206     PR_Free(serverThreads);
207     status = PR_Close(dummySock);
208     if (PR_FAILURE == status) {
209         fprintf(stderr, "PR_Close failed\n");
210         exit(1);
211     }
212     status = PR_Close(listenSock);
213     if (PR_FAILURE == status) {
214         fprintf(stderr, "PR_Close failed\n");
215         exit(1);
216     }
217 
218     printf("PASS\n");
219     return 0;
220 }
221