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 ***** */
46 #define BASE_PORT 9867
47 #define DEFAULT_THREADS 1
48 #define DEFAULT_BACKLOG 10
49 #define DEFAULT_TIMEOUT 10
50 #define RANDOM_RANGE 100 /* should be significantly smaller than RAND_MAX */
52 typedef enum {running, stopped} Status;
61 PRIntervalTime timeout;
62 PRFileDesc *listenSock;
66 static PRIntervalTime Timeout(const Shared *shared)
68 PRIntervalTime timeout = shared->timeout;
71 PRIntervalTime half = timeout >> 1; /* one half of the interval */
72 PRIntervalTime quarter = half >> 1; /* one quarter of the interval */
73 /* something in [0..timeout / 2) */
74 PRUint32 random = (rand() % RANDOM_RANGE) * half / RANDOM_RANGE;
75 timeout = (3 * quarter) + random; /* [75..125)% */
80 static void Accept(void *arg)
85 Shared *shared = (Shared*)arg;
86 PRInt32 recv_length = 0, flags = 0;
87 PRFileDesc *clientSock;
88 PRIntn toread, byte, bytes, loop = 0;
89 struct Descriptor { PRInt32 length; PRUint32 checksum; } descriptor;
93 PRUint32 checksum = 0;
94 if (NULL != shared->debug)
95 PR_fprintf(shared->debug, "[%d]accepting ... ", loop++);
96 clientSock = PR_Accept(
97 shared->listenSock, &clientAddr, Timeout(shared));
98 if (clientSock != NULL)
100 if (NULL != shared->debug)
101 PR_fprintf(shared->debug, "reading length ... ");
103 clientSock, &descriptor, sizeof(descriptor),
104 flags, Timeout(shared));
105 if (sizeof(descriptor) == bytes)
107 /* and, before doing something stupid ... */
108 descriptor.length = PR_ntohl(descriptor.length);
109 descriptor.checksum = PR_ntohl(descriptor.checksum);
110 if (NULL != shared->debug)
111 PR_fprintf(shared->debug, "%d bytes ... ", descriptor.length);
112 toread = descriptor.length;
113 if (recv_length < descriptor.length)
115 if (NULL != buffer) PR_DELETE(buffer);
116 buffer = (char*)PR_MALLOC(descriptor.length);
117 recv_length = descriptor.length;
119 for (toread = descriptor.length; toread > 0; toread -= bytes)
122 clientSock, &buffer[descriptor.length - toread],
123 toread, flags, Timeout(shared));
126 if (NULL != shared->debug)
127 PR_fprintf(shared->debug, "read data failed...");
132 else if (NULL != shared->debug)
134 PR_fprintf(shared->debug, "read desciptor failed...");
135 descriptor.length = -1;
137 if (NULL != shared->debug)
138 PR_fprintf(shared->debug, "closing");
139 rv = PR_Shutdown(clientSock, PR_SHUTDOWN_BOTH);
140 if ((PR_FAILURE == rv) && (NULL != shared->debug))
142 PR_fprintf(shared->debug, " failed");
143 shared->passed = PR_FALSE;
145 rv = PR_Close(clientSock);
146 if (PR_FAILURE == rv) if (NULL != shared->debug)
148 PR_fprintf(shared->debug, " failed");
149 shared->passed = PR_FALSE;
151 if (descriptor.length > 0)
153 for (byte = 0; byte < descriptor.length; ++byte)
155 PRUint32 overflow = checksum & 0x80000000;
156 checksum = (checksum << 1);
157 if (0x00000000 != overflow) checksum += 1;
158 checksum += buffer[byte];
160 if ((descriptor.checksum != checksum) && (NULL != shared->debug))
162 PR_fprintf(shared->debug, " ... data mismatch");
163 shared->passed = PR_FALSE;
166 else if (0 == descriptor.length)
169 shared->status = stopped;
170 PR_NotifyCondVar(shared->cv);
171 PR_Unlock(shared->ml);
173 if (NULL != shared->debug)
174 PR_fprintf(shared->debug, "\n");
178 if (PR_PENDING_INTERRUPT_ERROR != PR_GetError())
180 if (NULL != shared->debug) PL_PrintError("Accept");
181 shared->passed = PR_FALSE;
184 } while (running == shared->status);
185 if (NULL != buffer) PR_DELETE(buffer);
188 PRIntn Tmoacc(PRIntn argc, char **argv)
196 PRNetAddr listenAddr;
197 PRSocketOptionData sockOpt;
198 PRIntn timeout = DEFAULT_TIMEOUT;
199 PRIntn threads = DEFAULT_THREADS;
200 PRIntn backlog = DEFAULT_BACKLOG;
201 PRThreadScope thread_scope = PR_LOCAL_THREAD;
203 PLOptState *opt = PL_CreateOptState(argc, argv, "dGb:t:T:R");
205 shared = PR_NEWZAP(Shared);
207 shared->debug = NULL;
208 shared->passed = PR_TRUE;
209 shared->random = PR_TRUE;
210 shared->status = running;
211 shared->ml = PR_NewLock();
212 shared->cv = PR_NewCondVar(shared->ml);
214 while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
216 if (PL_OPT_BAD == os) continue;
219 case 'd': /* debug mode */
220 shared->debug = PR_GetSpecialFD(PR_StandardError);
222 case 'G': /* use global threads */
223 thread_scope = PR_GLOBAL_THREAD;
225 case 'b': /* size of listen backlog */
226 backlog = atoi(opt->value);
228 case 't': /* number of threads doing accept */
229 threads = atoi(opt->value);
231 case 'T': /* timeout used for network operations */
232 timeout = atoi(opt->value);
234 case 'R': /* randomize the timeout values */
235 shared->random = PR_TRUE;
241 PL_DestroyOptState(opt);
242 if (0 == threads) threads = DEFAULT_THREADS;
243 if (0 == backlog) backlog = DEFAULT_BACKLOG;
244 if (0 == timeout) timeout = DEFAULT_TIMEOUT;
247 memset(&listenAddr, 0, sizeof(listenAddr));
248 rv = PR_InitializeNetAddr(PR_IpAddrAny, BASE_PORT, &listenAddr);
249 PR_ASSERT(PR_SUCCESS == rv);
251 shared->timeout = PR_SecondsToInterval(timeout);
253 /* First bind to the socket */
254 shared->listenSock = PR_NewTCPSocket();
255 if (shared->listenSock)
257 sockOpt.option = PR_SockOpt_Reuseaddr;
258 sockOpt.value.reuse_addr = PR_TRUE;
259 rv = PR_SetSocketOption(shared->listenSock, &sockOpt);
260 PR_ASSERT(PR_SUCCESS == rv);
261 rv = PR_Bind(shared->listenSock, &listenAddr);
262 if (rv != PR_FAILURE)
264 rv = PR_Listen(shared->listenSock, threads + backlog);
265 if (PR_SUCCESS == rv)
267 thread = (PRThread**)PR_CALLOC(threads * sizeof(PRThread*));
268 for (index = 0; index < threads; ++index)
270 thread[index] = PR_CreateThread(
271 PR_USER_THREAD, Accept, shared,
272 PR_PRIORITY_NORMAL, thread_scope,
273 PR_JOINABLE_THREAD, 0);
274 PR_ASSERT(NULL != thread[index]);
278 while (shared->status == running)
279 PR_WaitCondVar(shared->cv, PR_INTERVAL_NO_TIMEOUT);
280 PR_Unlock(shared->ml);
281 for (index = 0; index < threads; ++index)
283 rv = PR_Interrupt(thread[index]);
284 PR_ASSERT(PR_SUCCESS== rv);
285 rv = PR_JoinThread(thread[index]);
286 PR_ASSERT(PR_SUCCESS== rv);
292 if (shared->debug) PL_PrintError("Listen");
293 shared->passed = PR_FALSE;
298 if (shared->debug) PL_PrintError("Bind");
299 shared->passed = PR_FALSE;
302 PR_Close(shared->listenSock);
306 if (shared->debug) PL_PrintError("Create");
307 shared->passed = PR_FALSE;
310 PR_DestroyCondVar(shared->cv);
311 PR_DestroyLock(shared->ml);
314 PR_GetSpecialFD(PR_StandardError), "%s\n",
315 ((shared->passed) ? "PASSED" : "FAILED"));
317 exitStatus = (shared->passed) ? 0 : 1;
322 int main(int argc, char **argv)
324 return (PR_VersionCheck(PR_VERSION)) ?
325 PR_Initialize(Tmoacc, argc, argv, 4) : -1;