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 ***** */
48 #define printf PR_LogPrint
51 #define BASE_PORT 9867
52 #define DEFAULT_THREADS 1
53 #define DEFAULT_BACKLOG 10
54 #define DEFAULT_TIMEOUT 10
55 #define RANDOM_RANGE 100 /* should be significantly smaller than RAND_MAX */
57 typedef enum {running
, stopped
} Status
;
66 PRIntervalTime timeout
;
67 PRFileDesc
*listenSock
;
71 static PRIntervalTime
Timeout(const Shared
*shared
)
73 PRIntervalTime timeout
= shared
->timeout
;
76 PRIntervalTime half
= timeout
>> 1; /* one half of the interval */
77 PRIntervalTime quarter
= half
>> 1; /* one quarter of the interval */
78 /* something in [0..timeout / 2) */
79 PRUint32 random
= (rand() % RANDOM_RANGE
) * half
/ RANDOM_RANGE
;
80 timeout
= (3 * quarter
) + random
; /* [75..125)% */
85 static void Accept(void *arg
)
90 Shared
*shared
= (Shared
*)arg
;
91 PRInt32 recv_length
= 0, flags
= 0;
92 PRFileDesc
*clientSock
;
93 PRIntn toread
, byte
, bytes
, loop
= 0;
94 struct Descriptor
{ PRInt32 length
; PRUint32 checksum
; } descriptor
;
98 PRUint32 checksum
= 0;
99 if (NULL
!= shared
->debug
)
100 PR_fprintf(shared
->debug
, "[%d]accepting ... ", loop
++);
101 clientSock
= PR_Accept(
102 shared
->listenSock
, &clientAddr
, Timeout(shared
));
103 if (clientSock
!= NULL
)
105 if (NULL
!= shared
->debug
)
106 PR_fprintf(shared
->debug
, "reading length ... ");
108 clientSock
, &descriptor
, sizeof(descriptor
),
109 flags
, Timeout(shared
));
110 if (sizeof(descriptor
) == bytes
)
112 /* and, before doing something stupid ... */
113 descriptor
.length
= PR_ntohl(descriptor
.length
);
114 descriptor
.checksum
= PR_ntohl(descriptor
.checksum
);
115 if (NULL
!= shared
->debug
)
116 PR_fprintf(shared
->debug
, "%d bytes ... ", descriptor
.length
);
117 toread
= descriptor
.length
;
118 if (recv_length
< descriptor
.length
)
120 if (NULL
!= buffer
) PR_DELETE(buffer
);
121 buffer
= (char*)PR_MALLOC(descriptor
.length
);
122 recv_length
= descriptor
.length
;
124 for (toread
= descriptor
.length
; toread
> 0; toread
-= bytes
)
127 clientSock
, &buffer
[descriptor
.length
- toread
],
128 toread
, flags
, Timeout(shared
));
131 if (NULL
!= shared
->debug
)
132 PR_fprintf(shared
->debug
, "read data failed...");
137 else if (NULL
!= shared
->debug
)
139 PR_fprintf(shared
->debug
, "read desciptor failed...");
140 descriptor
.length
= -1;
142 if (NULL
!= shared
->debug
)
143 PR_fprintf(shared
->debug
, "closing");
144 rv
= PR_Shutdown(clientSock
, PR_SHUTDOWN_BOTH
);
145 if ((PR_FAILURE
== rv
) && (NULL
!= shared
->debug
))
147 PR_fprintf(shared
->debug
, " failed");
148 shared
->passed
= PR_FALSE
;
150 rv
= PR_Close(clientSock
);
151 if (PR_FAILURE
== rv
) if (NULL
!= shared
->debug
)
153 PR_fprintf(shared
->debug
, " failed");
154 shared
->passed
= PR_FALSE
;
156 if (descriptor
.length
> 0)
158 for (byte
= 0; byte
< descriptor
.length
; ++byte
)
160 PRUint32 overflow
= checksum
& 0x80000000;
161 checksum
= (checksum
<< 1);
162 if (0x00000000 != overflow
) checksum
+= 1;
163 checksum
+= buffer
[byte
];
165 if ((descriptor
.checksum
!= checksum
) && (NULL
!= shared
->debug
))
167 PR_fprintf(shared
->debug
, " ... data mismatch");
168 shared
->passed
= PR_FALSE
;
171 else if (0 == descriptor
.length
)
174 shared
->status
= stopped
;
175 PR_NotifyCondVar(shared
->cv
);
176 PR_Unlock(shared
->ml
);
178 if (NULL
!= shared
->debug
)
179 PR_fprintf(shared
->debug
, "\n");
183 if (PR_PENDING_INTERRUPT_ERROR
!= PR_GetError())
185 if (NULL
!= shared
->debug
) PL_PrintError("Accept");
186 shared
->passed
= PR_FALSE
;
189 } while (running
== shared
->status
);
190 if (NULL
!= buffer
) PR_DELETE(buffer
);
193 PRIntn
Tmoacc(PRIntn argc
, char **argv
)
201 PRNetAddr listenAddr
;
202 PRSocketOptionData sockOpt
;
203 PRIntn timeout
= DEFAULT_TIMEOUT
;
204 PRIntn threads
= DEFAULT_THREADS
;
205 PRIntn backlog
= DEFAULT_BACKLOG
;
206 PRThreadScope thread_scope
= PR_LOCAL_THREAD
;
208 PLOptState
*opt
= PL_CreateOptState(argc
, argv
, "dGb:t:T:R");
210 shared
= PR_NEWZAP(Shared
);
212 shared
->debug
= NULL
;
213 shared
->passed
= PR_TRUE
;
214 shared
->random
= PR_TRUE
;
215 shared
->status
= running
;
216 shared
->ml
= PR_NewLock();
217 shared
->cv
= PR_NewCondVar(shared
->ml
);
219 while (PL_OPT_EOL
!= (os
= PL_GetNextOpt(opt
)))
221 if (PL_OPT_BAD
== os
) continue;
224 case 'd': /* debug mode */
225 shared
->debug
= PR_GetSpecialFD(PR_StandardError
);
227 case 'G': /* use global threads */
228 thread_scope
= PR_GLOBAL_THREAD
;
230 case 'b': /* size of listen backlog */
231 backlog
= atoi(opt
->value
);
233 case 't': /* number of threads doing accept */
234 threads
= atoi(opt
->value
);
236 case 'T': /* timeout used for network operations */
237 timeout
= atoi(opt
->value
);
239 case 'R': /* randomize the timeout values */
240 shared
->random
= PR_TRUE
;
246 PL_DestroyOptState(opt
);
247 if (0 == threads
) threads
= DEFAULT_THREADS
;
248 if (0 == backlog
) backlog
= DEFAULT_BACKLOG
;
249 if (0 == timeout
) timeout
= DEFAULT_TIMEOUT
;
252 memset(&listenAddr
, 0, sizeof(listenAddr
));
253 rv
= PR_InitializeNetAddr(PR_IpAddrAny
, BASE_PORT
, &listenAddr
);
254 PR_ASSERT(PR_SUCCESS
== rv
);
256 shared
->timeout
= PR_SecondsToInterval(timeout
);
258 /* First bind to the socket */
259 shared
->listenSock
= PR_NewTCPSocket();
260 if (shared
->listenSock
)
262 sockOpt
.option
= PR_SockOpt_Reuseaddr
;
263 sockOpt
.value
.reuse_addr
= PR_TRUE
;
264 rv
= PR_SetSocketOption(shared
->listenSock
, &sockOpt
);
265 PR_ASSERT(PR_SUCCESS
== rv
);
266 rv
= PR_Bind(shared
->listenSock
, &listenAddr
);
267 if (rv
!= PR_FAILURE
)
269 rv
= PR_Listen(shared
->listenSock
, threads
+ backlog
);
270 if (PR_SUCCESS
== rv
)
272 thread
= (PRThread
**)PR_CALLOC(threads
* sizeof(PRThread
*));
273 for (index
= 0; index
< threads
; ++index
)
275 thread
[index
] = PR_CreateThread(
276 PR_USER_THREAD
, Accept
, shared
,
277 PR_PRIORITY_NORMAL
, thread_scope
,
278 PR_JOINABLE_THREAD
, 0);
279 PR_ASSERT(NULL
!= thread
[index
]);
283 while (shared
->status
== running
)
284 PR_WaitCondVar(shared
->cv
, PR_INTERVAL_NO_TIMEOUT
);
285 PR_Unlock(shared
->ml
);
286 for (index
= 0; index
< threads
; ++index
)
288 rv
= PR_Interrupt(thread
[index
]);
289 PR_ASSERT(PR_SUCCESS
== rv
);
290 rv
= PR_JoinThread(thread
[index
]);
291 PR_ASSERT(PR_SUCCESS
== rv
);
297 if (shared
->debug
) PL_PrintError("Listen");
298 shared
->passed
= PR_FALSE
;
303 if (shared
->debug
) PL_PrintError("Bind");
304 shared
->passed
= PR_FALSE
;
307 PR_Close(shared
->listenSock
);
311 if (shared
->debug
) PL_PrintError("Create");
312 shared
->passed
= PR_FALSE
;
315 PR_DestroyCondVar(shared
->cv
);
316 PR_DestroyLock(shared
->ml
);
319 PR_GetSpecialFD(PR_StandardError
), "%s\n",
320 ((shared
->passed
) ? "PASSED" : "FAILED"));
322 exitStatus
= (shared
->passed
) ? 0 : 1;
327 int main(int argc
, char **argv
)
329 return (PR_VersionCheck(PR_VERSION
)) ?
330 PR_Initialize(Tmoacc
, argc
, argv
, 4) : -1;