server_test.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693
  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. ** This server simulates a server running in loopback mode.
  8. **
  9. ** The idea is that a single server is created. The server initially creates
  10. ** a number of worker threads. Then, with the server running, a number of
  11. ** clients are created which start requesting service from the server.
  12. **
  13. **
  14. ** Modification History:
  15. ** 19-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
  16. ** The debug mode will print all of the printfs associated with this test.
  17. ** The regress mode will be the default mode. Since the regress tool limits
  18. ** the output to a one line status:PASS or FAIL,all of the printf statements
  19. ** have been handled with an if (debug_mode) statement.
  20. ***********************************************************************/
  21. /***********************************************************************
  22. ** Includes
  23. ***********************************************************************/
  24. /* Used to get the command line option */
  25. #include "plgetopt.h"
  26. #include "nspr.h"
  27. #include "pprthred.h"
  28. #include <string.h>
  29. #define PORT 15004
  30. #define THREAD_STACKSIZE 0
  31. #define PASS 0
  32. #define FAIL 1
  33. static int debug_mode = 0;
  34. static int failed_already = 0;
  35. static int _iterations = 1000;
  36. static int _clients = 1;
  37. static int _client_data = 250;
  38. static int _server_data = (8*1024);
  39. static PRThreadScope ServerScope, ClientScope;
  40. #define SERVER "Server"
  41. #define MAIN "Main"
  42. #define SERVER_STATE_STARTUP 0
  43. #define SERVER_STATE_READY 1
  44. #define SERVER_STATE_DYING 2
  45. #define SERVER_STATE_DEAD 4
  46. int ServerState;
  47. PRLock *ServerStateCVLock;
  48. PRCondVar *ServerStateCV;
  49. #undef DEBUGPRINTS
  50. #ifdef DEBUGPRINTS
  51. #define DPRINTF printf
  52. #else
  53. #define DPRINTF
  54. #endif
  55. /***********************************************************************
  56. ** PRIVATE FUNCTION: Test_Result
  57. ** DESCRIPTION: Used in conjunction with the regress tool, prints out the
  58. ** status of the test case.
  59. ** INPUTS: PASS/FAIL
  60. ** OUTPUTS: None
  61. ** RETURN: None
  62. ** SIDE EFFECTS:
  63. **
  64. ** RESTRICTIONS:
  65. ** None
  66. ** MEMORY: NA
  67. ** ALGORITHM: Determine what the status is and print accordingly.
  68. **
  69. ***********************************************************************/
  70. static void Test_Result (int result)
  71. {
  72. switch (result)
  73. {
  74. case PASS:
  75. printf ("PASS\n");
  76. break;
  77. case FAIL:
  78. printf ("FAIL\n");
  79. failed_already = 1;
  80. break;
  81. default:
  82. break;
  83. }
  84. }
  85. static void do_work(void);
  86. /* --- Server state functions --------------------------------------------- */
  87. void
  88. SetServerState(char *waiter, PRInt32 state)
  89. {
  90. PR_Lock(ServerStateCVLock);
  91. ServerState = state;
  92. PR_NotifyCondVar(ServerStateCV);
  93. if (debug_mode) {
  94. DPRINTF("\t%s changed state to %d\n", waiter, state);
  95. }
  96. PR_Unlock(ServerStateCVLock);
  97. }
  98. int
  99. WaitServerState(char *waiter, PRInt32 state)
  100. {
  101. PRInt32 rv;
  102. PR_Lock(ServerStateCVLock);
  103. if (debug_mode) {
  104. DPRINTF("\t%s waiting for state %d\n", waiter, state);
  105. }
  106. while(!(ServerState & state)) {
  107. PR_WaitCondVar(ServerStateCV, PR_INTERVAL_NO_TIMEOUT);
  108. }
  109. rv = ServerState;
  110. if (debug_mode) DPRINTF("\t%s resuming from wait for state %d; state now %d\n",
  111. waiter, state, ServerState);
  112. PR_Unlock(ServerStateCVLock);
  113. return rv;
  114. }
  115. /* --- Server Functions ------------------------------------------- */
  116. PRLock *workerThreadsLock;
  117. PRInt32 workerThreads;
  118. PRInt32 workerThreadsBusy;
  119. void
  120. WorkerThreadFunc(void *_listenSock)
  121. {
  122. PRFileDesc *listenSock = (PRFileDesc *)_listenSock;
  123. PRInt32 bytesRead;
  124. PRInt32 bytesWritten;
  125. char *dataBuf;
  126. char *sendBuf;
  127. if (debug_mode) DPRINTF("\tServer buffer is %d bytes; %d data, %d netaddrs\n",
  128. _client_data+(2*sizeof(PRNetAddr))+32, _client_data, (2*sizeof(PRNetAddr))+32);
  129. dataBuf = (char *)PR_MALLOC(_client_data + 2*sizeof(PRNetAddr) + 32);
  130. if (!dataBuf)
  131. if (debug_mode) {
  132. printf("\tServer could not malloc space!?\n");
  133. }
  134. sendBuf = (char *)PR_MALLOC(_server_data *sizeof(char));
  135. if (!sendBuf)
  136. if (debug_mode) {
  137. printf("\tServer could not malloc space!?\n");
  138. }
  139. if (debug_mode) {
  140. DPRINTF("\tServer worker thread running\n");
  141. }
  142. while(1) {
  143. PRInt32 bytesToRead = _client_data;
  144. PRInt32 bytesToWrite = _server_data;
  145. PRFileDesc *newSock;
  146. PRNetAddr *rAddr;
  147. PRInt32 loops = 0;
  148. loops++;
  149. if (debug_mode) {
  150. DPRINTF("\tServer thread going into accept\n");
  151. }
  152. bytesRead = PR_AcceptRead(listenSock,
  153. &newSock,
  154. &rAddr,
  155. dataBuf,
  156. bytesToRead,
  157. PR_INTERVAL_NO_TIMEOUT);
  158. if (bytesRead < 0) {
  159. if (debug_mode) {
  160. printf("\tServer error in accept (%d)\n", bytesRead);
  161. }
  162. continue;
  163. }
  164. if (debug_mode) {
  165. DPRINTF("\tServer accepted connection (%d bytes)\n", bytesRead);
  166. }
  167. PR_AtomicIncrement(&workerThreadsBusy);
  168. if (workerThreadsBusy == workerThreads) {
  169. PR_Lock(workerThreadsLock);
  170. if (workerThreadsBusy == workerThreads) {
  171. PRThread *WorkerThread;
  172. WorkerThread = PR_CreateThread(
  173. PR_SYSTEM_THREAD,
  174. WorkerThreadFunc,
  175. listenSock,
  176. PR_PRIORITY_NORMAL,
  177. ServerScope,
  178. PR_UNJOINABLE_THREAD,
  179. THREAD_STACKSIZE);
  180. if (!WorkerThread) {
  181. if (debug_mode) {
  182. printf("Error creating client thread %d\n", workerThreads);
  183. }
  184. } else {
  185. PR_AtomicIncrement(&workerThreads);
  186. if (debug_mode) {
  187. DPRINTF("\tServer creates worker (%d)\n", workerThreads);
  188. }
  189. }
  190. }
  191. PR_Unlock(workerThreadsLock);
  192. }
  193. bytesToRead -= bytesRead;
  194. while (bytesToRead) {
  195. bytesRead = PR_Recv(newSock,
  196. dataBuf,
  197. bytesToRead,
  198. 0,
  199. PR_INTERVAL_NO_TIMEOUT);
  200. if (bytesRead < 0) {
  201. if (debug_mode) {
  202. printf("\tServer error receiving data (%d)\n", bytesRead);
  203. }
  204. continue;
  205. }
  206. if (debug_mode) {
  207. DPRINTF("\tServer received %d bytes\n", bytesRead);
  208. }
  209. }
  210. bytesWritten = PR_Send(newSock,
  211. sendBuf,
  212. bytesToWrite,
  213. 0,
  214. PR_INTERVAL_NO_TIMEOUT);
  215. if (bytesWritten != _server_data) {
  216. if (debug_mode) printf("\tError sending data to client (%d, %d)\n",
  217. bytesWritten, PR_GetOSError());
  218. } else {
  219. if (debug_mode) {
  220. DPRINTF("\tServer sent %d bytes\n", bytesWritten);
  221. }
  222. }
  223. PR_Close(newSock);
  224. PR_AtomicDecrement(&workerThreadsBusy);
  225. }
  226. }
  227. PRFileDesc *
  228. ServerSetup(void)
  229. {
  230. PRFileDesc *listenSocket;
  231. PRSocketOptionData sockOpt;
  232. PRNetAddr serverAddr;
  233. PRThread *WorkerThread;
  234. if ((listenSocket = PR_NewTCPSocket()) == NULL) {
  235. if (debug_mode) {
  236. printf("\tServer error creating listen socket\n");
  237. }
  238. else {
  239. Test_Result(FAIL);
  240. }
  241. return NULL;
  242. }
  243. sockOpt.option = PR_SockOpt_Reuseaddr;
  244. sockOpt.value.reuse_addr = PR_TRUE;
  245. if (PR_SetSocketOption(listenSocket, &sockOpt) != PR_SUCCESS) {
  246. if (debug_mode) printf("\tServer error setting socket option: OS error %d\n",
  247. PR_GetOSError());
  248. else {
  249. Test_Result(FAIL);
  250. }
  251. PR_Close(listenSocket);
  252. return NULL;
  253. }
  254. memset(&serverAddr, 0, sizeof(PRNetAddr));
  255. serverAddr.inet.family = PR_AF_INET;
  256. serverAddr.inet.port = PR_htons(PORT);
  257. serverAddr.inet.ip = PR_htonl(PR_INADDR_ANY);
  258. if (PR_Bind(listenSocket, &serverAddr) != PR_SUCCESS) {
  259. if (debug_mode) printf("\tServer error binding to server address: OS error %d\n",
  260. PR_GetOSError());
  261. else {
  262. Test_Result(FAIL);
  263. }
  264. PR_Close(listenSocket);
  265. return NULL;
  266. }
  267. if (PR_Listen(listenSocket, 128) != PR_SUCCESS) {
  268. if (debug_mode) {
  269. printf("\tServer error listening to server socket\n");
  270. }
  271. else {
  272. Test_Result(FAIL);
  273. }
  274. PR_Close(listenSocket);
  275. return NULL;
  276. }
  277. /* Create Clients */
  278. workerThreads = 0;
  279. workerThreadsBusy = 0;
  280. workerThreadsLock = PR_NewLock();
  281. WorkerThread = PR_CreateThread(
  282. PR_SYSTEM_THREAD,
  283. WorkerThreadFunc,
  284. listenSocket,
  285. PR_PRIORITY_NORMAL,
  286. ServerScope,
  287. PR_UNJOINABLE_THREAD,
  288. THREAD_STACKSIZE);
  289. if (!WorkerThread) {
  290. if (debug_mode) {
  291. printf("error creating working thread\n");
  292. }
  293. PR_Close(listenSocket);
  294. return NULL;
  295. }
  296. PR_AtomicIncrement(&workerThreads);
  297. if (debug_mode) {
  298. DPRINTF("\tServer created primordial worker thread\n");
  299. }
  300. return listenSocket;
  301. }
  302. /* The main server loop */
  303. void
  304. ServerThreadFunc(void *unused)
  305. {
  306. PRFileDesc *listenSocket;
  307. /* Do setup */
  308. listenSocket = ServerSetup();
  309. if (!listenSocket) {
  310. SetServerState(SERVER, SERVER_STATE_DEAD);
  311. } else {
  312. if (debug_mode) {
  313. DPRINTF("\tServer up\n");
  314. }
  315. /* Tell clients they can start now. */
  316. SetServerState(SERVER, SERVER_STATE_READY);
  317. /* Now wait for server death signal */
  318. WaitServerState(SERVER, SERVER_STATE_DYING);
  319. /* Cleanup */
  320. SetServerState(SERVER, SERVER_STATE_DEAD);
  321. }
  322. }
  323. /* --- Client Functions ------------------------------------------- */
  324. PRInt32 numRequests;
  325. PRInt32 numClients;
  326. PRMonitor *clientMonitor;
  327. void
  328. ClientThreadFunc(void *unused)
  329. {
  330. PRNetAddr serverAddr;
  331. PRFileDesc *clientSocket;
  332. char *sendBuf;
  333. char *recvBuf;
  334. PRInt32 rv;
  335. PRInt32 bytesNeeded;
  336. sendBuf = (char *)PR_MALLOC(_client_data * sizeof(char));
  337. if (!sendBuf)
  338. if (debug_mode) {
  339. printf("\tClient could not malloc space!?\n");
  340. }
  341. recvBuf = (char *)PR_MALLOC(_server_data * sizeof(char));
  342. if (!recvBuf)
  343. if (debug_mode) {
  344. printf("\tClient could not malloc space!?\n");
  345. }
  346. memset(&serverAddr, 0, sizeof(PRNetAddr));
  347. serverAddr.inet.family = PR_AF_INET;
  348. serverAddr.inet.port = PR_htons(PORT);
  349. serverAddr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK);
  350. while(numRequests > 0) {
  351. if ( (numRequests % 10) == 0 )
  352. if (debug_mode) {
  353. printf(".");
  354. }
  355. if (debug_mode) {
  356. DPRINTF("\tClient starting request %d\n", numRequests);
  357. }
  358. clientSocket = PR_NewTCPSocket();
  359. if (!clientSocket) {
  360. if (debug_mode) printf("Client error creating socket: OS error %d\n",
  361. PR_GetOSError());
  362. continue;
  363. }
  364. if (debug_mode) {
  365. DPRINTF("\tClient connecting\n");
  366. }
  367. rv = PR_Connect(clientSocket,
  368. &serverAddr,
  369. PR_INTERVAL_NO_TIMEOUT);
  370. if (!clientSocket) {
  371. if (debug_mode) {
  372. printf("\tClient error connecting\n");
  373. }
  374. continue;
  375. }
  376. if (debug_mode) {
  377. DPRINTF("\tClient connected\n");
  378. }
  379. rv = PR_Send(clientSocket,
  380. sendBuf,
  381. _client_data,
  382. 0,
  383. PR_INTERVAL_NO_TIMEOUT);
  384. if (rv != _client_data) {
  385. if (debug_mode) {
  386. printf("Client error sending data (%d)\n", rv);
  387. }
  388. PR_Close(clientSocket);
  389. continue;
  390. }
  391. if (debug_mode) {
  392. DPRINTF("\tClient sent %d bytes\n", rv);
  393. }
  394. bytesNeeded = _server_data;
  395. while(bytesNeeded) {
  396. rv = PR_Recv(clientSocket,
  397. recvBuf,
  398. bytesNeeded,
  399. 0,
  400. PR_INTERVAL_NO_TIMEOUT);
  401. if (rv <= 0) {
  402. if (debug_mode) printf("Client error receiving data (%d) (%d/%d)\n",
  403. rv, (_server_data - bytesNeeded), _server_data);
  404. break;
  405. }
  406. if (debug_mode) {
  407. DPRINTF("\tClient received %d bytes; need %d more\n", rv, bytesNeeded - rv);
  408. }
  409. bytesNeeded -= rv;
  410. }
  411. PR_Close(clientSocket);
  412. PR_AtomicDecrement(&numRequests);
  413. }
  414. PR_EnterMonitor(clientMonitor);
  415. --numClients;
  416. PR_Notify(clientMonitor);
  417. PR_ExitMonitor(clientMonitor);
  418. PR_DELETE(sendBuf);
  419. PR_DELETE(recvBuf);
  420. }
  421. void
  422. RunClients(void)
  423. {
  424. PRInt32 index;
  425. numRequests = _iterations;
  426. numClients = _clients;
  427. clientMonitor = PR_NewMonitor();
  428. for (index=0; index<_clients; index++) {
  429. PRThread *clientThread;
  430. clientThread = PR_CreateThread(
  431. PR_USER_THREAD,
  432. ClientThreadFunc,
  433. NULL,
  434. PR_PRIORITY_NORMAL,
  435. ClientScope,
  436. PR_UNJOINABLE_THREAD,
  437. THREAD_STACKSIZE);
  438. if (!clientThread) {
  439. if (debug_mode) {
  440. printf("\terror creating client thread %d\n", index);
  441. }
  442. } else if (debug_mode) {
  443. DPRINTF("\tMain created client %d/%d\n", index+1, _clients);
  444. }
  445. }
  446. PR_EnterMonitor(clientMonitor);
  447. while(numClients) {
  448. PR_Wait(clientMonitor, PR_INTERVAL_NO_TIMEOUT);
  449. }
  450. PR_ExitMonitor(clientMonitor);
  451. }
  452. /* --- Main Function ---------------------------------------------- */
  453. static
  454. void do_work()
  455. {
  456. PRThread *ServerThread;
  457. PRInt32 state;
  458. SetServerState(MAIN, SERVER_STATE_STARTUP);
  459. ServerThread = PR_CreateThread(
  460. PR_USER_THREAD,
  461. ServerThreadFunc,
  462. NULL,
  463. PR_PRIORITY_NORMAL,
  464. ServerScope,
  465. PR_JOINABLE_THREAD,
  466. THREAD_STACKSIZE);
  467. if (!ServerThread) {
  468. if (debug_mode) {
  469. printf("error creating main server thread\n");
  470. }
  471. return;
  472. }
  473. /* Wait for server to be ready */
  474. state = WaitServerState(MAIN, SERVER_STATE_READY|SERVER_STATE_DEAD);
  475. if (!(state & SERVER_STATE_DEAD)) {
  476. /* Run Test Clients */
  477. RunClients();
  478. /* Send death signal to server */
  479. SetServerState(MAIN, SERVER_STATE_DYING);
  480. }
  481. PR_JoinThread(ServerThread);
  482. }
  483. static void do_workUU(void)
  484. {
  485. ServerScope = PR_LOCAL_THREAD;
  486. ClientScope = PR_LOCAL_THREAD;
  487. do_work();
  488. }
  489. static void do_workUK(void)
  490. {
  491. ServerScope = PR_LOCAL_THREAD;
  492. ClientScope = PR_GLOBAL_THREAD;
  493. do_work();
  494. }
  495. static void do_workKU(void)
  496. {
  497. ServerScope = PR_GLOBAL_THREAD;
  498. ClientScope = PR_LOCAL_THREAD;
  499. do_work();
  500. }
  501. static void do_workKK(void)
  502. {
  503. ServerScope = PR_GLOBAL_THREAD;
  504. ClientScope = PR_GLOBAL_THREAD;
  505. do_work();
  506. }
  507. static void Measure(void (*func)(void), const char *msg)
  508. {
  509. PRIntervalTime start, stop;
  510. double d;
  511. start = PR_IntervalNow();
  512. (*func)();
  513. stop = PR_IntervalNow();
  514. d = (double)PR_IntervalToMicroseconds(stop - start);
  515. if (debug_mode) {
  516. printf("\n%40s: %6.2f usec\n", msg, d / _iterations);
  517. }
  518. }
  519. int main(int argc, char **argv)
  520. {
  521. /* The command line argument: -d is used to determine if the test is being run
  522. in debug mode. The regress tool requires only one line output:PASS or FAIL.
  523. All of the printfs associated with this test has been handled with a if (debug_mode)
  524. test.
  525. Usage: test_name -d
  526. */
  527. PLOptStatus os;
  528. PLOptState *opt = PL_CreateOptState(argc, argv, "d");
  529. while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
  530. {
  531. if (PL_OPT_BAD == os) {
  532. continue;
  533. }
  534. switch (opt->option)
  535. {
  536. case 'd': /* debug mode */
  537. debug_mode = 1;
  538. break;
  539. default:
  540. break;
  541. }
  542. }
  543. PL_DestroyOptState(opt);
  544. /* main test */
  545. if (debug_mode) {
  546. printf("Enter number of iterations: \n");
  547. scanf("%d", &_iterations);
  548. printf("Enter number of clients : \n");
  549. scanf("%d", &_clients);
  550. printf("Enter size of client data : \n");
  551. scanf("%d", &_client_data);
  552. printf("Enter size of server data : \n");
  553. scanf("%d", &_server_data);
  554. }
  555. else
  556. {
  557. _iterations = 10;
  558. _clients = 1;
  559. _client_data = 10;
  560. _server_data = 10;
  561. }
  562. if (debug_mode) {
  563. printf("\n\n%d iterations with %d client threads.\n",
  564. _iterations, _clients);
  565. printf("Sending %d bytes of client data and %d bytes of server data\n",
  566. _client_data, _server_data);
  567. }
  568. PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
  569. PR_STDIO_INIT();
  570. ServerStateCVLock = PR_NewLock();
  571. ServerStateCV = PR_NewCondVar(ServerStateCVLock);
  572. Measure(do_workUU, "server loop user/user");
  573. #if 0
  574. Measure(do_workUK, "server loop user/kernel");
  575. Measure(do_workKU, "server loop kernel/user");
  576. Measure(do_workKK, "server loop kernel/kernel");
  577. #endif
  578. PR_Cleanup();
  579. return failed_already;
  580. }