ipc: Add Windows support

This commit is contained in:
Julian Petrov 2022-07-27 13:04:09 -04:00 committed by Ryan Pavlik
parent 7669d2c545
commit d0f713c4c4
17 changed files with 739 additions and 53 deletions

View file

@ -241,7 +241,7 @@ option_with_deps(XRT_HAVE_KIMERA_SLAM "Enable Kimera support" DEPENDS kimera_vio
option(XRT_FEATURE_COLOR_LOG "Enable logging in color on supported platforms" ON)
option_with_deps(XRT_FEATURE_COMPOSITOR_MAIN "Build main compositor host functionality" DEPENDS XRT_HAVE_VULKAN "XRT_HAVE_WAYLAND OR XRT_HAVE_XCB OR ANDROID OR WIN32")
option_with_deps(XRT_FEATURE_COMPOSITOR_NULL "Build testing null compositor" DEPENDS XRT_HAVE_VULKAN)
option_with_deps(XRT_FEATURE_IPC "Enable the build of the IPC layer" DEPENDS "NOT WIN32")
option(XRT_FEATURE_IPC "Enable the build of the IPC layer" ON)
option_with_deps(XRT_FEATURE_OPENXR "Build OpenXR runtime target" DEPENDS "XRT_FEATURE_COMPOSITOR_MAIN OR XRT_FEATURE_COMPOSITOR_NULL")
option_with_deps(XRT_FEATURE_RENDERDOC "Enable RenderDoc API" DEPENDS "RT_LIBRARY OR WIN32")
option_with_deps(XRT_FEATURE_SERVICE "Enable separate service module for OpenXR runtime" DEPENDS XRT_FEATURE_IPC XRT_FEATURE_OPENXR)

View file

@ -39,6 +39,11 @@ add_library(ipc_shared STATIC ${IPC_COMMON_SOURCES})
target_include_directories(
ipc_shared PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}
)
if(WIN32)
target_sources(ipc_shared PRIVATE shared/ipc_utils_windows.cpp)
endif()
target_link_libraries(ipc_shared PRIVATE aux_util)
if(RT_LIBRARY)
@ -61,6 +66,13 @@ add_library(
target_include_directories(
ipc_client PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}
)
if(WIN32)
if(XRT_SERVICE_EXECUTABLE)
target_compile_definitions(
ipc_client PUBLIC XRT_SERVICE_EXECUTABLE="${XRT_SERVICE_EXECUTABLE}.exe"
)
endif()
endif()
target_link_libraries(ipc_client PRIVATE aux_util ipc_shared)
###
@ -111,4 +123,9 @@ elseif(XRT_HAVE_LINUX)
target_sources(
ipc_server PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/server/ipc_server_mainloop_linux.c
)
elseif(WIN32)
target_sources(
ipc_server
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/server/ipc_server_mainloop_windows.cpp
)
endif()

View file

@ -13,6 +13,10 @@
#include "xrt/xrt_defines.h"
#include "xrt/xrt_config_os.h"
#include "util/u_misc.h"
#include "util/u_handles.h"
#include "util/u_trace_marker.h"
#include "os/os_time.h"
#include "util/u_wait.h"
@ -25,9 +29,11 @@
#include <string.h>
#include <stdio.h>
#if !defined(XRT_OS_WINDOWS)
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#endif
#include <errno.h>
#include <assert.h>
@ -711,15 +717,10 @@ ipc_compositor_layer_commit(struct xrt_compositor *xc, int64_t frame_id, xrt_gra
// Reset.
icc->layers.layer_count = 0;
#ifdef XRT_GRAPHICS_SYNC_HANDLE_IS_FD
// Need to consume this handle.
if (valid_sync) {
close(sync_handle);
sync_handle = XRT_GRAPHICS_SYNC_HANDLE_INVALID;
u_graphics_sync_unref(&sync_handle);
}
#else
#error "Not yet implemented for this platform"
#endif
return res;
}

View file

@ -9,7 +9,6 @@
#include <math.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

View file

@ -9,7 +9,6 @@
#include <math.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

View file

@ -7,6 +7,10 @@
* @ingroup ipc_client
*/
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "xrt/xrt_instance.h"
#include "xrt/xrt_gfx_native.h"
#include "xrt/xrt_handles.h"
@ -25,6 +29,7 @@
#include "util/u_file.h"
#include <stdio.h>
#if !defined(XRT_OS_WINDOWS)
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/types.h>
@ -34,6 +39,7 @@
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#endif
#include <limits.h>
#ifdef XRT_GRAPHICS_BUFFER_HANDLE_IS_AHARDWAREBUFFER
@ -104,12 +110,132 @@ ipc_connect(struct ipc_connection *ipc_c)
return false;
}
ipc_c->imc.socket_fd = socket;
ipc_c->imc.ipc_handle = socket;
ipc_c->imc.log_level = ipc_c->log_level;
return true;
}
#elif defined(XRT_OS_WINDOWS)
#if defined(NO_XRT_SERVICE_LAUNCH) || !defined(XRT_SERVICE_EXECUTABLE)
static HANDLE
ipc_connect_pipe(struct ipc_connection *ipc_c, const char *pipe_name)
{
HANDLE pipe_inst = CreateFileA(pipe_name, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (pipe_inst == INVALID_HANDLE_VALUE) {
DWORD err = GetLastError();
IPC_ERROR(ipc_c, "Connect to %s failed: %d %s", pipe_name, err, ipc_winerror(err));
}
return pipe_inst;
}
#else
// N.B. quality of life fallback to try launch the XRT_SERVICE_EXECUTABLE if pipe is not found
static HANDLE
ipc_connect_pipe(struct ipc_connection *ipc_c, const char *pipe_name)
{
HANDLE pipe_inst = CreateFileA(pipe_name, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (pipe_inst != INVALID_HANDLE_VALUE) {
return pipe_inst;
}
DWORD err = GetLastError();
IPC_ERROR(ipc_c, "Connect to %s failed: %d %s", pipe_name, err, ipc_winerror(err));
if (err != ERROR_FILE_NOT_FOUND) {
return INVALID_HANDLE_VALUE;
}
IPC_INFO(ipc_c, "Trying to launch " XRT_SERVICE_EXECUTABLE "...");
HMODULE hmod;
if (!GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
(LPCSTR)ipc_connect_pipe, &hmod)) {
IPC_ERROR(ipc_c, "GetModuleHandleExA failed: %d %s", err, ipc_winerror(err));
return INVALID_HANDLE_VALUE;
}
char service_path[MAX_PATH];
if (!GetModuleFileNameA(hmod, service_path, sizeof(service_path))) {
IPC_ERROR(ipc_c, "GetModuleFileNameA failed: %d %s", err, ipc_winerror(err));
return INVALID_HANDLE_VALUE;
}
char *p = strrchr(service_path, '\\');
if (!p) {
IPC_ERROR(ipc_c, "failed to parse the path %s", service_path);
return INVALID_HANDLE_VALUE;
}
strcpy(p + 1, XRT_SERVICE_EXECUTABLE);
STARTUPINFOA si = {.cb = sizeof(si)};
PROCESS_INFORMATION pi;
if (!CreateProcessA(NULL, service_path, NULL, NULL, false, 0, NULL, NULL, &si, &pi)) {
*p = 0;
p = strrchr(service_path, '\\');
if (!p) {
err = GetLastError();
IPC_INFO(ipc_c, XRT_SERVICE_EXECUTABLE " not found in %s: %d %s", service_path, err,
ipc_winerror(err));
return INVALID_HANDLE_VALUE;
}
strcpy(p + 1, "service\\" XRT_SERVICE_EXECUTABLE);
if (!CreateProcessA(NULL, service_path, NULL, NULL, false, 0, NULL, NULL, &si, &pi)) {
err = GetLastError();
IPC_INFO(ipc_c, XRT_SERVICE_EXECUTABLE " not found at %s: %d %s", service_path, err,
ipc_winerror(err));
return INVALID_HANDLE_VALUE;
}
}
IPC_INFO(ipc_c, "Launched %s (pid %d)... Waiting for %s...", service_path, pi.dwProcessId, pipe_name);
CloseHandle(pi.hThread);
for (int i = 0;; i++) {
pipe_inst = CreateFileA(pipe_name, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (pipe_inst != INVALID_HANDLE_VALUE) {
IPC_INFO(ipc_c, "Connected to %s after %d msec on try %d!", pipe_name, i * 100, i + 1);
break;
}
err = GetLastError();
if (err != ERROR_FILE_NOT_FOUND || WaitForSingleObject(pi.hProcess, 100) != WAIT_TIMEOUT) {
IPC_ERROR(ipc_c, "Connect to %s failed: %d %s", pipe_name, err, ipc_winerror(err));
break;
}
}
CloseHandle(pi.hProcess);
return pipe_inst;
}
#endif
static bool
ipc_connect(struct ipc_connection *ipc_c)
{
ipc_c->log_level = debug_get_log_option_ipc_log();
const char pipe_prefix[] = "\\\\.\\pipe\\";
#define prefix_len sizeof(pipe_prefix) - 1
char pipe_name[MAX_PATH + prefix_len];
strcpy(pipe_name, pipe_prefix);
if (u_file_get_path_in_runtime_dir(XRT_IPC_MSG_SOCK_FILENAME, pipe_name + prefix_len, MAX_PATH) == -1) {
U_LOG_E("u_file_get_path_in_runtime_dir failed!");
return false;
}
HANDLE pipe_inst = ipc_connect_pipe(ipc_c, pipe_name);
if (pipe_inst == INVALID_HANDLE_VALUE) {
return false;
}
DWORD mode = PIPE_READMODE_MESSAGE | PIPE_WAIT;
if (!SetNamedPipeHandleState(pipe_inst, &mode, NULL, NULL)) {
DWORD err = GetLastError();
IPC_ERROR(ipc_c, "SetNamedPipeHandleState(PIPE_READMODE_MESSAGE | PIPE_WAIT) failed: %d %s", err,
ipc_winerror(err));
return false;
}
ipc_c->imc.ipc_handle = pipe_inst;
ipc_c->imc.log_level = ipc_c->log_level;
return true;
}
int
getpid()
{
return GetCurrentProcessId();
}
#else
static bool
@ -135,7 +261,7 @@ ipc_connect(struct ipc_connection *ipc_c)
int size = u_file_get_path_in_runtime_dir(XRT_IPC_MSG_SOCK_FILENAME, sock_file, PATH_MAX);
if (size == -1) {
IPC_ERROR(ipc_c, "Could not get socket file name");
return -1;
return false;
}
memset(&addr, 0, sizeof(addr));
@ -149,7 +275,7 @@ ipc_connect(struct ipc_connection *ipc_c)
return false;
}
ipc_c->imc.socket_fd = socket;
ipc_c->imc.ipc_handle = socket;
ipc_c->imc.log_level = ipc_c->log_level;
return true;
@ -299,8 +425,7 @@ ipc_instance_create(struct xrt_instance_info *i_info, struct xrt_instance **out_
ii->base.get_prober = ipc_client_instance_get_prober;
ii->base.destroy = ipc_client_instance_destroy;
// FDs needs to be set to something negative.
ii->ipc_c.imc.socket_fd = -1;
ii->ipc_c.imc.ipc_handle = XRT_IPC_HANDLE_INVALID;
ii->ipc_c.ism_handle = XRT_SHMEM_HANDLE_INVALID;
os_mutex_init(&ii->ipc_c.mutex);
@ -340,11 +465,16 @@ ipc_instance_create(struct xrt_instance_info *i_info, struct xrt_instance **out_
return xret;
}
const int flags = MAP_SHARED;
const int access = PROT_READ | PROT_WRITE;
const size_t size = sizeof(struct ipc_shared_memory);
#ifdef XRT_OS_WINDOWS
ii->ipc_c.ism = MapViewOfFile(ii->ipc_c.ism_handle, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, size);
#else
const int flags = MAP_SHARED;
const int access = PROT_READ | PROT_WRITE;
ii->ipc_c.ism = mmap(NULL, size, access, flags, ii->ipc_c.ism_handle, 0);
#endif
if (ii->ipc_c.ism == NULL) {
IPC_ERROR((&ii->ipc_c), "Failed to mmap shm!");
free(ii);

View file

@ -236,6 +236,23 @@ struct ipc_server_mainloop
#define XRT_IPC_GOT_IMPL
#endif
#if defined(XRT_OS_WINDOWS) || defined(XRT_DOXYGEN)
/*!
* @name Desktop Windows Mainloop Members
* @{
*/
//! Named Pipe that we accept connections on.
HANDLE pipe_handle;
//! Name of the Pipe that we accept connections on.
char *pipe_name;
/*! @} */
#define XRT_IPC_GOT_IMPL
#endif
#ifndef XRT_IPC_GOT_IMPL
#error "Need port"
#endif
@ -397,11 +414,11 @@ ipc_server_client_destroy_compositor(volatile struct ipc_client_state *ics);
* @{
*/
/*!
* Start a thread for a client connected at the other end of the file descriptor @p fd.
* Start a thread for a client connected at the other end of the ipc handle @p ipc_handle.
* @memberof ipc_server
*/
void
ipc_server_start_client_listener_thread(struct ipc_server *vs, int fd);
ipc_server_start_client_listener_thread(struct ipc_server *vs, xrt_ipc_handle_t ipc_handle);
/*!
* Perform whatever needs to be done when the mainloop polling encounters a failure.

View file

@ -637,7 +637,7 @@ ipc_handle_system_get_client_info(volatile struct ipc_client_state *_ics,
}
volatile struct ipc_client_state *ics = &_ics->server->threads[id].ics;
if (ics->imc.socket_fd <= 0) {
if (!xrt_ipc_handle_is_valid(ics->imc.ipc_handle)) {
return XRT_ERROR_IPC_FAILURE;
}
@ -707,7 +707,7 @@ ipc_handle_system_toggle_io_client(volatile struct ipc_client_state *_ics, uint3
ics = &_ics->server->threads[client_id].ics;
if (ics->imc.socket_fd <= 0) {
if (!xrt_ipc_handle_is_valid(ics->imc.ipc_handle)) {
return XRT_ERROR_IPC_FAILURE;
}

View file

@ -0,0 +1,160 @@
// Copyright 2022, Magic Leap, Inc.
// Copyright 2020-2022, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Server mainloop details on Windows.
* @author Julian Petrov <jpetrov@magicleap.com>
* @author Ryan Pavlik <ryan.pavlik@collabora.com>
* @author Jakob Bornecrantz <jakob@collabora.com>
* @ingroup ipc_server
*/
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "xrt/xrt_device.h"
#include "xrt/xrt_instance.h"
#include "xrt/xrt_compositor.h"
#include "xrt/xrt_config_have.h"
#include "xrt/xrt_config_os.h"
#include "os/os_time.h"
#include "util/u_var.h"
#include "util/u_misc.h"
#include "util/u_debug.h"
#include "util/u_trace_marker.h"
#include "util/u_file.h"
#include "shared/ipc_shmem.h"
#include "server/ipc_server.h"
#include <conio.h>
/*
*
* Static functions.
*
*/
static void
ipc_server_create_another_pipe_instance(struct ipc_server *vs, struct ipc_server_mainloop *ml)
{
ml->pipe_handle =
CreateNamedPipeA(ml->pipe_name, PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_NOWAIT | PIPE_REJECT_REMOTE_CLIENTS,
IPC_MAX_CLIENTS, IPC_BUF_SIZE, IPC_BUF_SIZE, 0, NULL);
if (ml->pipe_handle == INVALID_HANDLE_VALUE) {
DWORD err = GetLastError();
if (err == ERROR_PIPE_BUSY) {
U_LOG_W("CreateNamedPipeA failed: %d %s An existing client must disconnect first!", err,
ipc_winerror(err));
} else {
U_LOG_E("CreateNamedPipeA failed: %d %s", err, ipc_winerror(err));
ipc_server_handle_failure(vs);
}
}
}
/*
*
* Exported functions
*
*/
void
ipc_server_mainloop_poll(struct ipc_server *vs, struct ipc_server_mainloop *ml)
{
IPC_TRACE_MARKER();
if (_kbhit()) {
U_LOG_E("console input! exiting...");
ipc_server_handle_shutdown_signal(vs);
return;
}
if (!ml->pipe_handle) {
ipc_server_create_another_pipe_instance(vs, ml);
}
if (!ml->pipe_handle) {
return;
}
if (ConnectNamedPipe(ml->pipe_handle, NULL)) {
U_LOG_E("ConnectNamedPipe unexpected return TRUE (last error: %d). Treating as failure...",
GetLastError());
ipc_server_handle_failure(vs);
return;
}
switch (DWORD err = GetLastError()) {
case ERROR_PIPE_LISTENING: return;
case ERROR_PIPE_CONNECTED: {
DWORD mode = PIPE_READMODE_MESSAGE | PIPE_WAIT;
if (!SetNamedPipeHandleState(ml->pipe_handle, &mode, NULL, NULL)) {
err = GetLastError();
U_LOG_E("SetNamedPipeHandleState(PIPE_READMODE_MESSAGE | PIPE_WAIT) failed: %d %s", err,
ipc_winerror(err));
ipc_server_handle_failure(vs);
return;
}
ipc_server_start_client_listener_thread(vs, ml->pipe_handle);
ipc_server_create_another_pipe_instance(vs, ml);
return;
}
default:
U_LOG_E("ConnectNamedPipe failed: %d %s", err, ipc_winerror(err));
ipc_server_handle_failure(vs);
return;
}
}
int
ipc_server_mainloop_init(struct ipc_server_mainloop *ml)
{
IPC_TRACE_MARKER();
ml->pipe_handle = INVALID_HANDLE_VALUE;
ml->pipe_name = nullptr;
constexpr char pipe_prefix[] = "\\\\.\\pipe\\";
constexpr int prefix_len = sizeof(pipe_prefix) - 1;
char pipe_name[MAX_PATH + prefix_len];
strcpy(pipe_name, pipe_prefix);
if (u_file_get_path_in_runtime_dir(XRT_IPC_MSG_SOCK_FILENAME, pipe_name + prefix_len, MAX_PATH) == -1) {
U_LOG_E("u_file_get_path_in_runtime_dir failed!");
return -1;
}
ml->pipe_name = _strdup(pipe_name);
if (!ml->pipe_name) {
U_LOG_E("_strdup(\"%s\") failed!", pipe_name);
} else {
ml->pipe_handle = CreateNamedPipeA(ml->pipe_name, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_NOWAIT |
PIPE_REJECT_REMOTE_CLIENTS,
IPC_MAX_CLIENTS, IPC_BUF_SIZE, IPC_BUF_SIZE, 0, NULL);
if (ml->pipe_handle != INVALID_HANDLE_VALUE) {
return 0;
}
DWORD err = GetLastError();
U_LOG_E("CreateNamedPipeA \"%s\" first instance failed: %d %s", ml->pipe_name, err, ipc_winerror(err));
}
ipc_server_mainloop_deinit(ml);
return -1;
}
void
ipc_server_mainloop_deinit(struct ipc_server_mainloop *ml)
{
IPC_TRACE_MARKER();
if (ml->pipe_handle != INVALID_HANDLE_VALUE) {
CloseHandle(ml->pipe_handle);
ml->pipe_handle = INVALID_HANDLE_VALUE;
}
if (ml->pipe_name) {
free(ml->pipe_name);
ml->pipe_name = nullptr;
}
}

View file

@ -15,6 +15,8 @@
#include "server/ipc_server.h"
#include "ipc_server_generated.h"
#ifndef XRT_OS_WINDOWS
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
@ -34,7 +36,7 @@
static int
setup_epoll(volatile struct ipc_client_state *ics)
{
int listen_socket = ics->imc.socket_fd;
int listen_socket = ics->imc.ipc_handle;
assert(listen_socket >= 0);
int ret = epoll_create1(EPOLL_CLOEXEC);
@ -101,7 +103,7 @@ client_loop(volatile struct ipc_client_state *ics)
// Finally get the data that is waiting for us.
//! @todo replace this call
ssize_t len = recv(ics->imc.socket_fd, &buf, IPC_BUF_SIZE, 0);
ssize_t len = recv(ics->imc.ipc_handle, &buf, IPC_BUF_SIZE, 0);
if (len < 4) {
IPC_ERROR(ics->server, "Invalid packet received, disconnecting client.");
break;
@ -140,6 +142,62 @@ client_loop(volatile struct ipc_client_state *ics)
ipc_server_deactivate_session(ics);
}
#else // XRT_OS_WINDOWS
static void
client_loop(volatile struct ipc_client_state *ics)
{
IPC_INFO(ics->server, "Client connected");
uint8_t buf[IPC_BUF_SIZE];
while (ics->server->running) {
DWORD len;
if (!ReadFile(ics->imc.ipc_handle, buf, sizeof(buf), &len, NULL)) {
DWORD err = GetLastError();
if (err == ERROR_BROKEN_PIPE) {
IPC_INFO(ics->server, "ReadFile from pipe: %d %s", err, ipc_winerror(err));
} else {
IPC_ERROR(ics->server, "ReadFile from pipe failed: %d %s", err, ipc_winerror(err));
}
break;
}
if (len < sizeof(ipc_command_t)) {
IPC_ERROR(ics->server, "Invalid packet received, disconnecting client.");
break;
} else {
// Check the first 4 bytes of the message and dispatch.
ipc_command_t *ipc_command = (ipc_command_t *)buf;
xrt_result_t result = ipc_dispatch(ics, ipc_command);
if (result != XRT_SUCCESS) {
IPC_ERROR(ics->server, "During packet handling, disconnecting client.");
break;
}
}
}
// Multiple threads might be looking at these fields.
os_mutex_lock(&ics->server->global_state.lock);
ipc_message_channel_close((struct ipc_message_channel *)&ics->imc);
ics->server->threads[ics->server_thread_index].state = IPC_THREAD_STOPPING;
ics->server_thread_index = -1;
memset((void *)&ics->client_state, 0, sizeof(struct ipc_app_state));
os_mutex_unlock(&ics->server->global_state.lock);
ipc_server_client_destroy_compositor(ics);
// Should we stop the server when a client disconnects?
if (ics->server->exit_on_disconnect) {
ics->server->running = false;
}
ipc_server_deactivate_session(ics);
}
#endif // XRT_OS_WINDOWS
/*
*

View file

@ -30,14 +30,10 @@
#include "server/ipc_server.h"
#include <stdlib.h>
#include <unistd.h>
#include <inttypes.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
@ -196,7 +192,7 @@ handle_binding(struct ipc_shared_memory *ism,
isbp->name = xbp->name;
// Copy the initial state and also count the number in input_pairs.
size_t input_pair_start = input_pair_index;
uint32_t input_pair_start = input_pair_index;
for (size_t k = 0; k < xbp->input_count; k++) {
ism->input_pairs[input_pair_index++] = xbp->inputs[k];
}
@ -208,7 +204,7 @@ handle_binding(struct ipc_shared_memory *ism,
}
// Copy the initial state and also count the number in outputs.
size_t output_pair_start = output_pair_index;
uint32_t output_pair_start = output_pair_index;
for (size_t k = 0; k < xbp->output_count; k++) {
ism->output_pairs[output_pair_index++] = xbp->outputs[k];
}
@ -309,7 +305,7 @@ init_shm(struct ipc_server *s)
// Setup the tracking origin.
isdev->tracking_origin_index = (uint32_t)-1;
for (size_t k = 0; k < XRT_SYSTEM_MAX_DEVICES; k++) {
for (uint32_t k = 0; k < XRT_SYSTEM_MAX_DEVICES; k++) {
if (xdev->tracking_origin != s->xtracks[k]) {
continue;
}
@ -324,7 +320,7 @@ init_shm(struct ipc_server *s)
xrt_device_update_inputs(xdev);
// Bindings
size_t binding_start = binding_index;
uint32_t binding_start = binding_index;
for (size_t k = 0; k < xdev->binding_profile_count; k++) {
handle_binding(ism, &xdev->binding_profiles[k], &ism->binding_profiles[binding_index++],
&input_pair_index, &output_pair_index);
@ -337,7 +333,7 @@ init_shm(struct ipc_server *s)
}
// Copy the initial state and also count the number in inputs.
size_t input_start = input_index;
uint32_t input_start = input_index;
for (size_t k = 0; k < xdev->input_count; k++) {
ism->inputs[input_index++] = xdev->inputs[k];
}
@ -349,7 +345,7 @@ init_shm(struct ipc_server *s)
}
// Copy the initial state and also count the number in outputs.
size_t output_start = output_index;
uint32_t output_start = output_index;
for (size_t k = 0; k < xdev->output_count; k++) {
ism->outputs[output_index++] = xdev->outputs[k];
}
@ -392,7 +388,7 @@ ipc_server_handle_shutdown_signal(struct ipc_server *vs)
}
void
ipc_server_start_client_listener_thread(struct ipc_server *vs, int fd)
ipc_server_start_client_listener_thread(struct ipc_server *vs, xrt_ipc_handle_t ipc_handle)
{
volatile struct ipc_client_state *ics = NULL;
int32_t cs_index = -1;
@ -410,7 +406,7 @@ ipc_server_start_client_listener_thread(struct ipc_server *vs, int fd)
}
}
if (ics == NULL) {
close(fd);
xrt_ipc_handle_close(ipc_handle);
// Unlock when we are done.
os_mutex_unlock(&vs->global_state.lock);
@ -422,7 +418,7 @@ ipc_server_start_client_listener_thread(struct ipc_server *vs, int fd)
struct ipc_thread *it = &vs->threads[cs_index];
if (it->state != IPC_THREAD_READY && it->state != IPC_THREAD_STOPPING) {
// we should not get here
close(fd);
xrt_ipc_handle_close(ipc_handle);
// Unlock when we are done.
os_mutex_unlock(&vs->global_state.lock);
@ -438,7 +434,7 @@ ipc_server_start_client_listener_thread(struct ipc_server *vs, int fd)
}
it->state = IPC_THREAD_STARTING;
ics->imc.socket_fd = fd;
ics->imc.ipc_handle = ipc_handle;
ics->server = vs;
ics->server_thread_index = cs_index;
ics->io_active = true;

View file

@ -41,6 +41,10 @@
// example: v21.0.0-560-g586d33b5
#define IPC_VERSION_NAME_LEN 64
#ifdef XRT_OS_WINDOWS
typedef int pid_t;
#endif
/*
*
* Shared memory structs.

View file

@ -78,6 +78,28 @@ ipc_shmem_create(size_t size, xrt_shmem_handle_t *out_handle, void **out_map)
return XRT_SUCCESS;
}
#elif defined(XRT_OS_WINDOWS)
xrt_result_t
ipc_shmem_create(size_t size, xrt_shmem_handle_t *out_handle, void **out_map)
{
*out_handle = NULL;
LARGE_INTEGER sz = {.QuadPart = size};
HANDLE handle = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, sz.HighPart, sz.LowPart, NULL);
if (handle == NULL) {
return XRT_ERROR_IPC_FAILURE;
}
xrt_result_t result = ipc_shmem_map(handle, size, out_map);
if (result != XRT_SUCCESS) {
CloseHandle(handle);
return result;
}
*out_handle = handle;
return XRT_SUCCESS;
}
#else
#error "OS not yet supported"
#endif
@ -110,6 +132,30 @@ ipc_shmem_map(xrt_shmem_handle_t handle, size_t size, void **out_map)
*out_map = ptr;
return XRT_SUCCESS;
}
#elif defined(XRT_OS_WINDOWS)
void
ipc_shmem_destroy(xrt_shmem_handle_t *handle_ptr)
{
if (handle_ptr == NULL) {
return;
}
xrt_shmem_handle_t handle = *handle_ptr;
CloseHandle(handle);
*handle_ptr = NULL;
}
xrt_result_t
ipc_shmem_map(xrt_shmem_handle_t handle, size_t size, void **out_map)
{
void *ptr = MapViewOfFile(handle, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, size);
if (ptr == NULL) {
return XRT_ERROR_IPC_FAILURE;
}
*out_map = ptr;
return XRT_SUCCESS;
}
// BUGBUG: unmap?
#else
#error "OS not yet supported"
#endif

View file

@ -15,8 +15,10 @@
#include "shared/ipc_protocol.h"
#include <errno.h>
#if !defined(XRT_OS_WINDOWS)
#include <sys/socket.h>
#include <unistd.h>
#endif
#include <stdio.h>
#include <string.h>
@ -37,14 +39,15 @@
#define IPC_WARN(d, ...) U_LOG_IFL_W(d->log_level, __VA_ARGS__)
#define IPC_ERROR(d, ...) U_LOG_IFL_E(d->log_level, __VA_ARGS__)
#if !defined(XRT_OS_WINDOWS)
void
ipc_message_channel_close(struct ipc_message_channel *imc)
{
if (imc->socket_fd < 0) {
if (imc->ipc_handle < 0) {
return;
}
close(imc->socket_fd);
imc->socket_fd = -1;
close(imc->ipc_handle);
imc->ipc_handle = -1;
}
xrt_result_t
@ -62,11 +65,11 @@ ipc_send(struct ipc_message_channel *imc, const void *data, size_t size)
msg.msg_iovlen = 1;
msg.msg_flags = 0;
ssize_t ret = sendmsg(imc->socket_fd, &msg, MSG_NOSIGNAL);
ssize_t ret = sendmsg(imc->ipc_handle, &msg, MSG_NOSIGNAL);
if (ret < 0) {
int code = errno;
IPC_ERROR(imc, "ERROR: Sending plain message on socket %d failed with error: '%i' '%s'!",
(int)imc->socket_fd, code, strerror(code));
(int)imc->ipc_handle, code, strerror(code));
return XRT_ERROR_IPC_FAILURE;
}
@ -90,12 +93,12 @@ ipc_receive(struct ipc_message_channel *imc, void *out_data, size_t size)
msg.msg_iovlen = 1;
msg.msg_flags = 0;
ssize_t len = recvmsg(imc->socket_fd, &msg, MSG_NOSIGNAL);
ssize_t len = recvmsg(imc->ipc_handle, &msg, MSG_NOSIGNAL);
if (len < 0) {
int code = errno;
IPC_ERROR(imc, "ERROR: Receiving plain message on socket '%d' failed with error: '%i' '%s'!",
(int)imc->socket_fd, code, strerror(code));
(int)imc->ipc_handle, code, strerror(code));
return XRT_ERROR_IPC_FAILURE;
}
@ -135,7 +138,7 @@ ipc_receive_fds(struct ipc_message_channel *imc, void *out_data, size_t size, in
msg.msg_control = u.buf;
msg.msg_controllen = cmsg_size;
ssize_t len = recvmsg(imc->socket_fd, &msg, MSG_NOSIGNAL);
ssize_t len = recvmsg(imc->ipc_handle, &msg, MSG_NOSIGNAL);
if (len < 0) {
IPC_ERROR(imc, "recvmsg failed with error: '%s'!", strerror(errno));
return XRT_ERROR_IPC_FAILURE;
@ -187,10 +190,10 @@ ipc_send_fds(struct ipc_message_channel *imc, const void *data, size_t size, con
memcpy(CMSG_DATA(cmsg), handles, fds_size);
ssize_t ret = sendmsg(imc->socket_fd, &msg, MSG_NOSIGNAL);
ssize_t ret = sendmsg(imc->ipc_handle, &msg, MSG_NOSIGNAL);
if (ret < 0) {
IPC_ERROR(imc, "ERROR: sending %d FDs on socket %d failed with error: '%i' '%s'!", (int)handle_count,
imc->socket_fd, errno, strerror(errno));
imc->ipc_handle, errno, strerror(errno));
for (uint32_t i = 0; i < handle_count; i++) {
IPC_ERROR(imc, "\tfd #%i: %i", i, handles[i]);
}
@ -199,6 +202,8 @@ ipc_send_fds(struct ipc_message_channel *imc, const void *data, size_t size, con
return XRT_SUCCESS;
}
#endif
xrt_result_t
ipc_receive_handles_shmem(struct ipc_message_channel *imc,
void *out_data,
@ -244,7 +249,7 @@ ipc_receive_handles_graphics_buffer(struct ipc_message_channel *imc,
}
bool failed = false;
for (uint32_t i = 0; i < handle_count; ++i) {
int err = AHardwareBuffer_recvHandleFromUnixSocket(imc->socket_fd, &(out_handles[i]));
int err = AHardwareBuffer_recvHandleFromUnixSocket(imc->ipc_handle, &(out_handles[i]));
if (err != 0) {
failed = true;
}
@ -266,7 +271,7 @@ ipc_send_handles_graphics_buffer(struct ipc_message_channel *imc,
}
bool failed = false;
for (uint32_t i = 0; i < handle_count; ++i) {
int err = AHardwareBuffer_sendHandleToUnixSocket(handles[i], imc->socket_fd);
int err = AHardwareBuffer_sendHandleToUnixSocket(handles[i], imc->ipc_handle);
if (err != 0) {
failed = true;
}
@ -303,6 +308,7 @@ ipc_send_handles_graphics_buffer(struct ipc_message_channel *imc,
return ipc_send_fds(imc, data, size, handles, handle_count);
}
#elif defined(XRT_OS_WINDOWS)
#else
#error "Need port to transport these graphics buffers"
#endif
@ -344,6 +350,7 @@ ipc_send_handles_graphics_sync(struct ipc_message_channel *imc,
return ipc_send_fds(imc, data, size, handles, handle_count);
}
#elif defined(XRT_OS_WINDOWS)
#else
#error "Need port to transport these graphics buffers"
#endif

View file

@ -27,7 +27,7 @@ extern "C" {
*/
struct ipc_message_channel
{
int socket_fd;
xrt_ipc_handle_t ipc_handle;
enum u_logging_level log_level;
};
@ -111,6 +111,57 @@ ipc_receive_fds(struct ipc_message_channel *imc, void *out_data, size_t size, in
*/
xrt_result_t
ipc_send_fds(struct ipc_message_channel *imc, const void *data, size_t size, const int *handles, uint32_t handle_count);
#elif defined(XRT_OS_WINDOWS)
/*!
* Receive a message along with a known number of file HANDLEs over the IPC
* channel.
*
* @param imc Message channel to use
* @param[out] out_data Pointer to the buffer to fill with data. Must not be
* null.
* @param[in] size Maximum size to read, must be greater than 0
* @param[out] out_handles Array of file HANDLEs to populate. Must not be
* null.
* @param[in] handle_count Number of elements to receive into @p out_handles,
* must be greater than 0 and must match the value provided at the other end.
*
* @public @memberof ipc_message_channel
*/
xrt_result_t
ipc_receive_fds(
struct ipc_message_channel *imc, void *out_data, size_t size, HANDLE *out_handles, uint32_t handle_count);
/*!
* Send a message along with file HANDLEs over the IPC channel.
*
* @param imc Message channel to use
* @param[in] data Pointer to the data buffer to send. Must not be
* null: use a filler message if necessary.
* @param[in] size Size of data pointed-to by @p data, must be greater than 0
* @param[out] handles Array of file HANDLEs to send. Must not be
* null.
* @param[in] handle_count Number of elements in @p handles, must be greater
* than 0. If this is variable, it must also be separately transmitted ahead of
* time, because the receiver must have the same value in its receive call.
*
* @public @memberof ipc_message_channel
*/
xrt_result_t
ipc_send_fds(
struct ipc_message_channel *imc, const void *data, size_t size, const HANDLE *handles, uint32_t handle_count);
/*!
* Helper to convert windows error codes to human readable strings for logging
* N.B. This routine is not thread safe
*
* @param err windows error code
* @return human readable string corresponding to the error code
*
* @public @memberof ipc_message_channel
*/
const char *
ipc_winerror(DWORD err);
#endif // XRT_OS_UNIX
/*!
* @}

View file

@ -0,0 +1,201 @@
// Copyright 2022, Magic Leap, Inc.
// Copyright 2020-2022, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief IPC util helpers on Windows, for internal use only
* @author Julian Petrov <jpetrov@magicleap.com>
* @author Ryan Pavlik <ryan.pavlik@collabora.com>
* @author Pete Black <pblack@collabora.com>
* @author Jakob Bornecrantz <jakob@collabora.com>
* @ingroup ipc_shared
*/
#include "xrt/xrt_config_os.h"
#include "shared/ipc_utils.h"
#include "shared/ipc_protocol.h"
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <assert.h>
#include "util/u_logging.h"
#include <vector>
/*
*
* Logging
*
*/
#define IPC_TRACE(d, ...) U_LOG_IFL_T(d->log_level, __VA_ARGS__)
#define IPC_DEBUG(d, ...) U_LOG_IFL_D(d->log_level, __VA_ARGS__)
#define IPC_INFO(d, ...) U_LOG_IFL_I(d->log_level, __VA_ARGS__)
#define IPC_WARN(d, ...) U_LOG_IFL_W(d->log_level, __VA_ARGS__)
#define IPC_ERROR(d, ...) U_LOG_IFL_E(d->log_level, __VA_ARGS__)
const char *
ipc_winerror(DWORD err)
{
static char s_buf[4096]; // N.B. Not thread-safe. If needed, use a thread var
if (!FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, LANG_SYSTEM_DEFAULT, s_buf, sizeof(s_buf), NULL)) {
s_buf[0] = 0;
}
return s_buf;
}
void
ipc_message_channel_close(struct ipc_message_channel *imc)
{
if (imc->ipc_handle != INVALID_HANDLE_VALUE) {
CloseHandle(imc->ipc_handle);
imc->ipc_handle = INVALID_HANDLE_VALUE;
}
}
xrt_result_t
ipc_send(struct ipc_message_channel *imc, const void *data, size_t size)
{
DWORD len;
if (!WriteFile(imc->ipc_handle, data, DWORD(size), &len, NULL)) {
DWORD err = GetLastError();
IPC_ERROR(imc, "WriteFile on pipe %p failed: %d %s", imc->ipc_handle, err, ipc_winerror(err));
return XRT_ERROR_IPC_FAILURE;
}
return XRT_SUCCESS;
}
xrt_result_t
ipc_receive(struct ipc_message_channel *imc, void *out_data, size_t size)
{
DWORD len;
if (!ReadFile(imc->ipc_handle, out_data, DWORD(size), &len, NULL)) {
DWORD err = GetLastError();
IPC_ERROR(imc, "ReadFile from pipe %p failed: %d %s", imc->ipc_handle, err, ipc_winerror(err));
return XRT_ERROR_IPC_FAILURE;
}
return XRT_SUCCESS;
}
xrt_result_t
ipc_receive_fds(
struct ipc_message_channel *imc, void *out_data, size_t size, HANDLE *out_handles, uint32_t handle_count)
{
auto rc = ipc_receive(imc, out_data, size);
if (rc != XRT_SUCCESS || !handle_count) {
return rc;
}
return ipc_receive(imc, out_handles, handle_count * sizeof(*out_handles));
}
HANDLE
open_target_process_dup_handle(struct ipc_message_channel *imc)
{
DWORD flags;
if (!GetNamedPipeInfo(imc->ipc_handle, &flags, NULL, NULL, NULL)) {
DWORD err = GetLastError();
IPC_ERROR(imc, "GetNamedPipeInfo(%p) failed: %d %s", imc->ipc_handle, err, ipc_winerror(err));
return NULL;
}
ULONG pid;
if (flags & PIPE_SERVER_END) {
if (!GetNamedPipeClientProcessId(imc->ipc_handle, &pid)) {
DWORD err = GetLastError();
IPC_ERROR(imc, "GetNamedPipeClientProcessId(%p) failed: %d %s", imc->ipc_handle, err,
ipc_winerror(err));
return NULL;
}
} else {
if (!GetNamedPipeServerProcessId(imc->ipc_handle, &pid)) {
DWORD err = GetLastError();
IPC_ERROR(imc, "GetNamedPipeServerProcessId(%p) failed: %d %s", imc->ipc_handle, err,
ipc_winerror(err));
return NULL;
}
}
HANDLE h = OpenProcess(PROCESS_DUP_HANDLE, false, pid);
if (!h) {
DWORD err = GetLastError();
IPC_ERROR(imc, "OpenProcess(PROCESS_DUP_HANDLE, pid %d) failed: %d %s", pid, err, ipc_winerror(err));
}
return h;
}
xrt_result_t
ipc_send_fds(
struct ipc_message_channel *imc, const void *data, size_t size, const HANDLE *handles, uint32_t handle_count)
{
auto rc = ipc_send(imc, data, size);
if (rc != XRT_SUCCESS) {
return rc;
}
if (!handle_count) {
return ipc_send(imc, nullptr, 0);
}
HANDLE target_process = open_target_process_dup_handle(imc);
if (!target_process) {
DWORD err = GetLastError();
IPC_ERROR(imc, "open_target_process_dup_handle failed: %d %s", err, ipc_winerror(err));
return XRT_ERROR_IPC_FAILURE;
}
HANDLE current_process = GetCurrentProcess();
std::vector<HANDLE> v;
v.reserve(handle_count);
for (uint32_t i = 0; i < handle_count; i++) {
HANDLE handle;
if (!DuplicateHandle(current_process, handles[i], target_process, &handle, 0, false,
DUPLICATE_SAME_ACCESS)) {
DWORD err = GetLastError();
IPC_ERROR(imc, "DuplicateHandle(%p) failed: %d %s", handles[i], err, ipc_winerror(err));
CloseHandle(target_process);
return XRT_ERROR_IPC_FAILURE;
}
v.push_back(handle);
}
CloseHandle(target_process);
return ipc_send(imc, v.data(), v.size() * sizeof(*v.data()));
}
xrt_result_t
ipc_receive_handles_graphics_sync(struct ipc_message_channel *imc,
void *out_data,
size_t size,
xrt_graphics_sync_handle_t *out_handles,
uint32_t handle_count)
{
return ipc_receive_fds(imc, out_data, size, out_handles, handle_count);
}
xrt_result_t
ipc_send_handles_graphics_sync(struct ipc_message_channel *imc,
const void *data,
size_t size,
const xrt_graphics_sync_handle_t *handles,
uint32_t handle_count)
{
return ipc_send_fds(imc, data, size, handles, handle_count);
}
xrt_result_t
ipc_receive_handles_graphics_buffer(struct ipc_message_channel *imc,
void *out_data,
size_t size,
xrt_graphics_buffer_handle_t *out_handles,
uint32_t handle_count)
{
return ipc_receive_fds(imc, out_data, size, out_handles, handle_count);
}
xrt_result_t
ipc_send_handles_graphics_buffer(struct ipc_message_channel *imc,
const void *data,
size_t size,
const xrt_graphics_buffer_handle_t *handles,
uint32_t handle_count)
{
return ipc_send_fds(imc, data, size, handles, handle_count);
}

View file

@ -216,9 +216,9 @@ do_connect(struct ipc_connection *ipc_c)
* Connenct.
*/
ipc_c->imc.socket_fd = socket(PF_UNIX, SOCK_STREAM, 0);
if (ipc_c->imc.socket_fd < 0) {
ret = ipc_c->imc.socket_fd;
ipc_c->imc.ipc_handle = socket(PF_UNIX, SOCK_STREAM, 0);
if (ipc_c->imc.ipc_handle < 0) {
ret = ipc_c->imc.ipc_handle;
PE("Socket create error '%i'!\n", ret);
return -1;
}
@ -235,7 +235,7 @@ do_connect(struct ipc_connection *ipc_c)
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, sock_file);
ret = connect(ipc_c->imc.socket_fd, // socket
ret = connect(ipc_c->imc.ipc_handle, // socket
(struct sockaddr *)&addr, // address
sizeof(addr)); // size
if (ret < 0) {