| /* GStreamer | |
| * Copyright (C) 2023 Seungha Yang <seungha@centricular.com> | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Library General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Library General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Library General Public | |
| * License along with this library; if not, write to the | |
| * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, | |
| * Boston, MA 02110-1301, USA. | |
| */ | |
| #ifdef HAVE_CONFIG_H | |
| #include "config.h" | |
| #endif | |
| #include "gstcudaipcserver_win32.h" | |
| #include <string.h> | |
| #include <string> | |
| GST_DEBUG_CATEGORY_EXTERN (cuda_ipc_server_debug); | |
| #define GST_CAT_DEFAULT cuda_ipc_server_debug | |
| /* *INDENT-OFF* */ | |
| struct GstCudaIpcServerConnWin32 : public GstCudaIpcServerConn | |
| { | |
| GstCudaIpcServerConnWin32 (HANDLE pipe_handle) : pipe (pipe_handle) | |
| { | |
| OVERLAPPED *parent = static_cast<OVERLAPPED *> (this); | |
| parent->Internal = 0; | |
| parent->InternalHigh = 0; | |
| parent->Offset = 0; | |
| parent->OffsetHigh = 0; | |
| } | |
| virtual ~GstCudaIpcServerConnWin32 () | |
| { | |
| CloseConn (); | |
| } | |
| void CloseConn () | |
| { | |
| if (pipe != INVALID_HANDLE_VALUE) { | |
| CancelIo (pipe); | |
| DisconnectNamedPipe (pipe); | |
| CloseHandle (pipe); | |
| pipe = INVALID_HANDLE_VALUE; | |
| } | |
| } | |
| HANDLE pipe = INVALID_HANDLE_VALUE; | |
| }; | |
| struct GstCudaIpcServerWin32Private | |
| { | |
| GstCudaIpcServerWin32Private () | |
| { | |
| cancellable = CreateEvent (nullptr, TRUE, FALSE, nullptr); | |
| wakeup_event = CreateEvent (nullptr, FALSE, FALSE, nullptr); | |
| } | |
| ~GstCudaIpcServerWin32Private () | |
| { | |
| CloseHandle (cancellable); | |
| CloseHandle (wakeup_event); | |
| } | |
| std::string address; | |
| HANDLE cancellable; | |
| HANDLE wakeup_event; | |
| }; | |
| /* *INDENT-ON* */ | |
| struct _GstCudaIpcServerWin32 | |
| { | |
| GstCudaIpcServer parent; | |
| GstCudaIpcServerWin32Private *priv; | |
| }; | |
| static void gst_cuda_ipc_server_win32_finalize (GObject * object); | |
| static void gst_cuda_ipc_server_win32_loop (GstCudaIpcServer * server); | |
| static void gst_cuda_ipc_server_win32_terminate (GstCudaIpcServer * server); | |
| static void gst_cuda_ipc_server_win32_invoke (GstCudaIpcServer * server); | |
| static bool gst_cuda_ipc_server_win32_wait_msg (GstCudaIpcServer * server, | |
| GstCudaIpcServerConn * conn); | |
| static bool gst_cuda_ipc_server_win32_send_msg (GstCudaIpcServer * server, | |
| GstCudaIpcServerConn * conn); | |
| #define gst_cuda_ipc_server_win32_parent_class parent_class | |
| G_DEFINE_TYPE (GstCudaIpcServerWin32, | |
| gst_cuda_ipc_server_win32, GST_TYPE_CUDA_IPC_SERVER); | |
| static void | |
| gst_cuda_ipc_server_win32_class_init (GstCudaIpcServerWin32Class * klass) | |
| { | |
| GObjectClass *object_class = G_OBJECT_CLASS (klass); | |
| GstCudaIpcServerClass *server_class = GST_CUDA_IPC_SERVER_CLASS (klass); | |
| object_class->finalize = gst_cuda_ipc_server_win32_finalize; | |
| server_class->loop = GST_DEBUG_FUNCPTR (gst_cuda_ipc_server_win32_loop); | |
| server_class->terminate = | |
| GST_DEBUG_FUNCPTR (gst_cuda_ipc_server_win32_terminate); | |
| server_class->invoke = GST_DEBUG_FUNCPTR (gst_cuda_ipc_server_win32_invoke); | |
| server_class->wait_msg = | |
| GST_DEBUG_FUNCPTR (gst_cuda_ipc_server_win32_wait_msg); | |
| server_class->send_msg = | |
| GST_DEBUG_FUNCPTR (gst_cuda_ipc_server_win32_send_msg); | |
| } | |
| static void | |
| gst_cuda_ipc_server_win32_init (GstCudaIpcServerWin32 * self) | |
| { | |
| self->priv = new GstCudaIpcServerWin32Private (); | |
| } | |
| static void | |
| gst_cuda_ipc_server_win32_finalize (GObject * object) | |
| { | |
| GstCudaIpcServerWin32 *self = GST_CUDA_IPC_SERVER_WIN32 (object); | |
| GST_DEBUG_OBJECT (self, "finalize"); | |
| delete self->priv; | |
| G_OBJECT_CLASS (parent_class)->finalize (object); | |
| } | |
| static void | |
| gst_cuda_ipc_server_win32_terminate (GstCudaIpcServer * server) | |
| { | |
| GstCudaIpcServerWin32 *self = GST_CUDA_IPC_SERVER_WIN32 (server); | |
| GstCudaIpcServerWin32Private *priv = self->priv; | |
| GST_DEBUG_OBJECT (self, "terminate"); | |
| SetEvent (priv->cancellable); | |
| } | |
| static void | |
| gst_cuda_ipc_server_win32_invoke (GstCudaIpcServer * server) | |
| { | |
| GstCudaIpcServerWin32 *self = GST_CUDA_IPC_SERVER_WIN32 (server); | |
| GstCudaIpcServerWin32Private *priv = self->priv; | |
| SetEvent (priv->wakeup_event); | |
| } | |
| static void WINAPI | |
| gst_cuda_ipc_server_win32_payload_finish (DWORD error_code, DWORD size, | |
| OVERLAPPED * overlap) | |
| { | |
| GstCudaIpcServerConnWin32 *win32_conn = | |
| static_cast < GstCudaIpcServerConnWin32 * >(overlap); | |
| GstCudaIpcServerWin32 *self = GST_CUDA_IPC_SERVER_WIN32 (win32_conn->server); | |
| bool ret = true; | |
| if (error_code != ERROR_SUCCESS) { | |
| auto err = gst_cuda_ipc_win32_error_to_string (error_code); | |
| GST_WARNING_OBJECT (self, "ReadFileEx callback failed with 0x%x (%s)", | |
| (guint) error_code, err.c_str ()); | |
| ret = false; | |
| } | |
| gst_cuda_ipc_server_wait_msg_finish (win32_conn->server, win32_conn, ret); | |
| } | |
| static void WINAPI | |
| gst_cuda_ipc_server_win32_wait_msg_finish (DWORD error_code, DWORD size, | |
| OVERLAPPED * overlap) | |
| { | |
| GstCudaIpcServerConnWin32 *win32_conn = | |
| static_cast < GstCudaIpcServerConnWin32 * >(overlap); | |
| GstCudaIpcServerWin32 *self = GST_CUDA_IPC_SERVER_WIN32 (win32_conn->server); | |
| GstCudaIpcPacketHeader header; | |
| if (error_code != ERROR_SUCCESS) { | |
| auto err = gst_cuda_ipc_win32_error_to_string (error_code); | |
| GST_WARNING_OBJECT (self, "ReadFileEx callback failed with 0x%x (%s)", | |
| (guint) error_code, err.c_str ()); | |
| gst_cuda_ipc_server_wait_msg_finish (win32_conn->server, win32_conn, false); | |
| return; | |
| } | |
| if (!gst_cuda_ipc_pkt_identify (win32_conn->client_msg, header)) { | |
| GST_ERROR_OBJECT (self, "Broken header"); | |
| gst_cuda_ipc_server_wait_msg_finish (win32_conn->server, win32_conn, false); | |
| return; | |
| } | |
| if (header.payload_size == 0) { | |
| gst_cuda_ipc_server_wait_msg_finish (win32_conn->server, win32_conn, true); | |
| return; | |
| } | |
| GST_LOG_OBJECT (self, "Reading payload"); | |
| if (!ReadFileEx (win32_conn->pipe, &win32_conn->client_msg[0] + | |
| GST_CUDA_IPC_PKT_HEADER_SIZE, header.payload_size, win32_conn, | |
| gst_cuda_ipc_server_win32_payload_finish)) { | |
| guint last_err = GetLastError (); | |
| auto err = gst_cuda_ipc_win32_error_to_string (last_err); | |
| GST_WARNING_OBJECT (self, "ReadFileEx failed with 0x%x (%s)", | |
| last_err, err.c_str ()); | |
| gst_cuda_ipc_server_wait_msg_finish (win32_conn->server, win32_conn, false); | |
| } | |
| } | |
| static bool | |
| gst_cuda_ipc_server_win32_wait_msg (GstCudaIpcServer * server, | |
| GstCudaIpcServerConn * conn) | |
| { | |
| GstCudaIpcServerWin32 *self = GST_CUDA_IPC_SERVER_WIN32 (server); | |
| GstCudaIpcServerConnWin32 *win32_conn = | |
| static_cast < GstCudaIpcServerConnWin32 * >(conn); | |
| GST_LOG_OBJECT (self, "Waiting for client message"); | |
| if (!ReadFileEx (win32_conn->pipe, &conn->client_msg[0], | |
| GST_CUDA_IPC_PKT_HEADER_SIZE, win32_conn, | |
| gst_cuda_ipc_server_win32_wait_msg_finish)) { | |
| guint last_err = GetLastError (); | |
| auto err = gst_cuda_ipc_win32_error_to_string (last_err); | |
| GST_WARNING_OBJECT (self, "ReadFileEx failed with 0x%x (%s)", | |
| last_err, err.c_str ()); | |
| return false; | |
| } | |
| return true; | |
| } | |
| static void WINAPI | |
| gst_cuda_ipc_server_win32_send_msg_finish (DWORD error_code, DWORD size, | |
| OVERLAPPED * overlap) | |
| { | |
| GstCudaIpcServerConnWin32 *win32_conn = | |
| static_cast < GstCudaIpcServerConnWin32 * >(overlap); | |
| GstCudaIpcServerWin32 *self = GST_CUDA_IPC_SERVER_WIN32 (win32_conn->server); | |
| bool ret = true; | |
| if (error_code != ERROR_SUCCESS) { | |
| auto err = gst_cuda_ipc_win32_error_to_string (error_code); | |
| GST_WARNING_OBJECT (self, "ReadFileEx callback failed with 0x%x (%s)", | |
| (guint) error_code, err.c_str ()); | |
| ret = false; | |
| } | |
| GST_LOG_OBJECT (self, "Sent message"); | |
| gst_cuda_ipc_server_send_msg_finish (win32_conn->server, win32_conn, ret); | |
| } | |
| static bool | |
| gst_cuda_ipc_server_win32_send_msg (GstCudaIpcServer * server, | |
| GstCudaIpcServerConn * conn) | |
| { | |
| GstCudaIpcServerWin32 *self = GST_CUDA_IPC_SERVER_WIN32 (server); | |
| GstCudaIpcServerConnWin32 *win32_conn = | |
| static_cast < GstCudaIpcServerConnWin32 * >(conn); | |
| GST_LOG_OBJECT (self, "Sending message"); | |
| if (!WriteFileEx (win32_conn->pipe, &conn->server_msg[0], | |
| conn->server_msg.size (), win32_conn, | |
| gst_cuda_ipc_server_win32_send_msg_finish)) { | |
| guint last_err = GetLastError (); | |
| auto err = gst_cuda_ipc_win32_error_to_string (last_err); | |
| GST_WARNING_OBJECT (self, "WriteFileEx failed with 0x%x (%s)", | |
| last_err, err.c_str ()); | |
| return false; | |
| } | |
| return true; | |
| } | |
| static HANDLE | |
| gst_cuda_ipc_server_win32_create_pipe (GstCudaIpcServerWin32 * self, | |
| OVERLAPPED * overlap, bool &io_pending) | |
| { | |
| GstCudaIpcServerWin32Private *priv = self->priv; | |
| HANDLE pipe = CreateNamedPipeA (priv->address.c_str (), | |
| PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, | |
| PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, | |
| PIPE_UNLIMITED_INSTANCES, 1024, 1024, 5000, nullptr); | |
| if (pipe == INVALID_HANDLE_VALUE) { | |
| guint last_err = GetLastError (); | |
| auto err = gst_cuda_ipc_win32_error_to_string (last_err); | |
| GST_ERROR_OBJECT (self, "CreateNamedPipeA failed with 0x%x (%s)", | |
| last_err, err.c_str ()); | |
| return INVALID_HANDLE_VALUE; | |
| } | |
| if (ConnectNamedPipe (pipe, overlap)) { | |
| guint last_err = GetLastError (); | |
| auto err = gst_cuda_ipc_win32_error_to_string (last_err); | |
| GST_ERROR_OBJECT (self, "ConnectNamedPipe failed with 0x%x (%s)", | |
| last_err, err.c_str ()); | |
| return INVALID_HANDLE_VALUE; | |
| } | |
| io_pending = false; | |
| guint last_err = GetLastError (); | |
| switch (last_err) { | |
| case ERROR_IO_PENDING: | |
| io_pending = true; | |
| break; | |
| case ERROR_PIPE_CONNECTED: | |
| SetEvent (overlap->hEvent); | |
| break; | |
| default: | |
| { | |
| auto err = gst_cuda_ipc_win32_error_to_string (last_err); | |
| GST_ERROR_OBJECT (self, "ConnectNamedPipe failed with 0x%x (%s)", | |
| last_err, err.c_str ()); | |
| CloseHandle (pipe); | |
| return INVALID_HANDLE_VALUE; | |
| } | |
| } | |
| return pipe; | |
| } | |
| static void | |
| gst_cuda_ipc_server_win32_loop (GstCudaIpcServer * server) | |
| { | |
| GstCudaIpcServerWin32 *self = GST_CUDA_IPC_SERVER_WIN32 (server); | |
| GstCudaIpcServerWin32Private *priv = self->priv; | |
| bool io_pending = false; | |
| guint wait_ret; | |
| HANDLE pipe; | |
| OVERLAPPED overlap; | |
| GST_DEBUG_OBJECT (self, "Entering loop"); | |
| memset (&overlap, 0, sizeof (OVERLAPPED)); | |
| overlap.hEvent = CreateEvent (nullptr, TRUE, TRUE, nullptr); | |
| pipe = gst_cuda_ipc_server_win32_create_pipe (self, &overlap, io_pending); | |
| if (pipe == INVALID_HANDLE_VALUE) { | |
| CloseHandle (overlap.hEvent); | |
| gst_cuda_ipc_server_abort (server); | |
| return; | |
| } | |
| HANDLE waitables[] = | |
| { overlap.hEvent, priv->wakeup_event, priv->cancellable }; | |
| do { | |
| wait_ret = WaitForMultipleObjectsEx (G_N_ELEMENTS (waitables), waitables, | |
| FALSE, INFINITE, TRUE); | |
| if (wait_ret == WAIT_OBJECT_0 + 2) { | |
| GST_DEBUG_OBJECT (self, "Operation cancelled"); | |
| goto out; | |
| } | |
| switch (wait_ret) { | |
| case WAIT_OBJECT_0: | |
| { | |
| DWORD n_bytes; | |
| if (io_pending | |
| && !GetOverlappedResult (pipe, &overlap, &n_bytes, FALSE)) { | |
| guint last_err = GetLastError (); | |
| auto err = gst_cuda_ipc_win32_error_to_string (last_err); | |
| GST_WARNING_OBJECT (self, "GetOverlappedResult failed with 0x%x (%s)", | |
| last_err, err.c_str ()); | |
| CloseHandle (pipe); | |
| pipe = INVALID_HANDLE_VALUE; | |
| break; | |
| } | |
| auto conn = std::make_shared < GstCudaIpcServerConnWin32 > (pipe); | |
| pipe = INVALID_HANDLE_VALUE; | |
| gst_cuda_ipc_server_on_incoming_connection (server, conn); | |
| pipe = gst_cuda_ipc_server_win32_create_pipe (self, | |
| &overlap, io_pending); | |
| break; | |
| } | |
| case WAIT_IO_COMPLETION: | |
| break; | |
| case WAIT_OBJECT_0 + 1: | |
| gst_cuda_ipc_server_on_idle (server); | |
| break; | |
| default: | |
| { | |
| guint last_err = GetLastError (); | |
| auto err = gst_cuda_ipc_win32_error_to_string (last_err); | |
| GST_ERROR_OBJECT (self, | |
| "WaitForMultipleObjectsEx return 0x%x, last error 0x%x (%s)", | |
| wait_ret, last_err, err.c_str ()); | |
| gst_cuda_ipc_server_abort (server); | |
| goto out; | |
| } | |
| } | |
| } while (true); | |
| out: | |
| if (pipe != INVALID_HANDLE_VALUE) { | |
| CancelIo (pipe); | |
| DisconnectNamedPipe (pipe); | |
| CloseHandle (pipe); | |
| } | |
| CloseHandle (overlap.hEvent); | |
| GST_DEBUG_OBJECT (self, "Exit loop"); | |
| } | |
| GstCudaIpcServer * | |
| gst_cuda_ipc_server_new (const gchar * address, GstCudaContext * context, | |
| GstCudaIpcMode ipc_mode) | |
| { | |
| GstCudaIpcServerWin32 *self; | |
| GstCudaIpcServer *server; | |
| g_return_val_if_fail (address, nullptr); | |
| g_return_val_if_fail (GST_IS_CUDA_CONTEXT (context), nullptr); | |
| self = (GstCudaIpcServerWin32 *) | |
| g_object_new (GST_TYPE_CUDA_IPC_SERVER_WIN32, nullptr); | |
| gst_object_ref_sink (self); | |
| self->priv->address = address; | |
| server = GST_CUDA_IPC_SERVER (self); | |
| server->context = (GstCudaContext *) gst_object_ref (context); | |
| server->ipc_mode = ipc_mode; | |
| server->pid = GetCurrentProcessId (); | |
| gst_cuda_ipc_server_run (server); | |
| return server; | |
| } |