Skip to content

Commit 950345c

Browse files
committed
Add an IPC shared memory test
Add an IPC shared memory test using only the OS memory provider API (not using the API from ipc.h). Signed-off-by: Lukasz Dorau <lukasz.dorau@intel.com>
1 parent 90f758d commit 950345c

File tree

5 files changed

+592
-0
lines changed

5 files changed

+592
-0
lines changed

test/CMakeLists.txt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,3 +240,35 @@ endif()
240240
if(UMF_ENABLE_POOL_TRACKING)
241241
add_umf_test(NAME ipc SRCS ipcAPI.cpp)
242242
endif()
243+
244+
if(LINUX)
245+
set(BASE_NAME ipc_os_prov)
246+
set(TEST_NAME umf-${BASE_NAME})
247+
248+
foreach(loop_var IN ITEMS "producer" "consumer")
249+
set(EXEC_NAME umf_test-${BASE_NAME}_${loop_var})
250+
add_umf_executable(
251+
NAME ${EXEC_NAME}
252+
SRCS ${BASE_NAME}_${loop_var}.c
253+
LIBS umf)
254+
255+
target_include_directories(
256+
${EXEC_NAME} PRIVATE ${UMF_CMAKE_SOURCE_DIR}/src/utils
257+
${UMF_CMAKE_SOURCE_DIR}/include)
258+
259+
target_link_directories(${EXEC_NAME} PRIVATE ${LIBHWLOC_LIBRARY_DIRS})
260+
endforeach(loop_var)
261+
262+
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/${BASE_NAME}.sh
263+
DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
264+
265+
add_test(
266+
NAME ${TEST_NAME}
267+
COMMAND ${BASE_NAME}.sh
268+
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
269+
270+
set_tests_properties(${TEST_NAME} PROPERTIES LABELS "umf")
271+
else()
272+
message(
273+
STATUS "IPC shared memory test is supported on Linux only - skipping")
274+
endif()

test/ipc_os_prov.sh

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#
2+
# Copyright (C) 2024 Intel Corporation
3+
#
4+
# Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT.
5+
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
#
7+
8+
#!/bin/bash
9+
10+
# port should be a number from the range <1024, 65535>
11+
PORT=$(( 1024 + ( $$ % ( 65535 - 1024 ))))
12+
13+
# The ipc_os_prov example requires using pidfd_getfd(2)
14+
# to obtain a duplicate of another process's file descriptor.
15+
# Permission to duplicate another process's file descriptor
16+
# is governed by a ptrace access mode PTRACE_MODE_ATTACH_REALCREDS check (see ptrace(2))
17+
# that can be changed using the /proc/sys/kernel/yama/ptrace_scope interface.
18+
PTRACE_SCOPE_FILE="/proc/sys/kernel/yama/ptrace_scope"
19+
VAL=0
20+
if [ -f $PTRACE_SCOPE_FILE ]; then
21+
PTRACE_SCOPE_VAL=$(cat $PTRACE_SCOPE_FILE)
22+
if [ $PTRACE_SCOPE_VAL -ne $VAL ]; then
23+
echo "Setting ptrace_scope to 0 (classic ptrace permissions) ..."
24+
echo "$ sudo bash -c \"echo $VAL > $PTRACE_SCOPE_FILE\""
25+
sudo bash -c "echo $VAL > $PTRACE_SCOPE_FILE"
26+
fi
27+
PTRACE_SCOPE_VAL=$(cat $PTRACE_SCOPE_FILE)
28+
if [ $PTRACE_SCOPE_VAL -ne $VAL ]; then
29+
echo "SKIP: setting ptrace_scope to 0 (classic ptrace permissions) FAILED - skipping the test"
30+
exit 0
31+
fi
32+
fi
33+
34+
UMF_LOG_VAL="level:debug;flush:debug;output:stderr;pid:yes"
35+
36+
echo "Starting ipc_os_prov CONSUMER on port $PORT ..."
37+
UMF_LOG=$UMF_LOG_VAL ./umf_test-ipc_os_prov_consumer $PORT &
38+
39+
echo "Waiting 1 sec ..."
40+
sleep 1
41+
42+
echo "Starting ipc_os_prov PRODUCER on port $PORT ..."
43+
UMF_LOG=$UMF_LOG_VAL ./umf_test-ipc_os_prov_producer $PORT

test/ipc_os_prov_consumer.c

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
/*
2+
* Copyright (C) 2024 Intel Corporation
3+
*
4+
* Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT.
5+
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
*/
7+
8+
#include <arpa/inet.h>
9+
#include <stdio.h>
10+
#include <stdlib.h>
11+
#include <string.h>
12+
#include <sys/socket.h>
13+
#include <unistd.h>
14+
15+
#include <umf/providers/provider_os_memory.h>
16+
17+
#define INET_ADDR "127.0.0.1"
18+
#define MSG_SIZE 256
19+
#define RECV_BUFF_SIZE 1024
20+
21+
// consumer's response message
22+
#define CONSUMER_MSG \
23+
"This is the consumer. I just wrote a new number directly into your " \
24+
"shared memory!"
25+
26+
/*
27+
Generally communication between the producer and the consumer looks like:
28+
- Consumer starts
29+
- Consumer creates a socket
30+
- Consumer listens for incoming connections
31+
- Producer starts
32+
- Producer's shared memory contains a number: 140722582213392
33+
- Producer gets the IPC handle
34+
- Producer creates a socket
35+
- Producer connects to the consumer
36+
- Consumer connects at IP 127.0.0.1 and port 47770 to the producer
37+
- Producer sents the IPC handle to the consumer (24 bytes)
38+
- Consumer receives the IPC handle from the producer (24 bytes)
39+
- Consumer opens the IPC handle received from the producer
40+
- Consumer reads the number from the producer's shared memory: 140722582213392
41+
- Consumer writes a new number directly to the producer's shared memory: 70361291106696
42+
- Consumer sents a response message to the producer
43+
- Consumer closes the IPC handle received from the producer
44+
- Producer receives the response from the consumer: "This is the consumer. I just wrote a new number directly into your shared memory!"
45+
- Producer verifies the consumer wrote the correct value (the old one / 2) to the producer's shared memory: 70361291106696
46+
- Producer puts the IPC handle
47+
- Consumer shuts down
48+
- Producer shuts down
49+
*/
50+
51+
int main(int argc, char *argv[]) {
52+
struct sockaddr_in consumer_addr;
53+
struct sockaddr_in producer_addr;
54+
char consumer_message[MSG_SIZE];
55+
char recv_buffer[RECV_BUFF_SIZE];
56+
int producer_socket;
57+
int producer_addr_len;
58+
int consumer_socket;
59+
int ret = -1;
60+
61+
if (argc < 2) {
62+
fprintf(stderr, "usage: %s port\n", argv[0]);
63+
return -1;
64+
}
65+
66+
int port = atoi(argv[1]);
67+
68+
// zero the consumer_message buffer
69+
memset(consumer_message, 0, sizeof(consumer_message));
70+
71+
umf_memory_provider_handle_t OS_memory_provider = NULL;
72+
umf_os_memory_provider_params_t os_params;
73+
enum umf_result_t umf_result;
74+
75+
os_params = umfOsMemoryProviderParamsDefault();
76+
os_params.visibility = UMF_MEM_MAP_SHARED;
77+
78+
// create OS memory provider
79+
umf_result = umfMemoryProviderCreate(umfOsMemoryProviderOps(), &os_params,
80+
&OS_memory_provider);
81+
if (umf_result != UMF_RESULT_SUCCESS) {
82+
fprintf(stderr,
83+
"[consumer] ERROR: creating OS memory provider failed\n");
84+
return -1;
85+
}
86+
87+
// get the size of the IPC handle
88+
size_t IPC_handle_size;
89+
umf_result =
90+
umfMemoryProviderGetIPCHandleSize(OS_memory_provider, &IPC_handle_size);
91+
if (umf_result != UMF_RESULT_SUCCESS) {
92+
fprintf(stderr,
93+
"[consumer] ERROR: getting size of the IPC handle failed\n");
94+
goto err_umfMemoryProviderDestroy;
95+
}
96+
97+
// create a socket
98+
consumer_socket = socket(AF_INET, SOCK_STREAM, 0);
99+
if (consumer_socket < 0) {
100+
fprintf(stderr, "[consumer] ERROR: creating socket failed\n");
101+
goto err_umfMemoryProviderDestroy;
102+
}
103+
104+
fprintf(stderr, "[consumer] Socket created\n");
105+
106+
// set the IP address and the port
107+
consumer_addr.sin_family = AF_INET;
108+
consumer_addr.sin_port = htons(port);
109+
consumer_addr.sin_addr.s_addr = inet_addr(INET_ADDR);
110+
111+
// bind to the IP address and the port
112+
if (bind(consumer_socket, (struct sockaddr *)&consumer_addr,
113+
sizeof(consumer_addr)) < 0) {
114+
fprintf(stderr, "[consumer] ERROR: cannot bind to the port\n");
115+
goto err_close_consumer_socket;
116+
}
117+
118+
fprintf(stderr, "[consumer] Binding done\n");
119+
120+
// listen for the producer
121+
if (listen(consumer_socket, 1) < 0) {
122+
fprintf(stderr, "[consumer] ERROR: listen() failed\n");
123+
goto err_close_consumer_socket;
124+
}
125+
126+
fprintf(stderr, "[consumer] Listening for incoming connections ...\n");
127+
128+
// accept an incoming connection
129+
producer_addr_len = sizeof(producer_addr);
130+
producer_socket = accept(consumer_socket, (struct sockaddr *)&producer_addr,
131+
(socklen_t *)&producer_addr_len);
132+
if (producer_socket < 0) {
133+
fprintf(stderr, "[consumer] ERROR: accept() failed\n");
134+
goto err_close_consumer_socket;
135+
}
136+
137+
fprintf(stderr, "[consumer] Producer connected at IP %s and port %i\n",
138+
inet_ntoa(producer_addr.sin_addr), ntohs(producer_addr.sin_port));
139+
140+
// zero the receive buffer
141+
memset(recv_buffer, 0, RECV_BUFF_SIZE);
142+
143+
// receive a producer's message
144+
ssize_t len = recv(producer_socket, recv_buffer, RECV_BUFF_SIZE, 0);
145+
if (len < 0) {
146+
fprintf(stderr, "[consumer] ERROR: recv() failed\n");
147+
goto err_close_producer_socket;
148+
}
149+
if (len != IPC_handle_size) {
150+
fprintf(stderr,
151+
"[consumer] ERROR: recv() received a wrong number of bytes "
152+
"(%zi != %zu expected)\n",
153+
len, IPC_handle_size);
154+
goto err_close_producer_socket;
155+
}
156+
157+
void *IPC_handle = recv_buffer;
158+
159+
fprintf(
160+
stderr,
161+
"[consumer] Received the IPC handle from the producer (%zi bytes)\n",
162+
len);
163+
164+
void *SHM_ptr;
165+
umf_result = umfMemoryProviderOpenIPCHandle(OS_memory_provider, IPC_handle,
166+
&SHM_ptr);
167+
if (umf_result == UMF_RESULT_ERROR_NOT_SUPPORTED) {
168+
fprintf(stderr,
169+
"[consumer] SKIP: opening the IPC handle is not supported\n");
170+
ret = 1; // SKIP
171+
172+
// write the SKIP response to the consumer_message buffer
173+
strcpy(consumer_message, "SKIP");
174+
175+
// send the SKIP response to the producer
176+
send(producer_socket, consumer_message, strlen(consumer_message) + 1,
177+
0);
178+
179+
goto err_close_producer_socket;
180+
}
181+
if (umf_result != UMF_RESULT_SUCCESS) {
182+
fprintf(stderr, "[consumer] ERROR: opening the IPC handle failed\n");
183+
goto err_close_producer_socket;
184+
}
185+
186+
fprintf(stderr,
187+
"[consumer] Opened the IPC handle received from the producer\n");
188+
189+
// read the current value from the shared memory
190+
unsigned long long SHM_number_1 = *(unsigned long long *)SHM_ptr;
191+
fprintf(
192+
stderr,
193+
"[consumer] Read the number from the producer's shared memory: %llu\n",
194+
SHM_number_1);
195+
196+
// calculate the new value
197+
unsigned long long SHM_number_2 = SHM_number_1 / 2;
198+
199+
// write the new number directly to the producer's shared memory
200+
*(unsigned long long *)SHM_ptr = SHM_number_2;
201+
fprintf(stderr,
202+
"[consumer] Wrote a new number directly to the producer's shared "
203+
"memory: %llu\n",
204+
SHM_number_2);
205+
206+
// write the response to the consumer_message buffer
207+
strcpy(consumer_message, CONSUMER_MSG);
208+
209+
// send response to the producer
210+
if (send(producer_socket, consumer_message, strlen(consumer_message) + 1,
211+
0) < 0) {
212+
fprintf(stderr, "[consumer] ERROR: send() failed\n");
213+
goto err_closeIPCHandle;
214+
}
215+
216+
fprintf(stderr, "[consumer] Sent a response message to the producer\n");
217+
218+
ret = 0; // SUCCESS
219+
220+
err_closeIPCHandle:
221+
// we do not know the exact size of the remote shared memory
222+
umf_result = umfMemoryProviderCloseIPCHandle(OS_memory_provider, SHM_ptr,
223+
sizeof(unsigned long long));
224+
if (umf_result != UMF_RESULT_SUCCESS) {
225+
fprintf(stderr, "[consumer] ERROR: closing the IPC handle failed\n");
226+
}
227+
228+
fprintf(stderr,
229+
"[consumer] Closed the IPC handle received from the producer\n");
230+
231+
err_close_producer_socket:
232+
close(producer_socket);
233+
234+
err_close_consumer_socket:
235+
close(consumer_socket);
236+
237+
err_umfMemoryProviderDestroy:
238+
umfMemoryProviderDestroy(OS_memory_provider);
239+
240+
if (ret == 0) {
241+
fprintf(stderr, "[consumer] Shutting down (status OK) ...\n");
242+
} else if (ret == 1) {
243+
fprintf(stderr, "[consumer] Shutting down (status SKIP) ...\n");
244+
ret = 0;
245+
} else {
246+
fprintf(stderr, "[consumer] Shutting down (status ERROR) ...\n");
247+
}
248+
249+
return ret;
250+
}

0 commit comments

Comments
 (0)