This commit is contained in:
2025-12-03 21:39:49 +05:30
parent d3721e9144
commit 9f75db8f95
8 changed files with 345 additions and 3 deletions

View File

@ -1,4 +1,5 @@
set(SRC_FILES
"imp/cpp/IPC.cpp"
"imp/cpp/JSON.cpp"
"imp/cpp/IACore.cpp"
"imp/cpp/Logger.cpp"

View File

@ -28,15 +28,93 @@ namespace IACore
#if IA_PLATFORM_WINDOWS
::UnmapViewOfFile(std::get<1>(handles));
::CloseHandle(std::get<2>(handles));
if (std::get<0>(handles) != INVALID_HANDLE_VALUE)
::CloseHandle(std::get<0>(handles));
#elif IA_PLATFORM_UNIX
::munmap(std::get<1>(handles), (SIZE_T) std::get<2>(handles));
::close((INT32) ((UINT64) std::get<0>(handles)));
const auto fd = (INT32) ((UINT64) std::get<0>(handles));
if (fd != -1)
::close(fd);
#else
# error "IACore FileOps does not support this platform"
#endif
}
Expected<PCUINT8, String> FileOps::MapSharedMemory(IN CONST String &name, IN SIZE_T size, IN BOOL isOwner)
{
#if IA_PLATFORM_WINDOWS
int wchars_num = MultiByteToWideChar(CP_UTF8, 0, name.c_str(), -1, NULL, 0);
std::wstring wName(wchars_num, 0);
MultiByteToWideChar(CP_UTF8, 0, name.c_str(), -1, &wName[0], wchars_num);
HANDLE hMap = NULL;
if (isOwner)
hMap = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, (DWORD) (size >> 32),
(DWORD) (size & 0xFFFFFFFF), wName.c_str());
else
hMap = OpenFileMappingW(FILE_MAP_ALL_ACCESS, FALSE, wName.c_str());
if (hMap == NULL)
return MakeUnexpected(
std::format("Failed to {} shared memory '{}'", isOwner ? "owner" : "consumer", name.c_str()));
const auto result = static_cast<PCUINT8>(MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, size));
if (result == NULL)
{
CloseHandle(hMap);
return MakeUnexpected(std::format("Failed to map view of shared memory '{}'", name.c_str()));
}
s_mappedFiles[result] = std::make_tuple((PVOID) INVALID_HANDLE_VALUE, (PVOID) result, (PVOID) hMap);
return result;
#elif IA_PLATFORM_UNIX
int fd = -1;
if (isOwner)
{
fd = shm_open(name.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0666);
if (fd != -1)
{
if (ftruncate(fd, size) == -1)
{
close(fd);
shm_unlink(name.c_str());
return MakeUnexpected(std::format("Failed to truncate shared memory '{}'", name.c_str()));
}
}
}
else
fd = shm_open(name.c_str(), O_RDWR, 0666);
if (fd == -1)
return MakeUnexpected(
std::format("Failed to {} shared memory '{}'", isOwner ? "owner" : "consumer", name.c_str()));
void *addr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (addr == MAP_FAILED)
{
close(fd);
return MakeUnexpected(std::format("Failed to mmap shared memory '{}'", name.c_str()));
}
const auto result = static_cast<PCUINT8>(addr);
s_mappedFiles[result] = std::make_tuple((PVOID) ((UINT64) fd), (PVOID) addr, (PVOID) size);
return result;
#else
# error "IACore FileOps does not support this platform"
#endif
}
VOID FileOps::UnlinkSharedMemory(IN CONST String &name)
{
#if IA_PLATFORM_UNIX
shm_unlink(name.c_str());
#endif
}
Expected<PCUINT8, String> FileOps::MapFile(IN CONST FilePath &path, OUT SIZE_T &size)
{
#if IA_PLATFORM_WINDOWS
@ -192,7 +270,7 @@ namespace IACore
result = "./" + result;
return FilePath(result);
#else
# error "unreachable. previous checks must have stopped compilation beforehand .unsupported platform."
# error "IACore FileOps does not support this platform"
#endif
}
} // namespace IACore

View File

@ -0,0 +1,22 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2025 IAS (ias@iasoft.dev)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <IACore/IPC.hpp>
namespace IACore
{
}

View File

@ -18,6 +18,54 @@
namespace IACore
{
BOOL SocketOps::Listen(IN SocketHandle sock, IN INT32 queueSize)
{
return listen(sock, queueSize) == 0;
}
SocketHandle SocketOps::CreateUnixSocket()
{
return socket(AF_UNIX, SOCK_STREAM, 0);
}
BOOL SocketOps::BindUnixSocket(IN SocketHandle sock, IN PCCHAR path)
{
if (!IS_VALID_SOCKET(sock))
return FALSE;
UNLINK_FILE(path);
sockaddr_un addr{};
addr.sun_family = AF_UNIX;
size_t maxLen = sizeof(addr.sun_path) - 1;
strncpy(addr.sun_path, path, maxLen);
if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) == -1)
return FALSE;
return TRUE;
}
BOOL SocketOps::ConnectUnixSocket(IN SocketHandle sock, IN PCCHAR path)
{
if (!IS_VALID_SOCKET(sock))
return FALSE;
sockaddr_un addr{};
addr.sun_family = AF_UNIX;
size_t maxLen = sizeof(addr.sun_path) - 1;
strncpy(addr.sun_path, path, maxLen);
if (connect(sock, (struct sockaddr *) &addr, sizeof(addr)) == -1)
return FALSE;
return TRUE;
}
BOOL SocketOps::IsPortAvailable(IN UINT16 port, IN INT32 type)
{
SocketHandle sock = socket(AF_INET, type, IPPROTO_UDP);

View File

@ -0,0 +1,143 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2025 IAS (ias@iasoft.dev)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#pragma once
#include <IACore/PCH.hpp>
namespace IACore
{
class RingBufferView
{
public:
STATIC CONSTEXPR UINT16 PACKET_ID_SKIP = 0;
struct ControlBlock
{
alignas(64) Atomic<UINT32> WriteOffset{0};
alignas(64) Atomic<UINT32> ReadOffset{0};
UINT32 Capacity;
// Padding to ensure data starts on a cache line
UINT8 _padding[64 - sizeof(UINT32) * 3];
};
// All of the data in ring buffer will be stored as packets
struct PacketHeader
{
UINT16 ID{};
UINT16 PayloadSize{};
};
public:
INLINE RingBufferView(IN Span<UINT8> buffer);
INLINE BOOL Pop(OUT PacketHeader &outHeader, OUT Span<UINT8> outBuffer);
INLINE BOOL Push(IN UINT16 packetID, IN Span<CONST UINT8> data);
INLINE ControlBlock *GetControlBlock();
private:
PUINT8 m_dataPtr{};
UINT32 m_capacity{};
ControlBlock *m_controlBlock{};
private:
INLINE VOID WritePacket(IN UINT32 offset, IN UINT16 id, IN PCVOID data, IN UINT16 dataSize);
};
} // namespace IACore
namespace IACore
{
RingBufferView::RingBufferView(IN Span<UINT8> buffer)
{
IA_ASSERT(buffer.size() > sizeof(ControlBlock));
m_controlBlock = reinterpret_cast<ControlBlock *>(buffer.data());
m_dataPtr = buffer.data() + sizeof(ControlBlock);
m_controlBlock->Capacity = m_capacity = buffer.size() - sizeof(ControlBlock);
}
BOOL RingBufferView::Pop(OUT PacketHeader &outHeader, OUT Span<UINT8> outBuffer)
{
UINT32 currentWriteOffset = m_controlBlock->WriteOffset.load(std::memory_order_acquire);
UINT32 currentReadOffset = m_controlBlock->ReadOffset.load(std::memory_order_acquire);
if (currentReadOffset == currentWriteOffset)
return false;
const auto header = reinterpret_cast<PacketHeader *>(m_dataPtr + currentReadOffset);
if (header->ID == PACKET_ID_SKIP)
{
m_controlBlock->ReadOffset.store(0, std::memory_order_release);
return Pop(outHeader, outBuffer);
}
outHeader = *header;
if (outHeader.PayloadSize > outBuffer.size())
return false;
memcpy(outBuffer.data(), m_dataPtr + currentReadOffset + sizeof(PacketHeader), outHeader.PayloadSize);
m_controlBlock->ReadOffset.store(currentReadOffset + sizeof(PacketHeader) + outHeader.PayloadSize, std::memory_order_release);
return true;
}
BOOL RingBufferView::Push(IN UINT16 packetID, IN Span<CONST UINT8> data)
{
IA_ASSERT(data.size() <= UINT16_MAX);
UINT32 currentWriteOffset = m_controlBlock->WriteOffset.load(std::memory_order_acquire);
UINT32 currentReadOffset = m_controlBlock->ReadOffset.load(std::memory_order_acquire);
UINT32 requiredSpace = sizeof(PacketHeader) + data.size();
if (currentWriteOffset + requiredSpace <= m_capacity)
{
WritePacket(currentWriteOffset, packetID, data.data(), static_cast<UINT16>(data.size()));
m_controlBlock->WriteOffset.store(currentWriteOffset + requiredSpace, std::memory_order_release);
return true;
}
UINT32 remainingAtEnd = m_capacity - currentWriteOffset;
if (requiredSpace < currentReadOffset)
{
if (remainingAtEnd >= sizeof(PacketHeader))
{
const auto p = PacketHeader{PACKET_ID_SKIP, 0};
memcpy(m_dataPtr + currentWriteOffset, &p, sizeof(p));
}
WritePacket(0, packetID, data.data(), static_cast<UINT16>(data.size()));
m_controlBlock->WriteOffset.store(requiredSpace, std::memory_order_release);
return true;
}
return false;
}
RingBufferView::ControlBlock *RingBufferView::GetControlBlock()
{
return m_controlBlock;
}
VOID RingBufferView::WritePacket(IN UINT32 offset, IN UINT16 id, IN PCVOID data, IN UINT16 dataSize)
{
PacketHeader h = {id, dataSize};
memcpy(m_dataPtr + offset, &h, sizeof(PacketHeader));
memcpy(m_dataPtr + offset + sizeof(PacketHeader), data, dataSize);
}
} // namespace IACore

View File

@ -30,6 +30,10 @@ namespace IACore
STATIC VOID UnmapFile(IN PCUINT8 mappedPtr);
STATIC Expected<PCUINT8, String> MapFile(IN CONST FilePath &path, OUT SIZE_T &size);
// @param `isOwner` TRUE to allocate/truncate. FALSE to just open.
STATIC Expected<PCUINT8, String> MapSharedMemory(IN CONST String &name, IN SIZE_T size, IN BOOL isOwner);
STATIC VOID UnlinkSharedMemory(IN CONST String &name);
STATIC Expected<StreamReader, String> StreamFromFile(IN CONST FilePath &path);
STATIC Expected<StreamWriter, String> StreamToFile(IN CONST FilePath &path, IN BOOL overwrite = false);

View File

@ -0,0 +1,34 @@
// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2025 IAS (ias@iasoft.dev)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#pragma once
#include <IACore/PCH.hpp>
namespace IACore
{
class IPC_Node
{
public:
};
class IPC_Server
{
public:
};
}

View File

@ -21,19 +21,24 @@
#if IA_PLATFORM_WINDOWS
# include <winsock2.h>
# include <ws2tcpip.h>
# include <afunix.h>
# pragma comment(lib, "ws2_32.lib")
# define CLOSE_SOCKET(s) closesocket(s)
# define IS_VALID_SOCKET(s) (s != INVALID_SOCKET)
# define UNLINK_FILE(p) DeleteFileA(p)
using SocketHandle = SOCKET;
#elif IA_PLATFORM_UNIX
# include <sys/un.h>
# include <sys/types.h>
# include <sys/socket.h>
# include <netinet/in.h>
# define CLOSE_SOCKET(s) close(s)
# define IS_VALID_SOCKET(s) (s >= 0)
# define INVALID_SOCKET -1
# define UNLINK_FILE(p) unlink(p)
using SocketHandle = int;
#else
@ -72,6 +77,13 @@ namespace IACore
return IsPortAvailable(port, SOCK_DGRAM);
}
STATIC BOOL Listen(IN SocketHandle sock, IN INT32 queueSize = 5);
STATIC SocketHandle CreateUnixSocket();
STATIC BOOL BindUnixSocket(IN SocketHandle sock, IN PCCHAR path);
STATIC BOOL ConnectUnixSocket(IN SocketHandle sock, IN PCCHAR path);
private:
STATIC BOOL IsPortAvailable(IN UINT16 port, IN INT32 type);
};