VirtualBox

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

Last change on this file since 59665 was 58305, checked in by vboxsync, 9 years ago

RTLocalIpcServerCreate: Dropped the RTLOCALIPC_FLAGS_MULTI_SESSION as it's not easy to implement single session servers on windows and especially linux.

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