11/* ************************************************************************
22 * Copyright (c) 2026. All Rights Reserved.
3- * Cross-process IPC memory handle test (requires MPI and KunlunXin XPU).
3+ * Cross-process IPC memory handle test.
4+ *
5+ * Run exactly two MPI ranks on the same host. Device IPC handles are
6+ * host-local and cannot be transferred between different nodes.
47 ************************************************************************/
58
69#include < gtest/gtest.h>
@@ -32,16 +35,25 @@ namespace {
3235 } \
3336 } while (0 )
3437
38+ #define ASSERT_MPI_TRUE (condition ) \
39+ do { \
40+ if (!(condition)) { \
41+ ADD_FAILURE () << " MPI assertion failed: " << #condition; \
42+ MPI_Abort (MPI_COMM_WORLD , 1 ); \
43+ return ; \
44+ } \
45+ } while (0 )
46+
3547class IpcMemHandleMpiTest : public ::testing::Test {
3648protected:
3749 void SetUp () override {
3850 flagcxDeviceHandleInit (&devHandle);
39- ASSERT_NE (devHandle, nullptr );
51+ ASSERT_MPI_TRUE (devHandle != nullptr );
4052
4153 int deviceCount = 0 ;
4254 ASSERT_FLAGCX_SUCCESS (devHandle->getDeviceCount (&deviceCount));
4355 if (deviceCount <= 0 ) {
44- ADD_FAILURE () << " No visible XPU device" ;
56+ ADD_FAILURE () << " No visible device" ;
4557 MPI_Abort (MPI_COMM_WORLD , 1 );
4658 return ;
4759 }
@@ -70,35 +82,70 @@ TEST_F(IpcMemHandleMpiTest, CrossProcessLifecycle) {
7082 constexpr size_t bufferSize = 4096 ;
7183 constexpr int expectedValue = 0x12345678 ;
7284
85+ int localApisAvailable =
86+ devHandle->ipcMemHandleCreate != nullptr &&
87+ devHandle->ipcMemHandleGet != nullptr &&
88+ devHandle->ipcMemHandleOpen != nullptr &&
89+ devHandle->ipcMemHandleClose != nullptr &&
90+ devHandle->ipcMemHandleFree != nullptr ;
91+ int allApisAvailable = 0 ;
92+ ASSERT_MPI_SUCCESS (MPI_Allreduce (&localApisAvailable, &allApisAvailable, 1 ,
93+ MPI_INT , MPI_MIN , MPI_COMM_WORLD ));
94+ if (!allApisAvailable) {
95+ GTEST_SKIP () << " IPC memory handle APIs are not available" ;
96+ }
97+
98+ // Every rank creates its receive/export storage before communication. This
99+ // also provides a coordinated runtime capability check for stub backends.
100+ flagcxIpcMemHandle_t handle = nullptr ;
101+ size_t localHandleSize = 0 ;
102+ flagcxResult_t createResult =
103+ devHandle->ipcMemHandleCreate (&handle, &localHandleSize);
104+ if (createResult != flagcxSuccess &&
105+ createResult != flagcxNotSupported) {
106+ ADD_FAILURE () << " ipcMemHandleCreate returned "
107+ << static_cast <int >(createResult);
108+ MPI_Abort (MPI_COMM_WORLD , static_cast <int >(createResult));
109+ return ;
110+ }
111+ int localSupported = createResult != flagcxNotSupported;
112+ int allSupported = 0 ;
113+ ASSERT_MPI_SUCCESS (MPI_Allreduce (&localSupported, &allSupported, 1 , MPI_INT ,
114+ MPI_MIN , MPI_COMM_WORLD ));
115+ if (!allSupported) {
116+ if (createResult == flagcxSuccess && handle != nullptr ) {
117+ ASSERT_FLAGCX_SUCCESS (devHandle->ipcMemHandleFree (handle));
118+ }
119+ GTEST_SKIP () << " IPC memory handles are not supported" ;
120+ }
121+ ASSERT_FLAGCX_SUCCESS (createResult);
122+ ASSERT_MPI_TRUE (handle != nullptr );
123+ ASSERT_MPI_TRUE (localHandleSize > 0 );
124+
73125 if (rank == 0 ) {
74126 void *devPtr = nullptr ;
75127 ASSERT_FLAGCX_SUCCESS (devHandle->deviceMalloc (
76128 &devPtr, bufferSize, flagcxMemDevice, nullptr ));
77- ASSERT_NE (devPtr, nullptr );
129+ ASSERT_MPI_TRUE (devPtr != nullptr );
78130
79131 int hostValue = expectedValue;
80132 ASSERT_FLAGCX_SUCCESS (devHandle->deviceMemcpy (
81133 devPtr, &hostValue, sizeof (hostValue), flagcxMemcpyHostToDevice,
82134 nullptr ));
83135 ASSERT_FLAGCX_SUCCESS (devHandle->streamSynchronize (nullptr ));
84136
85- flagcxIpcMemHandle_t handle = nullptr ;
86- size_t handleSize = 0 ;
87- ASSERT_FLAGCX_SUCCESS (
88- devHandle->ipcMemHandleCreate (&handle, &handleSize));
89- ASSERT_NE (handle, nullptr );
90- ASSERT_GT (handleSize, static_cast <size_t >(0 ));
91137 ASSERT_FLAGCX_SUCCESS (devHandle->ipcMemHandleGet (handle, devPtr));
92138
93- ASSERT_MPI_SUCCESS (MPI_Send (&handleSize, sizeof (handleSize), MPI_BYTE , 1 ,
94- 0 , MPI_COMM_WORLD ));
95- ASSERT_MPI_SUCCESS (MPI_Send (handle, static_cast <int >(handleSize), MPI_BYTE ,
96- 1 , 1 , MPI_COMM_WORLD ));
139+ ASSERT_MPI_SUCCESS (MPI_Send (&localHandleSize, sizeof (localHandleSize),
140+ MPI_BYTE , 1 , 0 , MPI_COMM_WORLD ));
141+ ASSERT_MPI_SUCCESS (
142+ MPI_Send (handle, static_cast <int >(localHandleSize), MPI_BYTE , 1 , 1 ,
143+ MPI_COMM_WORLD ));
97144
98145 int acknowledgement = 0 ;
99146 ASSERT_MPI_SUCCESS (MPI_Recv (&acknowledgement, 1 , MPI_INT , 1 , 2 ,
100147 MPI_COMM_WORLD , MPI_STATUS_IGNORE ));
101- ASSERT_EQ (acknowledgement, 1 );
148+ ASSERT_MPI_TRUE (acknowledgement == 1 );
102149
103150 ASSERT_FLAGCX_SUCCESS (devHandle->ipcMemHandleFree (handle));
104151 ASSERT_FLAGCX_SUCCESS (
@@ -109,11 +156,6 @@ TEST_F(IpcMemHandleMpiTest, CrossProcessLifecycle) {
109156 MPI_BYTE , 0 , 0 , MPI_COMM_WORLD ,
110157 MPI_STATUS_IGNORE ));
111158
112- flagcxIpcMemHandle_t handle = nullptr ;
113- size_t localHandleSize = 0 ;
114- ASSERT_FLAGCX_SUCCESS (
115- devHandle->ipcMemHandleCreate (&handle, &localHandleSize));
116- ASSERT_NE (handle, nullptr );
117159 if (localHandleSize != receivedHandleSize) {
118160 ADD_FAILURE () << " IPC handle size mismatch: local=" << localHandleSize
119161 << " , remote=" << receivedHandleSize;
@@ -127,14 +169,14 @@ TEST_F(IpcMemHandleMpiTest, CrossProcessLifecycle) {
127169
128170 void *mappedPtr = nullptr ;
129171 ASSERT_FLAGCX_SUCCESS (devHandle->ipcMemHandleOpen (handle, &mappedPtr));
130- ASSERT_NE (mappedPtr, nullptr );
172+ ASSERT_MPI_TRUE (mappedPtr != nullptr );
131173
132174 int receivedValue = 0 ;
133175 ASSERT_FLAGCX_SUCCESS (devHandle->deviceMemcpy (
134176 &receivedValue, mappedPtr, sizeof (receivedValue),
135177 flagcxMemcpyDeviceToHost, nullptr ));
136178 ASSERT_FLAGCX_SUCCESS (devHandle->streamSynchronize (nullptr ));
137- EXPECT_EQ (receivedValue, expectedValue);
179+ ASSERT_MPI_TRUE (receivedValue == expectedValue);
138180
139181 ASSERT_FLAGCX_SUCCESS (devHandle->ipcMemHandleClose (mappedPtr));
140182 ASSERT_FLAGCX_SUCCESS (devHandle->ipcMemHandleFree (handle));
0 commit comments