tmoacc.c (8849B)
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 #include "nspr.h" 7 8 #include <stdlib.h> 9 #include <string.h> 10 11 #include "plerror.h" 12 #include "plgetopt.h" 13 14 #ifdef DEBUG 15 # define PORT_INC_DO +100 16 #else 17 # define PORT_INC_DO 18 #endif 19 #ifdef IS_64 20 # define PORT_INC_3264 +200 21 #else 22 # define PORT_INC_3264 23 #endif 24 25 #define BASE_PORT 9867 PORT_INC_DO PORT_INC_3264 26 #define DEFAULT_THREADS 1 27 #define DEFAULT_BACKLOG 10 28 #define DEFAULT_TIMEOUT 10 29 #define RANDOM_RANGE 100 /* should be significantly smaller than RAND_MAX */ 30 31 typedef enum { running, stopped } Status; 32 33 typedef struct Shared { 34 PRLock* ml; 35 PRCondVar* cv; 36 PRBool passed; 37 PRBool random; 38 PRFileDesc* debug; 39 PRIntervalTime timeout; 40 PRFileDesc* listenSock; 41 Status status; 42 } Shared; 43 44 static PRIntervalTime Timeout(const Shared* shared) { 45 PRIntervalTime timeout = shared->timeout; 46 if (shared->random) { 47 PRIntervalTime half = timeout >> 1; /* one half of the interval */ 48 PRIntervalTime quarter = half >> 1; /* one quarter of the interval */ 49 /* something in [0..timeout / 2) */ 50 PRUint32 random = (rand() % RANDOM_RANGE) * half / RANDOM_RANGE; 51 timeout = (3 * quarter) + random; /* [75..125)% */ 52 } 53 return timeout; 54 } /* Timeout */ 55 56 static void Accept(void* arg) { 57 PRStatus rv; 58 char* buffer = NULL; 59 PRNetAddr clientAddr; 60 Shared* shared = (Shared*)arg; 61 PRInt32 recv_length = 0, flags = 0; 62 PRFileDesc* clientSock; 63 PRIntn toread, byte, bytes, loop = 0; 64 struct Descriptor { 65 PRInt32 length; 66 PRUint32 checksum; 67 } descriptor; 68 69 do { 70 PRUint32 checksum = 0; 71 if (NULL != shared->debug) { 72 PR_fprintf(shared->debug, "[%d]accepting ... ", loop++); 73 } 74 clientSock = PR_Accept(shared->listenSock, &clientAddr, Timeout(shared)); 75 if (clientSock != NULL) { 76 if (NULL != shared->debug) { 77 PR_fprintf(shared->debug, "reading length ... "); 78 } 79 bytes = PR_Recv(clientSock, &descriptor, sizeof(descriptor), flags, 80 Timeout(shared)); 81 if (sizeof(descriptor) == bytes) { 82 /* and, before doing something stupid ... */ 83 descriptor.length = PR_ntohl(descriptor.length); 84 descriptor.checksum = PR_ntohl(descriptor.checksum); 85 if (NULL != shared->debug) { 86 PR_fprintf(shared->debug, "%d bytes ... ", descriptor.length); 87 } 88 toread = descriptor.length; 89 if (recv_length < descriptor.length) { 90 if (NULL != buffer) { 91 PR_DELETE(buffer); 92 } 93 buffer = (char*)PR_MALLOC(descriptor.length); 94 recv_length = descriptor.length; 95 } 96 for (toread = descriptor.length; toread > 0; toread -= bytes) { 97 bytes = PR_Recv(clientSock, &buffer[descriptor.length - toread], 98 toread, flags, Timeout(shared)); 99 if (-1 == bytes) { 100 if (NULL != shared->debug) { 101 PR_fprintf(shared->debug, "read data failed..."); 102 } 103 bytes = 0; 104 } 105 } 106 } else if (NULL != shared->debug) { 107 PR_fprintf(shared->debug, "read desciptor failed..."); 108 descriptor.length = -1; 109 } 110 if (NULL != shared->debug) { 111 PR_fprintf(shared->debug, "closing"); 112 } 113 rv = PR_Shutdown(clientSock, PR_SHUTDOWN_BOTH); 114 if ((PR_FAILURE == rv) && (NULL != shared->debug)) { 115 PR_fprintf(shared->debug, " failed"); 116 shared->passed = PR_FALSE; 117 } 118 rv = PR_Close(clientSock); 119 if (PR_FAILURE == rv) 120 if (NULL != shared->debug) { 121 PR_fprintf(shared->debug, " failed"); 122 shared->passed = PR_FALSE; 123 } 124 if (descriptor.length > 0) { 125 for (byte = 0; byte < descriptor.length; ++byte) { 126 PRUint32 overflow = checksum & 0x80000000; 127 checksum = (checksum << 1); 128 if (0x00000000 != overflow) { 129 checksum += 1; 130 } 131 checksum += buffer[byte]; 132 } 133 if ((descriptor.checksum != checksum) && (NULL != shared->debug)) { 134 PR_fprintf(shared->debug, " ... data mismatch"); 135 shared->passed = PR_FALSE; 136 } 137 } else if (0 == descriptor.length) { 138 PR_Lock(shared->ml); 139 shared->status = stopped; 140 PR_NotifyCondVar(shared->cv); 141 PR_Unlock(shared->ml); 142 } 143 if (NULL != shared->debug) { 144 PR_fprintf(shared->debug, "\n"); 145 } 146 } else { 147 if (PR_PENDING_INTERRUPT_ERROR != PR_GetError()) { 148 if (NULL != shared->debug) { 149 PL_PrintError("Accept"); 150 } 151 shared->passed = PR_FALSE; 152 } 153 } 154 } while (running == shared->status); 155 if (NULL != buffer) { 156 PR_DELETE(buffer); 157 } 158 } /* Accept */ 159 160 PRIntn Tmoacc(PRIntn argc, char** argv) { 161 PRStatus rv; 162 PRIntn exitStatus; 163 PRIntn index; 164 Shared* shared; 165 PLOptStatus os; 166 PRThread** thread; 167 PRNetAddr listenAddr; 168 PRSocketOptionData sockOpt; 169 PRIntn timeout = DEFAULT_TIMEOUT; 170 PRIntn threads = DEFAULT_THREADS; 171 PRIntn backlog = DEFAULT_BACKLOG; 172 PRThreadScope thread_scope = PR_LOCAL_THREAD; 173 174 PLOptState* opt = PL_CreateOptState(argc, argv, "dGb:t:T:R"); 175 176 shared = PR_NEWZAP(Shared); 177 178 shared->debug = NULL; 179 shared->passed = PR_TRUE; 180 shared->random = PR_TRUE; 181 shared->status = running; 182 shared->ml = PR_NewLock(); 183 shared->cv = PR_NewCondVar(shared->ml); 184 185 while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) { 186 if (PL_OPT_BAD == os) { 187 continue; 188 } 189 switch (opt->option) { 190 case 'd': /* debug mode */ 191 shared->debug = PR_GetSpecialFD(PR_StandardError); 192 break; 193 case 'G': /* use global threads */ 194 thread_scope = PR_GLOBAL_THREAD; 195 break; 196 case 'b': /* size of listen backlog */ 197 backlog = atoi(opt->value); 198 break; 199 case 't': /* number of threads doing accept */ 200 threads = atoi(opt->value); 201 break; 202 case 'T': /* timeout used for network operations */ 203 timeout = atoi(opt->value); 204 break; 205 case 'R': /* randomize the timeout values */ 206 shared->random = PR_TRUE; 207 break; 208 default: 209 break; 210 } 211 } 212 PL_DestroyOptState(opt); 213 if (0 == threads) { 214 threads = DEFAULT_THREADS; 215 } 216 if (0 == backlog) { 217 backlog = DEFAULT_BACKLOG; 218 } 219 if (0 == timeout) { 220 timeout = DEFAULT_TIMEOUT; 221 } 222 223 memset(&listenAddr, 0, sizeof(listenAddr)); 224 rv = PR_InitializeNetAddr(PR_IpAddrAny, BASE_PORT, &listenAddr); 225 PR_ASSERT(PR_SUCCESS == rv); 226 227 shared->timeout = PR_SecondsToInterval(timeout); 228 229 /* First bind to the socket */ 230 shared->listenSock = PR_NewTCPSocket(); 231 if (shared->listenSock) { 232 sockOpt.option = PR_SockOpt_Reuseaddr; 233 sockOpt.value.reuse_addr = PR_TRUE; 234 rv = PR_SetSocketOption(shared->listenSock, &sockOpt); 235 PR_ASSERT(PR_SUCCESS == rv); 236 rv = PR_Bind(shared->listenSock, &listenAddr); 237 if (rv != PR_FAILURE) { 238 rv = PR_Listen(shared->listenSock, threads + backlog); 239 if (PR_SUCCESS == rv) { 240 thread = (PRThread**)PR_CALLOC(threads * sizeof(PRThread*)); 241 for (index = 0; index < threads; ++index) { 242 thread[index] = PR_CreateThread(PR_USER_THREAD, Accept, shared, 243 PR_PRIORITY_NORMAL, thread_scope, 244 PR_JOINABLE_THREAD, 0); 245 PR_ASSERT(NULL != thread[index]); 246 } 247 248 PR_Lock(shared->ml); 249 while (shared->status == running) { 250 PR_WaitCondVar(shared->cv, PR_INTERVAL_NO_TIMEOUT); 251 } 252 PR_Unlock(shared->ml); 253 for (index = 0; index < threads; ++index) { 254 rv = PR_Interrupt(thread[index]); 255 PR_ASSERT(PR_SUCCESS == rv); 256 rv = PR_JoinThread(thread[index]); 257 PR_ASSERT(PR_SUCCESS == rv); 258 } 259 PR_DELETE(thread); 260 } else { 261 if (shared->debug) { 262 PL_PrintError("Listen"); 263 } 264 shared->passed = PR_FALSE; 265 } 266 } else { 267 if (shared->debug) { 268 PL_PrintError("Bind"); 269 } 270 shared->passed = PR_FALSE; 271 } 272 273 PR_Close(shared->listenSock); 274 } else { 275 if (shared->debug) { 276 PL_PrintError("Create"); 277 } 278 shared->passed = PR_FALSE; 279 } 280 281 PR_DestroyCondVar(shared->cv); 282 PR_DestroyLock(shared->ml); 283 284 PR_fprintf(PR_GetSpecialFD(PR_StandardError), "%s\n", 285 ((shared->passed) ? "PASSED" : "FAILED")); 286 287 exitStatus = (shared->passed) ? 0 : 1; 288 PR_DELETE(shared); 289 return exitStatus; 290 } 291 292 int main(int argc, char** argv) { 293 return (PR_VersionCheck(PR_VERSION)) ? PR_Initialize(Tmoacc, argc, argv, 4) 294 : -1; 295 } /* main */ 296 297 /* tmoacc */