VirtualBox

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

Last change on this file since 106061 was 106061, checked in by vboxsync, 8 weeks ago

Copyright year updates by scm.

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