VirtualBox

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

Last change on this file since 75734 was 69111, checked in by vboxsync, 7 years ago

(C) year

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