VirtualBox

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

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

RTLocalIpc fixes.

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

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