Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1059 lines
29 KiB

  1. /*++
  2. Copyright (C) Microsoft Corporation, 1996 - 1999
  3. Module Name:
  4. iosvr.c
  5. Abstract:
  6. I/O completion port perf test.
  7. Author:
  8. Mario Goertzel [MarioGo]
  9. Revision History:
  10. MarioGo 3/3/1996 Based on win32 sdk winnt\sockets sample.
  11. --*/
  12. #include "ioperf.h"
  13. // PERF CHECK: Must be less for 4K for optimum perf on small tests ??
  14. enum PROTOCOL {
  15. TCP = 0,
  16. SPX,
  17. NMPIPE,
  18. UDP
  19. } Protocol = TCP;
  20. CHAR *ProtocolNames[] = { "TCP/IP", "SPX", "Named Pipes", "UDP/IP" };
  21. DWORD ProtocolFrameSize[] = { 1460, 1400 /* ? */, ((64 * 1024) - 1)/4, 1472 };
  22. DWORD MaxWriteSize;
  23. BOOL fUseSend = FALSE;
  24. const char *USAGE = "-n <# clients> -n <request size> -n <reply size> -n <test case> -n <# threads> -n <concurrency factor> -t <protocol>\n"
  25. "\t-n <# clients> - for connection protocols. On datagram this\n"
  26. "\t controls the number of outstanding recv's\n"
  27. "\t-n <request size> - bytes, default 24\n"
  28. "\t-n <reply size> - bytes, default 24\n"
  29. "\t-n <test case>\n"
  30. "\t 1 - Uses async writes (64*frame size)\n"
  31. "\t 2 - Uses async writes (4096) - default \n"
  32. "\t 3 - (winsock only) Uses send() for reply\n"
  33. "\t-n <# threads> - worker threads, default # of processors * 2\n"
  34. "\t-n <concurrency factor> - default # of processors\n"
  35. "\t-t - tcp, spx, nmpipe (protseqs ok)\n"
  36. ;
  37. typedef long STATUS;
  38. typedef struct _PER_CLIENT_DATA {
  39. HANDLE hClient;
  40. struct _PER_CLIENT_DATA *pMe;
  41. OVERLAPPED OverlappedRead;
  42. struct _PER_CLIENT_DATA *pMe2;
  43. OVERLAPPED OverlappedWrite;
  44. PMESSAGE pRequest;
  45. PMESSAGE pReply;
  46. DWORD dwPreviousRead;
  47. DWORD dwPreviousWrite;
  48. DWORD dwTotalToWrite;
  49. DWORD dwRequestsProcessed;
  50. SOCKADDR_IN DgSendAddr;
  51. SOCKADDR_IN DgRecvAddr;
  52. DWORD dwRecvAddrSize;
  53. } PER_CLIENT_DATA, *PPER_CLIENT_DATA;
  54. PPER_CLIENT_DATA *ClientData;
  55. typedef struct _PER_THREAD_DATA {
  56. DWORD TotalTransactions;
  57. DWORD TotalRequestBytes;
  58. DWORD TotalReplyBytes;
  59. } PER_THREAD_DATA, *PPER_THREAD_DATA;
  60. PPER_THREAD_DATA *ThreadData;
  61. DWORD dwNumberOfClients;
  62. DWORD dwNumberOfWorkers;
  63. DWORD dwConcurrency;
  64. DWORD dwWorkIndex;
  65. DWORD dwRequestSize;
  66. DWORD dwReplySize;
  67. SYSTEM_INFO SystemInfo;
  68. HANDLE CompletionPort;
  69. DWORD dwActiveClientCount;
  70. HANDLE hBenchmarkStart;
  71. BOOL fClientsGoHome = FALSE;
  72. BOOL
  73. WINAPI
  74. CreateNetConnections(
  75. VOID
  76. );
  77. BOOL
  78. WINAPI
  79. CreateWorkers(
  80. VOID
  81. );
  82. DWORD
  83. WINAPI
  84. WorkerThread(
  85. LPVOID WorkContext
  86. );
  87. VOID
  88. WINAPI
  89. CompleteBenchmark(
  90. VOID
  91. );
  92. int __cdecl
  93. main (
  94. int argc,
  95. char *argv[],
  96. char *envp[]
  97. )
  98. {
  99. ParseArgv(argc, argv);
  100. //
  101. // try to get timing more accurate... Avoid context
  102. // switch that could occur when threads are released
  103. //
  104. SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_TIME_CRITICAL);
  105. //
  106. // Figure out how many processors we have to size the minimum
  107. // number of worker threads and concurrency
  108. //
  109. GetSystemInfo (&SystemInfo);
  110. dwNumberOfClients = 1;
  111. dwNumberOfWorkers = 2 * SystemInfo.dwNumberOfProcessors;
  112. dwConcurrency = SystemInfo.dwNumberOfProcessors;
  113. dwRequestSize = 24;
  114. dwReplySize = 24;
  115. if (Iterations == 1000)
  116. {
  117. Dump("Assuming 4 iterations for scalability test\n");
  118. Iterations = 4;
  119. }
  120. if (sizeof(MESSAGE) > 24)
  121. {
  122. ApiError("Configuration problem, message size > 24", 0);
  123. }
  124. if (_stricmp(Protseq, "tcp") == 0 || _stricmp(Protseq, "ncacn_ip_tcp") == 0 )
  125. {
  126. Protocol = TCP;
  127. }
  128. else if ( _stricmp(Protseq, "spx") == 0 || _stricmp(Protseq, "ncacn_spx") == 0 )
  129. {
  130. Protocol = SPX;
  131. }
  132. else if ( _stricmp(Protseq, "nmpipe") == 0 || _stricmp(Protseq, "ncacn_np") == 0 )
  133. {
  134. Protocol = NMPIPE;
  135. }
  136. else if ( _stricmp(Protseq, "udp") == 0 || _stricmp(Protseq, "ncadg_ip_udp") == 0 )
  137. {
  138. Protocol = UDP;
  139. }
  140. if (Options[0] > 0)
  141. dwNumberOfClients = Options[0];
  142. if (Options[1] > 0)
  143. {
  144. dwRequestSize = Options[1];
  145. }
  146. if (Options[2] > 0)
  147. {
  148. dwReplySize = Options[2];
  149. }
  150. if (Options[3] > 0)
  151. {
  152. switch(Options[3])
  153. {
  154. case 1:
  155. MaxWriteSize = 4 * ProtocolFrameSize[Protocol];
  156. break;
  157. case 2:
  158. MaxWriteSize = 4096;
  159. break;
  160. case 3:
  161. fUseSend = TRUE;
  162. break;
  163. default:
  164. printf("Invalid test case: %d\n", Options[3]);
  165. return(0);
  166. break;
  167. }
  168. }
  169. else
  170. {
  171. MaxWriteSize = 4096;
  172. }
  173. if (Options[4] > 0)
  174. {
  175. dwNumberOfWorkers = Options[4];
  176. }
  177. if (Options[5] > 0)
  178. {
  179. dwConcurrency = Options[5];
  180. }
  181. printf("%2d Clients %2d Workers Concurrency %d, listening on %s\n",
  182. dwNumberOfClients,
  183. dwNumberOfWorkers,
  184. dwConcurrency,
  185. ProtocolNames[Protocol]
  186. );
  187. ClientData = (PPER_CLIENT_DATA *)Allocate(sizeof(PPER_CLIENT_DATA) * dwNumberOfClients);
  188. ThreadData = (PPER_THREAD_DATA *)Allocate(sizeof(PPER_THREAD_DATA) * dwNumberOfWorkers);
  189. if (!ThreadData || !ClientData)
  190. {
  191. ApiError("malloc", GetLastError());
  192. }
  193. if (!CreateNetConnections())
  194. {
  195. return 1;
  196. }
  197. if (!CreateWorkers())
  198. {
  199. return 1;
  200. }
  201. CompleteBenchmark();
  202. return 0;
  203. }
  204. VOID
  205. SubmitWrite(PER_CLIENT_DATA *pClient, DWORD size)
  206. {
  207. DWORD t1, t2;
  208. INT write_type = Protocol;
  209. BOOL b;
  210. INT err;
  211. DWORD status;
  212. WSABUF buf;
  213. pClient->dwTotalToWrite = size;
  214. pClient->dwPreviousWrite = size;
  215. if ( ( write_type == TCP
  216. || write_type == SPX )
  217. && !fUseSend )
  218. {
  219. // We want to use WriteFile for TCP & SPX if not using send.
  220. write_type = NMPIPE;
  221. }
  222. switch(write_type)
  223. {
  224. case NMPIPE:
  225. if (size > MaxWriteSize)
  226. {
  227. size = MaxWriteSize;
  228. }
  229. pClient->dwPreviousWrite = size;
  230. b = WriteFile(pClient->hClient,
  231. pClient->pReply,
  232. size,
  233. &t1,
  234. &pClient->OverlappedWrite);
  235. if (!b && GetLastError() != ERROR_IO_PENDING)
  236. {
  237. ApiError("WriteFile", GetLastError());
  238. }
  239. break;
  240. case UDP:
  241. memcpy(&pClient->DgSendAddr, &pClient->DgRecvAddr, sizeof(SOCKADDR_IN));
  242. buf.buf = (PCHAR) pClient->pReply;
  243. buf.len = size;
  244. t1 = 0;
  245. err = WSASendTo((SOCKET)pClient->hClient,
  246. &buf,
  247. 1,
  248. &t1,
  249. 0,
  250. (PSOCKADDR)&pClient->DgSendAddr,
  251. sizeof(SOCKADDR_IN),
  252. &pClient->OverlappedWrite,
  253. 0);
  254. if (err != 0 && GetLastError() != ERROR_IO_PENDING)
  255. {
  256. ApiError("WSASendTo", GetLastError());
  257. }
  258. break;
  259. case TCP:
  260. case SPX:
  261. err = send((SOCKET)pClient->hClient,
  262. (PCHAR)pClient->pReply,
  263. sizeof(MESSAGE),
  264. 0);
  265. if (err == SOCKET_ERROR)
  266. {
  267. ApiError("send", GetLastError());
  268. }
  269. break;
  270. default:
  271. ApiError("Bad protocol", 0);
  272. }
  273. return;
  274. }
  275. VOID
  276. SubmitRead(PER_CLIENT_DATA *pClient)
  277. {
  278. DWORD t1, t2, t3;
  279. BOOL b;
  280. DWORD status;
  281. INT err;
  282. if (Protocol == UDP)
  283. {
  284. WSABUF buf;
  285. t1 = t2 = 0;
  286. pClient->dwRecvAddrSize = sizeof(pClient->DgRecvAddr);
  287. buf.buf = (PCHAR)pClient->pRequest;
  288. buf.len = dwRequestSize;
  289. status = WSARecvFrom((SOCKET)pClient->hClient,
  290. &buf,
  291. 1,
  292. &t1,
  293. &t2,
  294. (PSOCKADDR)&pClient->DgRecvAddr,
  295. &pClient->dwRecvAddrSize,
  296. &pClient->OverlappedRead,
  297. 0);
  298. if (status != NO_ERROR && GetLastError() != ERROR_IO_PENDING)
  299. {
  300. ApiError("WSARecvFrom", GetLastError());
  301. }
  302. }
  303. else
  304. {
  305. b = ReadFile(pClient->hClient,
  306. pClient->pRequest,
  307. dwRequestSize,
  308. &t1,
  309. &pClient->OverlappedRead
  310. );
  311. if (!b && GetLastError () != ERROR_IO_PENDING)
  312. {
  313. ApiError("ReadFile", GetLastError());
  314. }
  315. }
  316. return;
  317. }
  318. VOID
  319. WINAPI
  320. CompleteBenchmark (
  321. VOID
  322. )
  323. {
  324. DWORD StartCalls;
  325. DWORD TotalTicks, FinalCalls, MaxCalls, MinCalls, TotalCalls;
  326. DWORD i, j;
  327. PPER_CLIENT_DATA pClient;
  328. SetEvent(hBenchmarkStart);
  329. Sleep(1000);
  330. for (i = 0; i < Iterations; i++)
  331. {
  332. StartTime();
  333. StartCalls = 0;
  334. for (j = 0; j < dwNumberOfClients; j++ )
  335. {
  336. pClient = ClientData[j];
  337. StartCalls += pClient->dwRequestsProcessed;
  338. }
  339. Sleep(Interval * 1000);
  340. FinalCalls = MaxCalls = 0;
  341. MinCalls = ~0;
  342. for (j = 0; j < dwNumberOfClients; j++)
  343. {
  344. pClient = ClientData[j];
  345. FinalCalls += pClient->dwRequestsProcessed;
  346. if (pClient->dwRequestsProcessed < MinCalls )
  347. {
  348. MinCalls = pClient->dwRequestsProcessed;
  349. }
  350. if (pClient->dwRequestsProcessed > MaxCalls)
  351. {
  352. MaxCalls = pClient->dwRequestsProcessed;
  353. }
  354. }
  355. TotalCalls = FinalCalls - StartCalls;
  356. TotalTicks = FinishTiming();
  357. Dump("Ticks: %4d, Total: %4d, Average %4d, TPS %3d\n",
  358. TotalTicks,
  359. TotalCalls,
  360. TotalCalls / dwNumberOfClients,
  361. TotalCalls * 1000 / TotalTicks
  362. );
  363. Verbose("Max: %d, Min: %d\n", MaxCalls, MinCalls);
  364. }
  365. // Clients will be shutdown on next call...
  366. fClientsGoHome = TRUE;
  367. Sleep(5000);
  368. printf("Test Complete\n");
  369. for (i = 0; i < dwNumberOfWorkers; i++)
  370. {
  371. printf("\tThread[%2d] %d request and %d reply bytes in %d IOs\n",
  372. i,
  373. ThreadData[i]->TotalRequestBytes,
  374. ThreadData[i]->TotalReplyBytes,
  375. ThreadData[i]->TotalTransactions
  376. );
  377. }
  378. }
  379. BOOL
  380. WINAPI
  381. CreateNetConnections(
  382. void
  383. )
  384. {
  385. STATUS status;
  386. DWORD i;
  387. SOCKET listener;
  388. INT err;
  389. WSADATA WsaData;
  390. DWORD nbytes;
  391. BOOL b;
  392. PPER_CLIENT_DATA pClient;
  393. if (Protocol == TCP || Protocol == SPX)
  394. {
  395. status = WSAStartup (0x2, &WsaData);
  396. CHECK_STATUS(status, "WSAStartup");
  397. //
  398. // Open a socket to listen for incoming connections.
  399. //
  400. if (Protocol == TCP)
  401. {
  402. SOCKADDR_IN localAddr;
  403. listener = WSASocketW(AF_INET, SOCK_STREAM, 0, 0, 0, WSA_FLAG_OVERLAPPED);
  404. if (listener == INVALID_SOCKET)
  405. {
  406. ApiError("socket", GetLastError());
  407. }
  408. //
  409. // Bind our server to the agreed upon port number.
  410. //
  411. ZeroMemory (&localAddr, sizeof (localAddr));
  412. localAddr.sin_port = htons (TCP_PORT);
  413. localAddr.sin_family = AF_INET;
  414. err = bind (listener, (PSOCKADDR) & localAddr, sizeof (localAddr));
  415. if (err == SOCKET_ERROR)
  416. {
  417. ApiError("bind", GetLastError());
  418. }
  419. }
  420. else if (Protocol == SPX)
  421. {
  422. SOCKADDR_IPX localAddr;
  423. listener = socket (AF_IPX, SOCK_STREAM, NSPROTO_SPX);
  424. if (listener == INVALID_SOCKET)
  425. {
  426. ApiError("socket", GetLastError());
  427. }
  428. ZeroMemory (&localAddr, sizeof (localAddr));
  429. localAddr.sa_socket = htons (SPX_PORT);
  430. localAddr.sa_family = AF_IPX;
  431. err = bind (listener, (PSOCKADDR) & localAddr, sizeof (localAddr));
  432. if (err == SOCKET_ERROR)
  433. {
  434. ApiError("bind", GetLastError());
  435. }
  436. }
  437. else if (Protocol == SPX)
  438. {
  439. ApiError("Case not implemented", 0);
  440. }
  441. // Prepare to accept client connections. Allow up to 5 pending
  442. // connections.
  443. err = listen (listener, 5);
  444. if (err == SOCKET_ERROR)
  445. {
  446. ApiError("listen", GetLastError());
  447. }
  448. //
  449. // Only Handle a single Queue
  450. //
  451. for (i = 0; i < dwNumberOfClients; i++)
  452. {
  453. SOCKET s;
  454. pClient = Allocate(sizeof(PER_CLIENT_DATA));
  455. if (!pClient)
  456. {
  457. ApiError("Allocate", GetLastError());
  458. }
  459. pClient->pRequest = Allocate(dwRequestSize);
  460. pClient->pReply = Allocate(dwReplySize);
  461. if ( !pClient->pRequest
  462. || !pClient->pReply)
  463. {
  464. ApiError("Allocate", GetLastError());
  465. }
  466. // Accept incoming connect requests
  467. s = accept (listener, NULL, NULL);
  468. if (s == INVALID_SOCKET)
  469. {
  470. // exiting anyway, no need to cleanup.
  471. ApiError("accept", GetLastError());
  472. }
  473. dbgprintf("Accepted client %d\n", i);
  474. // Note that dwConcurrency says how many concurrent cpu bound threads to
  475. // allow thru this should be tunable based on the requests. CPU bound requests
  476. // will really really honor this.
  477. pClient->hClient = (HANDLE)s;
  478. CompletionPort = CreateIoCompletionPort(pClient->hClient,
  479. CompletionPort,
  480. (ULONG_PTR)pClient,
  481. dwConcurrency);
  482. if (!CompletionPort)
  483. {
  484. ApiError("CreateIoCompletionPort", GetLastError());
  485. }
  486. //
  487. // Start off an asynchronous read on the socket.
  488. //
  489. pClient->dwPreviousRead = 0;
  490. pClient->dwRequestsProcessed = 0;
  491. ZeroMemory(&pClient->OverlappedRead, sizeof(OVERLAPPED));
  492. ZeroMemory(&pClient->OverlappedWrite, sizeof(OVERLAPPED));
  493. b = ReadFile(pClient->hClient,
  494. pClient->pRequest,
  495. dwRequestSize,
  496. &nbytes,
  497. &pClient->OverlappedRead
  498. );
  499. if (!b && GetLastError() != ERROR_IO_PENDING)
  500. {
  501. ApiError("ReadFile", GetLastError());
  502. }
  503. ClientData[i] = pClient;
  504. }
  505. dwActiveClientCount = dwNumberOfClients;
  506. }
  507. else if (Protocol == NMPIPE)
  508. {
  509. HANDLE h;
  510. OVERLAPPED *lpo;
  511. DWORD nbytes, index;
  512. BOOL b;
  513. for (i = 0; i < dwNumberOfClients; i++)
  514. {
  515. h = CreateNamedPipe(NM_PORT,
  516. PIPE_ACCESS_DUPLEX
  517. | FILE_FLAG_OVERLAPPED,
  518. PIPE_TYPE_MESSAGE
  519. | (PIPE_READMODE_MESSAGE, 0) // ************
  520. | PIPE_WAIT,
  521. PIPE_UNLIMITED_INSTANCES,
  522. 4096, // ***************
  523. 4096, // ***************
  524. INFINITE,
  525. 0);
  526. if (!h)
  527. {
  528. ApiError("CreateNamedPipe", GetLastError());
  529. }
  530. //
  531. // Wait for clients to connect
  532. //
  533. pClient = Allocate(sizeof(PER_CLIENT_DATA));
  534. if (!pClient)
  535. {
  536. ApiError("Allocate", GetLastError());
  537. }
  538. ZeroMemory(pClient, sizeof(PER_CLIENT_DATA));
  539. pClient->pRequest = Allocate(dwRequestSize);
  540. pClient->pReply = Allocate(dwReplySize);
  541. if ( !pClient->pRequest
  542. || !pClient->pReply)
  543. {
  544. ApiError("Allocate", GetLastError());
  545. }
  546. // Accept incoming connect requests
  547. pClient->hClient = h;
  548. b = ConnectNamedPipe(pClient->hClient,
  549. &pClient->OverlappedRead);
  550. dbgprintf("ConnectNamedPipe: %d %d\n", b, GetLastError());
  551. if (b == 0)
  552. {
  553. if (GetLastError() == ERROR_IO_PENDING)
  554. {
  555. b = GetOverlappedResult(pClient->hClient,
  556. &pClient->OverlappedRead,
  557. &nbytes,
  558. TRUE);
  559. if (b == 0)
  560. {
  561. ApiError("GetOverlappedResult", GetLastError());
  562. }
  563. dbgprintf("Client connected\n");
  564. }
  565. else
  566. {
  567. ApiError("ConnectNamedPipe", GetLastError());
  568. }
  569. }
  570. // Add the clients pipe instance to the completion port.
  571. CompletionPort = CreateIoCompletionPort(h,
  572. CompletionPort,
  573. (ULONG_PTR)pClient,
  574. dwConcurrency);
  575. if (!CompletionPort)
  576. {
  577. ApiError("CreteIoCompletionPort", GetLastError());
  578. }
  579. //
  580. // Start off an asynchronous read on the socket.
  581. //
  582. pClient->dwPreviousRead = 0;
  583. pClient->dwRequestsProcessed = 0;
  584. ZeroMemory(&pClient->OverlappedRead, sizeof(OVERLAPPED));
  585. ZeroMemory(&pClient->OverlappedWrite, sizeof(OVERLAPPED));
  586. b = ReadFile(pClient->hClient,
  587. pClient->pRequest,
  588. dwRequestSize,
  589. &nbytes,
  590. &pClient->OverlappedRead
  591. );
  592. if (!b && GetLastError() != ERROR_IO_PENDING)
  593. {
  594. ApiError("ReadFile", GetLastError());
  595. }
  596. ClientData[i] = pClient;
  597. }
  598. dwActiveClientCount = dwNumberOfClients;
  599. }
  600. else if (Protocol == UDP)
  601. {
  602. SOCKADDR_IN localAddr;
  603. status = WSAStartup (0x2, &WsaData);
  604. CHECK_STATUS(status, "WSAStartup");
  605. listener = WSASocketW(AF_INET, SOCK_DGRAM, IPPROTO_UDP, 0,
  606. 0, WSA_FLAG_OVERLAPPED);
  607. if (listener == INVALID_SOCKET)
  608. {
  609. ApiError("socket", GetLastError());
  610. }
  611. //
  612. // Bind our server to the agreed upon port number.
  613. //
  614. ZeroMemory (&localAddr, sizeof (localAddr));
  615. localAddr.sin_port = htons (UDP_PORT);
  616. localAddr.sin_family = AF_INET;
  617. err = bind (listener, (PSOCKADDR) & localAddr, sizeof (localAddr));
  618. if (err == SOCKET_ERROR)
  619. {
  620. ApiError("bind", GetLastError());
  621. }
  622. CompletionPort = CreateIoCompletionPort((HANDLE) listener,
  623. CompletionPort,
  624. 0,
  625. dwConcurrency);
  626. if (!CompletionPort)
  627. {
  628. ApiError("CreateIoCompletionPort", GetLastError());
  629. }
  630. //
  631. // Start off asynchronous reads on the socket.
  632. //
  633. for(i = 0; i < dwNumberOfClients; i++)
  634. {
  635. pClient = Allocate(sizeof(PER_CLIENT_DATA));
  636. if (!pClient)
  637. {
  638. ApiError("Allocate", GetLastError());
  639. }
  640. pClient->pRequest = Allocate(dwRequestSize);
  641. pClient->pReply = Allocate(dwReplySize);
  642. if ( !pClient->pRequest
  643. || !pClient->pReply)
  644. {
  645. ApiError("Allocate", GetLastError());
  646. }
  647. ZeroMemory(&pClient->OverlappedRead, sizeof(OVERLAPPED));
  648. ZeroMemory(&pClient->OverlappedWrite, sizeof(OVERLAPPED));
  649. pClient->hClient = (HANDLE)listener;
  650. pClient->dwPreviousRead = 0;
  651. pClient->dwRequestsProcessed = 0;
  652. pClient->pMe = pClient;
  653. pClient->pMe2 = pClient;
  654. Trace("Created: %p %p %p\n", pClient, &pClient->OverlappedRead,
  655. &pClient->OverlappedWrite);
  656. ClientData[i] = pClient;
  657. SubmitRead(pClient);
  658. }
  659. dwActiveClientCount = dwNumberOfClients;
  660. }
  661. else
  662. {
  663. ApiError("Invalid protocol", 0);
  664. }
  665. // Protocol independent part
  666. hBenchmarkStart = CreateEvent (NULL, TRUE, FALSE, NULL);
  667. if (!hBenchmarkStart)
  668. {
  669. ApiError("CreateEvent", GetLastError());
  670. }
  671. return TRUE;
  672. }
  673. BOOL
  674. WINAPI
  675. CreateWorkers(
  676. void
  677. )
  678. {
  679. DWORD ThreadId;
  680. HANDLE ThreadHandle;
  681. DWORD i;
  682. PPER_THREAD_DATA pThreadData;
  683. for (i = 0; i < dwNumberOfWorkers; i++)
  684. {
  685. pThreadData = Allocate(sizeof(PER_THREAD_DATA));
  686. if (!pThreadData)
  687. {
  688. ApiError("malloc", GetLastError());
  689. }
  690. ZeroMemory(pThreadData, sizeof(PER_THREAD_DATA));
  691. ThreadHandle = CreateThread(NULL,
  692. 0,
  693. WorkerThread,
  694. (LPVOID)pThreadData,
  695. 0,
  696. &ThreadId
  697. );
  698. if (!ThreadHandle)
  699. {
  700. ApiError("CreateThread", GetLastError());
  701. }
  702. CloseHandle(ThreadHandle);
  703. ThreadData[i] = pThreadData;
  704. }
  705. return TRUE;
  706. }
  707. DWORD
  708. WINAPI
  709. WorkerThread (
  710. LPVOID WorkContext
  711. )
  712. {
  713. PPER_THREAD_DATA Me;
  714. INT err;
  715. DWORD ResponseLength;
  716. BOOL b;
  717. LPOVERLAPPED lpo;
  718. DWORD nbytes;
  719. ULONG_PTR WorkIndex;
  720. PPER_CLIENT_DATA pClient;
  721. LONG count;
  722. WaitForSingleObject (hBenchmarkStart, INFINITE);
  723. Me = (PPER_THREAD_DATA) WorkContext;
  724. for (;;)
  725. {
  726. lpo = 0;
  727. b = GetQueuedCompletionStatus(CompletionPort,
  728. &nbytes,
  729. &WorkIndex,
  730. &lpo,
  731. INFINITE
  732. );
  733. // dbgprintf("GetQueuedCompletionStatus: %d %d %p\n", b, nbytes, lpo);
  734. if (WorkIndex == 0)
  735. {
  736. // Must be a datagram read or write
  737. WorkIndex = (ULONG_PTR)((unsigned char *)lpo - 4);
  738. WorkIndex = *(PDWORD)WorkIndex;
  739. }
  740. Me->TotalTransactions++;
  741. if (b || lpo)
  742. {
  743. if (b)
  744. {
  745. DWORD nbytes2;
  746. pClient = (PPER_CLIENT_DATA)WorkIndex;
  747. if (lpo == &pClient->OverlappedWrite)
  748. {
  749. dbgprintf("Write completed %d (%p)\n", nbytes, pClient);
  750. Me->TotalReplyBytes += nbytes;
  751. nbytes = pClient->dwTotalToWrite - pClient->dwPreviousWrite;
  752. if (nbytes)
  753. {
  754. if (nbytes > MaxWriteSize)
  755. {
  756. nbytes = MaxWriteSize;
  757. }
  758. pClient->dwPreviousWrite += nbytes;
  759. b = WriteFile(pClient->hClient,
  760. (PBYTE)pClient->pReply + pClient->dwPreviousWrite - nbytes,
  761. nbytes,
  762. &nbytes2,
  763. &pClient->OverlappedWrite);
  764. dbgprintf("Write completed: %d %d (of %d) %d (of %d)\n", b, nbytes2, nbytes, pClient->dwPreviousWrite, dwReplySize);
  765. if (!b && GetLastError() != ERROR_IO_PENDING)
  766. {
  767. ApiError("WriteFile", GetLastError());
  768. }
  769. }
  770. }
  771. else
  772. {
  773. Me->TotalRequestBytes += nbytes;
  774. dbgprintf(" Read completed %d (%p)\n", nbytes, pClient);
  775. if (nbytes == 0)
  776. {
  777. Trace("Connection closed (zero byte read)\n");
  778. CloseHandle(pClient->hClient);
  779. continue;
  780. }
  781. switch (pClient->pRequest->MessageType)
  782. {
  783. case CONNECT:
  784. // Send test parameters back to the client
  785. pClient->pReply->MessageType = SETUP;
  786. pClient->pReply->u.Setup.RequestSize = dwRequestSize;
  787. pClient->pReply->u.Setup.ReplySize = dwReplySize;
  788. SubmitWrite(pClient, sizeof(MESSAGE));
  789. pClient->dwPreviousRead = 0;
  790. SubmitRead(pClient);
  791. break;
  792. case DATA_RQ:
  793. // Make sure we got all the data and no more.
  794. pClient->dwRequestsProcessed++;
  795. nbytes += pClient->dwPreviousRead;
  796. if (nbytes > dwRequestSize)
  797. {
  798. ApiError("Too much data returned\n", 0);
  799. }
  800. if (nbytes < dwRequestSize)
  801. {
  802. dbgprintf("Partial receive of %d (of %d)\n", nbytes, dwRequestSize);
  803. // Resubmit the IO for the rest of the request.
  804. pClient->dwPreviousRead = nbytes;
  805. b = ReadFile(pClient->hClient,
  806. ((PBYTE)pClient->pRequest) + nbytes,
  807. dwRequestSize - nbytes,
  808. &nbytes,
  809. &pClient->OverlappedRead);
  810. if (!b && GetLastError () != ERROR_IO_PENDING)
  811. {
  812. ApiError("ReadFile for remainder", GetLastError());
  813. }
  814. // Pickup this or another IO
  815. break;
  816. }
  817. if (nbytes != pClient->pRequest->u.Data.TotalSize)
  818. {
  819. printf("Invalid request size, got %d, expected %d\n",
  820. nbytes, pClient->pRequest->u.Data.TotalSize);
  821. ApiError("test sync", 0);
  822. }
  823. pClient->dwPreviousRead = 0;
  824. // Could sleep/do work here.
  825. // Send a response and post another asynchronous read on the
  826. // socket.
  827. if (fClientsGoHome == FALSE)
  828. {
  829. pClient->pReply->MessageType = DATA_RP;
  830. }
  831. else
  832. {
  833. pClient->pReply->MessageType = FINISH;
  834. }
  835. pClient->pReply->u.Data.TotalSize = dwReplySize;
  836. SubmitWrite(pClient, dwReplySize);
  837. SubmitRead(pClient);
  838. break;
  839. default:
  840. ApiError("Invalid message type", pClient->pRequest->MessageType);
  841. }
  842. } // read or write
  843. }
  844. else
  845. {
  846. Trace("Client closed connection\n", GetLastError());
  847. }
  848. }
  849. else
  850. {
  851. ApiError("Wait failed", GetLastError());
  852. }
  853. } // loop
  854. // not reached
  855. return 0;
  856. }