VirtualBox

source: vbox/trunk/src/VBox/Runtime/testcase/tstRTLocalIpc.cpp@ 93138

Last change on this file since 93138 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.3 KB
Line 
1/* $Id: tstRTLocalIpc.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * IPRT Testcase - RTLocalIpc API.
4 */
5
6/*
7 * Copyright (C) 2013-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include <iprt/localipc.h>
32
33#include <iprt/asm.h>
34#include <iprt/env.h>
35#include <iprt/err.h>
36#include <iprt/initterm.h>
37#include <iprt/mem.h>
38#include <iprt/message.h>
39#include <iprt/path.h>
40#include <iprt/process.h>
41#include <iprt/rand.h>
42#include <iprt/string.h>
43#include <iprt/test.h>
44#include <iprt/thread.h>
45#include <iprt/time.h>
46
47
48/*********************************************************************************************************************************
49* Global Variables *
50*********************************************************************************************************************************/
51/** The test instance.*/
52static RTTEST g_hTest;
53
54
55
56static void testBasics(void)
57{
58 RTTestISub("Basics");
59
60 /* Server-side. */
61 RTTESTI_CHECK_RC(RTLocalIpcServerCreate(NULL, NULL, 0), VERR_INVALID_POINTER);
62 RTLOCALIPCSERVER hIpcServer;
63 int rc;
64 RTTESTI_CHECK_RC(rc = RTLocalIpcServerCreate(&hIpcServer, NULL, 0), VERR_INVALID_POINTER);
65 if (RT_SUCCESS(rc)) RTLocalIpcServerDestroy(hIpcServer);
66 RTTESTI_CHECK_RC(rc = RTLocalIpcServerCreate(&hIpcServer, "", 0), VERR_INVALID_NAME);
67 if (RT_SUCCESS(rc)) RTLocalIpcServerDestroy(hIpcServer);
68 RTTESTI_CHECK_RC(rc = RTLocalIpcServerCreate(&hIpcServer, "BasicTest", 1234 /* Invalid flags */), VERR_INVALID_FLAGS);
69 if (RT_SUCCESS(rc)) RTLocalIpcServerDestroy(hIpcServer);
70
71 RTTESTI_CHECK_RC(RTLocalIpcServerCancel(NULL), VERR_INVALID_HANDLE);
72 RTTESTI_CHECK_RC(RTLocalIpcServerDestroy(NULL), VINF_SUCCESS);
73
74 /* Basic server creation / destruction. */
75 RTTESTI_CHECK_RC_RETV(RTLocalIpcServerCreate(&hIpcServer, "BasicTest", 0), VINF_SUCCESS);
76 RTTESTI_CHECK_RC(RTLocalIpcServerCancel(hIpcServer), VINF_SUCCESS);
77 RTTESTI_CHECK_RC(RTLocalIpcServerDestroy(hIpcServer), VINF_OBJECT_DESTROYED);
78
79 /* Client-side (per session). */
80 RTTESTI_CHECK_RC(RTLocalIpcSessionConnect(NULL, NULL, 0), VERR_INVALID_POINTER);
81 RTLOCALIPCSESSION hIpcSession;
82 RTTESTI_CHECK_RC(RTLocalIpcSessionConnect(&hIpcSession, NULL, 0), VERR_INVALID_POINTER);
83 if (RT_SUCCESS(rc)) RTLocalIpcSessionClose(hIpcSession);
84 RTTESTI_CHECK_RC(RTLocalIpcSessionConnect(&hIpcSession, "", 0), VERR_INVALID_NAME);
85 if (RT_SUCCESS(rc)) RTLocalIpcSessionClose(hIpcSession);
86 RTTESTI_CHECK_RC(RTLocalIpcSessionConnect(&hIpcSession, "BasicTest", 1234 /* Invalid flags */), VERR_INVALID_FLAGS);
87 if (RT_SUCCESS(rc)) RTLocalIpcSessionClose(hIpcSession);
88
89 RTTESTI_CHECK_RC(RTLocalIpcSessionCancel(NULL), VERR_INVALID_HANDLE);
90 RTTESTI_CHECK_RC(RTLocalIpcSessionClose(NULL), VINF_SUCCESS);
91
92 /* Basic client creation / destruction. */
93 RTTESTI_CHECK_RC_RETV(rc = RTLocalIpcSessionConnect(&hIpcSession, "BasicTest", 0), VERR_FILE_NOT_FOUND);
94 if (RT_SUCCESS(rc)) RTLocalIpcSessionClose(hIpcSession);
95 //RTTESTI_CHECK_RC(RTLocalIpcServerCancel(hIpcServer), VERR_INVALID_HANDLE); - accessing freed memory, bad idea.
96 //RTTESTI_CHECK_RC(RTLocalIpcServerDestroy(hIpcServer), VERR_INVALID_HANDLE); - accessing freed memory, bad idea.
97}
98
99
100
101/*********************************************************************************************************************************
102* *
103* testSessionConnection - Connecting. *
104* *
105*********************************************************************************************************************************/
106
107static DECLCALLBACK(int) testServerListenThread(RTTHREAD hSelf, void *pvUser)
108{
109 RTLOCALIPCSERVER hIpcServer = (RTLOCALIPCSERVER)pvUser;
110 RTTEST_CHECK_RC_OK_RET(g_hTest, RTTestSetDefault(g_hTest, NULL), rcCheck);
111
112 RTTESTI_CHECK_RC_OK(RTThreadUserSignal(hSelf));
113
114 int rc;
115 for (;;)
116 {
117 RTLOCALIPCSESSION hIpcSession;
118 rc = RTLocalIpcServerListen(hIpcServer, &hIpcSession);
119 if (RT_SUCCESS(rc))
120 {
121 RTThreadSleep(8); /* windows output fudge (purely esthetical) */
122 RTTestIPrintf(RTTESTLVL_INFO, "testServerListenThread: Got new client connection.\n");
123 RTTESTI_CHECK_RC(RTLocalIpcSessionClose(hIpcSession), VINF_OBJECT_DESTROYED);
124 }
125 else
126 {
127 RTTESTI_CHECK_RC(rc, VERR_CANCELLED);
128 break;
129 }
130 }
131 return rc;
132}
133
134
135/**
136 * Used both as a thread procedure and child process worker.
137 */
138static DECLCALLBACK(int) tstRTLocalIpcSessionConnectionChild(RTTHREAD hSelf, void *pvUser)
139{
140 RTLOCALIPCSESSION hClientSession;
141 RT_NOREF_PV(hSelf); RT_NOREF_PV(pvUser);
142
143 RTTEST_CHECK_RC_OK_RET(g_hTest, RTTestSetDefault(g_hTest, NULL), rcCheck);
144
145 RTTEST_CHECK_RC_RET(g_hTest, RTLocalIpcSessionConnect(&hClientSession, "tstRTLocalIpcSessionConnection",0 /* Flags */),
146 VINF_SUCCESS, rcCheck);
147 RTTEST_CHECK_RC_RET(g_hTest, RTLocalIpcSessionClose(hClientSession),
148 VINF_OBJECT_DESTROYED, rcCheck);
149
150 return VINF_SUCCESS;
151}
152
153
154static void testSessionConnection(const char *pszExecPath)
155{
156 RTTestISub(!pszExecPath ? "Connect from thread" : "Connect from child");
157
158 /*
159 * Create the test server.
160 */
161 RTLOCALIPCSERVER hIpcServer;
162 RTTESTI_CHECK_RC_RETV(RTLocalIpcServerCreate(&hIpcServer, "tstRTLocalIpcSessionConnection", 0), VINF_SUCCESS);
163
164 /*
165 * Create worker thread that listens and closes incoming connections until
166 * cancelled.
167 */
168 int rc;
169 RTTHREAD hListenThread;
170 RTTESTI_CHECK_RC_OK(rc = RTThreadCreate(&hListenThread, testServerListenThread, hIpcServer, 0 /* Stack */,
171 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "listen-1"));
172 if (RT_SUCCESS(rc))
173 {
174 RTThreadUserWait(hListenThread, 32);
175
176 /*
177 * Two variations here: Client connects from thread or a child process.
178 */
179 if (pszExecPath)
180 {
181 RTPROCESS hClientProc;
182 const char *apszArgs[4] = { pszExecPath, "child", "tstRTLocalIpcSessionConnectionChild", NULL };
183 RTTESTI_CHECK_RC_OK(rc = RTProcCreate(pszExecPath, apszArgs, RTENV_DEFAULT, 0 /* fFlags*/, &hClientProc));
184 if (RT_SUCCESS(rc))
185 {
186 RTPROCSTATUS ProcStatus;
187 RTTESTI_CHECK_RC_OK(rc = RTProcWait(hClientProc, RTPROCWAIT_FLAGS_BLOCK, &ProcStatus));
188 if (RT_SUCCESS(rc) && (ProcStatus.enmReason != RTPROCEXITREASON_NORMAL || ProcStatus.iStatus != 0))
189 RTTestIFailed("Chiled exited with enmReason=%d iStatus=%d", ProcStatus.enmReason, ProcStatus.iStatus);
190 }
191 }
192 else
193 {
194 RTTHREAD hClientThread;
195 RTTESTI_CHECK_RC_OK(rc = RTThreadCreate(&hClientThread, tstRTLocalIpcSessionConnectionChild, NULL,
196 0 /* Stack */, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "client-1"));
197 if (RT_SUCCESS(rc))
198 {
199 int rcThread;
200 RTTESTI_CHECK_RC_OK(rc = RTThreadWait(hClientThread, RT_MS_1MIN / 2, &rcThread));
201 if (RT_SUCCESS(rc))
202 RTTESTI_CHECK_RC(rcThread, VINF_SUCCESS);
203 }
204 }
205
206
207 /*
208 * Terminate the server thread.
209 */
210 //RTTestIPrintf(RTTESTLVL_INFO, "Child terminated, waiting for server thread ...\n");
211 RTTESTI_CHECK_RC(RTLocalIpcServerCancel(hIpcServer), VINF_SUCCESS);
212 int rcThread;
213 RTTESTI_CHECK_RC(rc = RTThreadWait(hListenThread, 30 * 1000 /* 30s timeout */, &rcThread), VINF_SUCCESS);
214 if (RT_SUCCESS(rc))
215 RTTESTI_CHECK_RC(rcThread, VERR_CANCELLED);
216 }
217
218 RTTESTI_CHECK_RC(RTLocalIpcServerDestroy(hIpcServer), VINF_OBJECT_DESTROYED);
219}
220
221
222
223/*********************************************************************************************************************************
224* *
225* testSessionWait - RTLocalIpcSessionWaitForData. *
226* *
227*********************************************************************************************************************************/
228
229static DECLCALLBACK(int) testSessionWaitThread(RTTHREAD hSelf, void *pvUser)
230{
231 RTLOCALIPCSERVER hIpcServer = (RTLOCALIPCSERVER)pvUser;
232 RTTEST_CHECK_RC_OK_RET(g_hTest, RTTestSetDefault(g_hTest, NULL), rcCheck);
233
234 int rc;
235 for (;;)
236 {
237 RTLOCALIPCSESSION hIpcSession;
238 rc = RTLocalIpcServerListen(hIpcServer, &hIpcSession);
239 if (RT_SUCCESS(rc))
240 {
241 RTTestIPrintf(RTTESTLVL_INFO, "testSessionWaitThread: Got new client connection.\n");
242
243 /* Wait for the client to trigger a disconnect by writing us something. */
244 RTTESTI_CHECK_RC(RTLocalIpcSessionWaitForData(hIpcSession, RT_MS_1MIN), VINF_SUCCESS);
245
246 size_t cbRead;
247 char szCmd[64];
248 RT_ZERO(szCmd);
249 RTTESTI_CHECK_RC(rc = RTLocalIpcSessionReadNB(hIpcSession, szCmd, sizeof(szCmd) - 1, &cbRead), VINF_SUCCESS);
250 if (RT_SUCCESS(rc) && (cbRead != sizeof("disconnect") - 1 || strcmp(szCmd, "disconnect")) )
251 RTTestIFailed("cbRead=%zu, expected %zu; szCmd='%s', expected 'disconnect'\n",
252 cbRead, sizeof("disconnect") - 1, szCmd);
253
254 RTTESTI_CHECK_RC(RTLocalIpcSessionClose(hIpcSession), VINF_OBJECT_DESTROYED);
255 RTTESTI_CHECK_RC_OK(RTThreadUserSignal(hSelf));
256 }
257 else
258 {
259 RTTESTI_CHECK_RC(rc, VERR_CANCELLED);
260 break;
261 }
262 }
263 RTTESTI_CHECK_RC_OK(RTThreadUserSignal(hSelf));
264 return rc;
265}
266
267
268/**
269 * Used both as a thread procedure and child process worker.
270 */
271static DECLCALLBACK(int) tstRTLocalIpcSessionWaitChild(RTTHREAD hSelf, void *pvUser)
272{
273 RTTEST_CHECK_RC_OK_RET(g_hTest, RTTestSetDefault(g_hTest, NULL), rcCheck);
274 RT_NOREF_PV(hSelf); RT_NOREF_PV(pvUser);
275
276 RTLOCALIPCSESSION hClientSession;
277 RTTESTI_CHECK_RC_RET(RTLocalIpcSessionConnect(&hClientSession, "tstRTLocalIpcSessionWait", 0 /*fFlags*/),
278 VINF_SUCCESS, rcCheck);
279
280 /*
281 * The server side won't write anything. It will close the connection
282 * as soon as we write something.
283 */
284 RTTESTI_CHECK_RC(RTLocalIpcSessionWaitForData(hClientSession, 0 /*cMsTimeout*/), VERR_TIMEOUT);
285 RTTESTI_CHECK_RC(RTLocalIpcSessionWaitForData(hClientSession, 8 /*cMsTimeout*/), VERR_TIMEOUT);
286 uint8_t abBuf[4];
287 size_t cbRead = _4M-1;
288 RTTESTI_CHECK_RC(RTLocalIpcSessionReadNB(hClientSession, abBuf, sizeof(abBuf), &cbRead), VINF_TRY_AGAIN);
289 RTTESTI_CHECK(cbRead == 0);
290
291 /* Trigger server disconnect. */
292 int rc;
293 RTTESTI_CHECK_RC(rc = RTLocalIpcSessionWrite(hClientSession, RT_STR_TUPLE("disconnect")), VINF_SUCCESS);
294 if (RT_SUCCESS(rc))
295 {
296 /*
297 * When we wait now, we should get an broken pipe error as
298 * the server has close its end.
299 */
300 RTTESTI_CHECK_RC(rc = RTLocalIpcSessionWaitForData(hClientSession, RT_MS_1MIN), VERR_BROKEN_PIPE);
301 RTTESTI_CHECK_RC(RTLocalIpcSessionWaitForData(hClientSession, 0), VERR_BROKEN_PIPE);
302 RTTESTI_CHECK_RC(RTLocalIpcSessionWaitForData(hClientSession, RT_MS_1SEC), VERR_BROKEN_PIPE);
303
304 bool fMayPanic = RTAssertSetMayPanic(false);
305 bool fQuiet = RTAssertSetQuiet(true);
306
307 RTTESTI_CHECK_RC(RTLocalIpcSessionWrite(hClientSession, RT_STR_TUPLE("broken")), VERR_BROKEN_PIPE);
308 RTTESTI_CHECK_RC(RTLocalIpcSessionRead(hClientSession, abBuf, sizeof(abBuf), NULL), VERR_BROKEN_PIPE);
309 cbRead = _4M-1;
310 RTTESTI_CHECK_RC(RTLocalIpcSessionRead(hClientSession, abBuf, sizeof(abBuf), &cbRead), VERR_BROKEN_PIPE);
311 cbRead = _1G/2;
312 RTTESTI_CHECK_RC(RTLocalIpcSessionReadNB(hClientSession, abBuf, sizeof(abBuf), &cbRead), VERR_BROKEN_PIPE);
313
314 RTAssertSetMayPanic(fMayPanic);
315 RTAssertSetQuiet(fQuiet);
316 }
317
318 RTTESTI_CHECK_RC(RTLocalIpcSessionClose(hClientSession), VINF_OBJECT_DESTROYED);
319
320 return VINF_SUCCESS;
321}
322
323
324/**
325 * @note This is identical to testSessionData with a couple of string and
326 * function pointers replaced.
327 */
328static void testSessionWait(const char *pszExecPath)
329{
330 RTTestISub(!pszExecPath ? "Wait for data in thread" : "Wait for data in child");
331
332 /*
333 * Create the test server.
334 */
335 RTLOCALIPCSERVER hIpcServer;
336 RTTESTI_CHECK_RC_RETV(RTLocalIpcServerCreate(&hIpcServer, "tstRTLocalIpcSessionWait", 0), VINF_SUCCESS);
337
338 /*
339 * Create worker thread that listens and processes incoming connections
340 * until cancelled.
341 */
342 int rc;
343 RTTHREAD hListenThread;
344 RTTESTI_CHECK_RC_OK(rc = RTThreadCreate(&hListenThread, testSessionWaitThread, hIpcServer, 0 /* Stack */,
345 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "listen-2"));
346 if (RT_SUCCESS(rc))
347 {
348 /*
349 * Create a client process or thread and connects to the server.
350 * It will perform the wait-for-data test.
351 */
352 RTPROCESS hClientProc = NIL_RTPROCESS;
353 RTTHREAD hClientThread = NIL_RTTHREAD;
354 if (pszExecPath)
355 {
356 const char *apszArgs[4] = { pszExecPath, "child", "tstRTLocalIpcSessionWaitChild", NULL };
357 RTTESTI_CHECK_RC_OK(rc = RTProcCreate(pszExecPath, apszArgs, RTENV_DEFAULT, 0 /* fFlags*/, &hClientProc));
358 }
359 else
360 RTTESTI_CHECK_RC_OK(rc = RTThreadCreate(&hClientThread, tstRTLocalIpcSessionWaitChild, g_hTest, 0 /*cbStack*/,
361 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "client-2"));
362
363 /*
364 * Wait for the server thread to indicate that it has processed one
365 * connection, then shut it all down.
366 */
367 if (RT_SUCCESS(rc))
368 RTTESTI_CHECK_RC_OK(RTThreadUserWait(hListenThread, RT_MS_1MIN / 2));
369
370 RTTESTI_CHECK_RC(RTLocalIpcServerCancel(hIpcServer), VINF_SUCCESS);
371 int rcThread;
372 RTTESTI_CHECK_RC(rc = RTThreadWait(hListenThread, RT_MS_1MIN / 2, &rcThread), VINF_SUCCESS);
373 if (RT_SUCCESS(rc))
374 RTTESTI_CHECK_RC(rcThread, VERR_CANCELLED);
375
376 RTTESTI_CHECK_RC(RTLocalIpcServerDestroy(hIpcServer), VINF_OBJECT_DESTROYED);
377
378 /*
379 * Check that client ran successfully.
380 */
381 if (pszExecPath)
382 {
383 if (hClientProc != NIL_RTPROCESS)
384 {
385 RTPROCSTATUS ProcStatus;
386 RTTESTI_CHECK_RC_OK(rc = RTProcWait(hClientProc, RTPROCWAIT_FLAGS_BLOCK, &ProcStatus));
387 if (RT_SUCCESS(rc) && (ProcStatus.enmReason != RTPROCEXITREASON_NORMAL || ProcStatus.iStatus != 0))
388 RTTestIFailed("Chiled exited with enmReason=%d iStatus=%d", ProcStatus.enmReason, ProcStatus.iStatus);
389 }
390 }
391 else if (hClientThread != NIL_RTTHREAD)
392 {
393 RTTESTI_CHECK_RC_OK(rc = RTThreadWait(hClientThread, RT_MS_1MIN / 2, &rcThread));
394 if (RT_SUCCESS(rc))
395 RTTESTI_CHECK_RC(rcThread, VINF_SUCCESS);
396 }
397 }
398}
399
400
401
402/*********************************************************************************************************************************
403* *
404* testSessionData - Data transfer integrity. *
405* *
406*********************************************************************************************************************************/
407
408/** The max message size. */
409#define MAX_DATA_MSG_SIZE _1M
410
411static int testSessionDataReadMessages(RTLOCALIPCSESSION hIpcSession, uint32_t cRounds)
412{
413 /*
414 * Message scratch buffer. Search message starts with a uint32_t word
415 * that indicates the message length. The remaining words are set to
416 * the message number.
417 */
418 uint32_t *pau32ScratchBuf = (uint32_t *)RTMemAlloc(MAX_DATA_MSG_SIZE);
419 RTTESTI_CHECK_RET(pau32ScratchBuf != NULL, VERR_NO_MEMORY);
420
421 int rc = VINF_SUCCESS;
422 for (uint32_t iRound = 0; iRound < cRounds && rc == VINF_SUCCESS; iRound++)
423 {
424 /* Read the message length. */
425 uint32_t cbMsg;
426 RTTESTI_CHECK_RC_BREAK(rc = RTLocalIpcSessionRead(hIpcSession, &cbMsg, sizeof(cbMsg), NULL), VINF_SUCCESS);
427 if (cbMsg >= sizeof(cbMsg) && cbMsg <= MAX_DATA_MSG_SIZE)
428 {
429 pau32ScratchBuf[0] = cbMsg;
430
431 /* Read the message body. */
432 uint32_t cbLeft = cbMsg - sizeof(uint32_t);
433 uint8_t *pbCur = (uint8_t *)&pau32ScratchBuf[1];
434 while (cbLeft > 0)
435 {
436 uint32_t cbCur = RTRandU32Ex(1, cbLeft + cbLeft / 4);
437 cbCur = RT_MIN(cbCur, cbLeft);
438 if ((iRound % 3) == 1)
439 {
440 size_t cbRead = _1G;
441 RTTESTI_CHECK_RC_BREAK(rc = RTLocalIpcSessionRead(hIpcSession, pbCur, cbCur, &cbRead), VINF_SUCCESS);
442 RTTESTI_CHECK(cbCur >= cbRead);
443 cbCur = (uint32_t)cbRead;
444 }
445 else
446 RTTESTI_CHECK_RC_BREAK(rc = RTLocalIpcSessionRead(hIpcSession, pbCur, cbCur, NULL), VINF_SUCCESS);
447 pbCur += cbCur;
448 cbLeft -= cbCur;
449 }
450
451 /* Check the message body. */
452 if (RT_SUCCESS(rc))
453 {
454 uint32_t offLast = cbMsg & (sizeof(uint32_t) - 1);
455 if (offLast)
456 memcpy((uint8_t *)pau32ScratchBuf + cbMsg, (uint8_t const *)&iRound + offLast, sizeof(uint32_t) - offLast);
457
458 ASMCompilerBarrier(); /* Guard against theoretical alias issues in the above code. */
459
460 uint32_t cWords = RT_ALIGN_32(cbMsg, sizeof(uint32_t)) / sizeof(uint32_t);
461 for (uint32_t iWord = 1; iWord < cWords; iWord++)
462 if (pau32ScratchBuf[iWord] != iRound)
463 {
464 RTTestIFailed("Message body word #%u mismatch: %#x, expected %#x", iWord, pau32ScratchBuf[iWord], iRound);
465 break;
466 }
467 }
468 }
469 else
470 {
471 RTTestIFailed("cbMsg=%#x is out of range", cbMsg);
472 rc = VERR_OUT_OF_RANGE;
473 }
474 }
475
476 RTMemFree(pau32ScratchBuf);
477 return rc;
478}
479
480
481static int testSessionDataWriteMessages(RTLOCALIPCSESSION hIpcSession, uint32_t cRounds)
482{
483 /*
484 * Message scratch buffer. Search message starts with a uint32_t word
485 * that indicates the message length. The remaining words are set to
486 * the message number.
487 */
488 uint32_t cbScratchBuf = RTRandU32Ex(64, MAX_DATA_MSG_SIZE);
489 cbScratchBuf = RT_ALIGN_32(cbScratchBuf, sizeof(uint32_t));
490
491 uint32_t *pau32ScratchBuf = (uint32_t *)RTMemAlloc(cbScratchBuf);
492 RTTESTI_CHECK_RET(pau32ScratchBuf != NULL, VERR_NO_MEMORY);
493
494 size_t cbSent = 0;
495 int rc = VINF_SUCCESS;
496 for (uint32_t iRound = 0; iRound < cRounds && rc == VINF_SUCCESS; iRound++)
497 {
498 /* Construct the message. */
499 uint32_t cbMsg = RTRandU32Ex(sizeof(uint32_t), cbScratchBuf);
500 uint32_t cWords = RT_ALIGN_32(cbMsg, sizeof(uint32_t)) / sizeof(uint32_t);
501
502 uint32_t iWord = 0;
503 pau32ScratchBuf[iWord++] = cbMsg;
504 while (iWord < cWords)
505 pau32ScratchBuf[iWord++] = iRound;
506
507 /* Send it. */
508 uint32_t cbLeft = cbMsg;
509 uint8_t const *pbCur = (uint8_t *)pau32ScratchBuf;
510 while (cbLeft > 0)
511 {
512 uint32_t cbCur = RT_MIN(iRound + 1, cbLeft);
513 RTTESTI_CHECK_RC_BREAK(rc = RTLocalIpcSessionWrite(hIpcSession, pbCur, cbCur), VINF_SUCCESS);
514 pbCur += cbCur;
515 cbSent += cbCur;
516 cbLeft -= cbCur;
517 }
518 }
519
520 RTTestIPrintf(RTTESTLVL_ALWAYS, "Sent %'zu bytes over %u rounds.\n", cbSent, cRounds);
521 RTMemFree(pau32ScratchBuf);
522 return rc;
523}
524
525
526static DECLCALLBACK(int) testSessionDataThread(RTTHREAD hSelf, void *pvUser)
527{
528 RTLOCALIPCSERVER hIpcServer = (RTLOCALIPCSERVER)pvUser;
529 RTTEST_CHECK_RC_OK_RET(g_hTest, RTTestSetDefault(g_hTest, NULL), rcCheck);
530
531 int rc;
532 for (;;)
533 {
534 RTLOCALIPCSESSION hIpcSession;
535 rc = RTLocalIpcServerListen(hIpcServer, &hIpcSession);
536 if (RT_SUCCESS(rc))
537 {
538 RTTestIPrintf(RTTESTLVL_INFO, "testSessionDataThread: Got new client connection\n");
539
540 /* The server is the initator. First message sets the number of rounds. */
541 uint32_t cRounds = RTRandU32Ex(32, _1K);
542 RTTESTI_CHECK_RC(rc = RTLocalIpcSessionWrite(hIpcSession, &cRounds, sizeof(cRounds)), VINF_SUCCESS);
543 if (RT_SUCCESS(rc))
544 {
545 rc = testSessionDataWriteMessages(hIpcSession, cRounds);
546 if (RT_SUCCESS(rc))
547 rc = testSessionDataReadMessages(hIpcSession, cRounds);
548 }
549
550 RTTESTI_CHECK_RC(RTLocalIpcSessionClose(hIpcSession), VINF_OBJECT_DESTROYED);
551 RTTESTI_CHECK_RC_OK(RTThreadUserSignal(hSelf));
552 }
553 else
554 {
555 RTTESTI_CHECK_RC(rc, VERR_CANCELLED);
556 break;
557 }
558 }
559 RTTESTI_CHECK_RC_OK(RTThreadUserSignal(hSelf));
560 return rc;
561}
562
563
564/**
565 * Used both as a thread procedure and child process worker.
566 */
567static DECLCALLBACK(int) tstRTLocalIpcSessionDataChild(RTTHREAD hSelf, void *pvUser)
568{
569 RTTEST_CHECK_RC_OK_RET(g_hTest, RTTestSetDefault(g_hTest, NULL), rcCheck);
570 RT_NOREF_PV(hSelf); RT_NOREF_PV(pvUser);
571
572 /*
573 * Connect.
574 */
575 RTLOCALIPCSESSION hClientSession;
576 RTTESTI_CHECK_RC_RET(RTLocalIpcSessionConnect(&hClientSession, "tstRTLocalIpcSessionData", 0 /*fFlags*/),
577 VINF_SUCCESS, rcCheck);
578
579 /*
580 * The server first sends us a rounds count.
581 */
582 int rc;
583 uint32_t cRounds = 0;
584 RTTESTI_CHECK_RC(rc = RTLocalIpcSessionRead(hClientSession, &cRounds, sizeof(cRounds), NULL), VINF_SUCCESS);
585 if (RT_SUCCESS(rc))
586 {
587 if (cRounds >= 32 && cRounds <= _1K)
588 {
589 rc = testSessionDataReadMessages(hClientSession, cRounds);
590 if (RT_SUCCESS(rc))
591 rc = testSessionDataWriteMessages(hClientSession, cRounds);
592 }
593 else
594 RTTestIFailed("cRounds=%#x is out of range", cRounds);
595 }
596
597 RTTESTI_CHECK_RC(RTLocalIpcSessionClose(hClientSession), VINF_OBJECT_DESTROYED);
598
599 return rc;
600}
601
602
603/**
604 * @note This is identical to testSessionWait with a couple of strings, function
605 * pointers, and timeouts replaced.
606 */
607static void testSessionData(const char *pszExecPath)
608{
609 RTTestISub(!pszExecPath ? "Data exchange with thread" : "Data exchange with child");
610
611 /*
612 * Create the test server.
613 */
614 RTLOCALIPCSERVER hIpcServer;
615 RTTESTI_CHECK_RC_RETV(RTLocalIpcServerCreate(&hIpcServer, "tstRTLocalIpcSessionData", 0), VINF_SUCCESS);
616
617 /*
618 * Create worker thread that listens and processes incoming connections
619 * until cancelled.
620 */
621 int rc;
622 RTTHREAD hListenThread;
623 RTTESTI_CHECK_RC_OK(rc = RTThreadCreate(&hListenThread, testSessionDataThread, hIpcServer, 0 /* Stack */,
624 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "listen-3"));
625 if (RT_SUCCESS(rc))
626 {
627 /*
628 * Create a client thread or process.
629 */
630 RTPROCESS hClientProc = NIL_RTPROCESS;
631 RTTHREAD hClientThread = NIL_RTTHREAD;
632 if (pszExecPath)
633 {
634 const char *apszArgs[4] = { pszExecPath, "child", "tstRTLocalIpcSessionDataChild", NULL };
635 RTTESTI_CHECK_RC_OK(rc = RTProcCreate(pszExecPath, apszArgs, RTENV_DEFAULT, 0 /* fFlags*/, &hClientProc));
636 }
637 else
638 RTTESTI_CHECK_RC_OK(rc = RTThreadCreate(&hClientThread, tstRTLocalIpcSessionDataChild, g_hTest, 0 /*cbStack*/,
639 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "client-2"));
640
641 /*
642 * Wait for the server thread to indicate that it has processed one
643 * connection, then shut it all down.
644 */
645 if (RT_SUCCESS(rc))
646 RTTESTI_CHECK_RC_OK(RTThreadUserWait(hListenThread, RT_MS_1MIN * 3));
647
648 RTTESTI_CHECK_RC(RTLocalIpcServerCancel(hIpcServer), VINF_SUCCESS);
649 int rcThread;
650 RTTESTI_CHECK_RC(rc = RTThreadWait(hListenThread, RT_MS_1MIN / 2, &rcThread), VINF_SUCCESS);
651 if (RT_SUCCESS(rc))
652 RTTESTI_CHECK_RC(rcThread, VERR_CANCELLED);
653
654 RTTESTI_CHECK_RC(RTLocalIpcServerDestroy(hIpcServer), VINF_OBJECT_DESTROYED);
655
656 /*
657 * Check that client ran successfully.
658 */
659 if (pszExecPath)
660 {
661 if (hClientProc != NIL_RTPROCESS)
662 {
663 RTPROCSTATUS ProcStatus;
664 RTTESTI_CHECK_RC_OK(rc = RTProcWait(hClientProc, RTPROCWAIT_FLAGS_BLOCK, &ProcStatus));
665 if (RT_SUCCESS(rc) && (ProcStatus.enmReason != RTPROCEXITREASON_NORMAL || ProcStatus.iStatus != 0))
666 RTTestIFailed("Chiled exited with enmReason=%d iStatus=%d", ProcStatus.enmReason, ProcStatus.iStatus);
667 }
668 }
669 else if (hClientThread != NIL_RTTHREAD)
670 {
671 RTTESTI_CHECK_RC_OK(rc = RTThreadWait(hClientThread, RT_MS_1MIN / 2, &rcThread));
672 if (RT_SUCCESS(rc))
673 RTTESTI_CHECK_RC(rcThread, VINF_SUCCESS);
674 }
675 }
676}
677
678
679/*********************************************************************************************************************************
680* *
681* testSessionPerf - Performance measurements. *
682* *
683*********************************************************************************************************************************/
684
685#define IPC_PERF_LAST_MSG UINT32_C(0x7fffeeee)
686#define IPC_PERF_MSG_REPLY(uMsg) ((uMsg) | RT_BIT_32(31))
687
688
689static DECLCALLBACK(int) testSessionPerfThread(RTTHREAD hSelf, void *pvUser)
690{
691 RTLOCALIPCSERVER hIpcServer = (RTLOCALIPCSERVER)pvUser;
692 RTTEST_CHECK_RC_OK_RET(g_hTest, RTTestSetDefault(g_hTest, NULL), rcCheck);
693
694 int rc;
695 for (;;)
696 {
697 RTLOCALIPCSESSION hIpcSession;
698 rc = RTLocalIpcServerListen(hIpcServer, &hIpcSession);
699 if (RT_SUCCESS(rc))
700 {
701 RTTestIPrintf(RTTESTLVL_INFO, "testSessionPerfThread: Got new client connection\n");
702
703 /* The server is the initator, so we start sending messages. */
704 uint64_t cNsElapsed = _4G;
705 uint64_t nsStart = RTTimeNanoTS();
706 uint32_t cMessages = 0;
707 for (;; )
708 {
709 uint32_t uMsg = cMessages;
710 RTTESTI_CHECK_RC_BREAK(rc = RTLocalIpcSessionWrite(hIpcSession, &uMsg, sizeof(uMsg)), VINF_SUCCESS);
711 uMsg = UINT32_MAX;
712 RTTESTI_CHECK_RC_BREAK(rc = RTLocalIpcSessionRead(hIpcSession, &uMsg, sizeof(uMsg), NULL), VINF_SUCCESS);
713 if (uMsg == IPC_PERF_MSG_REPLY(cMessages))
714 { /* likely */ }
715 else
716 {
717 RTTestIFailed("uMsg=%#x expected %#x", uMsg, IPC_PERF_MSG_REPLY(cMessages));
718 rc = VERR_OUT_OF_RANGE;
719 break;
720 }
721
722 /* next */
723 cMessages++;
724 if (cMessages & _16K)
725 { /* likely */ }
726 else
727 {
728 cNsElapsed = RTTimeNanoTS() - nsStart;
729 if (cNsElapsed > 2*RT_NS_1SEC_64)
730 {
731 uMsg = IPC_PERF_LAST_MSG;
732 RTTESTI_CHECK_RC_BREAK(rc = RTLocalIpcSessionWrite(hIpcSession, &uMsg, sizeof(uMsg)), VINF_SUCCESS);
733 break;
734 }
735 }
736 }
737 if (RT_SUCCESS(rc))
738 {
739 RTThreadSleep(8); /* windows output fudge (purely esthetical) */
740 RTTestIValue("roundtrip", cNsElapsed / cMessages, RTTESTUNIT_NS_PER_ROUND_TRIP);
741 RTTestIValue("roundtrips", RT_NS_1SEC / (cNsElapsed / cMessages), RTTESTUNIT_OCCURRENCES_PER_SEC);
742 }
743
744 RTTESTI_CHECK_RC(RTLocalIpcSessionClose(hIpcSession), VINF_OBJECT_DESTROYED);
745 RTTESTI_CHECK_RC_OK(RTThreadUserSignal(hSelf));
746 }
747 else
748 {
749 RTTESTI_CHECK_RC(rc, VERR_CANCELLED);
750 break;
751 }
752 }
753 RTTESTI_CHECK_RC_OK(RTThreadUserSignal(hSelf));
754 return rc;
755}
756
757
758/**
759 * Used both as a thread procedure and child process worker.
760 */
761static DECLCALLBACK(int) tstRTLocalIpcSessionPerfChild(RTTHREAD hSelf, void *pvUser)
762{
763 RTTEST_CHECK_RC_OK_RET(g_hTest, RTTestSetDefault(g_hTest, NULL), rcCheck);
764 RT_NOREF_PV(hSelf); RT_NOREF_PV(pvUser);
765
766 /*
767 * Connect.
768 */
769 RTLOCALIPCSESSION hClientSession;
770 RTTESTI_CHECK_RC_RET(RTLocalIpcSessionConnect(&hClientSession, "tstRTLocalIpcSessionPerf", 0 /*fFlags*/),
771 VINF_SUCCESS, rcCheck);
772
773 /*
774 * Process messages. Server does all the timing and stuff.
775 */
776 int rc = VINF_SUCCESS;
777 for (uint32_t cMessages = 0; ; cMessages++)
778 {
779 /* Read the next message from the server. */
780 uint32_t uMsg = UINT32_MAX;
781 RTTESTI_CHECK_RC_BREAK(rc = RTLocalIpcSessionRead(hClientSession, &uMsg, sizeof(uMsg), NULL), VINF_SUCCESS);
782 if (uMsg == cMessages)
783 {
784 uMsg = IPC_PERF_MSG_REPLY(uMsg);
785 RTTESTI_CHECK_RC_BREAK(rc = RTLocalIpcSessionWrite(hClientSession, &uMsg, sizeof(uMsg)), VINF_SUCCESS);
786 }
787 else if (uMsg == IPC_PERF_LAST_MSG)
788 break;
789 else
790 {
791 RTTestIFailed("uMsg=%#x expected %#x", uMsg, cMessages);
792 rc = VERR_OUT_OF_RANGE;
793 break;
794 }
795 }
796
797 RTTESTI_CHECK_RC(RTLocalIpcSessionClose(hClientSession), VINF_OBJECT_DESTROYED);
798 return rc;
799}
800
801
802/**
803 * @note This is identical to testSessionWait with a couple of string and
804 * function pointers replaced.
805 */
806static void testSessionPerf(const char *pszExecPath)
807{
808 RTTestISub(!pszExecPath ? "Thread performance" : "Child performance");
809
810 /*
811 * Create the test server.
812 */
813 RTLOCALIPCSERVER hIpcServer;
814 RTTESTI_CHECK_RC_RETV(RTLocalIpcServerCreate(&hIpcServer, "tstRTLocalIpcSessionPerf", 0), VINF_SUCCESS);
815
816 /*
817 * Create worker thread that listens and processes incoming connections
818 * until cancelled.
819 */
820 int rc;
821 RTTHREAD hListenThread;
822 RTTESTI_CHECK_RC_OK(rc = RTThreadCreate(&hListenThread, testSessionPerfThread, hIpcServer, 0 /* Stack */,
823 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "listen-3"));
824 if (RT_SUCCESS(rc))
825 {
826 /*
827 * Create a client thread or process.
828 */
829 RTPROCESS hClientProc = NIL_RTPROCESS;
830 RTTHREAD hClientThread = NIL_RTTHREAD;
831 if (pszExecPath)
832 {
833 const char *apszArgs[4] = { pszExecPath, "child", "tstRTLocalIpcSessionPerfChild", NULL };
834 RTTESTI_CHECK_RC_OK(rc = RTProcCreate(pszExecPath, apszArgs, RTENV_DEFAULT, 0 /* fFlags*/, &hClientProc));
835 }
836 else
837 RTTESTI_CHECK_RC_OK(rc = RTThreadCreate(&hClientThread, tstRTLocalIpcSessionPerfChild, g_hTest, 0 /*cbStack*/,
838 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "client-2"));
839
840 /*
841 * Wait for the server thread to indicate that it has processed one
842 * connection, then shut it all down.
843 */
844 if (RT_SUCCESS(rc))
845 RTTESTI_CHECK_RC_OK(RTThreadUserWait(hListenThread, RT_MS_1MIN / 2));
846
847 RTTESTI_CHECK_RC(RTLocalIpcServerCancel(hIpcServer), VINF_SUCCESS);
848 int rcThread;
849 RTTESTI_CHECK_RC(rc = RTThreadWait(hListenThread, RT_MS_1MIN / 2, &rcThread), VINF_SUCCESS);
850 if (RT_SUCCESS(rc))
851 RTTESTI_CHECK_RC(rcThread, VERR_CANCELLED);
852
853 RTTESTI_CHECK_RC(RTLocalIpcServerDestroy(hIpcServer), VINF_OBJECT_DESTROYED);
854
855 /*
856 * Check that client ran successfully.
857 */
858 if (pszExecPath)
859 {
860 if (hClientProc != NIL_RTPROCESS)
861 {
862 RTPROCSTATUS ProcStatus;
863 RTTESTI_CHECK_RC_OK(rc = RTProcWait(hClientProc, RTPROCWAIT_FLAGS_BLOCK, &ProcStatus));
864 if (RT_SUCCESS(rc) && (ProcStatus.enmReason != RTPROCEXITREASON_NORMAL || ProcStatus.iStatus != 0))
865 RTTestIFailed("Chiled exited with enmReason=%d iStatus=%d", ProcStatus.enmReason, ProcStatus.iStatus);
866 }
867 }
868 else if (hClientThread != NIL_RTTHREAD)
869 {
870 RTTESTI_CHECK_RC_OK(rc = RTThreadWait(hClientThread, RT_MS_1MIN / 2, &rcThread));
871 if (RT_SUCCESS(rc))
872 RTTESTI_CHECK_RC(rcThread, VINF_SUCCESS);
873 }
874 }
875}
876
877
878int main(int argc, char **argv)
879{
880 int rc = RTR3InitExe(argc, &argv, 0);
881 if (RT_FAILURE(rc))
882 return RTMsgInitFailure(rc);
883
884 /*
885 * Main process.
886 */
887 if (argc == 1)
888 {
889 rc = RTTestCreate("tstRTLocalIpc", &g_hTest);
890 if (RT_FAILURE(rc))
891 return RTEXITCODE_FAILURE;
892 RTTestBanner(g_hTest);
893
894 /* Basics first. */
895 bool fMayPanic = RTAssertSetMayPanic(false);
896 bool fQuiet = RTAssertSetQuiet(true);
897 testBasics();
898 RTAssertSetMayPanic(fMayPanic);
899 RTAssertSetQuiet(fQuiet);
900
901 /* Do real tests if the basics are fine. */
902 char szExecPath[RTPATH_MAX];
903 if (RTProcGetExecutablePath(szExecPath, sizeof(szExecPath)))
904 {
905 if (RTTestErrorCount(g_hTest) == 0)
906 testSessionConnection(NULL);
907 if (RTTestErrorCount(g_hTest) == 0)
908 testSessionConnection(szExecPath);
909
910 if (RTTestErrorCount(g_hTest) == 0)
911 testSessionWait(NULL);
912 if (RTTestErrorCount(g_hTest) == 0)
913 testSessionWait(szExecPath);
914
915 if (RTTestErrorCount(g_hTest) == 0)
916 testSessionData(NULL);
917 if (RTTestErrorCount(g_hTest) == 0)
918 testSessionData(szExecPath);
919
920 if (RTTestErrorCount(g_hTest) == 0)
921 testSessionPerf(NULL);
922 if (RTTestErrorCount(g_hTest) == 0)
923 testSessionPerf(szExecPath);
924 }
925 else
926 RTTestIFailed("RTProcGetExecutablePath failed");
927 }
928 /*
929 * Child process.
930 */
931 else if ( argc == 3
932 && !strcmp(argv[1], "child"))
933 {
934 rc = RTTestCreateChild(argv[2], &g_hTest);
935 if (RT_FAILURE(rc))
936 return RTEXITCODE_FAILURE;
937
938 if (!strcmp(argv[2], "tstRTLocalIpcSessionConnectionChild"))
939 tstRTLocalIpcSessionConnectionChild(RTThreadSelf(), g_hTest);
940 else if (!strcmp(argv[2], "tstRTLocalIpcSessionWaitChild"))
941 tstRTLocalIpcSessionWaitChild(RTThreadSelf(), g_hTest);
942 else if (!strcmp(argv[2], "tstRTLocalIpcSessionDataChild"))
943 tstRTLocalIpcSessionDataChild(RTThreadSelf(), g_hTest);
944 else if (!strcmp(argv[2], "tstRTLocalIpcSessionPerfChild"))
945 tstRTLocalIpcSessionPerfChild(RTThreadSelf(), g_hTest);
946 else
947 RTTestIFailed("Unknown child function '%s'", argv[2]);
948 }
949 /*
950 * Invalid parameters.
951 */
952 else
953 return RTEXITCODE_SYNTAX;
954
955 /*
956 * Summary.
957 */
958 return RTTestSummaryAndDestroy(g_hTest);
959}
960
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette