mirror of
https://github.com/mutouyun/cpp-ipc.git
synced 2026-01-01 03:12:13 +08:00
Compare commits
No commits in common. "master" and "v1.4.0" have entirely different histories.
2
.github/workflows/c-cpp.yml
vendored
2
.github/workflows/c-cpp.yml
vendored
@ -2,7 +2,7 @@ name: C/C++ CI
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ master, develop, issue-*, feature/* ]
|
branches: [ master, develop, issue-* ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ master, develop ]
|
branches: [ master, develop ]
|
||||||
|
|
||||||
|
|||||||
38
.github/workflows/codecov.yml
vendored
38
.github/workflows/codecov.yml
vendored
@ -1,38 +0,0 @@
|
|||||||
name: Upload CodeCov Report
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ master ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ master ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout Code
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Configure
|
|
||||||
run: cmake -DCMAKE_BUILD_TYPE=Debug -DLIBIPC_BUILD_TESTS=ON -DLIBIPC_CODECOV=ON .
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: make -j
|
|
||||||
|
|
||||||
- name: Test
|
|
||||||
env:
|
|
||||||
LD_LIBRARY_PATH: ./bin
|
|
||||||
run: ./bin/test-ipc
|
|
||||||
|
|
||||||
- name: Upload coverage to Codecov
|
|
||||||
uses: codecov/codecov-action@v4.0.1
|
|
||||||
with:
|
|
||||||
verbose: true
|
|
||||||
exclude: |
|
|
||||||
3rdparty/**
|
|
||||||
test/**
|
|
||||||
demo/**
|
|
||||||
env:
|
|
||||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
|
||||||
@ -1,11 +1,10 @@
|
|||||||
cmake_minimum_required(VERSION 3.10)
|
cmake_minimum_required(VERSION 3.10)
|
||||||
project(cpp-ipc VERSION 1.4.1)
|
project(cpp-ipc)
|
||||||
|
|
||||||
option(LIBIPC_BUILD_TESTS "Build all of libipc's own tests." OFF)
|
option(LIBIPC_BUILD_TESTS "Build all of libipc's own tests." OFF)
|
||||||
option(LIBIPC_BUILD_DEMOS "Build all of libipc's own demos." OFF)
|
option(LIBIPC_BUILD_DEMOS "Build all of libipc's own demos." OFF)
|
||||||
option(LIBIPC_BUILD_SHARED_LIBS "Build shared libraries (DLLs)." OFF)
|
option(LIBIPC_BUILD_SHARED_LIBS "Build shared libraries (DLLs)." OFF)
|
||||||
option(LIBIPC_USE_STATIC_CRT "Set to ON to build with static CRT on Windows (/MT)." OFF)
|
option(LIBIPC_USE_STATIC_CRT "Set to ON to build with static CRT on Windows (/MT)." OFF)
|
||||||
option(LIBIPC_CODECOV "Build with unit test coverage." OFF)
|
|
||||||
|
|
||||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
@ -14,12 +13,6 @@ if(NOT MSVC)
|
|||||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2")
|
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Code coverage support
|
|
||||||
if (LIBIPC_CODECOV AND NOT MSVC)
|
|
||||||
add_compile_options(--coverage)
|
|
||||||
add_link_options(-lgcov --coverage)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
set(CompilerFlags
|
set(CompilerFlags
|
||||||
CMAKE_CXX_FLAGS
|
CMAKE_CXX_FLAGS
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
[](https://github.com/mutouyun/cpp-ipc/blob/master/LICENSE)
|
[](https://github.com/mutouyun/cpp-ipc/blob/master/LICENSE)
|
||||||
[](https://github.com/mutouyun/cpp-ipc/actions)
|
[](https://github.com/mutouyun/cpp-ipc/actions)
|
||||||
[](https://codecov.io/github/mutouyun/cpp-ipc)
|
|
||||||
[](https://ci.appveyor.com/project/mutouyun/cpp-ipc)
|
[](https://ci.appveyor.com/project/mutouyun/cpp-ipc)
|
||||||
[](https://github.com/microsoft/vcpkg/tree/master/ports/cpp-ipc)
|
[](https://github.com/microsoft/vcpkg/tree/master/ports/cpp-ipc)
|
||||||
|
|
||||||
|
|||||||
30
codecov.yml
30
codecov.yml
@ -1,30 +0,0 @@
|
|||||||
codecov:
|
|
||||||
require_ci_to_pass: yes
|
|
||||||
|
|
||||||
coverage:
|
|
||||||
precision: 2
|
|
||||||
round: down
|
|
||||||
range: "70...100"
|
|
||||||
|
|
||||||
status:
|
|
||||||
project:
|
|
||||||
default:
|
|
||||||
target: auto
|
|
||||||
threshold: 0%
|
|
||||||
base: auto
|
|
||||||
patch:
|
|
||||||
default:
|
|
||||||
target: auto
|
|
||||||
threshold: 0%
|
|
||||||
base: auto
|
|
||||||
|
|
||||||
ignore:
|
|
||||||
- "3rdparty/**/*"
|
|
||||||
- "3rdparty/**"
|
|
||||||
- "test/**/*"
|
|
||||||
- "demo/**/*"
|
|
||||||
|
|
||||||
comment:
|
|
||||||
layout: "reach,diff,flags,tree"
|
|
||||||
behavior: default
|
|
||||||
require_changes: false
|
|
||||||
@ -5,12 +5,12 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
#include "libipc/imp/export.h"
|
#include "libipc/export.h"
|
||||||
#include "libipc/def.h"
|
#include "libipc/def.h"
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
|
|
||||||
class LIBIPC_EXPORT buffer {
|
class IPC_EXPORT buffer {
|
||||||
public:
|
public:
|
||||||
using destructor_t = void (*)(void*, std::size_t);
|
using destructor_t = void (*)(void*, std::size_t);
|
||||||
|
|
||||||
@ -59,8 +59,8 @@ public:
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
friend LIBIPC_EXPORT bool operator==(buffer const & b1, buffer const & b2);
|
friend IPC_EXPORT bool operator==(buffer const & b1, buffer const & b2);
|
||||||
friend LIBIPC_EXPORT bool operator!=(buffer const & b1, buffer const & b2);
|
friend IPC_EXPORT bool operator!=(buffer const & b1, buffer const & b2);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class buffer_;
|
class buffer_;
|
||||||
|
|||||||
@ -1,66 +0,0 @@
|
|||||||
/**
|
|
||||||
* \file libconcur/intrusive_stack.h
|
|
||||||
* \author mutouyun (orz@orzz.org)
|
|
||||||
* \brief Define concurrent intrusive stack.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
namespace concur {
|
|
||||||
|
|
||||||
/// \brief Intrusive stack node.
|
|
||||||
/// \tparam T The type of the value.
|
|
||||||
template <typename T>
|
|
||||||
struct intrusive_node {
|
|
||||||
T value;
|
|
||||||
std::atomic<intrusive_node *> next;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// \brief Intrusive stack.
|
|
||||||
/// \tparam T The type of the value.
|
|
||||||
/// \tparam Node The type of the node.
|
|
||||||
template <typename T, typename Node = intrusive_node<T>>
|
|
||||||
class intrusive_stack {
|
|
||||||
public:
|
|
||||||
using node = Node;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::atomic<node *> top_{nullptr};
|
|
||||||
|
|
||||||
public:
|
|
||||||
intrusive_stack(intrusive_stack const &) = delete;
|
|
||||||
intrusive_stack(intrusive_stack &&) = delete;
|
|
||||||
intrusive_stack &operator=(intrusive_stack const &) = delete;
|
|
||||||
intrusive_stack &operator=(intrusive_stack &&) = delete;
|
|
||||||
|
|
||||||
constexpr intrusive_stack() noexcept = default;
|
|
||||||
|
|
||||||
bool empty() const noexcept {
|
|
||||||
return top_.load(std::memory_order_acquire) == nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void push(node *n) noexcept {
|
|
||||||
node *old_top = top_.load(std::memory_order_acquire);
|
|
||||||
do {
|
|
||||||
n->next.store(old_top, std::memory_order_relaxed);
|
|
||||||
} while (!top_.compare_exchange_weak(old_top, n, std::memory_order_release
|
|
||||||
, std::memory_order_acquire));
|
|
||||||
}
|
|
||||||
|
|
||||||
node *pop() noexcept {
|
|
||||||
node *old_top = top_.load(std::memory_order_acquire);
|
|
||||||
do {
|
|
||||||
if (old_top == nullptr) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
} while (!top_.compare_exchange_weak(old_top, old_top->next.load(std::memory_order_relaxed)
|
|
||||||
, std::memory_order_release
|
|
||||||
, std::memory_order_acquire));
|
|
||||||
return old_top;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace concur
|
|
||||||
} // namespace ipc
|
|
||||||
@ -2,14 +2,14 @@
|
|||||||
|
|
||||||
#include <cstdint> // std::uint64_t
|
#include <cstdint> // std::uint64_t
|
||||||
|
|
||||||
#include "libipc/imp/export.h"
|
#include "libipc/export.h"
|
||||||
#include "libipc/def.h"
|
#include "libipc/def.h"
|
||||||
#include "libipc/mutex.h"
|
#include "libipc/mutex.h"
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
namespace sync {
|
namespace sync {
|
||||||
|
|
||||||
class LIBIPC_EXPORT condition {
|
class IPC_EXPORT condition {
|
||||||
condition(condition const &) = delete;
|
condition(condition const &) = delete;
|
||||||
condition &operator=(condition const &) = delete;
|
condition &operator=(condition const &) = delete;
|
||||||
|
|
||||||
|
|||||||
@ -31,7 +31,6 @@ enum : std::uint32_t {
|
|||||||
};
|
};
|
||||||
|
|
||||||
enum : std::size_t {
|
enum : std::size_t {
|
||||||
central_cache_default_size = 1024 * 1024, ///< 1MB
|
|
||||||
data_length = 64,
|
data_length = 64,
|
||||||
large_msg_limit = data_length,
|
large_msg_limit = data_length,
|
||||||
large_msg_align = 1024,
|
large_msg_align = 1024,
|
||||||
|
|||||||
54
include/libipc/export.h
Executable file
54
include/libipc/export.h
Executable file
@ -0,0 +1,54 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if defined(Q_DECL_EXPORT) && defined(Q_DECL_IMPORT)
|
||||||
|
|
||||||
|
# define IPC_DECL_EXPORT Q_DECL_EXPORT
|
||||||
|
# define IPC_DECL_IMPORT Q_DECL_IMPORT
|
||||||
|
|
||||||
|
#else // defined(Q_DECL_EXPORT) && defined(Q_DECL_IMPORT)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compiler & system detection for IPC_DECL_EXPORT & IPC_DECL_IMPORT.
|
||||||
|
* Not using QtCore cause it shouldn't depend on Qt.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
# define IPC_DECL_EXPORT __declspec(dllexport)
|
||||||
|
# define IPC_DECL_IMPORT __declspec(dllimport)
|
||||||
|
#elif defined(__ARMCC__) || defined(__CC_ARM)
|
||||||
|
# if defined(ANDROID) || defined(__linux__) || defined(__linux)
|
||||||
|
# define IPC_DECL_EXPORT __attribute__((visibility("default")))
|
||||||
|
# define IPC_DECL_IMPORT __attribute__((visibility("default")))
|
||||||
|
# else
|
||||||
|
# define IPC_DECL_EXPORT __declspec(dllexport)
|
||||||
|
# define IPC_DECL_IMPORT __declspec(dllimport)
|
||||||
|
# endif
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
# if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || \
|
||||||
|
defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
|
||||||
|
# define IPC_DECL_EXPORT __declspec(dllexport)
|
||||||
|
# define IPC_DECL_IMPORT __declspec(dllimport)
|
||||||
|
# else
|
||||||
|
# define IPC_DECL_EXPORT __attribute__((visibility("default")))
|
||||||
|
# define IPC_DECL_IMPORT __attribute__((visibility("default")))
|
||||||
|
# endif
|
||||||
|
#else
|
||||||
|
# define IPC_DECL_EXPORT __attribute__((visibility("default")))
|
||||||
|
# define IPC_DECL_IMPORT __attribute__((visibility("default")))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // defined(Q_DECL_EXPORT) && defined(Q_DECL_IMPORT)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Define IPC_EXPORT for exporting function & class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef IPC_EXPORT
|
||||||
|
#if defined(LIBIPC_LIBRARY_SHARED_BUILDING__)
|
||||||
|
# define IPC_EXPORT IPC_DECL_EXPORT
|
||||||
|
#elif defined(LIBIPC_LIBRARY_SHARED_USING__)
|
||||||
|
# define IPC_EXPORT IPC_DECL_IMPORT
|
||||||
|
#else
|
||||||
|
# define IPC_EXPORT
|
||||||
|
#endif
|
||||||
|
#endif /*IPC_EXPORT*/
|
||||||
@ -1,74 +0,0 @@
|
|||||||
/**
|
|
||||||
* \file libipc/aligned.h
|
|
||||||
* \author mutouyun (orz@orzz.org)
|
|
||||||
* \brief Defines the type suitable for use as uninitialized storage for types of given type.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <cstddef>
|
|
||||||
|
|
||||||
#include "libipc/imp/byte.h"
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief The type suitable for use as uninitialized storage for types of given type.
|
|
||||||
* std::aligned_storage is deprecated in C++23, so we define our own.
|
|
||||||
* \tparam T The type to be aligned.
|
|
||||||
* \tparam AlignT The alignment of the type.
|
|
||||||
* \see https://en.cppreference.com/w/cpp/types/aligned_storage
|
|
||||||
* https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1413r3.pdf
|
|
||||||
*/
|
|
||||||
template <typename T, std::size_t AlignT = alignof(T)>
|
|
||||||
class aligned {
|
|
||||||
alignas(AlignT) std::array<ipc::byte, sizeof(T)> storage_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* \brief Returns a pointer to the aligned storage.
|
|
||||||
* \return A pointer to the aligned storage.
|
|
||||||
*/
|
|
||||||
T *ptr() noexcept {
|
|
||||||
return reinterpret_cast<T *>(storage_.data());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Returns a pointer to the aligned storage.
|
|
||||||
* \return A pointer to the aligned storage.
|
|
||||||
*/
|
|
||||||
T const *ptr() const noexcept {
|
|
||||||
return reinterpret_cast<const T *>(storage_.data());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Returns a reference to the aligned storage.
|
|
||||||
* \return A reference to the aligned storage.
|
|
||||||
*/
|
|
||||||
T &ref() noexcept {
|
|
||||||
return *ptr();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Returns a reference to the aligned storage.
|
|
||||||
* \return A reference to the aligned storage.
|
|
||||||
*/
|
|
||||||
T const &ref() const noexcept {
|
|
||||||
return *ptr();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Rounds up the given value to the given alignment.
|
|
||||||
* \tparam T The type of the value.
|
|
||||||
* \param value The value to be rounded up.
|
|
||||||
* \param alignment The alignment to be rounded up to.
|
|
||||||
* \return The rounded up value.
|
|
||||||
* \see https://stackoverflow.com/questions/3407012/c-rounding-up-to-the-nearest-multiple-of-a-number
|
|
||||||
*/
|
|
||||||
template <typename T>
|
|
||||||
constexpr T round_up(T value, T alignment) noexcept {
|
|
||||||
return (value + alignment - 1) & ~(alignment - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ipc
|
|
||||||
@ -1,178 +0,0 @@
|
|||||||
/**
|
|
||||||
* \file libipc/byte.h
|
|
||||||
* \author mutouyun (orz@orzz.org)
|
|
||||||
* \brief Define the byte type.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <type_traits>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstddef> // std::byte (since C++17)
|
|
||||||
|
|
||||||
#include "libipc/imp/detect_plat.h"
|
|
||||||
#include "libipc/imp/span.h"
|
|
||||||
#include "libipc/imp/fmt.h"
|
|
||||||
|
|
||||||
#if defined(LIBIPC_CPP_17) && defined(__cpp_lib_byte)
|
|
||||||
#define LIBIPC_CPP_LIB_BYTE_
|
|
||||||
#endif // __cpp_lib_byte
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
|
|
||||||
class byte;
|
|
||||||
|
|
||||||
namespace detail_byte {
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
using is_integral =
|
|
||||||
typename std::enable_if<std::is_integral<T>::value>::type;
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
using is_not_byte =
|
|
||||||
typename std::enable_if<!std::is_same<
|
|
||||||
typename std::remove_cv<T>::type, byte>::value>::type;
|
|
||||||
|
|
||||||
} // namespace detail_byte
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief A distinct type that implements the concept of byte as specified in the C++ language definition.
|
|
||||||
* \see https://en.cppreference.com/w/cpp/types/byte
|
|
||||||
*/
|
|
||||||
class byte {
|
|
||||||
std::uint8_t bits_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
byte() noexcept = default;
|
|
||||||
|
|
||||||
template <typename T, typename = detail_byte::is_integral<T>>
|
|
||||||
constexpr byte(T v) noexcept
|
|
||||||
: bits_(static_cast<std::uint8_t>(v)) {}
|
|
||||||
|
|
||||||
#ifdef LIBIPC_CPP_LIB_BYTE_
|
|
||||||
constexpr byte(std::byte b) noexcept
|
|
||||||
: byte(std::to_integer<std::uint8_t>(b)) {}
|
|
||||||
#endif // LIBIPC_CPP_LIB_BYTE_
|
|
||||||
|
|
||||||
template <typename T, typename = detail_byte::is_integral<T>>
|
|
||||||
constexpr operator T() const noexcept {
|
|
||||||
return static_cast<T>(bits_);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef LIBIPC_CPP_LIB_BYTE_
|
|
||||||
constexpr operator std::byte() const noexcept {
|
|
||||||
/// \brief C++17 relaxed enum class initialization rules.
|
|
||||||
/// \see https://en.cppreference.com/w/cpp/language/enum#enum_relaxed_init_cpp17
|
|
||||||
return std::byte{bits_};
|
|
||||||
}
|
|
||||||
#endif // LIBIPC_CPP_LIB_BYTE_
|
|
||||||
|
|
||||||
friend bool operator==(byte const &lhs, byte const &rhs) noexcept {
|
|
||||||
return lhs.bits_ == rhs.bits_;
|
|
||||||
}
|
|
||||||
|
|
||||||
friend bool operator!=(byte const &lhs, byte const &rhs) noexcept {
|
|
||||||
return !(lhs == rhs);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Non-member functions.
|
|
||||||
*/
|
|
||||||
|
|
||||||
template <typename T, typename = detail_byte::is_integral<T>>
|
|
||||||
constexpr T to_integer(byte b) noexcept {
|
|
||||||
return T(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief std::operator<<, operator>>
|
|
||||||
|
|
||||||
template <typename T, typename = detail_byte::is_integral<T>>
|
|
||||||
constexpr byte operator<<(byte b, T shift) noexcept {
|
|
||||||
return byte(to_integer<unsigned>(b) << shift);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename = detail_byte::is_integral<T>>
|
|
||||||
constexpr byte operator>>(byte b, T shift) noexcept {
|
|
||||||
return byte(to_integer<unsigned>(b) >> shift);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief std::operator<<=, operator>>=
|
|
||||||
|
|
||||||
template <typename T, typename = detail_byte::is_integral<T>>
|
|
||||||
constexpr byte &operator<<=(byte &b, T shift) noexcept {
|
|
||||||
return b = b << shift;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename = detail_byte::is_integral<T>>
|
|
||||||
constexpr byte &operator>>=(byte &b, T shift) noexcept {
|
|
||||||
return b = b >> shift;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief std::operator|, operator&, operator^, operator~
|
|
||||||
|
|
||||||
constexpr byte operator|(byte l, byte r) noexcept { return byte(to_integer<unsigned>(l) | to_integer<unsigned>(r)); }
|
|
||||||
constexpr byte operator&(byte l, byte r) noexcept { return byte(to_integer<unsigned>(l) & to_integer<unsigned>(r)); }
|
|
||||||
constexpr byte operator^(byte l, byte r) noexcept { return byte(to_integer<unsigned>(l) ^ to_integer<unsigned>(r)); }
|
|
||||||
constexpr byte operator~(byte b) noexcept { return byte(~to_integer<unsigned>(b)); }
|
|
||||||
|
|
||||||
/// \brief std::operator|=, operator&=, operator^=
|
|
||||||
|
|
||||||
constexpr byte &operator|=(byte &l, byte r) noexcept { return l = l | r; }
|
|
||||||
constexpr byte &operator&=(byte &l, byte r) noexcept { return l = l & r; }
|
|
||||||
constexpr byte &operator^=(byte &l, byte r) noexcept { return l = l ^ r; }
|
|
||||||
|
|
||||||
/// \brief Cast pointer to byte*.
|
|
||||||
|
|
||||||
template <typename T, typename = detail_byte::is_not_byte<T>>
|
|
||||||
byte *byte_cast(T *p) noexcept {
|
|
||||||
return reinterpret_cast<byte *>(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename = detail_byte::is_not_byte<T>>
|
|
||||||
byte const *byte_cast(T const *p) noexcept {
|
|
||||||
return reinterpret_cast<byte const *>(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief Cast byte* to a pointer of another type.
|
|
||||||
|
|
||||||
template <typename T, typename = detail_byte::is_not_byte<T>>
|
|
||||||
T *byte_cast(byte *p) noexcept {
|
|
||||||
if (reinterpret_cast<std::size_t>(p) % alignof(T) != 0) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return reinterpret_cast<T *>(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename U = typename std::add_const<T>::type,
|
|
||||||
typename = detail_byte::is_not_byte<T>>
|
|
||||||
U *byte_cast(byte const *p) noexcept {
|
|
||||||
if (reinterpret_cast<std::size_t>(p) % alignof(T) != 0) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return reinterpret_cast<U *>(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief Converts a span into a view of its underlying bytes.
|
|
||||||
/// \see https://en.cppreference.com/w/cpp/container/span/as_bytes
|
|
||||||
|
|
||||||
template <typename T,
|
|
||||||
typename Byte = typename std::conditional<std::is_const<T>::value, byte const, byte>::type>
|
|
||||||
auto as_bytes(span<T> s) noexcept -> span<Byte> {
|
|
||||||
return {byte_cast(s.data()), s.size_bytes()};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief Custom defined fmt_to method for imp::fmt
|
|
||||||
namespace detail_tag_invoke {
|
|
||||||
|
|
||||||
inline bool tag_invoke(decltype(ipc::fmt_to), fmt_context &ctx, ipc::byte b) {
|
|
||||||
return ipc::to_string(ctx, static_cast<std::uint8_t>(b), "02x");
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T,
|
|
||||||
typename = std::enable_if_t<std::is_same<std::decay_t<T>, ipc::byte>::value>>
|
|
||||||
bool tag_invoke(decltype(ipc::fmt_to), fmt_context &ctx, fmt_ref<T> arg) noexcept {
|
|
||||||
return ipc::to_string(ctx, static_cast<std::uint8_t>(arg.param), arg.fstr);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace detail_tag_invoke
|
|
||||||
} // namespace ipc
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
/**
|
|
||||||
* \file libipc/codecvt.h
|
|
||||||
* \author mutouyun (orz@orzz.org)
|
|
||||||
* \brief Character set conversion interface.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <cstddef>
|
|
||||||
|
|
||||||
#include "libipc/imp/export.h"
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief The transform between UTF-8/16/32
|
|
||||||
*
|
|
||||||
* \param des The target string pointer can be nullptr
|
|
||||||
* \param dlen The target string length can be 0
|
|
||||||
*/
|
|
||||||
template <typename CharT, typename CharU>
|
|
||||||
LIBIPC_EXPORT std::size_t cvt_cstr(CharT const *src, std::size_t slen, CharU *des, std::size_t dlen) noexcept;
|
|
||||||
|
|
||||||
template <typename CharT, typename TraitsT, typename AllocT,
|
|
||||||
typename CharU, typename TraitsU, typename AllocU>
|
|
||||||
void cvt_sstr(std::basic_string<CharT, TraitsT, AllocT> const &src, std::basic_string<CharU, TraitsU, AllocU> &des) {
|
|
||||||
std::size_t dlen = cvt_cstr(src.c_str(), src.size(), (CharU *)nullptr, 0);
|
|
||||||
if (dlen == 0) {
|
|
||||||
des.clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
des.resize(dlen);
|
|
||||||
cvt_cstr(src.c_str(), src.size(), &des[0], des.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ipc
|
|
||||||
@ -1,228 +0,0 @@
|
|||||||
/**
|
|
||||||
* \file libipc/detect_plat.h
|
|
||||||
* \author mutouyun (orz@orzz.org)
|
|
||||||
* \brief Define platform detection related interfaces.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
/// \brief OS check.
|
|
||||||
|
|
||||||
#if defined(WINCE) || defined(_WIN32_WCE)
|
|
||||||
# define LIBIPC_OS_WINCE
|
|
||||||
#elif defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || \
|
|
||||||
(defined(__x86_64) && defined(__MSYS__))
|
|
||||||
#define LIBIPC_OS_WIN64
|
|
||||||
#elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || \
|
|
||||||
defined(__NT__) || defined(__MSYS__)
|
|
||||||
# define LIBIPC_OS_WIN32
|
|
||||||
#elif defined(__FreeBSD__)
|
|
||||||
# define LIBIPC_OS_FREEBSD
|
|
||||||
#elif defined(__QNX__) || defined(__QNXNTO__)
|
|
||||||
# define LIBIPC_OS_QNX
|
|
||||||
#elif defined(__APPLE__)
|
|
||||||
# define LIBIPC_OS_APPLE
|
|
||||||
#elif defined(ANDROID) || defined(__ANDROID__)
|
|
||||||
# define LIBIPC_OS_ANDROID
|
|
||||||
#elif defined(__linux__) || defined(__linux)
|
|
||||||
# define LIBIPC_OS_LINUX
|
|
||||||
#elif defined(_POSIX_VERSION)
|
|
||||||
# define LIBIPC_OS_POSIX
|
|
||||||
#else
|
|
||||||
# error "This OS is unsupported."
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(LIBIPC_OS_WIN32) || defined(LIBIPC_OS_WIN64) || \
|
|
||||||
defined(LIBIPC_OS_WINCE)
|
|
||||||
# define LIBIPC_OS_WIN
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// \brief Compiler check.
|
|
||||||
|
|
||||||
#if defined(_MSC_VER)
|
|
||||||
# define LIBIPC_CC_MSVC _MSC_VER
|
|
||||||
# define LIBIPC_CC_MSVC_2015 1900
|
|
||||||
# define LIBIPC_CC_MSVC_2017 1910
|
|
||||||
# define LIBIPC_CC_MSVC_2019 1920
|
|
||||||
# define LIBIPC_CC_MSVC_2022 1930
|
|
||||||
#elif defined(__GNUC__)
|
|
||||||
# define LIBIPC_CC_GNUC __GNUC__
|
|
||||||
# if defined(__clang__)
|
|
||||||
# define LIBIPC_CC_CLANG
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
# error "This compiler is unsupported."
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// \brief Instruction set.
|
|
||||||
/// \see https://sourceforge.net/p/predef/wiki/Architectures/
|
|
||||||
|
|
||||||
#if defined(_M_X64) || defined(_M_AMD64) || \
|
|
||||||
defined(__x86_64__) || defined(__x86_64) || \
|
|
||||||
defined(__amd64__) || defined(__amd64)
|
|
||||||
# define LIBIPC_INSTR_X64
|
|
||||||
#elif defined(_M_IA64) || defined(__IA64__) || defined(_IA64) || \
|
|
||||||
defined(__ia64__) || defined(__ia64)
|
|
||||||
# define LIBIPC_INSTR_I64
|
|
||||||
#elif defined(_M_IX86) || defined(_X86_) || defined(__i386__) || defined(__i386)
|
|
||||||
# define LIBIPC_INSTR_X86
|
|
||||||
#elif defined(_M_ARM64) || defined(__arm64__) || defined(__aarch64__)
|
|
||||||
# define LIBIPC_INSTR_ARM64
|
|
||||||
#elif defined(_M_ARM) || defined(_ARM) || defined(__arm__) || defined(__arm)
|
|
||||||
# define LIBIPC_INSTR_ARM32
|
|
||||||
#else
|
|
||||||
# error "This instruction set is unsupported."
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(LIBIPC_INSTR_X86) || defined(LIBIPC_INSTR_X64)
|
|
||||||
# define LIBIPC_INSTR_X86_64
|
|
||||||
#elif defined(LIBIPC_INSTR_ARM32) || defined(LIBIPC_INSTR_ARM64)
|
|
||||||
# define LIBIPC_INSTR_ARM
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// \brief Byte order.
|
|
||||||
|
|
||||||
#if defined(__BYTE_ORDER__)
|
|
||||||
# define LIBIPC_ENDIAN_BIG (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
|
|
||||||
# define LIBIPC_ENDIAN_LIT (!LIBIPC_ENDIAN_BIG)
|
|
||||||
#else
|
|
||||||
# define LIBIPC_ENDIAN_BIG (0)
|
|
||||||
# define LIBIPC_ENDIAN_LIT (1)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// \brief C++ version.
|
|
||||||
|
|
||||||
#if (__cplusplus >= 202002L) && !defined(LIBIPC_CPP_20)
|
|
||||||
# define LIBIPC_CPP_20
|
|
||||||
#endif
|
|
||||||
#if (__cplusplus >= 201703L) && !defined(LIBIPC_CPP_17)
|
|
||||||
# define LIBIPC_CPP_17
|
|
||||||
#endif
|
|
||||||
#if /*(__cplusplus >= 201402L) &&*/ !defined(LIBIPC_CPP_14)
|
|
||||||
# define LIBIPC_CPP_14
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(LIBIPC_CPP_20) && \
|
|
||||||
!defined(LIBIPC_CPP_17) && \
|
|
||||||
!defined(LIBIPC_CPP_14)
|
|
||||||
# error "This C++ version is unsupported."
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// \brief Feature cross-platform adaptation.
|
|
||||||
|
|
||||||
#if defined(LIBIPC_CPP_17)
|
|
||||||
# define LIBIPC_INLINE_CONSTEXPR inline constexpr
|
|
||||||
#else
|
|
||||||
# define LIBIPC_INLINE_CONSTEXPR constexpr
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// \brief C++ attributes.
|
|
||||||
/// \see https://en.cppreference.com/w/cpp/language/attributes
|
|
||||||
|
|
||||||
#if defined(__has_cpp_attribute)
|
|
||||||
# if __has_cpp_attribute(fallthrough)
|
|
||||||
# define LIBIPC_FALLTHROUGH [[fallthrough]]
|
|
||||||
# endif
|
|
||||||
# if __has_cpp_attribute(maybe_unused)
|
|
||||||
# define LIBIPC_UNUSED [[maybe_unused]]
|
|
||||||
# endif
|
|
||||||
# if __has_cpp_attribute(likely)
|
|
||||||
# define LIBIPC_LIKELY(...) (__VA_ARGS__) [[likely]]
|
|
||||||
# endif
|
|
||||||
# if __has_cpp_attribute(unlikely)
|
|
||||||
# define LIBIPC_UNLIKELY(...) (__VA_ARGS__) [[unlikely]]
|
|
||||||
# endif
|
|
||||||
# if __has_cpp_attribute(nodiscard)
|
|
||||||
# define LIBIPC_NODISCARD [[nodiscard]]
|
|
||||||
# endif
|
|
||||||
# if __has_cpp_attribute(assume)
|
|
||||||
# define LIBIPC_ASSUME(...) [[assume(__VA_ARGS__)]]
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(LIBIPC_FALLTHROUGH)
|
|
||||||
# if defined(LIBIPC_CC_GNUC)
|
|
||||||
# define LIBIPC_FALLTHROUGH __attribute__((__fallthrough__))
|
|
||||||
# else
|
|
||||||
# define LIBIPC_FALLTHROUGH
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(LIBIPC_UNUSED)
|
|
||||||
# if defined(LIBIPC_CC_GNUC)
|
|
||||||
# define LIBIPC_UNUSED __attribute__((__unused__))
|
|
||||||
# elif defined(LIBIPC_CC_MSVC)
|
|
||||||
# define LIBIPC_UNUSED __pragma(warning(suppress: 4100 4101 4189))
|
|
||||||
# else
|
|
||||||
# define LIBIPC_UNUSED
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(LIBIPC_LIKELY)
|
|
||||||
# if defined(__has_builtin)
|
|
||||||
# if __has_builtin(__builtin_expect)
|
|
||||||
# define LIBIPC_LIKELY(...) (__builtin_expect(!!(__VA_ARGS__), 1))
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(LIBIPC_LIKELY)
|
|
||||||
# define LIBIPC_LIKELY(...) (__VA_ARGS__)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(LIBIPC_UNLIKELY)
|
|
||||||
# if defined(__has_builtin)
|
|
||||||
# if __has_builtin(__builtin_expect)
|
|
||||||
# define LIBIPC_UNLIKELY(...) (__builtin_expect(!!(__VA_ARGS__), 0))
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(LIBIPC_UNLIKELY)
|
|
||||||
# define LIBIPC_UNLIKELY(...) (__VA_ARGS__)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(LIBIPC_NODISCARD)
|
|
||||||
/// \see https://stackoverflow.com/questions/4226308/msvc-equivalent-of-attribute-warn-unused-result
|
|
||||||
# if defined(LIBIPC_CC_GNUC) && (LIBIPC_CC_GNUC >= 4)
|
|
||||||
# define LIBIPC_NODISCARD __attribute__((warn_unused_result))
|
|
||||||
# elif defined(LIBIPC_CC_MSVC) && (LIBIPC_CC_MSVC >= 1700)
|
|
||||||
# define LIBIPC_NODISCARD _Check_return_
|
|
||||||
# else
|
|
||||||
# define LIBIPC_NODISCARD
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(LIBIPC_ASSUME)
|
|
||||||
# if defined(__has_builtin)
|
|
||||||
# if __has_builtin(__builtin_assume)
|
|
||||||
/// \see https://clang.llvm.org/docs/LanguageExtensions.html#langext-builtin-assume
|
|
||||||
# define LIBIPC_ASSUME(...) __builtin_assume(__VA_ARGS__)
|
|
||||||
# elif __has_builtin(__builtin_unreachable)
|
|
||||||
/// \see https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#index-_005f_005fbuiltin_005funreachable
|
|
||||||
# define LIBIPC_ASSUME(...) do { if (!(__VA_ARGS__)) __builtin_unreachable(); } while (false)
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(LIBIPC_ASSUME)
|
|
||||||
# if defined(LIBIPC_CC_MSVC)
|
|
||||||
/// \see https://learn.microsoft.com/en-us/cpp/intrinsics/assume?view=msvc-140
|
|
||||||
# define LIBIPC_ASSUME(...) __assume(__VA_ARGS__)
|
|
||||||
# else
|
|
||||||
# define LIBIPC_ASSUME(...)
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// \see https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_exceptions.html
|
|
||||||
/// https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros
|
|
||||||
/// https://stackoverflow.com/questions/6487013/programmatically-determine-whether-exceptions-are-enabled
|
|
||||||
#if defined(__cpp_exceptions) && __cpp_exceptions || \
|
|
||||||
defined(__EXCEPTIONS) || defined(_CPPUNWIND)
|
|
||||||
# define LIBIPC_TRY try
|
|
||||||
# define LIBIPC_CATCH(...) catch (__VA_ARGS__)
|
|
||||||
# define LIBIPC_THROW($EXCEPTION, ...) throw $EXCEPTION
|
|
||||||
#else
|
|
||||||
# define LIBIPC_TRY if (true)
|
|
||||||
# define LIBIPC_CATCH(...) else if (false)
|
|
||||||
# define LIBIPC_THROW($EXCEPTION, ...) return __VA_ARGS__
|
|
||||||
#endif
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
/**
|
|
||||||
* \file libipc/error.h
|
|
||||||
* \author mutouyun (orz@orzz.org)
|
|
||||||
* \brief A platform-dependent error code.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <system_error>
|
|
||||||
#include <string>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#include "libipc/imp/export.h"
|
|
||||||
#include "libipc/imp/fmt_cpo.h"
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Custom defined fmt_to method for imp::fmt
|
|
||||||
*/
|
|
||||||
namespace detail_tag_invoke {
|
|
||||||
|
|
||||||
inline bool tag_invoke(decltype(ipc::fmt_to), fmt_context &ctx, std::error_code const &ec) noexcept {
|
|
||||||
return fmt_to(ctx, '[', ec.value(), ": ", ec.message(), ']');
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace detail_tag_invoke
|
|
||||||
} // namespace ipc
|
|
||||||
@ -1,392 +0,0 @@
|
|||||||
/**
|
|
||||||
* \file libipc/expected.h
|
|
||||||
* \author mutouyun (orz@orzz.org)
|
|
||||||
* \brief Provides a way to store either of two values.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <type_traits>
|
|
||||||
#include <utility> // std::exchange
|
|
||||||
#include <array>
|
|
||||||
#include <memory> // std::addressof
|
|
||||||
#include <cstddef> // std::nullptr_t
|
|
||||||
|
|
||||||
#include "libipc/imp/uninitialized.h"
|
|
||||||
#include "libipc/imp/generic.h"
|
|
||||||
#include "libipc/imp/byte.h"
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief In-place construction tag for unexpected value in expected.
|
|
||||||
* \see https://en.cppreference.com/w/cpp/utility/expected/unexpect_t
|
|
||||||
*/
|
|
||||||
struct unexpected_t {
|
|
||||||
explicit unexpected_t() = default;
|
|
||||||
};
|
|
||||||
constexpr unexpected_t unexpected{};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \class template <typename T, typename E> expected
|
|
||||||
* \brief Provides a way to store either of two values.
|
|
||||||
* \see https://en.cppreference.com/w/cpp/utility/expected
|
|
||||||
* \tparam T - the type of the expected value.
|
|
||||||
* \tparam E - the type of the unexpected value.
|
|
||||||
*/
|
|
||||||
template <typename T, typename E>
|
|
||||||
class expected;
|
|
||||||
|
|
||||||
namespace detail_expected {
|
|
||||||
|
|
||||||
template <typename T, typename E>
|
|
||||||
struct data_union {
|
|
||||||
using const_value_t = typename std::add_const<T>::type;
|
|
||||||
using const_error_t = typename std::add_const<E>::type;
|
|
||||||
|
|
||||||
union {
|
|
||||||
T value_; ///< the expected value
|
|
||||||
E error_; ///< the unexpected value
|
|
||||||
};
|
|
||||||
|
|
||||||
data_union(data_union const &) = delete;
|
|
||||||
data_union &operator=(data_union const &) = delete;
|
|
||||||
|
|
||||||
data_union(std::nullptr_t) noexcept {}
|
|
||||||
~data_union() {}
|
|
||||||
|
|
||||||
template <typename... A>
|
|
||||||
data_union(in_place_t, A &&...args) : value_{std::forward<A>(args)...} {}
|
|
||||||
template <typename... A>
|
|
||||||
data_union(unexpected_t, A &&...args) : error_{std::forward<A>(args)...} {}
|
|
||||||
|
|
||||||
void destruct_value() noexcept { destroy(&value_); }
|
|
||||||
void destruct_error() noexcept { destroy(&error_); }
|
|
||||||
|
|
||||||
const_value_t & value() const & noexcept { return value_; }
|
|
||||||
T & value() & noexcept { return value_; }
|
|
||||||
const_value_t &&value() const && noexcept { return std::move(value_); }
|
|
||||||
T && value() && noexcept { return std::move(value_); }
|
|
||||||
|
|
||||||
const_error_t & error() const & noexcept { return error_; }
|
|
||||||
E & error() & noexcept { return error_; }
|
|
||||||
const_error_t &&error() const && noexcept { return std::move(error_); }
|
|
||||||
E && error() && noexcept { return std::move(error_); }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename E>
|
|
||||||
struct data_union<void, E> {
|
|
||||||
using const_error_t = typename std::add_const<E>::type;
|
|
||||||
|
|
||||||
alignas(E) std::array<byte, sizeof(E)> error_; ///< the unexpected value
|
|
||||||
|
|
||||||
data_union(data_union const &) = delete;
|
|
||||||
data_union &operator=(data_union const &) = delete;
|
|
||||||
|
|
||||||
data_union(std::nullptr_t) noexcept {}
|
|
||||||
|
|
||||||
template <typename... A>
|
|
||||||
data_union(in_place_t, A &&...) noexcept {}
|
|
||||||
template <typename... A>
|
|
||||||
data_union(unexpected_t, A &&...args) {
|
|
||||||
construct<E>(&error_, std::forward<A>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
void destruct_value() noexcept {}
|
|
||||||
void destruct_error() noexcept { destroy(reinterpret_cast<E *>(&error_)); }
|
|
||||||
|
|
||||||
const_error_t & error() const & noexcept { return *reinterpret_cast<E *>(error_.data()); }
|
|
||||||
E & error() & noexcept { return *reinterpret_cast<E *>(error_.data()); }
|
|
||||||
const_error_t &&error() const && noexcept { return std::move(*reinterpret_cast<E *>(error_.data())); }
|
|
||||||
E && error() && noexcept { return std::move(*reinterpret_cast<E *>(error_.data())); }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T, typename E>
|
|
||||||
auto destruct(bool /*has_value*/, data_union<T, E> &/*data*/) noexcept
|
|
||||||
-> typename std::enable_if<std::is_trivially_destructible<T>::value &&
|
|
||||||
std::is_trivially_destructible<E>::value>::type {
|
|
||||||
// Do nothing.
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename E>
|
|
||||||
auto destruct(bool has_value, data_union<T, E> &data) noexcept
|
|
||||||
-> typename std::enable_if<!std::is_trivially_destructible<T>::value &&
|
|
||||||
std::is_trivially_destructible<E>::value>::type {
|
|
||||||
if (has_value) data.destruct_value();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename E>
|
|
||||||
auto destruct(bool has_value, data_union<T, E> &data) noexcept
|
|
||||||
-> typename std::enable_if< std::is_trivially_destructible<T>::value &&
|
|
||||||
!std::is_trivially_destructible<E>::value>::type {
|
|
||||||
if (!has_value) data.destruct_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename E>
|
|
||||||
auto destruct(bool has_value, data_union<T, E> &data) noexcept
|
|
||||||
-> typename std::enable_if<!std::is_trivially_destructible<T>::value &&
|
|
||||||
!std::is_trivially_destructible<E>::value>::type {
|
|
||||||
if (has_value) {
|
|
||||||
data.destruct_value();
|
|
||||||
} else {
|
|
||||||
data.destruct_error();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename S, typename T, typename E>
|
|
||||||
struct value_getter : data_union<T, E> {
|
|
||||||
using data_union<T, E>::data_union;
|
|
||||||
|
|
||||||
template <typename U>
|
|
||||||
value_getter(U &&other) : data_union<T, E>(nullptr) {
|
|
||||||
if (other) {
|
|
||||||
construct<data_union<T, E>>(this, in_place, std::forward<U>(other).value());
|
|
||||||
} else {
|
|
||||||
construct<data_union<T, E>>(this, unexpected, std::forward<U>(other).error());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
T const *operator->() const noexcept { return std::addressof(this->value()); }
|
|
||||||
T * operator->() noexcept { return std::addressof(this->value()); }
|
|
||||||
|
|
||||||
T const & operator*() const & noexcept { return this->value(); }
|
|
||||||
T & operator*() & noexcept { return this->value(); }
|
|
||||||
T const &&operator*() const && noexcept { return std::move(this->value()); }
|
|
||||||
T && operator*() && noexcept { return std::move(this->value()); }
|
|
||||||
|
|
||||||
template <typename U>
|
|
||||||
T value_or(U &&def) const & {
|
|
||||||
return bool(*static_cast<S *>(this)) ? **this : static_cast<T>(std::forward<U>(def));
|
|
||||||
}
|
|
||||||
template <typename U>
|
|
||||||
T value_or(U &&def) && {
|
|
||||||
return bool(*static_cast<S *>(this)) ? std::move(**this) : static_cast<T>(std::forward<U>(def));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename... A>
|
|
||||||
T &emplace(A &&...args) {
|
|
||||||
static_cast<S *>(this)->reconstruct(in_place, std::forward<A>(args)...);
|
|
||||||
return this->value();
|
|
||||||
}
|
|
||||||
|
|
||||||
void swap(S &other) {
|
|
||||||
if (bool(*static_cast<S *>(this)) && bool(other)) {
|
|
||||||
std::swap(this->value(), other.value());
|
|
||||||
} else if (!*static_cast<S *>(this) && !other) {
|
|
||||||
std::swap(this->error(), other.error());
|
|
||||||
} else if (!*static_cast<S *>(this) && bool(other)) {
|
|
||||||
E err(std::move(this->error()));
|
|
||||||
this->emplace(std::move(other.value()));
|
|
||||||
other.reconstruct(unexpected, std::move(err));
|
|
||||||
} else /*if (bool(*this) && !other)*/ {
|
|
||||||
E err(std::move(other.error()));
|
|
||||||
other.emplace(std::move(this->value()));
|
|
||||||
static_cast<S *>(this)->reconstruct(unexpected, std::move(err));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename S, typename E>
|
|
||||||
struct value_getter<S, void, E> : data_union<void, E> {
|
|
||||||
using data_union<void, E>::data_union;
|
|
||||||
|
|
||||||
template <typename U>
|
|
||||||
value_getter(U &&other) : data_union<void, E>(nullptr) {
|
|
||||||
if (other) {
|
|
||||||
construct<data_union<void, E>>(this, in_place);
|
|
||||||
} else {
|
|
||||||
construct<data_union<void, E>>(this, unexpected, std::forward<U>(other).error());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void emplace() noexcept {
|
|
||||||
static_cast<S *>(this)->reconstruct(in_place);
|
|
||||||
}
|
|
||||||
|
|
||||||
void swap(S &other) {
|
|
||||||
if (bool(*static_cast<S *>(this)) && bool(other)) {
|
|
||||||
return;
|
|
||||||
} else if (!*static_cast<S *>(this) && !other) {
|
|
||||||
std::swap(this->error(), other.error());
|
|
||||||
} else if (!*static_cast<S *>(this) && bool(other)) {
|
|
||||||
E err(std::move(this->error()));
|
|
||||||
this->emplace();
|
|
||||||
other.reconstruct(unexpected, std::move(err));
|
|
||||||
} else /*if (bool(*this) && !other)*/ {
|
|
||||||
E err(std::move(other.error()));
|
|
||||||
other.emplace();
|
|
||||||
static_cast<S *>(this)->reconstruct(unexpected, std::move(err));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Define the expected storage.
|
|
||||||
*/
|
|
||||||
template <typename T, typename E>
|
|
||||||
struct storage : value_getter<storage<T, E>, T, E> {
|
|
||||||
using getter_t = value_getter<storage<T, E>, T, E>;
|
|
||||||
|
|
||||||
bool has_value_;
|
|
||||||
|
|
||||||
template <typename... A>
|
|
||||||
storage(in_place_t, A &&...args)
|
|
||||||
: getter_t(in_place, std::forward<A>(args)...)
|
|
||||||
, has_value_(true) {}
|
|
||||||
|
|
||||||
template <typename... A>
|
|
||||||
storage(unexpected_t, A &&...args)
|
|
||||||
: getter_t(unexpected, std::forward<A>(args)...)
|
|
||||||
, has_value_(false) {}
|
|
||||||
|
|
||||||
storage(storage const &other)
|
|
||||||
: getter_t(other)
|
|
||||||
, has_value_(other.has_value_) {}
|
|
||||||
|
|
||||||
storage(storage &&other)
|
|
||||||
: getter_t(std::move(other))
|
|
||||||
/// After construction, has_value() is equal to other.has_value().
|
|
||||||
, has_value_(other.has_value_) {}
|
|
||||||
|
|
||||||
template <typename T_, typename E_>
|
|
||||||
storage(storage<T_, E_> const &other)
|
|
||||||
: getter_t(other)
|
|
||||||
, has_value_(other.has_value_) {}
|
|
||||||
|
|
||||||
template <typename T_, typename E_>
|
|
||||||
storage(storage<T_, E_> &&other)
|
|
||||||
: getter_t(std::move(other))
|
|
||||||
/// After construction, has_value() is equal to other.has_value().
|
|
||||||
, has_value_(other.has_value_) {}
|
|
||||||
|
|
||||||
bool has_value() const noexcept {
|
|
||||||
return has_value_;
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit operator bool() const noexcept {
|
|
||||||
return this->has_value();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
friend getter_t;
|
|
||||||
|
|
||||||
template <typename... A>
|
|
||||||
void reconstruct(A &&...args) {
|
|
||||||
destroy(this);
|
|
||||||
construct<storage>(this, std::forward<A>(args)...);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// \brief The invoke forwarding helper.
|
|
||||||
|
|
||||||
template <typename F, typename... A>
|
|
||||||
auto invoke(F &&f, A &&...args) noexcept(
|
|
||||||
noexcept(std::forward<F>(f)(std::forward<A>(args)...)))
|
|
||||||
-> decltype(std::forward<F>(f)(std::forward<A>(args)...)) {
|
|
||||||
return std::forward<F>(f)(std::forward<A>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename F, typename... A>
|
|
||||||
auto invoke(F &&f, A &&...args) noexcept(
|
|
||||||
noexcept(std::forward<F>(f)()))
|
|
||||||
-> decltype(std::forward<F>(f)()) {
|
|
||||||
return std::forward<F>(f)();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief and_then helper.
|
|
||||||
|
|
||||||
template <typename E, typename F,
|
|
||||||
typename R = decltype(invoke(std::declval<F>(), *std::declval<E>()))>
|
|
||||||
R and_then(E &&exp, F &&f) {
|
|
||||||
static_assert(is_specialized<expected, R>::value, "F must return an `expected`.");
|
|
||||||
return bool(exp) ? invoke(std::forward<F>(f), *std::forward<E>(exp))
|
|
||||||
: R(unexpected, std::forward<E>(exp).error());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief or_else helper.
|
|
||||||
|
|
||||||
template <typename E, typename F,
|
|
||||||
typename R = decltype(invoke(std::declval<F>(), std::declval<E>().error()))>
|
|
||||||
R or_else(E &&exp, F &&f) {
|
|
||||||
static_assert(is_specialized<expected, R>::value, "F must return an `expected`.");
|
|
||||||
return bool(exp) ? std::forward<E>(exp)
|
|
||||||
: invoke(std::forward<F>(f), std::forward<E>(exp).error());
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace detail_expected
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \class template <typename T, typename E> expected
|
|
||||||
* \brief Provides a way to store either of two values.
|
|
||||||
*/
|
|
||||||
template <typename T, typename E>
|
|
||||||
class expected : public detail_expected::storage<typename std::remove_cv<T>::type, E> {
|
|
||||||
public:
|
|
||||||
using value_type = typename std::remove_cv<T>::type;
|
|
||||||
using error_type = E;
|
|
||||||
|
|
||||||
using detail_expected::storage<value_type, E>::storage;
|
|
||||||
|
|
||||||
expected(expected const &) = default;
|
|
||||||
expected(expected &&) = default;
|
|
||||||
|
|
||||||
expected()
|
|
||||||
: detail_expected::storage<value_type, E>(in_place) {}
|
|
||||||
|
|
||||||
expected &operator=(expected other) {
|
|
||||||
this->swap(other);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Monadic operations
|
|
||||||
|
|
||||||
template <typename F>
|
|
||||||
auto and_then(F &&f) & {
|
|
||||||
return detail_expected::and_then(*this, std::forward<F>(f));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename F>
|
|
||||||
auto and_then(F &&f) const & {
|
|
||||||
return detail_expected::and_then(*this, std::forward<F>(f));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename F>
|
|
||||||
auto and_then(F &&f) && {
|
|
||||||
return detail_expected::and_then(std::move(*this), std::forward<F>(f));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename F>
|
|
||||||
auto or_else(F &&f) & {
|
|
||||||
return detail_expected::or_else(*this, std::forward<F>(f));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename F>
|
|
||||||
auto or_else(F &&f) const & {
|
|
||||||
return detail_expected::or_else(*this, std::forward<F>(f));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename F>
|
|
||||||
auto or_else(F &&f) && {
|
|
||||||
return detail_expected::or_else(std::move(*this), std::forward<F>(f));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Compares
|
|
||||||
|
|
||||||
template <typename T1, typename E1, typename T2, typename E2>
|
|
||||||
bool operator==(expected<T1, E1> const &lhs, expected<T2, E2> const &rhs) {
|
|
||||||
return (lhs.has_value() == rhs.has_value())
|
|
||||||
&& (lhs.has_value() ? *lhs == *rhs : lhs.error() == rhs.error());
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename E1, typename E2>
|
|
||||||
bool operator==(expected<void, E1> const &lhs, expected<void, E2> const &rhs) {
|
|
||||||
return (lhs.has_value() == rhs.has_value())
|
|
||||||
&& (lhs.has_value() || lhs.error() == rhs.error());
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T1, typename E1, typename T2, typename E2>
|
|
||||||
bool operator!=(expected<T1, E1> const &lhs, expected<T2, E2> const &rhs) {
|
|
||||||
return !(lhs == rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ipc
|
|
||||||
@ -1,45 +0,0 @@
|
|||||||
/**
|
|
||||||
* \file libipc/export.h
|
|
||||||
* \author mutouyun (orz@orzz.org)
|
|
||||||
* \brief Define the symbol export interfaces.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "libipc/imp/detect_plat.h"
|
|
||||||
|
|
||||||
#if defined(Q_DECL_EXPORT) && defined(Q_DECL_IMPORT)
|
|
||||||
|
|
||||||
# define LIBIPC_DECL_EXPORT Q_DECL_EXPORT
|
|
||||||
# define LIBIPC_DECL_IMPORT Q_DECL_IMPORT
|
|
||||||
|
|
||||||
#else // defined(Q_DECL_EXPORT) && defined(Q_DECL_IMPORT)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Compiler & system detection for LIBIPC_DECL_EXPORT & LIBIPC_DECL_IMPORT.
|
|
||||||
* Not using QtCore cause it shouldn't depend on Qt.
|
|
||||||
*/
|
|
||||||
# if defined(LIBIPC_CC_MSVC) || defined(LIBIPC_OS_WIN)
|
|
||||||
# define LIBIPC_DECL_EXPORT __declspec(dllexport)
|
|
||||||
# define LIBIPC_DECL_IMPORT __declspec(dllimport)
|
|
||||||
# elif defined(LIBIPC_OS_ANDROID) || defined(LIBIPC_OS_LINUX) || defined(LIBIPC_CC_GNUC)
|
|
||||||
# define LIBIPC_DECL_EXPORT __attribute__((visibility("default")))
|
|
||||||
# define LIBIPC_DECL_IMPORT __attribute__((visibility("default")))
|
|
||||||
# else
|
|
||||||
# define LIBIPC_DECL_EXPORT __attribute__((visibility("default")))
|
|
||||||
# define LIBIPC_DECL_IMPORT __attribute__((visibility("default")))
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#endif // defined(Q_DECL_EXPORT) && defined(Q_DECL_IMPORT)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Define LIBIPC_EXPORT for exporting function & class.
|
|
||||||
*/
|
|
||||||
#ifndef LIBIPC_EXPORT
|
|
||||||
# if defined(LIBIPC_LIBRARY_SHARED_BUILDING__)
|
|
||||||
# define LIBIPC_EXPORT LIBIPC_DECL_EXPORT
|
|
||||||
# elif defined(LIBIPC_LIBRARY_SHARED_USING__)
|
|
||||||
# define LIBIPC_EXPORT LIBIPC_DECL_IMPORT
|
|
||||||
# else
|
|
||||||
# define LIBIPC_EXPORT
|
|
||||||
# endif
|
|
||||||
#endif /*LIBIPC_EXPORT*/
|
|
||||||
@ -1,177 +0,0 @@
|
|||||||
/**
|
|
||||||
* \file libipc/fmt.h
|
|
||||||
* \author mutouyun (orz@orzz.org)
|
|
||||||
* \brief String formatting.
|
|
||||||
*
|
|
||||||
* \remarks The current performance is not high,
|
|
||||||
* because I use std::sprintf directly for formatting for convenience.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <chrono> // std::chrono::time_point
|
|
||||||
#include <tuple>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <ctime> // std::tm, std::localtime
|
|
||||||
|
|
||||||
#include "libipc/imp/fmt_cpo.h"
|
|
||||||
#include "libipc/imp/span.h"
|
|
||||||
#include "libipc/imp/detect_plat.h"
|
|
||||||
#include "libipc/imp/export.h"
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief The format string reference wrapper.
|
|
||||||
*/
|
|
||||||
template <typename T>
|
|
||||||
struct fmt_ref {
|
|
||||||
span<char const> fstr;
|
|
||||||
T param;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Conversion specifiers.
|
|
||||||
*
|
|
||||||
* \remarks Just like printf, the format string is of the form
|
|
||||||
* [flags][field_width][.precision][conversion_character]
|
|
||||||
*
|
|
||||||
* \see http://personal.ee.surrey.ac.uk/Personal/R.Bowden/C/printf.html
|
|
||||||
*/
|
|
||||||
template <std::size_t N>
|
|
||||||
auto spec(char const (&fstr)[N]) noexcept {
|
|
||||||
return [&fstr](auto &&arg) noexcept {
|
|
||||||
using arg_t = decltype(arg);
|
|
||||||
return fmt_ref<arg_t> {{fstr}, static_cast<arg_t>(arg)};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief String formatting function.
|
|
||||||
*
|
|
||||||
* \param args arguments that support the fmt output
|
|
||||||
* \return an empty string if the fmt output fails
|
|
||||||
*/
|
|
||||||
template <typename... A>
|
|
||||||
LIBIPC_NODISCARD std::string fmt(A &&...args) {
|
|
||||||
std::string joined;
|
|
||||||
fmt_context ctx(joined);
|
|
||||||
if (fmt_to(ctx, std::forward<A>(args)...)) {
|
|
||||||
return ctx.finish() ? joined : "";
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief String types.
|
|
||||||
LIBIPC_EXPORT bool to_string(fmt_context &ctx, char const * a) noexcept;
|
|
||||||
LIBIPC_EXPORT bool to_string(fmt_context &ctx, std::string const &a) noexcept;
|
|
||||||
LIBIPC_EXPORT bool to_string(fmt_context &ctx, char const * a, span<char const> fstr) noexcept;
|
|
||||||
inline bool to_string(fmt_context &ctx, std::string const &a, span<char const> fstr) noexcept { return to_string(ctx, a.c_str(), fstr); }
|
|
||||||
|
|
||||||
/// \brief Character to string conversion.
|
|
||||||
LIBIPC_EXPORT bool to_string(fmt_context &ctx, char a) noexcept;
|
|
||||||
#if defined(LIBIPC_CPP_20)
|
|
||||||
inline bool to_string(fmt_context &ctx, char8_t a) noexcept { return to_string(ctx, (char)a); }
|
|
||||||
#endif // defined(LIBIPC_CPP_20)
|
|
||||||
LIBIPC_EXPORT bool to_string(fmt_context &ctx, wchar_t a) noexcept;
|
|
||||||
LIBIPC_EXPORT bool to_string(fmt_context &ctx, char16_t a) noexcept;
|
|
||||||
LIBIPC_EXPORT bool to_string(fmt_context &ctx, char32_t a) noexcept;
|
|
||||||
|
|
||||||
/// \brief Conversion of numeric types to strings.
|
|
||||||
LIBIPC_EXPORT bool to_string(fmt_context &ctx, signed short a, span<char const> fstr = {}) noexcept;
|
|
||||||
LIBIPC_EXPORT bool to_string(fmt_context &ctx, unsigned short a, span<char const> fstr = {}) noexcept;
|
|
||||||
LIBIPC_EXPORT bool to_string(fmt_context &ctx, signed int a, span<char const> fstr = {}) noexcept;
|
|
||||||
LIBIPC_EXPORT bool to_string(fmt_context &ctx, unsigned int a, span<char const> fstr = {}) noexcept;
|
|
||||||
LIBIPC_EXPORT bool to_string(fmt_context &ctx, signed long a, span<char const> fstr = {}) noexcept;
|
|
||||||
LIBIPC_EXPORT bool to_string(fmt_context &ctx, unsigned long a, span<char const> fstr = {}) noexcept;
|
|
||||||
LIBIPC_EXPORT bool to_string(fmt_context &ctx, signed long long a, span<char const> fstr = {}) noexcept;
|
|
||||||
LIBIPC_EXPORT bool to_string(fmt_context &ctx, unsigned long long a, span<char const> fstr = {}) noexcept;
|
|
||||||
inline bool to_string(fmt_context &ctx, signed char a, span<char const> fstr = {}) noexcept { return to_string(ctx, (int)a, fstr); }
|
|
||||||
inline bool to_string(fmt_context &ctx, unsigned char a, span<char const> fstr = {}) noexcept { return to_string(ctx, (unsigned)a, fstr); }
|
|
||||||
|
|
||||||
/// \brief Conversion of floating point type to strings.
|
|
||||||
LIBIPC_EXPORT bool to_string(fmt_context &ctx, double a, span<char const> fstr = {}) noexcept;
|
|
||||||
LIBIPC_EXPORT bool to_string(fmt_context &ctx, long double a, span<char const> fstr = {}) noexcept;
|
|
||||||
inline bool to_string(fmt_context &ctx, float a, span<char const> fstr = {}) noexcept { return to_string(ctx, (double)a, fstr); }
|
|
||||||
|
|
||||||
/// \brief Pointer.
|
|
||||||
LIBIPC_EXPORT bool to_string(fmt_context &ctx, std::nullptr_t) noexcept;
|
|
||||||
LIBIPC_EXPORT bool to_string(fmt_context &ctx, void const volatile *a) noexcept;
|
|
||||||
template <typename T,
|
|
||||||
typename = std::enable_if_t<!std::is_same<T, char>::value>>
|
|
||||||
inline bool to_string(fmt_context &ctx, T const volatile *a) noexcept { return to_string(ctx, (void *)a); }
|
|
||||||
|
|
||||||
/// \brief Date and time.
|
|
||||||
LIBIPC_EXPORT bool to_string(fmt_context &ctx, std::tm const &a, span<char const> fstr = {}) noexcept;
|
|
||||||
|
|
||||||
namespace detail_fmt {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Convert std::time_t to std::string.
|
|
||||||
* \return an empty string if the conversion fails
|
|
||||||
*/
|
|
||||||
inline bool time_to_string(fmt_context &ctx, std::time_t tt, span<char const> fstr) noexcept {
|
|
||||||
#if defined(LIBIPC_CC_MSVC)
|
|
||||||
/// \see https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/localtime-s-localtime32-s-localtime64-s
|
|
||||||
std::tm tm{};
|
|
||||||
if (::localtime_s(&tm, &tt) != 0) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return to_string(ctx, tm, fstr);
|
|
||||||
#else
|
|
||||||
return to_string(ctx, *std::localtime(&tt), fstr);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace detail_fmt
|
|
||||||
|
|
||||||
template <class Clock, class Duration>
|
|
||||||
bool to_string(fmt_context &ctx, std::chrono::time_point<Clock, Duration> const &a, span<char const> fstr = {}) noexcept {
|
|
||||||
return detail_fmt::time_to_string(ctx, std::chrono::system_clock::to_time_t(a), fstr);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Predefined fmt_to method
|
|
||||||
*/
|
|
||||||
namespace detail_tag_invoke {
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
auto tag_invoke(decltype(ipc::fmt_to), fmt_context &ctx, T &&arg) noexcept
|
|
||||||
-> decltype(ipc::to_string(ctx, std::forward<T>(arg))) {
|
|
||||||
return ipc::to_string(ctx, std::forward<T>(arg));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
auto tag_invoke(decltype(ipc::fmt_to), fmt_context &ctx, fmt_ref<T> arg) noexcept
|
|
||||||
-> decltype(ipc::to_string(ctx, static_cast<T>(arg.param), arg.fstr)) {
|
|
||||||
return ipc::to_string(ctx, static_cast<T>(arg.param), arg.fstr);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool tag_invoke(decltype(ipc::fmt_to), fmt_context &ctx, span<T> s) {
|
|
||||||
if (s.empty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!fmt_to(ctx, s[0])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (std::size_t i = 1; i < s.size(); ++i) {
|
|
||||||
if (!fmt_to(ctx, ' ', s[i])) return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Tp, std::size_t... I>
|
|
||||||
bool unfold_tuple_fmt_to(fmt_context &ctx, Tp const &tp, std::index_sequence<I...>) {
|
|
||||||
return fmt_to(ctx, std::get<I>(tp)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename... T>
|
|
||||||
bool tag_invoke(decltype(ipc::fmt_to), fmt_context &ctx, std::tuple<T...> const &tp) {
|
|
||||||
return unfold_tuple_fmt_to(ctx, tp, std::index_sequence_for<T...>{});
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace detail_tag_invoke
|
|
||||||
} // namespace ipc
|
|
||||||
@ -1,66 +0,0 @@
|
|||||||
/**
|
|
||||||
* \file libipc/fmt_cpo.h
|
|
||||||
* \author mutouyun (orz@orzz.org)
|
|
||||||
* \brief String formatting CPO.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <array>
|
|
||||||
|
|
||||||
#include "libipc/imp/generic.h"
|
|
||||||
#include "libipc/imp/detect_plat.h"
|
|
||||||
#include "libipc/imp/span.h"
|
|
||||||
#include "libipc/imp/export.h"
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \class class LIBIPC_EXPORT fmt_context
|
|
||||||
* \brief The context of fmt.
|
|
||||||
*/
|
|
||||||
class LIBIPC_EXPORT fmt_context {
|
|
||||||
std::array<char, 2048U> sbuf_; ///< stack buffer
|
|
||||||
|
|
||||||
std::string &joined_;
|
|
||||||
std::size_t offset_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
fmt_context(std::string &j) noexcept;
|
|
||||||
|
|
||||||
std::size_t capacity() noexcept;
|
|
||||||
void reset() noexcept;
|
|
||||||
bool finish() noexcept;
|
|
||||||
span<char> buffer(std::size_t sz) noexcept;
|
|
||||||
void expend(std::size_t sz) noexcept;
|
|
||||||
bool append(span<char const> const &str) noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// \brief Supports custom fmt_to methods for ipc::fmt.
|
|
||||||
namespace detail_tag_invoke {
|
|
||||||
|
|
||||||
class fmt_to_t {
|
|
||||||
template <typename A1>
|
|
||||||
bool get_result(fmt_context &ctx, A1 && a1) const {
|
|
||||||
return ipc::tag_invoke(fmt_to_t{}, ctx, std::forward<A1>(a1));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename A1, typename... A>
|
|
||||||
bool get_result(fmt_context &ctx, A1 && a1, A &&...args) const {
|
|
||||||
return get_result(ctx, std::forward<A1>(a1))
|
|
||||||
&& get_result(ctx, std::forward<A>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
template <typename... A>
|
|
||||||
bool operator()(fmt_context &ctx, A &&...args) const {
|
|
||||||
return get_result(ctx, std::forward<A>(args)...);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace detail_tag_invoke
|
|
||||||
|
|
||||||
constexpr detail_tag_invoke::fmt_to_t fmt_to{};
|
|
||||||
|
|
||||||
} // namespace ipc
|
|
||||||
@ -1,301 +0,0 @@
|
|||||||
/**
|
|
||||||
* \file libipc/generic.h
|
|
||||||
* \author mutouyun (orz@orzz.org)
|
|
||||||
* \brief Tools for generic programming.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <utility>
|
|
||||||
#include <type_traits> // std::declval, std::true_type, std::false_type
|
|
||||||
#include <cstddef> // std::size_t
|
|
||||||
|
|
||||||
#include "libipc/imp/detect_plat.h"
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Utility metafunction that maps a sequence of any types to the type void
|
|
||||||
* \see https://en.cppreference.com/w/cpp/types/void_t
|
|
||||||
*/
|
|
||||||
template <typename...>
|
|
||||||
using void_t = void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief A type-list for generic programming.
|
|
||||||
*/
|
|
||||||
template <typename...>
|
|
||||||
struct types {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief To indicate that the contained object should be constructed in-place.
|
|
||||||
* \see https://en.cppreference.com/w/cpp/utility/in_place
|
|
||||||
*/
|
|
||||||
#if defined(LIBIPC_CPP_17)
|
|
||||||
using std::in_place_t;
|
|
||||||
using std::in_place;
|
|
||||||
#else /*!LIBIPC_CPP_17*/
|
|
||||||
struct in_place_t {
|
|
||||||
explicit in_place_t() = default;
|
|
||||||
};
|
|
||||||
constexpr in_place_t in_place{};
|
|
||||||
#endif/*!LIBIPC_CPP_17*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief A general pattern for supporting customisable functions
|
|
||||||
* \see https://www.open-std.org/jtc1/sc22/WG21/docs/papers/2019/p1895r0.pdf
|
|
||||||
*/
|
|
||||||
namespace detail_tag_invoke {
|
|
||||||
|
|
||||||
void tag_invoke();
|
|
||||||
|
|
||||||
struct tag_invoke_t {
|
|
||||||
template <typename T, typename... A>
|
|
||||||
constexpr auto operator()(T tag, A &&...args) const
|
|
||||||
noexcept(noexcept(tag_invoke(std::forward<T>(tag), std::forward<A>(args)...)))
|
|
||||||
-> decltype(tag_invoke(std::forward<T>(tag), std::forward<A>(args)...)) {
|
|
||||||
return tag_invoke(std::forward<T>(tag), std::forward<A>(args)...);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace detail_tag_invoke
|
|
||||||
|
|
||||||
constexpr detail_tag_invoke::tag_invoke_t tag_invoke{};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Circumventing forwarding reference may override copy and move constructs.
|
|
||||||
* \see https://mpark.github.io/programming/2014/06/07/beware-of-perfect-forwarding-constructors/
|
|
||||||
*/
|
|
||||||
namespace detail_not_match {
|
|
||||||
|
|
||||||
template <typename T, typename... A>
|
|
||||||
struct is_same_first : std::false_type {};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct is_same_first<T, T> : std::true_type {};
|
|
||||||
|
|
||||||
} // namespace detail_not_match
|
|
||||||
|
|
||||||
template <typename T, typename... A>
|
|
||||||
using not_match =
|
|
||||||
typename std::enable_if<!detail_not_match::is_same_first<T,
|
|
||||||
typename std::decay<A>::type...>::value, bool>::type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Determines whether a type is specialized from a particular template.
|
|
||||||
*/
|
|
||||||
template <template <typename...> class Tt, typename T>
|
|
||||||
struct is_specialized : std::false_type {};
|
|
||||||
|
|
||||||
template <template <typename...> class Tt, typename... A>
|
|
||||||
struct is_specialized<Tt, Tt<A...>> : std::true_type {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Copy the cv qualifier and reference of the source type to the target type.
|
|
||||||
*/
|
|
||||||
template <typename Src, typename Des>
|
|
||||||
struct copy_cvref {
|
|
||||||
using type = Des;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Src, typename Des>
|
|
||||||
struct copy_cvref<Src const, Des> {
|
|
||||||
using type = typename std::add_const<Des>::type;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Src, typename Des>
|
|
||||||
struct copy_cvref<Src volatile, Des> {
|
|
||||||
using type = typename std::add_volatile<Des>::type;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Src, typename Des>
|
|
||||||
struct copy_cvref<Src const volatile, Des> {
|
|
||||||
using type = typename std::add_cv<Des>::type;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Src, typename Des>
|
|
||||||
struct copy_cvref<Src &, Des> {
|
|
||||||
using type = typename std::add_lvalue_reference<
|
|
||||||
typename copy_cvref<Src, Des>::type>::type;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Src, typename Des>
|
|
||||||
struct copy_cvref<Src &&, Des> {
|
|
||||||
using type = typename std::add_rvalue_reference<
|
|
||||||
typename copy_cvref<Src, Des>::type>::type;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Src, typename Des>
|
|
||||||
using copy_cvref_t = typename copy_cvref<Src, Des>::type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Returns the size of the given range.
|
|
||||||
* \see https://en.cppreference.com/w/cpp/iterator/size
|
|
||||||
*/
|
|
||||||
namespace detail_countof {
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct trait_has_size {
|
|
||||||
private:
|
|
||||||
template <typename Type>
|
|
||||||
static std::true_type check(decltype(std::declval<Type>().size())*);
|
|
||||||
template <typename Type>
|
|
||||||
static std::false_type check(...);
|
|
||||||
public:
|
|
||||||
using type = decltype(check<T>(nullptr));
|
|
||||||
static constexpr auto value = type::value;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct trait_has_Size {
|
|
||||||
private:
|
|
||||||
template <typename Type>
|
|
||||||
static std::true_type check(decltype(std::declval<Type>().Size())*);
|
|
||||||
template <typename Type>
|
|
||||||
static std::false_type check(...);
|
|
||||||
public:
|
|
||||||
using type = decltype(check<T>(nullptr));
|
|
||||||
static constexpr auto value = type::value;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename C, bool = trait_has_size<C>::value
|
|
||||||
, bool = trait_has_Size<C>::value>
|
|
||||||
struct trait;
|
|
||||||
|
|
||||||
template <typename T, std::size_t N>
|
|
||||||
struct trait<T[N], false, false> {
|
|
||||||
static constexpr auto countof(T const (&)[N]) noexcept {
|
|
||||||
return N;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename C, bool B>
|
|
||||||
struct trait<C, true, B> {
|
|
||||||
static constexpr auto countof(C const &c) noexcept(noexcept(c.size())) {
|
|
||||||
return c.size();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename C>
|
|
||||||
struct trait<C, false, true> {
|
|
||||||
static constexpr auto countof(C const &c) noexcept(noexcept(c.Size())) {
|
|
||||||
return c.Size();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace detail_countof
|
|
||||||
|
|
||||||
template <typename C,
|
|
||||||
typename T = detail_countof::trait<C>,
|
|
||||||
typename R = decltype(T::countof(std::declval<C const &>()))>
|
|
||||||
constexpr R countof(C const &c) noexcept(noexcept(T::countof(c))) {
|
|
||||||
return T::countof(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Returns the data pointer of the given range.
|
|
||||||
* \see https://en.cppreference.com/w/cpp/iterator/data
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace detail_dataof {
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct trait_has_data {
|
|
||||||
private:
|
|
||||||
template <typename Type>
|
|
||||||
static std::true_type check(decltype(std::declval<Type>().data())*);
|
|
||||||
template <typename Type>
|
|
||||||
static std::false_type check(...);
|
|
||||||
public:
|
|
||||||
using type = decltype(check<T>(nullptr));
|
|
||||||
static constexpr auto value = type::value;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct trait_has_Data {
|
|
||||||
private:
|
|
||||||
template <typename Type>
|
|
||||||
static std::true_type check(decltype(std::declval<Type>().Data())*);
|
|
||||||
template <typename Type>
|
|
||||||
static std::false_type check(...);
|
|
||||||
public:
|
|
||||||
using type = decltype(check<T>(nullptr));
|
|
||||||
static constexpr auto value = type::value;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename C, bool = trait_has_data<C>::value
|
|
||||||
, bool = trait_has_Data<C>::value>
|
|
||||||
struct trait;
|
|
||||||
|
|
||||||
template <typename T, std::size_t N>
|
|
||||||
struct trait<T[N], false, false> {
|
|
||||||
static constexpr T const *dataof(T const (&arr)[N]) noexcept {
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
static constexpr T *dataof(T (&arr)[N]) noexcept {
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename C, bool B>
|
|
||||||
struct trait<C, true, B> {
|
|
||||||
template <typename T>
|
|
||||||
static constexpr auto dataof(T &&c) noexcept(noexcept(c.data())) {
|
|
||||||
return std::forward<T>(c).data();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename C>
|
|
||||||
struct trait<C, false, true> {
|
|
||||||
template <typename T>
|
|
||||||
static constexpr auto dataof(T &&c) noexcept(noexcept(c.Data())) {
|
|
||||||
return std::forward<T>(c).Data();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename C>
|
|
||||||
struct trait<C, false, false> {
|
|
||||||
template <typename T>
|
|
||||||
static constexpr T const *dataof(std::initializer_list<T> il) noexcept {
|
|
||||||
return il.begin();
|
|
||||||
}
|
|
||||||
template <typename T>
|
|
||||||
static constexpr T const *dataof(T const *p) noexcept {
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace detail_dataof
|
|
||||||
|
|
||||||
template <typename C,
|
|
||||||
typename T = detail_dataof::trait<std::remove_cv_t<std::remove_reference_t<C>>>,
|
|
||||||
typename R = decltype(T::dataof(std::declval<C>()))>
|
|
||||||
constexpr R dataof(C &&c) noexcept(noexcept(T::dataof(std::forward<C>(c)))) {
|
|
||||||
return T::dataof(std::forward<C>(c));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief Returns after converting the value to the underlying type of E.
|
|
||||||
/// \see https://en.cppreference.com/w/cpp/types/underlying_type
|
|
||||||
/// https://en.cppreference.com/w/cpp/utility/to_underlying
|
|
||||||
template <typename E>
|
|
||||||
constexpr auto underlyof(E e) noexcept {
|
|
||||||
return static_cast<std::underlying_type_t<E>>(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace detail_horrible_cast {
|
|
||||||
|
|
||||||
template <typename T, typename U>
|
|
||||||
union temp {
|
|
||||||
U in;
|
|
||||||
T out;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace detail_horrible_cast
|
|
||||||
|
|
||||||
template <typename T, typename U>
|
|
||||||
constexpr auto horrible_cast(U &&in) noexcept
|
|
||||||
-> typename std::enable_if<std::is_trivially_copyable<T>::value
|
|
||||||
&& std::is_trivially_copyable<std::decay_t<U>>::value, T>::type {
|
|
||||||
return detail_horrible_cast::temp<T, std::decay_t<U>>{std::forward<U>(in)}.out;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ipc
|
|
||||||
@ -1,195 +0,0 @@
|
|||||||
/**
|
|
||||||
* \file libipc/log.h
|
|
||||||
* \author mutouyun (orz@orzz.org)
|
|
||||||
* \brief Simple log output component.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <chrono>
|
|
||||||
#include <tuple>
|
|
||||||
#include <utility>
|
|
||||||
#include <exception>
|
|
||||||
|
|
||||||
#include "libipc/imp/detect_plat.h"
|
|
||||||
#include "libipc/imp/export.h"
|
|
||||||
#include "libipc/imp/fmt.h"
|
|
||||||
#include "libipc/imp/generic.h"
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
namespace log {
|
|
||||||
|
|
||||||
enum class level : std::int32_t {
|
|
||||||
trace,
|
|
||||||
debug,
|
|
||||||
info,
|
|
||||||
warning,
|
|
||||||
error,
|
|
||||||
failed,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// \struct template <typename... T> struct context
|
|
||||||
/// \brief Logging context.
|
|
||||||
/// \tparam ...T - a log records all parameter types passed
|
|
||||||
template <typename... T>
|
|
||||||
struct context {
|
|
||||||
log::level level;
|
|
||||||
std::chrono::system_clock::time_point tp;
|
|
||||||
char const *func;
|
|
||||||
std::tuple<T...> params;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// \brief Custom defined fmt_to method for imp::fmt
|
|
||||||
namespace detail_log {
|
|
||||||
|
|
||||||
template <typename Tp, std::size_t... I, typename... A>
|
|
||||||
bool unfold_tuple_fmt_to(fmt_context &ctx, Tp const &tp, std::index_sequence<I...>, A &&...args) {
|
|
||||||
return fmt_to(ctx, std::forward<A>(args)..., std::get<I>(tp)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace detail_log
|
|
||||||
|
|
||||||
template <typename... T>
|
|
||||||
bool context_to_string(fmt_context &f_ctx, context<T...> const &l_ctx) {
|
|
||||||
static constexpr char types[] = {
|
|
||||||
'T', 'D', 'I', 'W', 'E', 'F',
|
|
||||||
};
|
|
||||||
auto ms = std::chrono::time_point_cast<std::chrono::milliseconds>(l_ctx.tp).time_since_epoch().count() % 1000;
|
|
||||||
return detail_log::unfold_tuple_fmt_to(f_ctx, l_ctx.params, std::index_sequence_for<T...>{},
|
|
||||||
"[", types[underlyof(l_ctx.level)], "]"
|
|
||||||
"[", l_ctx.tp, ".", spec("03")(ms), "]"
|
|
||||||
"[", l_ctx.func, "] ");
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename... T>
|
|
||||||
std::string context_to_string(context<T...> const &l_ctx) {
|
|
||||||
std::string log_txt;
|
|
||||||
fmt_context f_ctx(log_txt);
|
|
||||||
LIBIPC_TRY {
|
|
||||||
if (!context_to_string(f_ctx, l_ctx)) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
f_ctx.finish();
|
|
||||||
return log_txt;
|
|
||||||
} LIBIPC_CATCH(...) {
|
|
||||||
f_ctx.finish();
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief Standard console output object.
|
|
||||||
inline auto &make_std_out() noexcept {
|
|
||||||
static auto std_out = [](auto const &ctx) {
|
|
||||||
auto s = context_to_string(ctx);
|
|
||||||
switch (ctx.level) {
|
|
||||||
case level::trace:
|
|
||||||
case level::debug:
|
|
||||||
case level::info:
|
|
||||||
std::fprintf(stdout, "%s\n", s.c_str());
|
|
||||||
break;
|
|
||||||
case level::warning:
|
|
||||||
case level::error:
|
|
||||||
case level::failed:
|
|
||||||
std::fprintf(stderr, "%s\n", s.c_str());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return std_out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief Get the exception information.
|
|
||||||
inline char const *exception_string(std::exception_ptr eptr) noexcept {
|
|
||||||
LIBIPC_TRY {
|
|
||||||
if (eptr) {
|
|
||||||
std::rethrow_exception(eptr);
|
|
||||||
}
|
|
||||||
} LIBIPC_CATCH(std::exception const &e) {
|
|
||||||
return e.what();
|
|
||||||
} LIBIPC_CATCH(...) {
|
|
||||||
return "unknown";
|
|
||||||
}
|
|
||||||
return "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief Record the last information when an exception occurs.
|
|
||||||
inline void exception_print(char const *func, std::exception_ptr eptr) noexcept {
|
|
||||||
if (func == nullptr) {
|
|
||||||
func = "-";
|
|
||||||
}
|
|
||||||
std::fprintf(stderr, "[F][%s] exception: %s\n", func, exception_string(eptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Log information base class.
|
|
||||||
*/
|
|
||||||
class logger_base {
|
|
||||||
protected:
|
|
||||||
char const *func_;
|
|
||||||
level level_limit_;
|
|
||||||
|
|
||||||
logger_base(char const *func, level level_limit) noexcept
|
|
||||||
: func_ (func)
|
|
||||||
, level_limit_(level_limit) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Log information grips.
|
|
||||||
*/
|
|
||||||
template <typename Outputer>
|
|
||||||
class logger : public logger_base {
|
|
||||||
Outputer out_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
template <typename O>
|
|
||||||
logger(char const *func, O &&out, level level_limit) noexcept
|
|
||||||
: logger_base(func, level_limit)
|
|
||||||
, out_ (std::forward<O>(out)) {}
|
|
||||||
|
|
||||||
template <typename... A>
|
|
||||||
logger const &operator()(log::level l, A &&...args) const noexcept {
|
|
||||||
if (underlyof(l) < underlyof(level_limit_)) {
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
LIBIPC_TRY {
|
|
||||||
out_(context<A &&...> {
|
|
||||||
l, std::chrono::system_clock::now(), func_,
|
|
||||||
std::forward_as_tuple(std::forward<A>(args)...),
|
|
||||||
});
|
|
||||||
} LIBIPC_CATCH(...) {
|
|
||||||
exception_print(func_, std::current_exception());
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename... A> logger const &trace (A &&...args) const noexcept { return (*this)(log::level::trace , std::forward<A>(args)...); }
|
|
||||||
template <typename... A> logger const &debug (A &&...args) const noexcept { return (*this)(log::level::debug , std::forward<A>(args)...); }
|
|
||||||
template <typename... A> logger const &info (A &&...args) const noexcept { return (*this)(log::level::info , std::forward<A>(args)...); }
|
|
||||||
template <typename... A> logger const &warning(A &&...args) const noexcept { return (*this)(log::level::warning, std::forward<A>(args)...); }
|
|
||||||
template <typename... A> logger const &error (A &&...args) const noexcept { return (*this)(log::level::error , std::forward<A>(args)...); }
|
|
||||||
template <typename... A> logger const &failed (A &&...args) const noexcept { return (*this)(log::level::failed , std::forward<A>(args)...); }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename O>
|
|
||||||
inline auto make_logger(char const *func, O &&out, level level_limit = level::info) noexcept {
|
|
||||||
return logger<std::decay_t<O>>(func, std::forward<O>(out), level_limit);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline auto make_logger(char const *func, level level_limit = level::info) noexcept {
|
|
||||||
return make_logger(func, make_std_out(), level_limit);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline auto make_logger(char const * /*ignore*/, char const *name, level level_limit = level::info) noexcept {
|
|
||||||
return make_logger(name, make_std_out(), level_limit);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define LIBIPC_LOG(...) \
|
|
||||||
auto log \
|
|
||||||
= [](auto &&...args) noexcept { \
|
|
||||||
return ::ipc::log::make_logger(__func__, std::forward<decltype(args)>(args)...); \
|
|
||||||
}(__VA_ARGS__)
|
|
||||||
|
|
||||||
} // namespace log
|
|
||||||
} // namespace ipc
|
|
||||||
@ -1,43 +0,0 @@
|
|||||||
/**
|
|
||||||
* \file libipc/nameof.h
|
|
||||||
* \author mutouyun (orz@orzz.org)
|
|
||||||
* \brief Gets the name string of a type.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <typeinfo>
|
|
||||||
#include <string>
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
#include "libipc/imp/export.h"
|
|
||||||
#include "libipc/imp/span.h"
|
|
||||||
#include "libipc/imp/detect_plat.h"
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief The conventional way to obtain demangled symbol name.
|
|
||||||
* \see https://www.boost.org/doc/libs/1_80_0/libs/core/doc/html/core/demangle.html
|
|
||||||
*
|
|
||||||
* \param name the mangled name
|
|
||||||
* \return std::string a human-readable demangled type name
|
|
||||||
*/
|
|
||||||
LIBIPC_EXPORT std::string demangle(std::string name) noexcept;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Returns an implementation defined string containing the name of the type.
|
|
||||||
* \see https://en.cppreference.com/w/cpp/types/type_info/name
|
|
||||||
*
|
|
||||||
* \tparam T a type
|
|
||||||
* \return std::string a human-readable demangled type name
|
|
||||||
*/
|
|
||||||
template <typename T>
|
|
||||||
std::string nameof() noexcept {
|
|
||||||
LIBIPC_TRY {
|
|
||||||
return demangle(typeid(T).name());
|
|
||||||
} LIBIPC_CATCH(...) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ipc
|
|
||||||
@ -1,138 +0,0 @@
|
|||||||
/**
|
|
||||||
* \file libipc/result.h
|
|
||||||
* \author mutouyun (orz@orzz.org)
|
|
||||||
* \brief Define the return value type with an error status code.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <type_traits>
|
|
||||||
#include <utility>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#include "libipc/imp/expected.h"
|
|
||||||
#include "libipc/imp/error.h"
|
|
||||||
#include "libipc/imp/generic.h"
|
|
||||||
#include "libipc/imp/fmt.h"
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
namespace detail_result {
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct generic_initializer {
|
|
||||||
using storage_t = expected<T, std::error_code>;
|
|
||||||
|
|
||||||
/// \brief Custom initialization.
|
|
||||||
static constexpr storage_t init_code() noexcept {
|
|
||||||
return {unexpected, std::error_code(-1, std::generic_category())};
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr storage_t init_code(T value) noexcept {
|
|
||||||
return {in_place, value};
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr storage_t init_code(std::error_code const &ec) noexcept {
|
|
||||||
return {unexpected, ec};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T, typename = void>
|
|
||||||
struct result_base;
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct result_base<T, std::enable_if_t<std::is_pointer<T>::value>>
|
|
||||||
: generic_initializer<T> {
|
|
||||||
|
|
||||||
using storage_t = typename generic_initializer<T>::storage_t;
|
|
||||||
using generic_initializer<T>::init_code;
|
|
||||||
|
|
||||||
static constexpr storage_t init_code(std::nullptr_t, std::error_code const &ec) noexcept {
|
|
||||||
return {unexpected, ec};
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr storage_t init_code(std::nullptr_t) noexcept {
|
|
||||||
return {unexpected, std::error_code(-1, std::generic_category())};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct result_base<T, std::enable_if_t<std::is_integral<T>::value || std::is_enum<T>::value>>
|
|
||||||
: generic_initializer<T> {
|
|
||||||
|
|
||||||
using storage_t = typename generic_initializer<T>::storage_t;
|
|
||||||
using generic_initializer<T>::init_code;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace detail_result
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \class class result
|
|
||||||
* \brief The generic wrapper for the result type.
|
|
||||||
*/
|
|
||||||
template <typename T>
|
|
||||||
class result : public detail_result::result_base<T> {
|
|
||||||
private:
|
|
||||||
using base_t = detail_result::result_base<T>;
|
|
||||||
using storage_t = typename base_t::storage_t;
|
|
||||||
|
|
||||||
storage_t ret_; ///< internal data
|
|
||||||
|
|
||||||
public:
|
|
||||||
template <typename... A,
|
|
||||||
typename = not_match<result, A...>,
|
|
||||||
typename = decltype(base_t::init_code(std::declval<A>()...))>
|
|
||||||
result(A &&...args) noexcept
|
|
||||||
: ret_(base_t::init_code(std::forward<A>(args)...)) {}
|
|
||||||
|
|
||||||
std::string format_string() const {
|
|
||||||
if LIBIPC_LIKELY(ret_) {
|
|
||||||
return fmt("value = ", ret_.value());
|
|
||||||
} else {
|
|
||||||
return fmt("error = ", ret_.error());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
T value() const noexcept { return ret_ ? ret_.value() : T{}; }
|
|
||||||
bool ok () const noexcept { return ret_.has_value(); }
|
|
||||||
std::error_code error() const noexcept { return ret_.error(); }
|
|
||||||
|
|
||||||
T operator * () const noexcept { return value(); }
|
|
||||||
explicit operator bool() const noexcept { return ok (); }
|
|
||||||
|
|
||||||
friend bool operator==(result const &lhs, result const &rhs) noexcept { return lhs.ret_ == rhs.ret_; }
|
|
||||||
friend bool operator!=(result const &lhs, result const &rhs) noexcept { return !(lhs == rhs); }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
class result<void> {
|
|
||||||
private:
|
|
||||||
std::error_code ret_; ///< internal data
|
|
||||||
|
|
||||||
public:
|
|
||||||
result() noexcept
|
|
||||||
: ret_(-1, std::generic_category()) {}
|
|
||||||
|
|
||||||
result(std::error_code const &ec) noexcept
|
|
||||||
: ret_(ec) {}
|
|
||||||
|
|
||||||
std::string format_string() const {
|
|
||||||
return fmt("error = ", error());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ok () const noexcept { return !ret_; }
|
|
||||||
std::error_code error() const noexcept { return ret_; }
|
|
||||||
explicit operator bool () const noexcept { return ok(); }
|
|
||||||
|
|
||||||
friend bool operator==(result const &lhs, result const &rhs) noexcept { return lhs.ret_ == rhs.ret_; }
|
|
||||||
friend bool operator!=(result const &lhs, result const &rhs) noexcept { return !(lhs == rhs); }
|
|
||||||
};
|
|
||||||
|
|
||||||
/// \brief Custom defined fmt_to method for imp::fmt
|
|
||||||
namespace detail_tag_invoke {
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
inline bool tag_invoke(decltype(ipc::fmt_to), fmt_context &ctx, result<T> const &r) {
|
|
||||||
return fmt_to(ctx, (r ? "succ" : "fail"), ", ", r.format_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace detail_tag_invoke
|
|
||||||
} // namespace ipc
|
|
||||||
@ -1,77 +0,0 @@
|
|||||||
/**
|
|
||||||
* \file libipc/scope_exit.h
|
|
||||||
* \author mutouyun (orz@orzz.org)
|
|
||||||
* \brief Execute guard function when the enclosing scope exits.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <utility> // std::forward, std::move
|
|
||||||
#include <functional> // std::function
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
#include "libipc/imp/detect_plat.h"
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
|
|
||||||
template <typename F = std::function<void()>>
|
|
||||||
class scope_exit {
|
|
||||||
F destructor_;
|
|
||||||
mutable bool released_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
template <typename G>
|
|
||||||
explicit scope_exit(G &&destructor) noexcept
|
|
||||||
: destructor_(std::forward<G>(destructor))
|
|
||||||
, released_ (false) {}
|
|
||||||
|
|
||||||
scope_exit(scope_exit &&other) noexcept
|
|
||||||
: destructor_(std::move(other.destructor_))
|
|
||||||
, released_ (std::exchange(other.released_, true)) /*release rhs*/ {}
|
|
||||||
|
|
||||||
scope_exit &operator=(scope_exit &&other) noexcept {
|
|
||||||
destructor_ = std::move(other.destructor_);
|
|
||||||
released_ = std::exchange(other.released_, true);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
~scope_exit() noexcept {
|
|
||||||
if (!released_) destructor_();
|
|
||||||
}
|
|
||||||
|
|
||||||
void release() const noexcept {
|
|
||||||
released_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void do_exit() noexcept {
|
|
||||||
if (released_) return;
|
|
||||||
destructor_();
|
|
||||||
released_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void swap(scope_exit &other) noexcept {
|
|
||||||
std::swap(destructor_, other.destructor_);
|
|
||||||
std::swap(released_ , other.released_);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// \brief Creates a scope_exit object.
|
|
||||||
template <typename F>
|
|
||||||
auto make_scope_exit(F &&destructor) noexcept {
|
|
||||||
return scope_exit<std::decay_t<F>>(std::forward<F>(destructor));
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace detail_scope_exit {
|
|
||||||
|
|
||||||
struct scope_exit_helper {
|
|
||||||
template <typename F>
|
|
||||||
auto operator=(F &&f) const noexcept {
|
|
||||||
return make_scope_exit(std::forward<F>(f));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace detail_scope_exit
|
|
||||||
|
|
||||||
#define LIBIPC_SCOPE_EXIT($VAL) \
|
|
||||||
LIBIPC_UNUSED auto $VAL = ::ipc::detail_scope_exit::scope_exit_helper{}
|
|
||||||
|
|
||||||
} // namespace ipc
|
|
||||||
@ -1,281 +0,0 @@
|
|||||||
/**
|
|
||||||
* \file libipc/span.h
|
|
||||||
* \author mutouyun (orz@orzz.org)
|
|
||||||
* \brief Describes an object that can refer to a contiguous sequence of objects.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <type_traits>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <iterator>
|
|
||||||
#include <array>
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
#include <limits>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <initializer_list>
|
|
||||||
|
|
||||||
#include "libipc/imp/detect_plat.h"
|
|
||||||
#include "libipc/imp/generic.h"
|
|
||||||
|
|
||||||
#if defined(LIBIPC_CPP_20) && defined(__cpp_lib_span)
|
|
||||||
#include <span>
|
|
||||||
#define LIBIPC_CPP_LIB_SPAN_
|
|
||||||
#endif // __cpp_lib_span
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
namespace detail_span {
|
|
||||||
|
|
||||||
/// \brief Helper trait for span.
|
|
||||||
|
|
||||||
template <typename From, typename To>
|
|
||||||
using array_convertible = std::is_convertible<From(*)[], To(*)[]>;
|
|
||||||
|
|
||||||
template <typename From, typename To>
|
|
||||||
using is_array_convertible =
|
|
||||||
typename std::enable_if<array_convertible<From, To>::value>::type;
|
|
||||||
|
|
||||||
template <typename T, typename Ref>
|
|
||||||
using compatible_ref = array_convertible<typename std::remove_reference<Ref>::type, T>;
|
|
||||||
|
|
||||||
template <typename It>
|
|
||||||
using iter_reference_t = decltype(*std::declval<It&>());
|
|
||||||
|
|
||||||
template <typename T, typename It>
|
|
||||||
using is_compatible_iter =
|
|
||||||
typename std::enable_if<compatible_ref<T, iter_reference_t<It>>::value>::type;
|
|
||||||
|
|
||||||
template <typename From, typename To>
|
|
||||||
using is_inconvertible =
|
|
||||||
typename std::enable_if<!std::is_convertible<From, To>::value>::type;
|
|
||||||
|
|
||||||
template <typename S, typename I>
|
|
||||||
using is_sized_sentinel_for =
|
|
||||||
typename std::enable_if<std::is_convertible<decltype(std::declval<S>() - std::declval<I>()),
|
|
||||||
std::ptrdiff_t>::value>::type;
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
using is_continuous_container =
|
|
||||||
decltype(dataof(std::declval<T>()), countof(std::declval<T>()));
|
|
||||||
|
|
||||||
/// \brief Obtain the address represented by p
|
|
||||||
/// without forming a reference to the object pointed to by p.
|
|
||||||
/// \see https://en.cppreference.com/w/cpp/memory/to_address
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
constexpr T *to_address(T *ptr) noexcept {
|
|
||||||
static_assert(!std::is_function<T>::value, "ptr shouldn't a function pointer");
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
constexpr auto to_address(T const &ptr)
|
|
||||||
noexcept(noexcept(ptr.operator->()))
|
|
||||||
-> decltype(ptr.operator->()) {
|
|
||||||
return to_address(ptr.operator->());
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace detail_span
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief A simple implementation of span.
|
|
||||||
* \see https://en.cppreference.com/w/cpp/container/span
|
|
||||||
*/
|
|
||||||
template <typename T>
|
|
||||||
class span {
|
|
||||||
public:
|
|
||||||
using element_type = T;
|
|
||||||
using value_type = typename std::remove_cv<element_type>::type;
|
|
||||||
using size_type = std::size_t;
|
|
||||||
using difference_type = std::ptrdiff_t;
|
|
||||||
using pointer = element_type *;
|
|
||||||
using const_pointer = typename std::remove_const<element_type>::type const *;
|
|
||||||
using reference = element_type &;
|
|
||||||
using const_reference = typename std::remove_const<element_type>::type const &;
|
|
||||||
using iterator = pointer;
|
|
||||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
|
||||||
|
|
||||||
private:
|
|
||||||
pointer ptr_ {nullptr};
|
|
||||||
size_type extent_ {0};
|
|
||||||
|
|
||||||
public:
|
|
||||||
constexpr span() noexcept = default;
|
|
||||||
constexpr span(span const &) noexcept = default;
|
|
||||||
#if (LIBIPC_CC_MSVC > LIBIPC_CC_MSVC_2015)
|
|
||||||
constexpr
|
|
||||||
#endif
|
|
||||||
span & operator=(span const &) noexcept = default;
|
|
||||||
|
|
||||||
template <typename It,
|
|
||||||
typename = detail_span::is_compatible_iter<T, It>>
|
|
||||||
constexpr span(It first, size_type count) noexcept
|
|
||||||
: ptr_ (detail_span::to_address(first))
|
|
||||||
, extent_(count) {}
|
|
||||||
|
|
||||||
template <typename It, typename End,
|
|
||||||
typename = detail_span::is_compatible_iter<T, It>,
|
|
||||||
typename = detail_span::is_sized_sentinel_for<End, It>,
|
|
||||||
typename = detail_span::is_inconvertible<End, size_type>>
|
|
||||||
constexpr span(It first, End last) noexcept(noexcept(last - first))
|
|
||||||
: ptr_ (detail_span::to_address(first))
|
|
||||||
, extent_(static_cast<size_type>(last - first)) {}
|
|
||||||
|
|
||||||
template <typename U,
|
|
||||||
typename = detail_span::is_continuous_container<U>,
|
|
||||||
typename = detail_span::is_array_convertible<std::remove_pointer_t<
|
|
||||||
decltype(dataof(std::declval<U>()))>, element_type>>
|
|
||||||
constexpr span(U &&u) noexcept(noexcept(dataof (std::forward<U>(u)),
|
|
||||||
countof(std::forward<U>(u))))
|
|
||||||
: ptr_ (dataof (std::forward<U>(u)))
|
|
||||||
, extent_(countof(std::forward<U>(u))) {}
|
|
||||||
|
|
||||||
template <typename U, std::size_t E,
|
|
||||||
typename = detail_span::is_array_convertible<U, element_type>>
|
|
||||||
constexpr span(U (&arr)[E]) noexcept
|
|
||||||
: span(static_cast<pointer>(arr), E) {}
|
|
||||||
|
|
||||||
template <typename U, std::size_t E,
|
|
||||||
typename = detail_span::is_array_convertible<U, element_type>>
|
|
||||||
constexpr span(std::array<U, E> &arr) noexcept
|
|
||||||
: span(static_cast<pointer>(arr.data()), E) {}
|
|
||||||
|
|
||||||
template <typename U, std::size_t E,
|
|
||||||
typename = detail_span::is_array_convertible<typename std::add_const<U>::type, element_type>>
|
|
||||||
constexpr span(std::array<U, E> const &arr) noexcept
|
|
||||||
: span(static_cast<pointer>(arr.data()), E) {}
|
|
||||||
|
|
||||||
template <typename U,
|
|
||||||
typename = detail_span::is_array_convertible<U, T>>
|
|
||||||
constexpr span(span<U> const &s) noexcept
|
|
||||||
: ptr_ (s.data())
|
|
||||||
, extent_(s.size()) {}
|
|
||||||
|
|
||||||
#ifdef LIBIPC_CPP_LIB_SPAN_
|
|
||||||
template <typename U, std::size_t E,
|
|
||||||
typename = detail_span::is_array_convertible<U, T>>
|
|
||||||
constexpr span(std::span<U, E> const &s) noexcept
|
|
||||||
: ptr_ (s.data())
|
|
||||||
, extent_(s.size()) {}
|
|
||||||
#endif // LIBIPC_CPP_LIB_SPAN_
|
|
||||||
|
|
||||||
constexpr size_type size() const noexcept {
|
|
||||||
return extent_;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr size_type size_bytes() const noexcept {
|
|
||||||
return size() * sizeof(element_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr bool empty() const noexcept {
|
|
||||||
return size() == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr pointer data() const noexcept {
|
|
||||||
return this->ptr_;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr reference front() const noexcept {
|
|
||||||
return *data();
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr reference back() const noexcept {
|
|
||||||
return *(data() + (size() - 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr reference operator[](size_type idx) const noexcept {
|
|
||||||
return *(data() + idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr iterator begin() const noexcept {
|
|
||||||
return iterator(data());
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr iterator end() const noexcept {
|
|
||||||
return iterator(data() + this->size());
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr reverse_iterator rbegin() const noexcept {
|
|
||||||
return reverse_iterator(this->end());
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr reverse_iterator rend() const noexcept {
|
|
||||||
return reverse_iterator(this->begin());
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr span first(size_type count) const noexcept {
|
|
||||||
return span(begin(), count);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr span last(size_type count) const noexcept {
|
|
||||||
return span(end() - count, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr span subspan(size_type offset, size_type count = (std::numeric_limits<size_type>::max)()) const noexcept {
|
|
||||||
return (offset >= size()) ? span() : span(begin() + offset, (std::min)(size() - offset, count));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// \brief Support for span equals comparison.
|
|
||||||
|
|
||||||
template <typename T, typename U,
|
|
||||||
typename = decltype(std::declval<T>() == std::declval<U>())>
|
|
||||||
bool operator==(span<T> a, span<U> b) noexcept {
|
|
||||||
if (a.size() != b.size()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (std::size_t i = 0; i < a.size(); ++i) {
|
|
||||||
if (a[i] != b[i]) return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief Constructs an object of type T and wraps it in a span.
|
|
||||||
/// Before C++17, template argument deduction for class templates was not supported.
|
|
||||||
/// \see https://en.cppreference.com/w/cpp/language/template_argument_deduction
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
auto make_span(T *arr, std::size_t count) noexcept -> span<T> {
|
|
||||||
return {arr, count};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, std::size_t E>
|
|
||||||
auto make_span(T (&arr)[E]) noexcept -> span<T> {
|
|
||||||
return {arr};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, std::size_t E>
|
|
||||||
auto make_span(std::array<T, E> &arr) noexcept -> span<T> {
|
|
||||||
return {arr};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, std::size_t E>
|
|
||||||
auto make_span(std::array<T, E> const &arr) noexcept -> span<typename std::add_const<T>::type> {
|
|
||||||
return {arr};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
auto make_span(std::vector<T> &arr) noexcept -> span<T> {
|
|
||||||
return {arr.data(), arr.size()};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
auto make_span(std::vector<T> const &arr) noexcept -> span<typename std::add_const<T>::type> {
|
|
||||||
return {arr.data(), arr.size()};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
auto make_span(std::initializer_list<T> list) noexcept -> span<typename std::add_const<T>::type> {
|
|
||||||
return {list.begin(), list.end()};
|
|
||||||
}
|
|
||||||
|
|
||||||
inline auto make_span(std::string &str) noexcept -> span<char> {
|
|
||||||
return {const_cast<char *>(str.data()), str.size()};
|
|
||||||
}
|
|
||||||
|
|
||||||
inline auto make_span(std::string const &str) noexcept -> span<char const> {
|
|
||||||
return {str.data(), str.size()};
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ipc
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
/**
|
|
||||||
* \file libipc/system.h
|
|
||||||
* \author mutouyun (orz@orzz.org)
|
|
||||||
* \brief Isolation and encapsulation of system APIs.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <ostream> // std::ostream
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#include "libipc/imp/export.h"
|
|
||||||
#include "libipc/imp/error.h"
|
|
||||||
#include "libipc/imp/result.h"
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
namespace sys {
|
|
||||||
|
|
||||||
/// \brief A platform-dependent error code.
|
|
||||||
LIBIPC_EXPORT std::error_code error() noexcept;
|
|
||||||
|
|
||||||
/// \enum The name of the `conf()` argument used to inquire about its value.
|
|
||||||
/// \brief Certain options are supported,
|
|
||||||
/// or what the value is of certain configurable constants or limits.
|
|
||||||
enum class info : std::int32_t {
|
|
||||||
page_size,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// \brief Get system configuration information at run time.
|
|
||||||
LIBIPC_EXPORT result<std::int64_t> conf(info) noexcept;
|
|
||||||
|
|
||||||
} // namespace sys
|
|
||||||
} // namespace ipc
|
|
||||||
@ -1,163 +0,0 @@
|
|||||||
/**
|
|
||||||
* \file libipc/uninitialized.h
|
|
||||||
* \author mutouyun (orz@orzz.org)
|
|
||||||
* \brief Uninitialized memory algorithms.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <new> // placement-new
|
|
||||||
#include <type_traits> // std::enable_if_t
|
|
||||||
#include <utility> // std::forward
|
|
||||||
#include <memory> // std::construct_at, std::destroy_at, std::addressof
|
|
||||||
#include <cstddef>
|
|
||||||
|
|
||||||
#include "libipc/imp/detect_plat.h"
|
|
||||||
#include "libipc/imp/generic.h"
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Creates an object at a given address, like 'construct_at' in c++20
|
|
||||||
* \see https://en.cppreference.com/w/cpp/memory/construct_at
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Overload for zero arguments - use value initialization
|
|
||||||
template <typename T>
|
|
||||||
T* construct(void *p) {
|
|
||||||
#if defined(LIBIPC_CPP_20)
|
|
||||||
return std::construct_at(static_cast<T *>(p));
|
|
||||||
#else
|
|
||||||
return ::new (p) T();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Overload for one or more arguments - prefer direct initialization
|
|
||||||
template <typename T, typename A1, typename... A>
|
|
||||||
auto construct(void *p, A1 &&arg1, A &&...args)
|
|
||||||
-> std::enable_if_t<::std::is_constructible<T, A1, A...>::value, T *> {
|
|
||||||
#if defined(LIBIPC_CPP_20)
|
|
||||||
return std::construct_at(static_cast<T *>(p), std::forward<A1>(arg1), std::forward<A>(args)...);
|
|
||||||
#else
|
|
||||||
return ::new (p) T(std::forward<A1>(arg1), std::forward<A>(args)...);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Overload for non-constructible types - use aggregate initialization
|
|
||||||
template <typename T, typename A1, typename... A>
|
|
||||||
auto construct(void *p, A1 &&arg1, A &&...args)
|
|
||||||
-> std::enable_if_t<!::std::is_constructible<T, A1, A...>::value, T *> {
|
|
||||||
return ::new (p) T{std::forward<A1>(arg1), std::forward<A>(args)...};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Destroys an object at a given address, like 'destroy_at' in c++17
|
|
||||||
* \see https://en.cppreference.com/w/cpp/memory/destroy_at
|
|
||||||
*/
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void *destroy(T *p) noexcept {
|
|
||||||
if (p == nullptr) return nullptr;
|
|
||||||
#if defined(LIBIPC_CPP_17)
|
|
||||||
std::destroy_at(p);
|
|
||||||
#else
|
|
||||||
p->~T();
|
|
||||||
#endif
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
inline void *destroy<void>(void *p) noexcept {
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, std::size_t N>
|
|
||||||
void *destroy(T (*p)[N]) noexcept {
|
|
||||||
if (p == nullptr) return nullptr;
|
|
||||||
#if defined(LIBIPC_CPP_20)
|
|
||||||
std::destroy_at(p);
|
|
||||||
#elif defined(LIBIPC_CPP_17)
|
|
||||||
std::destroy(std::begin(*p), std::end(*p));
|
|
||||||
#else
|
|
||||||
for (auto &elem : *p) destroy(std::addressof(elem));
|
|
||||||
#endif
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Destroys a range of objects.
|
|
||||||
* \see https://en.cppreference.com/w/cpp/memory/destroy
|
|
||||||
*/
|
|
||||||
template <typename ForwardIt>
|
|
||||||
void destroy(ForwardIt first, ForwardIt last) noexcept {
|
|
||||||
#if defined(LIBIPC_CPP_17)
|
|
||||||
std::destroy(first, last);
|
|
||||||
#else
|
|
||||||
for (; first != last; ++first) {
|
|
||||||
destroy(std::addressof(*first));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Destroys a number of objects in a range.
|
|
||||||
* \see https://en.cppreference.com/w/cpp/memory/destroy_n
|
|
||||||
*/
|
|
||||||
template <typename ForwardIt, typename Size>
|
|
||||||
ForwardIt destroy_n(ForwardIt first, Size n) noexcept {
|
|
||||||
#if defined(LIBIPC_CPP_17)
|
|
||||||
return std::destroy_n(first, n);
|
|
||||||
#else
|
|
||||||
for (; n > 0; (void) ++first, --n)
|
|
||||||
destroy(std::addressof(*first));
|
|
||||||
return first;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Constructs objects by default-initialization
|
|
||||||
* in an uninitialized area of memory, defined by a start and a count.
|
|
||||||
* \see https://en.cppreference.com/w/cpp/memory/uninitialized_default_construct_n
|
|
||||||
*/
|
|
||||||
template <typename ForwardIt, typename Size>
|
|
||||||
ForwardIt uninitialized_default_construct_n(ForwardIt first, Size n) {
|
|
||||||
#if defined(LIBIPC_CPP_17)
|
|
||||||
return std::uninitialized_default_construct_n(first, n);
|
|
||||||
#else
|
|
||||||
using T = typename std::iterator_traits<ForwardIt>::value_type;
|
|
||||||
ForwardIt current = first;
|
|
||||||
LIBIPC_TRY {
|
|
||||||
for (; n > 0; (void) ++current, --n)
|
|
||||||
::new (horrible_cast<void *>(std::addressof(*current))) T;
|
|
||||||
return current;
|
|
||||||
} LIBIPC_CATCH(...) {
|
|
||||||
destroy(first, current);
|
|
||||||
LIBIPC_THROW(, first);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Moves a number of objects to an uninitialized area of memory.
|
|
||||||
* \see https://en.cppreference.com/w/cpp/memory/uninitialized_move_n
|
|
||||||
*/
|
|
||||||
template <typename InputIt, typename Size, typename NoThrowForwardIt>
|
|
||||||
auto uninitialized_move_n(InputIt first, Size count, NoThrowForwardIt d_first)
|
|
||||||
-> std::pair<InputIt, NoThrowForwardIt> {
|
|
||||||
#if defined(LIBIPC_CPP_17)
|
|
||||||
return std::uninitialized_move_n(first, count, d_first);
|
|
||||||
#else
|
|
||||||
using Value = typename std::iterator_traits<NoThrowForwardIt>::value_type;
|
|
||||||
NoThrowForwardIt current = d_first;
|
|
||||||
LIBIPC_TRY {
|
|
||||||
for (; count > 0; ++first, (void) ++current, --count) {
|
|
||||||
::new (static_cast<void *>(std::addressof(*current))) Value(std::move(*first));
|
|
||||||
}
|
|
||||||
} LIBIPC_CATCH(...) {
|
|
||||||
destroy(d_first, current);
|
|
||||||
LIBIPC_THROW(, {first, d_first});
|
|
||||||
}
|
|
||||||
return {first, current};
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ipc
|
|
||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "libipc/imp/export.h"
|
#include "libipc/export.h"
|
||||||
#include "libipc/def.h"
|
#include "libipc/def.h"
|
||||||
#include "libipc/buffer.h"
|
#include "libipc/buffer.h"
|
||||||
#include "libipc/shm.h"
|
#include "libipc/shm.h"
|
||||||
@ -18,7 +18,7 @@ enum : unsigned {
|
|||||||
};
|
};
|
||||||
|
|
||||||
template <typename Flag>
|
template <typename Flag>
|
||||||
struct LIBIPC_EXPORT chan_impl {
|
struct IPC_EXPORT chan_impl {
|
||||||
static ipc::handle_t init_first();
|
static ipc::handle_t init_first();
|
||||||
|
|
||||||
static bool connect (ipc::handle_t * ph, char const * name, unsigned mode);
|
static bool connect (ipc::handle_t * ph, char const * name, unsigned mode);
|
||||||
|
|||||||
@ -1,121 +0,0 @@
|
|||||||
/**
|
|
||||||
* \file libipc/block_pool.h
|
|
||||||
* \author mutouyun (orz@orzz.org)
|
|
||||||
* \brief The fixed-length memory block pool.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
|
|
||||||
#include "libipc/mem/central_cache_pool.h"
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
namespace mem {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Fixed-length memory block pool.
|
|
||||||
* \tparam BlockSize specifies the memory block size
|
|
||||||
* \tparam BlockPoolExpansion specifies the default number of blocks to expand when the block pool is exhausted
|
|
||||||
*/
|
|
||||||
template <std::size_t BlockSize, std::size_t BlockPoolExpansion>
|
|
||||||
class block_pool;
|
|
||||||
|
|
||||||
/// \brief General-purpose block pool for any size of memory block.
|
|
||||||
/// \note This block pool can only be used to deallocate a group of memory blocks of unknown but consistent size,
|
|
||||||
/// and cannot be used for memory block allocation.
|
|
||||||
template <>
|
|
||||||
class block_pool<0, 0> {
|
|
||||||
|
|
||||||
template <std::size_t BlockSize, std::size_t BlockPoolExpansion>
|
|
||||||
friend class block_pool;
|
|
||||||
|
|
||||||
/// \brief The block type.
|
|
||||||
struct block_t {
|
|
||||||
block_t *next;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// \brief The central cache pool type.
|
|
||||||
using central_cache_pool_t = central_cache_pool<block_t, 0>;
|
|
||||||
|
|
||||||
public:
|
|
||||||
static constexpr std::size_t block_size = 0;
|
|
||||||
|
|
||||||
block_pool() noexcept : cursor_(central_cache_pool_t::instance().aqueire()) {}
|
|
||||||
~block_pool() noexcept {
|
|
||||||
central_cache_pool_t::instance().release(cursor_);
|
|
||||||
}
|
|
||||||
|
|
||||||
block_pool(block_pool const &) = delete;
|
|
||||||
block_pool& operator=(block_pool const &) = delete;
|
|
||||||
|
|
||||||
block_pool(block_pool &&rhs) noexcept : cursor_(std::exchange(rhs.cursor_, nullptr)) {}
|
|
||||||
block_pool &operator=(block_pool &&) noexcept = delete;
|
|
||||||
|
|
||||||
void deallocate(void *p) noexcept {
|
|
||||||
if (p == nullptr) return;
|
|
||||||
block_t *b = static_cast<block_t *>(p);
|
|
||||||
b->next = cursor_;
|
|
||||||
cursor_ = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
block_t *cursor_;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// \brief A block pool for a block of memory of a specific size.
|
|
||||||
template <std::size_t BlockSize, std::size_t BlockPoolExpansion>
|
|
||||||
class block_pool {
|
|
||||||
|
|
||||||
/// \brief The block type.
|
|
||||||
using block_t = block<BlockSize>;
|
|
||||||
/// \brief The central cache pool type.
|
|
||||||
using central_cache_pool_t = central_cache_pool<block_t, BlockPoolExpansion>;
|
|
||||||
|
|
||||||
/// \brief Expand the block pool when it is exhausted.
|
|
||||||
block_t *expand() noexcept {
|
|
||||||
return central_cache_pool_t::instance().aqueire();
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
static constexpr std::size_t block_size = BlockSize;
|
|
||||||
|
|
||||||
block_pool() noexcept : cursor_(expand()) {}
|
|
||||||
~block_pool() noexcept {
|
|
||||||
central_cache_pool_t::instance().release(cursor_);
|
|
||||||
}
|
|
||||||
|
|
||||||
block_pool(block_pool const &) = delete;
|
|
||||||
block_pool& operator=(block_pool const &) = delete;
|
|
||||||
|
|
||||||
block_pool(block_pool &&rhs) noexcept
|
|
||||||
: cursor_(std::exchange(rhs.cursor_, nullptr)) {}
|
|
||||||
block_pool &operator=(block_pool &&) noexcept = delete;
|
|
||||||
|
|
||||||
/// \brief Used to take all memory blocks from within a general-purpose block pool.
|
|
||||||
/// \note Of course, the actual memory blocks they manage must be the same size.
|
|
||||||
block_pool(block_pool<0, 0> &&rhs) noexcept
|
|
||||||
: cursor_(reinterpret_cast<block_t *>(std::exchange(rhs.cursor_, nullptr))) {}
|
|
||||||
|
|
||||||
void *allocate() noexcept {
|
|
||||||
if (cursor_ == nullptr) {
|
|
||||||
cursor_ = expand();
|
|
||||||
if (cursor_ == nullptr) return nullptr;
|
|
||||||
}
|
|
||||||
block_t *p = cursor_;
|
|
||||||
cursor_ = cursor_->next;
|
|
||||||
return p->storage.data();
|
|
||||||
}
|
|
||||||
|
|
||||||
void deallocate(void *p) noexcept {
|
|
||||||
if (p == nullptr) return;
|
|
||||||
block_t *b = static_cast<block_t *>(p);
|
|
||||||
b->next = cursor_;
|
|
||||||
cursor_ = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
block_t *cursor_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace mem
|
|
||||||
} // namespace ipc
|
|
||||||
@ -1,163 +0,0 @@
|
|||||||
/**
|
|
||||||
* \file libipc/bytes_allocator.h
|
|
||||||
* \author mutouyun (orz@orzz.org)
|
|
||||||
* \brief A generic polymorphic memory allocator.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <type_traits>
|
|
||||||
#include <array>
|
|
||||||
#include <limits> // std::numeric_limits
|
|
||||||
#include <utility> // std::forward
|
|
||||||
#include <tuple> // std::ignore
|
|
||||||
#include <cstddef>
|
|
||||||
|
|
||||||
#include "libipc/imp/export.h"
|
|
||||||
#include "libipc/imp/uninitialized.h"
|
|
||||||
#include "libipc/imp/byte.h"
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
namespace mem {
|
|
||||||
|
|
||||||
/// \brief Helper trait for memory resource.
|
|
||||||
|
|
||||||
template <typename T, typename = void>
|
|
||||||
struct has_allocate : std::false_type {};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct has_allocate<T,
|
|
||||||
typename std::enable_if<std::is_convertible<
|
|
||||||
decltype(std::declval<T &>().allocate(std::declval<std::size_t>(),
|
|
||||||
std::declval<std::size_t>())), void *
|
|
||||||
>::value>::type> : std::true_type {};
|
|
||||||
|
|
||||||
template <typename T, typename = void>
|
|
||||||
struct has_deallocate : std::false_type {};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct has_deallocate<T,
|
|
||||||
decltype(std::declval<T &>().deallocate(std::declval<void *>(),
|
|
||||||
std::declval<std::size_t>(),
|
|
||||||
std::declval<std::size_t>()))
|
|
||||||
> : std::true_type {};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
using is_memory_resource =
|
|
||||||
std::enable_if_t<has_allocate <T>::value &&
|
|
||||||
has_deallocate<T>::value, bool>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief An allocator which exhibits different allocation behavior
|
|
||||||
* depending upon the memory resource from which it is constructed.
|
|
||||||
*
|
|
||||||
* \note Unlike `std::pmr::container_allocator`, it does not
|
|
||||||
* rely on a specific inheritance relationship and only restricts
|
|
||||||
* the interface behavior of the incoming memory resource object to
|
|
||||||
* conform to `std::pmr::memory_resource`.
|
|
||||||
*
|
|
||||||
* \see https://en.cppreference.com/w/cpp/memory/memory_resource
|
|
||||||
* https://en.cppreference.com/w/cpp/memory/container_allocator
|
|
||||||
*/
|
|
||||||
class LIBIPC_EXPORT bytes_allocator {
|
|
||||||
|
|
||||||
class holder_mr_base {
|
|
||||||
public:
|
|
||||||
virtual ~holder_mr_base() noexcept = default;
|
|
||||||
virtual void *alloc(std::size_t, std::size_t) const = 0;
|
|
||||||
virtual void dealloc(void *, std::size_t, std::size_t) const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename MR, typename = bool>
|
|
||||||
class holder_mr;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief An empty holding class used to calculate a reasonable memory size for the holder.
|
|
||||||
* \tparam MR cannot be converted to the type of memory resource
|
|
||||||
*/
|
|
||||||
template <typename MR, typename U>
|
|
||||||
class holder_mr : public holder_mr_base {
|
|
||||||
protected:
|
|
||||||
MR *res_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
holder_mr(MR *p_mr) noexcept
|
|
||||||
: res_(p_mr) {}
|
|
||||||
|
|
||||||
// [MSVC] error C2259: 'bytes_allocator::holder_mr<void *,bool>': cannot instantiate abstract class.
|
|
||||||
void *alloc(std::size_t s, std::size_t a) const override { return nullptr; }
|
|
||||||
void dealloc(void *p, std::size_t s, std::size_t a) const override {}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief A memory resource pointer holder class for type erasure.
|
|
||||||
* \tparam MR memory resource type
|
|
||||||
*/
|
|
||||||
template <typename MR>
|
|
||||||
class holder_mr<MR, is_memory_resource<MR>> : public holder_mr<MR, void> {
|
|
||||||
using base_t = holder_mr<MR, void>;
|
|
||||||
|
|
||||||
public:
|
|
||||||
holder_mr(MR *p_mr) noexcept
|
|
||||||
: base_t{p_mr} {}
|
|
||||||
|
|
||||||
void *alloc(std::size_t s, std::size_t a) const override {
|
|
||||||
return base_t::res_->allocate(s, a);
|
|
||||||
}
|
|
||||||
|
|
||||||
void dealloc(void *p, std::size_t s, std::size_t a) const override {
|
|
||||||
base_t::res_->deallocate(p, s, a);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
using void_holder_t = holder_mr<void *>;
|
|
||||||
alignas(void_holder_t) std::array<ipc::byte, sizeof(void_holder_t)> holder_;
|
|
||||||
|
|
||||||
holder_mr_base & get_holder() noexcept;
|
|
||||||
holder_mr_base const &get_holder() const noexcept;
|
|
||||||
|
|
||||||
void init_default_resource() noexcept;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// \brief Constructs an `bytes_allocator` using the return value of
|
|
||||||
/// `new_delete_resource::get()` as the underlying memory resource.
|
|
||||||
bytes_allocator() noexcept;
|
|
||||||
~bytes_allocator() noexcept;
|
|
||||||
|
|
||||||
bytes_allocator(bytes_allocator const &other) noexcept = default;
|
|
||||||
bytes_allocator &operator=(bytes_allocator const &other) & noexcept = default;
|
|
||||||
|
|
||||||
bytes_allocator(bytes_allocator &&other) noexcept = default;
|
|
||||||
bytes_allocator &operator=(bytes_allocator &&other) & noexcept = default;
|
|
||||||
|
|
||||||
/// \brief Constructs a `bytes_allocator` from a memory resource pointer.
|
|
||||||
/// \note The lifetime of the pointer must be longer than that of bytes_allocator.
|
|
||||||
template <typename T, is_memory_resource<T> = true>
|
|
||||||
bytes_allocator(T *p_mr) noexcept {
|
|
||||||
if (p_mr == nullptr) {
|
|
||||||
init_default_resource();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
std::ignore = ipc::construct<holder_mr<T>>(holder_.data(), p_mr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void swap(bytes_allocator &other) noexcept;
|
|
||||||
|
|
||||||
/// \brief Allocate/deallocate memory.
|
|
||||||
void *allocate(std::size_t s, std::size_t = alignof(std::max_align_t)) const;
|
|
||||||
void deallocate(void *p, std::size_t s, std::size_t = alignof(std::max_align_t)) const;
|
|
||||||
|
|
||||||
/// \brief Allocates uninitialized memory and constructs an object of type T in the memory.
|
|
||||||
template <typename T, typename... A>
|
|
||||||
T *construct(A &&...args) const {
|
|
||||||
return ipc::construct<T>(allocate(sizeof(T), alignof(T)), std::forward<A>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief Calls the destructor of the object pointed to by p and deallocates the memory.
|
|
||||||
template <typename T>
|
|
||||||
void destroy(T *p) const noexcept {
|
|
||||||
deallocate(ipc::destroy(p), sizeof(T), alignof(T));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace mem
|
|
||||||
} // namespace ipc
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
/**
|
|
||||||
* \file libipc/central_cache_allocator.h
|
|
||||||
* \author mutouyun (orz@orzz.org)
|
|
||||||
* \brief The central cache allocator getter.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "libipc/imp/export.h"
|
|
||||||
#include "libipc/mem/bytes_allocator.h"
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
namespace mem {
|
|
||||||
|
|
||||||
/// \brief Get the central cache allocator.
|
|
||||||
/// \note The central cache allocator is used to allocate memory for the central cache pool.
|
|
||||||
/// The underlying memory resource is a `monotonic_buffer_resource` with a fixed-size buffer.
|
|
||||||
LIBIPC_EXPORT bytes_allocator ¢ral_cache_allocator() noexcept;
|
|
||||||
|
|
||||||
} // namespace mem
|
|
||||||
} // namespace ipc
|
|
||||||
@ -1,132 +0,0 @@
|
|||||||
/**
|
|
||||||
* \file libipc/central_cache_pool.h
|
|
||||||
* \author mutouyun (orz@orzz.org)
|
|
||||||
* \brief The fixed-length memory block central cache pool.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <deque>
|
|
||||||
#include <utility>
|
|
||||||
#include <array>
|
|
||||||
|
|
||||||
#include "libipc/imp/byte.h"
|
|
||||||
#include "libipc/concur/intrusive_stack.h"
|
|
||||||
#include "libipc/mem/central_cache_allocator.h"
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
namespace mem {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief The block type.
|
|
||||||
* \tparam BlockSize specifies the memory block size
|
|
||||||
*/
|
|
||||||
template <std::size_t BlockSize>
|
|
||||||
union block {
|
|
||||||
block *next;
|
|
||||||
alignas(std::max_align_t) std::array<byte, BlockSize> storage;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief A fixed-length memory block central cache pool.
|
|
||||||
* \tparam BlockT specifies the memory block type
|
|
||||||
*/
|
|
||||||
template <typename BlockT, std::size_t BlockPoolExpansion>
|
|
||||||
class central_cache_pool {
|
|
||||||
|
|
||||||
/// \brief The block type, which should be a union of a pointer and a storage.
|
|
||||||
using block_t = BlockT;
|
|
||||||
/// \brief The chunk type, which is an array of blocks.
|
|
||||||
using chunk_t = std::array<block_t, BlockPoolExpansion>;
|
|
||||||
/// \brief The node type, which is used to store the block pointer.
|
|
||||||
using node_t = typename concur::intrusive_stack<block_t *>::node;
|
|
||||||
|
|
||||||
/// \brief The central cache stack.
|
|
||||||
concur::intrusive_stack<block_t *> cached_;
|
|
||||||
concur::intrusive_stack<block_t *> aqueired_;
|
|
||||||
|
|
||||||
central_cache_pool() noexcept = default;
|
|
||||||
|
|
||||||
public:
|
|
||||||
block_t *aqueire() noexcept {
|
|
||||||
auto *n = cached_.pop();
|
|
||||||
if (n != nullptr) {
|
|
||||||
aqueired_.push(n);
|
|
||||||
return n->value;
|
|
||||||
}
|
|
||||||
auto *chunk = central_cache_allocator().construct<chunk_t>();
|
|
||||||
if (chunk == nullptr) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
for (std::size_t i = 0; i < BlockPoolExpansion - 1; ++i) {
|
|
||||||
(*chunk)[i].next = &(*chunk)[i + 1];
|
|
||||||
}
|
|
||||||
chunk->back().next = nullptr;
|
|
||||||
return chunk->data();
|
|
||||||
}
|
|
||||||
|
|
||||||
void release(block_t *p) noexcept {
|
|
||||||
if (p == nullptr) return;
|
|
||||||
auto *a = aqueired_.pop();
|
|
||||||
if (a == nullptr) {
|
|
||||||
a = central_cache_allocator().construct<node_t>();
|
|
||||||
if (a == nullptr) return;
|
|
||||||
}
|
|
||||||
a->value = p;
|
|
||||||
cached_.push(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief Get the singleton instance.
|
|
||||||
static central_cache_pool &instance() noexcept {
|
|
||||||
static central_cache_pool pool;
|
|
||||||
return pool;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// \brief A fixed-length memory block central cache pool with no default expansion size.
|
|
||||||
template <typename BlockT>
|
|
||||||
class central_cache_pool<BlockT, 0> {
|
|
||||||
|
|
||||||
/// \brief The block type, which should be a union of a pointer and a storage.
|
|
||||||
using block_t = BlockT;
|
|
||||||
/// \brief The node type, which is used to store the block pointer.
|
|
||||||
using node_t = typename concur::intrusive_stack<block_t *>::node;
|
|
||||||
|
|
||||||
/// \brief The central cache stack.
|
|
||||||
concur::intrusive_stack<block_t *> cached_;
|
|
||||||
concur::intrusive_stack<block_t *> aqueired_;
|
|
||||||
|
|
||||||
central_cache_pool() noexcept = default;
|
|
||||||
|
|
||||||
public:
|
|
||||||
block_t *aqueire() noexcept {
|
|
||||||
auto *n = cached_.pop();
|
|
||||||
if (n != nullptr) {
|
|
||||||
aqueired_.push(n);
|
|
||||||
return n->value;
|
|
||||||
}
|
|
||||||
// For pools with no default expansion size,
|
|
||||||
// the central cache pool is only buffered, not allocated.
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void release(block_t *p) noexcept {
|
|
||||||
if (p == nullptr) return;
|
|
||||||
auto *a = aqueired_.pop();
|
|
||||||
if (a == nullptr) {
|
|
||||||
a = central_cache_allocator().construct<node_t>();
|
|
||||||
if (a == nullptr) return;
|
|
||||||
}
|
|
||||||
a->value = p;
|
|
||||||
cached_.push(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief Get the singleton instance.
|
|
||||||
static central_cache_pool &instance() noexcept {
|
|
||||||
static central_cache_pool pool;
|
|
||||||
return pool;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace mem
|
|
||||||
} // namespace ipc
|
|
||||||
@ -1,101 +0,0 @@
|
|||||||
/**
|
|
||||||
* \file libipc/container_allocator.h
|
|
||||||
* \author mutouyun (orz@orzz.org)
|
|
||||||
* \brief An allocator that can be used by all standard library containers.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <utility>
|
|
||||||
#include <limits>
|
|
||||||
|
|
||||||
#include "libipc/imp/uninitialized.h"
|
|
||||||
#include "libipc/mem/new.h"
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
namespace mem {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief An allocator that can be used by all standard library containers.
|
|
||||||
*
|
|
||||||
* \see https://en.cppreference.com/w/cpp/memory/allocator
|
|
||||||
* https://en.cppreference.com/w/cpp/memory/polymorphic_allocator
|
|
||||||
*/
|
|
||||||
template <typename T>
|
|
||||||
class container_allocator {
|
|
||||||
|
|
||||||
template <typename U>
|
|
||||||
friend class container_allocator;
|
|
||||||
|
|
||||||
public:
|
|
||||||
// type definitions
|
|
||||||
typedef T value_type;
|
|
||||||
typedef value_type * pointer;
|
|
||||||
typedef const value_type *const_pointer;
|
|
||||||
typedef value_type & reference;
|
|
||||||
typedef const value_type &const_reference;
|
|
||||||
typedef std::size_t size_type;
|
|
||||||
typedef std::ptrdiff_t difference_type;
|
|
||||||
|
|
||||||
// the other type of std_allocator
|
|
||||||
template <typename U>
|
|
||||||
struct rebind {
|
|
||||||
using other = container_allocator<U>;
|
|
||||||
};
|
|
||||||
|
|
||||||
container_allocator() noexcept {}
|
|
||||||
|
|
||||||
// construct by copying (do nothing)
|
|
||||||
container_allocator (container_allocator<T> const &) noexcept {}
|
|
||||||
container_allocator& operator=(container_allocator<T> const &) noexcept { return *this; }
|
|
||||||
|
|
||||||
// construct from a related allocator (do nothing)
|
|
||||||
template <typename U> container_allocator (container_allocator<U> const &) noexcept {}
|
|
||||||
template <typename U> container_allocator &operator=(container_allocator<U> const &) noexcept { return *this; }
|
|
||||||
|
|
||||||
container_allocator (container_allocator &&) noexcept = default;
|
|
||||||
container_allocator& operator=(container_allocator &&) noexcept = default;
|
|
||||||
|
|
||||||
constexpr size_type max_size() const noexcept {
|
|
||||||
return (std::numeric_limits<size_type>::max)() / sizeof(value_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
pointer allocate(size_type count) noexcept {
|
|
||||||
if (count == 0) return nullptr;
|
|
||||||
if (count > this->max_size()) return nullptr;
|
|
||||||
// Allocate raw memory without constructing objects
|
|
||||||
// Construction should be done by construct() member function
|
|
||||||
void *p = mem::alloc(sizeof(value_type) * count);
|
|
||||||
return static_cast<pointer>(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
void deallocate(pointer p, size_type count) noexcept {
|
|
||||||
if (count == 0) return;
|
|
||||||
if (count > this->max_size()) return;
|
|
||||||
// Deallocate raw memory without destroying objects
|
|
||||||
// Destruction should be done by destroy() member function before deallocate
|
|
||||||
mem::free(p, sizeof(value_type) * count);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename... P>
|
|
||||||
static void construct(pointer p, P && ... params) {
|
|
||||||
std::ignore = ipc::construct<T>(p, std::forward<P>(params)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void destroy(pointer p) {
|
|
||||||
std::ignore = ipc::destroy(p);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T, typename U>
|
|
||||||
constexpr bool operator==(container_allocator<T> const &, container_allocator<U> const &) noexcept {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename U>
|
|
||||||
constexpr bool operator!=(container_allocator<T> const &, container_allocator<U> const &) noexcept {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace mem
|
|
||||||
} // namespace ipc
|
|
||||||
@ -1,83 +0,0 @@
|
|||||||
/**
|
|
||||||
* \file libipc/memory_resource.h
|
|
||||||
* \author mutouyun (orz@orzz.org)
|
|
||||||
* \brief Implement memory allocation strategies that can be used by ipc::mem::bytes_allocator.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <type_traits>
|
|
||||||
#include <cstddef> // std::size_t, std::max_align_t
|
|
||||||
|
|
||||||
#include "libipc/imp/export.h"
|
|
||||||
#include "libipc/imp/span.h"
|
|
||||||
#include "libipc/imp/byte.h"
|
|
||||||
#include "libipc/mem/bytes_allocator.h"
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
namespace mem {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \class LIBIPC_EXPORT new_delete_resource
|
|
||||||
* \brief A memory resource that uses the
|
|
||||||
* standard memory allocation and deallocation interface to allocate memory.
|
|
||||||
* \see https://en.cppreference.com/w/cpp/memory/new_delete_resource
|
|
||||||
*/
|
|
||||||
class LIBIPC_EXPORT new_delete_resource {
|
|
||||||
public:
|
|
||||||
/// \brief Returns a pointer to a `new_delete_resource`.
|
|
||||||
static new_delete_resource *get() noexcept;
|
|
||||||
|
|
||||||
/// \brief Allocates storage with a size of at least bytes bytes, aligned to the specified alignment.
|
|
||||||
/// \remark Returns nullptr if storage of the requested size and alignment cannot be obtained.
|
|
||||||
/// \see https://en.cppreference.com/w/cpp/memory/memory_resource/do_allocate
|
|
||||||
void *allocate(std::size_t bytes, std::size_t alignment = alignof(std::max_align_t)) noexcept;
|
|
||||||
|
|
||||||
/// \brief Deallocates the storage pointed to by p.
|
|
||||||
/// \see https://en.cppreference.com/w/cpp/memory/memory_resource/deallocate
|
|
||||||
void deallocate(void *p, std::size_t bytes, std::size_t alignment = alignof(std::max_align_t)) noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \class LIBIPC_EXPORT monotonic_buffer_resource
|
|
||||||
* \brief A special-purpose memory resource class
|
|
||||||
* that releases the allocated memory only when the resource is destroyed.
|
|
||||||
* \see https://en.cppreference.com/w/cpp/memory/monotonic_buffer_resource
|
|
||||||
*/
|
|
||||||
class LIBIPC_EXPORT monotonic_buffer_resource {
|
|
||||||
|
|
||||||
bytes_allocator upstream_;
|
|
||||||
|
|
||||||
struct node {
|
|
||||||
node *next;
|
|
||||||
std::size_t size;
|
|
||||||
} *free_list_;
|
|
||||||
|
|
||||||
ipc::byte * head_;
|
|
||||||
ipc::byte * tail_;
|
|
||||||
std::size_t next_size_;
|
|
||||||
|
|
||||||
ipc::byte * const initial_buffer_;
|
|
||||||
std::size_t const initial_size_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
monotonic_buffer_resource() noexcept;
|
|
||||||
explicit monotonic_buffer_resource(bytes_allocator upstream) noexcept;
|
|
||||||
explicit monotonic_buffer_resource(std::size_t initial_size) noexcept;
|
|
||||||
monotonic_buffer_resource(std::size_t initial_size, bytes_allocator upstream) noexcept;
|
|
||||||
monotonic_buffer_resource(ipc::span<ipc::byte> buffer) noexcept;
|
|
||||||
monotonic_buffer_resource(ipc::span<ipc::byte> buffer, bytes_allocator upstream) noexcept;
|
|
||||||
|
|
||||||
~monotonic_buffer_resource() noexcept;
|
|
||||||
|
|
||||||
monotonic_buffer_resource(monotonic_buffer_resource const &) = delete;
|
|
||||||
monotonic_buffer_resource &operator=(monotonic_buffer_resource const &) = delete;
|
|
||||||
|
|
||||||
bytes_allocator upstream_resource() const noexcept;
|
|
||||||
void release() noexcept;
|
|
||||||
|
|
||||||
void *allocate(std::size_t bytes, std::size_t alignment = alignof(std::max_align_t)) noexcept;
|
|
||||||
void deallocate(void *p, std::size_t bytes, std::size_t alignment = alignof(std::max_align_t)) noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace mem
|
|
||||||
} // namespace ipc
|
|
||||||
@ -1,117 +0,0 @@
|
|||||||
/**
|
|
||||||
* \file libipc/mem.h
|
|
||||||
* \author mutouyun (orz@orzz.org)
|
|
||||||
* \brief Global memory management.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <limits>
|
|
||||||
#include <tuple>
|
|
||||||
|
|
||||||
#include "libipc/imp/aligned.h"
|
|
||||||
#include "libipc/imp/uninitialized.h"
|
|
||||||
#include "libipc/imp/byte.h"
|
|
||||||
#include "libipc/imp/detect_plat.h"
|
|
||||||
#include "libipc/imp/export.h"
|
|
||||||
#include "libipc/mem/memory_resource.h"
|
|
||||||
#include "libipc/mem/block_pool.h"
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
namespace mem {
|
|
||||||
|
|
||||||
/// \brief Defines the memory block collector interface.
|
|
||||||
class LIBIPC_EXPORT block_collector {
|
|
||||||
public:
|
|
||||||
virtual ~block_collector() noexcept = default;
|
|
||||||
virtual void *allocate(std::size_t /*bytes*/) noexcept = 0;
|
|
||||||
virtual void deallocate(void * /*p*/, std::size_t /*bytes*/) noexcept = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// \brief Matches the appropriate memory block resource based on a specified size.
|
|
||||||
LIBIPC_EXPORT block_collector &get_regular_resource(std::size_t s) noexcept;
|
|
||||||
|
|
||||||
/// \brief Allocates storage with a size of at least bytes bytes.
|
|
||||||
LIBIPC_EXPORT void *alloc(std::size_t bytes) noexcept;
|
|
||||||
LIBIPC_EXPORT void free (void *p, std::size_t bytes) noexcept;
|
|
||||||
|
|
||||||
namespace detail_new {
|
|
||||||
|
|
||||||
#if defined(LIBIPC_CPP_17)
|
|
||||||
using recycle_t = void (*)(void *p) noexcept;
|
|
||||||
#else
|
|
||||||
using recycle_t = void (*)(void *p);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static constexpr std::size_t recycler_size = round_up(sizeof(recycle_t), alignof(std::size_t));
|
|
||||||
static constexpr std::size_t allocated_size = sizeof(std::size_t);
|
|
||||||
static constexpr std::size_t regular_head_size = round_up(recycler_size + allocated_size, alignof(std::max_align_t));
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct do_allocate {
|
|
||||||
template <typename... A>
|
|
||||||
static T *apply(A &&... args) noexcept {
|
|
||||||
void *b = mem::alloc(regular_head_size + sizeof(T));
|
|
||||||
auto *p = static_cast<byte *>(b) + regular_head_size;
|
|
||||||
LIBIPC_TRY {
|
|
||||||
T *t = construct<T>(p, std::forward<A>(args)...);
|
|
||||||
*reinterpret_cast<recycle_t *>(b)
|
|
||||||
= [](void *p) noexcept {
|
|
||||||
mem::free(static_cast<byte *>(destroy(static_cast<T *>(p))) - regular_head_size
|
|
||||||
, regular_head_size + sizeof(T));
|
|
||||||
};
|
|
||||||
return t;
|
|
||||||
} LIBIPC_CATCH(...) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct do_allocate<void> {
|
|
||||||
static void *apply(std::size_t bytes) noexcept {
|
|
||||||
if (bytes == 0) return nullptr;
|
|
||||||
std::size_t rbz = regular_head_size + bytes;
|
|
||||||
void *b = mem::alloc(rbz);
|
|
||||||
*reinterpret_cast<recycle_t *>(b)
|
|
||||||
= [](void *p) noexcept {
|
|
||||||
auto *b = static_cast<byte *>(p) - regular_head_size;
|
|
||||||
mem::free(b, *reinterpret_cast<std::size_t *>(b + recycler_size));
|
|
||||||
};
|
|
||||||
auto *z = static_cast<byte *>(b) + recycler_size;
|
|
||||||
*reinterpret_cast<std::size_t *>(z) = rbz;
|
|
||||||
return static_cast<byte *>(b) + regular_head_size;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace detail_new
|
|
||||||
|
|
||||||
/// \brief Creates an object based on the specified type and parameters with block pool resource.
|
|
||||||
/// \note This function is thread-safe.
|
|
||||||
template <typename T, typename... A>
|
|
||||||
T *$new(A &&... args) noexcept {
|
|
||||||
return detail_new::do_allocate<T>::apply(std::forward<A>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief Destroys object previously allocated by the `$new` and releases obtained memory area.
|
|
||||||
/// \note This function is thread-safe. If the pointer type passed in is different from `$new`,
|
|
||||||
/// additional performance penalties may be incurred.
|
|
||||||
inline void $delete(void *p) noexcept {
|
|
||||||
if (p == nullptr) return;
|
|
||||||
auto *r = reinterpret_cast<detail_new::recycle_t *>(static_cast<byte *>(p) - detail_new::regular_head_size);
|
|
||||||
(*r)(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief The destruction policy used by std::unique_ptr.
|
|
||||||
/// \see https://en.cppreference.com/w/cpp/memory/default_delete
|
|
||||||
struct deleter {
|
|
||||||
template <typename T>
|
|
||||||
void operator()(T *p) const noexcept {
|
|
||||||
$delete(p);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace mem
|
|
||||||
} // namespace ipc
|
|
||||||
@ -3,13 +3,13 @@
|
|||||||
#include <cstdint> // std::uint64_t
|
#include <cstdint> // std::uint64_t
|
||||||
#include <system_error>
|
#include <system_error>
|
||||||
|
|
||||||
#include "libipc/imp/export.h"
|
#include "libipc/export.h"
|
||||||
#include "libipc/def.h"
|
#include "libipc/def.h"
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
namespace sync {
|
namespace sync {
|
||||||
|
|
||||||
class LIBIPC_EXPORT mutex {
|
class IPC_EXPORT mutex {
|
||||||
mutex(mutex const &) = delete;
|
mutex(mutex const &) = delete;
|
||||||
mutex &operator=(mutex const &) = delete;
|
mutex &operator=(mutex const &) = delete;
|
||||||
|
|
||||||
|
|||||||
103
include/libipc/pool_alloc.h
Executable file
103
include/libipc/pool_alloc.h
Executable file
@ -0,0 +1,103 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <new>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "libipc/export.h"
|
||||||
|
#include "libipc/def.h"
|
||||||
|
|
||||||
|
namespace ipc {
|
||||||
|
namespace mem {
|
||||||
|
|
||||||
|
class IPC_EXPORT pool_alloc {
|
||||||
|
public:
|
||||||
|
static void* alloc(std::size_t size) noexcept;
|
||||||
|
static void free (void* p, std::size_t size) noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
/// construct/destruct an object
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct impl {
|
||||||
|
template <typename... P>
|
||||||
|
static T* construct(T* p, P&&... params) {
|
||||||
|
::new (p) T(std::forward<P>(params)...);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void destruct(T* p) {
|
||||||
|
reinterpret_cast<T*>(p)->~T();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, size_t N>
|
||||||
|
struct impl<T[N]> {
|
||||||
|
using type = T[N];
|
||||||
|
|
||||||
|
template <typename... P>
|
||||||
|
static type* construct(type* p, P&&... params) {
|
||||||
|
for (size_t i = 0; i < N; ++i) {
|
||||||
|
impl<T>::construct(&((*p)[i]), std::forward<P>(params)...);
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void destruct(type* p) {
|
||||||
|
for (size_t i = 0; i < N; ++i) {
|
||||||
|
impl<T>::destruct(&((*p)[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
template <typename T, typename... P>
|
||||||
|
T* construct(T* p, P&&... params) {
|
||||||
|
return detail::impl<T>::construct(p, std::forward<P>(params)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename... P>
|
||||||
|
T* construct(void* p, P&&... params) {
|
||||||
|
return construct(static_cast<T*>(p), std::forward<P>(params)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void destruct(T* p) {
|
||||||
|
return detail::impl<T>::destruct(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void destruct(void* p) {
|
||||||
|
destruct(static_cast<T*>(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
/// general alloc/free
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
inline void* alloc(std::size_t size) {
|
||||||
|
return pool_alloc::alloc(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename... P>
|
||||||
|
T* alloc(P&&... params) {
|
||||||
|
return construct<T>(pool_alloc::alloc(sizeof(T)), std::forward<P>(params)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void free(void* p, std::size_t size) {
|
||||||
|
pool_alloc::free(p, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void free(T* p) {
|
||||||
|
if (p == nullptr) return;
|
||||||
|
destruct(p);
|
||||||
|
pool_alloc::free(p, sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace mem
|
||||||
|
} // namespace ipc
|
||||||
@ -2,13 +2,13 @@
|
|||||||
|
|
||||||
#include <cstdint> // std::uint64_t
|
#include <cstdint> // std::uint64_t
|
||||||
|
|
||||||
#include "libipc/imp/export.h"
|
#include "libipc/export.h"
|
||||||
#include "libipc/def.h"
|
#include "libipc/def.h"
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
namespace sync {
|
namespace sync {
|
||||||
|
|
||||||
class LIBIPC_EXPORT semaphore {
|
class IPC_EXPORT semaphore {
|
||||||
semaphore(semaphore const &) = delete;
|
semaphore(semaphore const &) = delete;
|
||||||
semaphore &operator=(semaphore const &) = delete;
|
semaphore &operator=(semaphore const &) = delete;
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
#include "libipc/imp/export.h"
|
#include "libipc/export.h"
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
namespace shm {
|
namespace shm {
|
||||||
@ -15,8 +15,8 @@ enum : unsigned {
|
|||||||
open = 0x02
|
open = 0x02
|
||||||
};
|
};
|
||||||
|
|
||||||
LIBIPC_EXPORT id_t acquire(char const * name, std::size_t size, unsigned mode = create | open);
|
IPC_EXPORT id_t acquire(char const * name, std::size_t size, unsigned mode = create | open);
|
||||||
LIBIPC_EXPORT void * get_mem(id_t id, std::size_t * size);
|
IPC_EXPORT void * get_mem(id_t id, std::size_t * size);
|
||||||
|
|
||||||
// Release shared memory resource and clean up disk file if reference count reaches zero.
|
// Release shared memory resource and clean up disk file if reference count reaches zero.
|
||||||
// This function decrements the reference counter. When the counter reaches zero, it:
|
// This function decrements the reference counter. When the counter reaches zero, it:
|
||||||
@ -25,7 +25,7 @@ LIBIPC_EXPORT void * get_mem(id_t id, std::size_t * size);
|
|||||||
// 3. Frees the id structure
|
// 3. Frees the id structure
|
||||||
// After calling this function, the id becomes invalid and must not be used again.
|
// After calling this function, the id becomes invalid and must not be used again.
|
||||||
// Returns: The reference count before decrement, or -1 on error.
|
// Returns: The reference count before decrement, or -1 on error.
|
||||||
LIBIPC_EXPORT std::int32_t release(id_t id) noexcept;
|
IPC_EXPORT std::int32_t release(id_t id) noexcept;
|
||||||
|
|
||||||
// Release shared memory resource and force cleanup of disk file.
|
// Release shared memory resource and force cleanup of disk file.
|
||||||
// This function calls release(id) internally, then unconditionally attempts to
|
// This function calls release(id) internally, then unconditionally attempts to
|
||||||
@ -34,19 +34,19 @@ LIBIPC_EXPORT std::int32_t release(id_t id) noexcept;
|
|||||||
// not in combination with release().
|
// not in combination with release().
|
||||||
// Typical use case: Force cleanup when you want to ensure the disk file is removed
|
// Typical use case: Force cleanup when you want to ensure the disk file is removed
|
||||||
// regardless of reference count state.
|
// regardless of reference count state.
|
||||||
LIBIPC_EXPORT void remove (id_t id) noexcept;
|
IPC_EXPORT void remove (id_t id) noexcept;
|
||||||
|
|
||||||
// Remove shared memory backing file by name.
|
// Remove shared memory backing file by name.
|
||||||
// This function only removes the disk file and does not affect any active memory
|
// This function only removes the disk file and does not affect any active memory
|
||||||
// mappings or id structures. Use this for cleanup of orphaned files or for explicit
|
// mappings or id structures. Use this for cleanup of orphaned files or for explicit
|
||||||
// file removal without affecting runtime resources.
|
// file removal without affecting runtime resources.
|
||||||
// Safe to call at any time, even if shared memory is still in use elsewhere.
|
// Safe to call at any time, even if shared memory is still in use elsewhere.
|
||||||
LIBIPC_EXPORT void remove (char const * name) noexcept;
|
IPC_EXPORT void remove (char const * name) noexcept;
|
||||||
|
|
||||||
LIBIPC_EXPORT std::int32_t get_ref(id_t id);
|
IPC_EXPORT std::int32_t get_ref(id_t id);
|
||||||
LIBIPC_EXPORT void sub_ref(id_t id);
|
IPC_EXPORT void sub_ref(id_t id);
|
||||||
|
|
||||||
class LIBIPC_EXPORT handle {
|
class IPC_EXPORT handle {
|
||||||
public:
|
public:
|
||||||
handle();
|
handle();
|
||||||
handle(char const * name, std::size_t size, unsigned mode = create | open);
|
handle(char const * name, std::size_t size, unsigned mode = create | open);
|
||||||
@ -83,4 +83,3 @@ private:
|
|||||||
|
|
||||||
} // namespace shm
|
} // namespace shm
|
||||||
} // namespace ipc
|
} // namespace ipc
|
||||||
|
|
||||||
|
|||||||
@ -1,12 +1,10 @@
|
|||||||
project(ipc)
|
project(ipc)
|
||||||
|
|
||||||
set (PACKAGE_VERSION 1.4.1)
|
set (PACKAGE_VERSION 1.3.0)
|
||||||
|
|
||||||
aux_source_directory(${LIBIPC_PROJECT_DIR}/src/libipc SRC_FILES)
|
aux_source_directory(${LIBIPC_PROJECT_DIR}/src/libipc SRC_FILES)
|
||||||
aux_source_directory(${LIBIPC_PROJECT_DIR}/src/libipc/sync SRC_FILES)
|
aux_source_directory(${LIBIPC_PROJECT_DIR}/src/libipc/sync SRC_FILES)
|
||||||
aux_source_directory(${LIBIPC_PROJECT_DIR}/src/libipc/platform SRC_FILES)
|
aux_source_directory(${LIBIPC_PROJECT_DIR}/src/libipc/platform SRC_FILES)
|
||||||
aux_source_directory(${LIBIPC_PROJECT_DIR}/src/libipc/imp SRC_FILES)
|
|
||||||
aux_source_directory(${LIBIPC_PROJECT_DIR}/src/libipc/mem SRC_FILES)
|
|
||||||
|
|
||||||
file(GLOB HEAD_FILES
|
file(GLOB HEAD_FILES
|
||||||
${LIBIPC_PROJECT_DIR}/include/libipc/*.h
|
${LIBIPC_PROJECT_DIR}/include/libipc/*.h
|
||||||
|
|||||||
@ -33,7 +33,7 @@ public:
|
|||||||
void init() {
|
void init() {
|
||||||
/* DCLP */
|
/* DCLP */
|
||||||
if (!constructed_.load(std::memory_order_acquire)) {
|
if (!constructed_.load(std::memory_order_acquire)) {
|
||||||
LIBIPC_UNUSED auto guard = ipc::detail::unique_lock(lc_);
|
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lc_);
|
||||||
if (!constructed_.load(std::memory_order_relaxed)) {
|
if (!constructed_.load(std::memory_order_relaxed)) {
|
||||||
::new (this) conn_head_base;
|
::new (this) conn_head_base;
|
||||||
constructed_.store(true, std::memory_order_release);
|
constructed_.store(true, std::memory_order_release);
|
||||||
|
|||||||
@ -1,365 +0,0 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <cstring>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#include "libipc/imp/codecvt.h"
|
|
||||||
#include "libipc/imp/detect_plat.h"
|
|
||||||
|
|
||||||
#if defined(LIBIPC_OS_WIN)
|
|
||||||
# include "libipc/platform/win/codecvt.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief The transform between local-character-set(UTF-8/GBK/...) and UTF-16/32.
|
|
||||||
*
|
|
||||||
* Modified from UnicodeConverter.
|
|
||||||
* Copyright (c) 2010. Jianhui Qin (http://blog.csdn.net/jhqin).
|
|
||||||
*
|
|
||||||
* \remarks codecvt_utf8_utf16/std::wstring_convert is deprecated.
|
|
||||||
* \see https://codingtidbit.com/2020/02/09/c17-codecvt_utf8-is-deprecated/
|
|
||||||
* https://stackoverflow.com/questions/42946335/deprecated-header-codecvt-replacement
|
|
||||||
* https://en.cppreference.com/w/cpp/locale/codecvt/in
|
|
||||||
*/
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
/// \brief X-bit unicode transformation format
|
|
||||||
enum class ufmt {
|
|
||||||
utf8,
|
|
||||||
utf16,
|
|
||||||
utf32,
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T, ufmt, typename = void>
|
|
||||||
struct utf_compatible : std::false_type {};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct utf_compatible<T, ufmt::utf8,
|
|
||||||
std::enable_if_t<std::is_fundamental<T>::value && (sizeof(T) == 1)>> : std::true_type {};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct utf_compatible<T, ufmt::utf16,
|
|
||||||
std::enable_if_t<std::is_fundamental<T>::value && (sizeof(T) == 2)>> : std::true_type {};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct utf_compatible<T, ufmt::utf32,
|
|
||||||
std::enable_if_t<std::is_fundamental<T>::value && (sizeof(T) == 4)>> : std::true_type {};
|
|
||||||
|
|
||||||
template <typename T, ufmt Fmt>
|
|
||||||
constexpr bool utf_compatible_v = utf_compatible<T, Fmt>::value;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief UTF-32 --> UTF-8
|
|
||||||
*/
|
|
||||||
template <typename T, typename U>
|
|
||||||
auto cvt_char(T src, U* des, std::size_t dlen) noexcept
|
|
||||||
-> std::enable_if_t<utf_compatible_v<T, ufmt::utf32> &&
|
|
||||||
utf_compatible_v<U, ufmt::utf8>, std::size_t> {
|
|
||||||
if (src == 0) return 0;
|
|
||||||
|
|
||||||
constexpr std::uint8_t prefix[] = {
|
|
||||||
0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC
|
|
||||||
};
|
|
||||||
constexpr std::uint32_t codeup[] = {
|
|
||||||
0x80, // U+00000000 - U+0000007F
|
|
||||||
0x800, // U+00000080 - U+000007FF
|
|
||||||
0x10000, // U+00000800 - U+0000FFFF
|
|
||||||
0x200000, // U+00010000 - U+001FFFFF
|
|
||||||
0x4000000, // U+00200000 - U+03FFFFFF
|
|
||||||
0x80000000 // U+04000000 - U+7FFFFFFF
|
|
||||||
};
|
|
||||||
|
|
||||||
std::size_t i, len = sizeof(codeup) / sizeof(std::uint32_t);
|
|
||||||
for(i = 0; i < len; ++i) {
|
|
||||||
if (static_cast<std::uint32_t>(src) < codeup[i]) break;
|
|
||||||
}
|
|
||||||
if (i == len) return 0; // the src is invalid
|
|
||||||
|
|
||||||
len = i + 1;
|
|
||||||
if (des != nullptr) {
|
|
||||||
if (dlen > i) for (; i > 0; --i) {
|
|
||||||
des[i] = static_cast<U>((src & 0x3F) | 0x80);
|
|
||||||
src >>= 6;
|
|
||||||
}
|
|
||||||
des[0] = static_cast<U>(src | prefix[len - 1]);
|
|
||||||
}
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief UTF-8 --> UTF-32
|
|
||||||
*/
|
|
||||||
template <typename T, typename U>
|
|
||||||
auto cvt_char(T const *src, std::size_t slen, U &des) noexcept
|
|
||||||
-> std::enable_if_t<utf_compatible_v<T, ufmt::utf8> &&
|
|
||||||
utf_compatible_v<U, ufmt::utf32>, std::size_t> {
|
|
||||||
if ((src == nullptr) || (*src) == 0) return 0;
|
|
||||||
if (slen == 0) return 0;
|
|
||||||
|
|
||||||
std::uint8_t b = (std::uint8_t)*(src++);
|
|
||||||
|
|
||||||
if (b < 0x80) {
|
|
||||||
des = b;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (b < 0xC0 || b > 0xFD) return 0; // the src is invalid
|
|
||||||
|
|
||||||
std::size_t len;
|
|
||||||
if (b < 0xE0) {
|
|
||||||
des = b & 0x1F;
|
|
||||||
len = 2;
|
|
||||||
} else if (b < 0xF0) {
|
|
||||||
des = b & 0x0F;
|
|
||||||
len = 3;
|
|
||||||
} else if (b < 0xF8) {
|
|
||||||
des = b & 0x07;
|
|
||||||
len = 4;
|
|
||||||
} else if (b < 0xFC) {
|
|
||||||
des = b & 0x03;
|
|
||||||
len = 5;
|
|
||||||
} else {
|
|
||||||
des = b & 0x01;
|
|
||||||
len = 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (slen < len) return 0;
|
|
||||||
std::size_t i = 1;
|
|
||||||
for(; i < len; ++i) {
|
|
||||||
b = *(src++);
|
|
||||||
if ((b < 0x80) || (b > 0xBF)) return 0; // the src is invalid
|
|
||||||
des = (des << 6) + (b & 0x3F);
|
|
||||||
}
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief UTF-32 --> UTF-16
|
|
||||||
*/
|
|
||||||
template <typename T, typename U>
|
|
||||||
auto cvt_char(T src, U *des, std::size_t dlen) noexcept
|
|
||||||
-> std::enable_if_t<utf_compatible_v<T, ufmt::utf32> &&
|
|
||||||
utf_compatible_v<U, ufmt::utf16>, std::size_t> {
|
|
||||||
if (src == 0) return 0;
|
|
||||||
|
|
||||||
if (src <= 0xFFFF) {
|
|
||||||
if ((des != nullptr) && (dlen != 0)) {
|
|
||||||
(*des) = static_cast<U>(src);
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
} else if (src <= 0xEFFFF) {
|
|
||||||
if ((des != nullptr) && (dlen > 1)) {
|
|
||||||
des[0] = static_cast<U>(0xD800 + (src >> 10) - 0x40); // high
|
|
||||||
des[1] = static_cast<U>(0xDC00 + (src & 0x03FF)); // low
|
|
||||||
}
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief UTF-16 --> UTF-32
|
|
||||||
*/
|
|
||||||
template <typename T, typename U>
|
|
||||||
auto cvt_char(T const *src, std::size_t slen, U &des)
|
|
||||||
-> std::enable_if_t<utf_compatible_v<T, ufmt::utf16> &&
|
|
||||||
utf_compatible_v<U, ufmt::utf32>, std::size_t> {
|
|
||||||
if ((src == nullptr) || (*src) == 0) return 0;
|
|
||||||
if (slen == 0) return 0;
|
|
||||||
|
|
||||||
std::uint16_t w1 = src[0];
|
|
||||||
if ((w1 >= 0xD800) && (w1 <= 0xDFFF)) {
|
|
||||||
if (w1 < 0xDC00) {
|
|
||||||
if (slen < 2) return 0;
|
|
||||||
std::uint16_t w2 = src[1];
|
|
||||||
if ((w2 >= 0xDC00) && (w2 <= 0xDFFF)) {
|
|
||||||
des = (w2 & 0x03FF) + (((w1 & 0x03FF) + 0x40) << 10);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0; // the src is invalid
|
|
||||||
}
|
|
||||||
des = w1;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief UTF-16 --> UTF-8
|
|
||||||
*/
|
|
||||||
template <typename T, typename U>
|
|
||||||
auto cvt_char(T src, U *des, std::size_t dlen) noexcept
|
|
||||||
-> std::enable_if_t<utf_compatible_v<T, ufmt::utf16> &&
|
|
||||||
utf_compatible_v<U, ufmt::utf8>, std::size_t> {
|
|
||||||
// make utf-16 to utf-32
|
|
||||||
std::uint32_t tmp;
|
|
||||||
if (cvt_char(&src, 1, tmp) != 1) return 0;
|
|
||||||
// make utf-32 to utf-8
|
|
||||||
return cvt_char(tmp, des, dlen);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief UTF-8 --> UTF-16
|
|
||||||
*/
|
|
||||||
template <typename T, typename U>
|
|
||||||
auto cvt_char(T const *src, std::size_t slen, U &des)
|
|
||||||
-> std::enable_if_t<utf_compatible_v<T, ufmt::utf8> &&
|
|
||||||
utf_compatible_v<U, ufmt::utf16>, std::size_t> {
|
|
||||||
// make utf-8 to utf-32
|
|
||||||
std::uint32_t tmp;
|
|
||||||
std::size_t len = cvt_char(src, slen, tmp);
|
|
||||||
if (len == 0) return 0;
|
|
||||||
// make utf-32 to utf-16
|
|
||||||
if (cvt_char(tmp, &des, 1) != 1) return 0;
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief UTF-32 string --> UTF-8/16 string
|
|
||||||
*/
|
|
||||||
template <typename T, typename U>
|
|
||||||
auto cvt_cstr_utf(T const *src, std::size_t slen, U *des, std::size_t dlen) noexcept
|
|
||||||
-> std::enable_if_t<utf_compatible_v<T, ufmt::utf32> &&
|
|
||||||
(utf_compatible_v<U, ufmt::utf16> || utf_compatible_v<U, ufmt::utf8>), std::size_t> {
|
|
||||||
if ((src == nullptr) || ((*src) == 0) || (slen == 0)) {
|
|
||||||
// source string is empty
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
std::size_t num = 0, len = 0;
|
|
||||||
for (std::size_t i = 0; (i < slen) && ((*src) != 0); ++src, ++i) {
|
|
||||||
len = cvt_char(*src, des, dlen);
|
|
||||||
if (len == 0) return 0;
|
|
||||||
if (des != nullptr) {
|
|
||||||
des += len;
|
|
||||||
if (dlen < len) {
|
|
||||||
dlen = 0;
|
|
||||||
} else {
|
|
||||||
dlen -= len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
num += len;
|
|
||||||
}
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief UTF-8/16 string --> UTF-32 string
|
|
||||||
*/
|
|
||||||
template <typename T, typename U>
|
|
||||||
auto cvt_cstr_utf(T const *src, std::size_t slen, U *des, std::size_t dlen) noexcept
|
|
||||||
-> std::enable_if_t<utf_compatible_v<U, ufmt::utf32> &&
|
|
||||||
(utf_compatible_v<T, ufmt::utf16> || utf_compatible_v<T, ufmt::utf8>), std::size_t> {
|
|
||||||
if ((src == nullptr) || ((*src) == 0) || (slen == 0)) {
|
|
||||||
// source string is empty
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
std::size_t num = 0;
|
|
||||||
for (std::size_t i = 0; (i < slen) && ((*src) != 0);) {
|
|
||||||
std::uint32_t tmp;
|
|
||||||
std::size_t len = cvt_char(src, slen - i, tmp);
|
|
||||||
if (len == 0) return 0;
|
|
||||||
if ((des != nullptr) && (dlen > 0)) {
|
|
||||||
(*des) = tmp;
|
|
||||||
++des;
|
|
||||||
dlen -= 1;
|
|
||||||
}
|
|
||||||
src += len;
|
|
||||||
i += len;
|
|
||||||
num += 1;
|
|
||||||
}
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief UTF-8/16 string --> UTF-16/8 string
|
|
||||||
*/
|
|
||||||
template <typename T, typename U>
|
|
||||||
auto cvt_cstr_utf(T const *src, std::size_t slen, U *des, std::size_t dlen) noexcept
|
|
||||||
-> std::enable_if_t<(utf_compatible_v<T, ufmt::utf8> && utf_compatible_v<U, ufmt::utf16>) ||
|
|
||||||
(utf_compatible_v<T, ufmt::utf16> && utf_compatible_v<U, ufmt::utf8>), std::size_t> {
|
|
||||||
if ((src == nullptr) || ((*src) == 0) || (slen == 0)) {
|
|
||||||
// source string is empty
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
std::size_t num = 0;
|
|
||||||
for (std::size_t i = 0; (i < slen) && ((*src) != 0);) {
|
|
||||||
// make utf-x to utf-32
|
|
||||||
std::uint32_t tmp;
|
|
||||||
std::size_t len = cvt_char(src, slen - i, tmp);
|
|
||||||
if (len == 0) return 0;
|
|
||||||
src += len;
|
|
||||||
i += len;
|
|
||||||
// make utf-32 to utf-y
|
|
||||||
len = cvt_char(tmp, des, dlen);
|
|
||||||
if (len == 0) return 0;
|
|
||||||
if (des != nullptr) {
|
|
||||||
des += len;
|
|
||||||
if (dlen < len) {
|
|
||||||
dlen = 0;
|
|
||||||
} else {
|
|
||||||
dlen -= len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
num += len;
|
|
||||||
}
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename U>
|
|
||||||
auto cvt_cstr_utf(T const *src, std::size_t slen, U *des, std::size_t dlen) noexcept
|
|
||||||
-> std::enable_if_t<(sizeof(T) == sizeof(U)), std::size_t> {
|
|
||||||
if ((des == nullptr) || (dlen == 0)) {
|
|
||||||
return slen;
|
|
||||||
}
|
|
||||||
std::size_t r = (std::min)(slen, dlen);
|
|
||||||
std::memcpy(des, src, r * sizeof(T));
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
#define LIBIPC_DEF_CVT_CSTR_($CHAR_T, $CHAR_U) \
|
|
||||||
template <> \
|
|
||||||
std::size_t cvt_cstr($CHAR_T const *src, std::size_t slen, $CHAR_U *des, std::size_t dlen) noexcept { \
|
|
||||||
return cvt_cstr_utf(src, slen, des, dlen); \
|
|
||||||
}
|
|
||||||
// #define LIBIPC_DEF_CVT_CSTR_($CHAR_T, $CHAR_U)
|
|
||||||
|
|
||||||
LIBIPC_DEF_CVT_CSTR_(char , char)
|
|
||||||
LIBIPC_DEF_CVT_CSTR_(char , char16_t)
|
|
||||||
LIBIPC_DEF_CVT_CSTR_(char , char32_t)
|
|
||||||
LIBIPC_DEF_CVT_CSTR_(wchar_t , wchar_t)
|
|
||||||
LIBIPC_DEF_CVT_CSTR_(char16_t, char16_t)
|
|
||||||
LIBIPC_DEF_CVT_CSTR_(char16_t, char)
|
|
||||||
LIBIPC_DEF_CVT_CSTR_(char16_t, char32_t)
|
|
||||||
LIBIPC_DEF_CVT_CSTR_(char32_t, char32_t)
|
|
||||||
LIBIPC_DEF_CVT_CSTR_(char32_t, char)
|
|
||||||
LIBIPC_DEF_CVT_CSTR_(char32_t, char16_t)
|
|
||||||
#if !defined(LIBIPC_OS_WIN)
|
|
||||||
LIBIPC_DEF_CVT_CSTR_(char , wchar_t)
|
|
||||||
LIBIPC_DEF_CVT_CSTR_(wchar_t , char)
|
|
||||||
LIBIPC_DEF_CVT_CSTR_(wchar_t , char16_t)
|
|
||||||
LIBIPC_DEF_CVT_CSTR_(wchar_t , char32_t)
|
|
||||||
LIBIPC_DEF_CVT_CSTR_(char16_t, wchar_t)
|
|
||||||
LIBIPC_DEF_CVT_CSTR_(char32_t, wchar_t)
|
|
||||||
#endif // !defined(LIBIPC_OS_WIN)
|
|
||||||
|
|
||||||
#if defined(LIBIPC_CPP_20)
|
|
||||||
LIBIPC_DEF_CVT_CSTR_(char8_t , char8_t)
|
|
||||||
LIBIPC_DEF_CVT_CSTR_(char8_t , char)
|
|
||||||
LIBIPC_DEF_CVT_CSTR_(char8_t , char16_t)
|
|
||||||
LIBIPC_DEF_CVT_CSTR_(char8_t , char32_t)
|
|
||||||
LIBIPC_DEF_CVT_CSTR_(char , char8_t)
|
|
||||||
LIBIPC_DEF_CVT_CSTR_(char16_t, char8_t)
|
|
||||||
LIBIPC_DEF_CVT_CSTR_(char32_t, char8_t)
|
|
||||||
#if !defined(LIBIPC_OS_WIN)
|
|
||||||
LIBIPC_DEF_CVT_CSTR_(char8_t , wchar_t)
|
|
||||||
LIBIPC_DEF_CVT_CSTR_(wchar_t , char8_t)
|
|
||||||
#endif // !defined(LIBIPC_OS_WIN)
|
|
||||||
#endif // defined(LIBIPC_CPP_20)
|
|
||||||
|
|
||||||
#undef LIBIPC_DEF_CVT_CSTR_
|
|
||||||
|
|
||||||
} // namespace ipc
|
|
||||||
@ -1,329 +0,0 @@
|
|||||||
|
|
||||||
#include <cstdio> // std::snprintf
|
|
||||||
#include <iomanip> // std::put_time
|
|
||||||
#include <sstream> // std::ostringstream
|
|
||||||
#include <array>
|
|
||||||
#include <cstring> // std::memcpy
|
|
||||||
#include <algorithm> // std::min
|
|
||||||
#include <initializer_list>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#include "libipc/imp/fmt.h"
|
|
||||||
#include "libipc/imp/codecvt.h"
|
|
||||||
#include "libipc/imp/detect_plat.h"
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Format conversions helpers.
|
|
||||||
* \see http://personal.ee.surrey.ac.uk/Personal/R.Bowden/C/printf.html
|
|
||||||
* https://en.cppreference.com/w/cpp/io/c/fprintf
|
|
||||||
*/
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
struct sfmt_policy {
|
|
||||||
static constexpr std::size_t aligned_size = 32U;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Policy = sfmt_policy>
|
|
||||||
span<char> local_fmt_sbuf() noexcept {
|
|
||||||
thread_local std::array<char, Policy::aligned_size> sbuf;
|
|
||||||
return sbuf;
|
|
||||||
}
|
|
||||||
|
|
||||||
span<char const> normalize(span<char const> const &a) {
|
|
||||||
if (a.empty()) return {};
|
|
||||||
return a.first(a.size() - (a.back() == '\0' ? 1 : 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
span<char> smem_cpy(span<char> const &sbuf, span<char const> a) noexcept {
|
|
||||||
if (sbuf.empty()) return {};
|
|
||||||
a = normalize(a);
|
|
||||||
auto sz = (std::min)(sbuf.size() - 1, a.size());
|
|
||||||
if (sz != 0) std::memcpy(sbuf.data(), a.data(), sz);
|
|
||||||
return sbuf.first(sz);
|
|
||||||
}
|
|
||||||
|
|
||||||
span<char> sbuf_cpy(span<char> sbuf, span<char const> const &a) noexcept {
|
|
||||||
sbuf = smem_cpy(sbuf, a);
|
|
||||||
*sbuf.end() = '\0';
|
|
||||||
return sbuf;
|
|
||||||
}
|
|
||||||
|
|
||||||
span<char> sbuf_cat(span<char> const &sbuf, std::initializer_list<span<char const>> args) noexcept {
|
|
||||||
std::size_t remain = sbuf.size();
|
|
||||||
for (auto s : args) {
|
|
||||||
remain -= smem_cpy(sbuf.last(remain), s).size();
|
|
||||||
}
|
|
||||||
auto sz = sbuf.size() - remain;
|
|
||||||
sbuf[sz] = '\0';
|
|
||||||
return sbuf.first(sz);
|
|
||||||
}
|
|
||||||
|
|
||||||
char const *as_cstr(span<char const> const &a) {
|
|
||||||
if (a.empty()) return "";
|
|
||||||
if (a.back() == '\0') return a.data();
|
|
||||||
return sbuf_cpy(local_fmt_sbuf(), a).data();
|
|
||||||
}
|
|
||||||
|
|
||||||
span<char> fmt_of(span<char const> const &fstr, span<char const> const &s) {
|
|
||||||
return sbuf_cat(local_fmt_sbuf(), {"%", fstr, s});
|
|
||||||
}
|
|
||||||
|
|
||||||
span<char> fmt_of_unsigned(span<char const> fstr, span<char const> const &l) {
|
|
||||||
if (fstr.empty()) {
|
|
||||||
return fmt_of(l, "u");
|
|
||||||
}
|
|
||||||
fstr = normalize(fstr);
|
|
||||||
switch (fstr.back()) {
|
|
||||||
case 'o':
|
|
||||||
case 'x':
|
|
||||||
case 'X':
|
|
||||||
case 'u': return sbuf_cat(local_fmt_sbuf(), {"%", fstr.first(fstr.size() - 1), l, fstr.last(1)});
|
|
||||||
default : return sbuf_cat(local_fmt_sbuf(), {"%", fstr, l, "u"});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
span<char> fmt_of_signed(span<char const> fstr, span<char const> const &l) {
|
|
||||||
if (fstr.empty()) {
|
|
||||||
return fmt_of(l, "d");
|
|
||||||
}
|
|
||||||
fstr = normalize(fstr);
|
|
||||||
switch (fstr.back()) {
|
|
||||||
case 'o':
|
|
||||||
case 'x':
|
|
||||||
case 'X':
|
|
||||||
case 'u': return fmt_of_unsigned(fstr, l);
|
|
||||||
default : return sbuf_cat(local_fmt_sbuf(), {"%", fstr, l, "d"});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
span<char> fmt_of_float(span<char const> fstr, span<char const> const &l) {
|
|
||||||
if (fstr.empty()) {
|
|
||||||
return fmt_of(l, "f");
|
|
||||||
}
|
|
||||||
fstr = normalize(fstr);
|
|
||||||
switch (fstr.back()) {
|
|
||||||
case 'e':
|
|
||||||
case 'E':
|
|
||||||
case 'g':
|
|
||||||
case 'G': return sbuf_cat(local_fmt_sbuf(), {"%", fstr.first(fstr.size() - 1), l, fstr.last(1)});
|
|
||||||
default : return sbuf_cat(local_fmt_sbuf(), {"%", fstr, l, "f"});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename A /*a fundamental or pointer type*/>
|
|
||||||
int sprintf(fmt_context &ctx, span<char const> const &sfmt, A a) {
|
|
||||||
for (std::int32_t sz = -1;;) {
|
|
||||||
auto sbuf = ctx.buffer(sz + 1);
|
|
||||||
if (sbuf.size() < std::size_t(sz + 1)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
sz = std::snprintf(sbuf.data(), sbuf.size(), sfmt.data(), a);
|
|
||||||
if (sz <= 0) {
|
|
||||||
return sz;
|
|
||||||
}
|
|
||||||
if (std::size_t(sz) < sbuf.size()) {
|
|
||||||
ctx.expend(sz);
|
|
||||||
return sz;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename F /*a function pointer*/,
|
|
||||||
typename A /*a fundamental or pointer type*/>
|
|
||||||
bool sprintf(fmt_context &ctx, F fop, span<char const> const &fstr, span<char const> const &s, A a) noexcept {
|
|
||||||
LIBIPC_TRY {
|
|
||||||
return ipc::sprintf(ctx, fop(fstr, s), a) >= 0;
|
|
||||||
} LIBIPC_CATCH(...) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
/// \brief The context of fmt.
|
|
||||||
|
|
||||||
fmt_context::fmt_context(std::string &j) noexcept
|
|
||||||
: joined_(j)
|
|
||||||
, offset_(0) {}
|
|
||||||
|
|
||||||
std::size_t fmt_context::capacity() noexcept {
|
|
||||||
return (offset_ < sbuf_.size()) ? sbuf_.size() : joined_.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
void fmt_context::reset() noexcept {
|
|
||||||
offset_ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool fmt_context::finish() noexcept {
|
|
||||||
LIBIPC_TRY {
|
|
||||||
if (offset_ < sbuf_.size()) {
|
|
||||||
joined_.assign(sbuf_.data(), offset_);
|
|
||||||
} else {
|
|
||||||
joined_.resize(offset_);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} LIBIPC_CATCH(...) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
span<char> fmt_context::buffer(std::size_t sz) noexcept {
|
|
||||||
auto roundup = [](std::size_t sz) noexcept {
|
|
||||||
constexpr std::size_t fmt_context_aligned_size = 512U;
|
|
||||||
return (sz & ~(fmt_context_aligned_size - 1)) + fmt_context_aligned_size;
|
|
||||||
};
|
|
||||||
auto sbuf = make_span(sbuf_);
|
|
||||||
LIBIPC_TRY {
|
|
||||||
if (offset_ < sbuf.size()) {
|
|
||||||
if ((offset_ + sz) < sbuf.size()) {
|
|
||||||
return sbuf.subspan(offset_);
|
|
||||||
} else {
|
|
||||||
/// \remark switch the cache to std::string
|
|
||||||
joined_.assign(sbuf.data(), offset_);
|
|
||||||
joined_.resize(roundup(offset_ + sz));
|
|
||||||
}
|
|
||||||
} else if ((offset_ + sz) >= joined_.size()) {
|
|
||||||
joined_.resize(roundup(offset_ + sz));
|
|
||||||
}
|
|
||||||
return {&joined_[offset_], joined_.size() - offset_};
|
|
||||||
} LIBIPC_CATCH(...) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void fmt_context::expend(std::size_t sz) noexcept {
|
|
||||||
offset_ += sz;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool fmt_context::append(span<char const> const &str) noexcept {
|
|
||||||
auto sz = str.size();
|
|
||||||
if (sz == 0) return true;
|
|
||||||
if (str.back() == '\0') --sz;
|
|
||||||
auto sbuf = buffer(sz);
|
|
||||||
if (sbuf.size() < sz) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
std::memcpy(sbuf.data(), str.data(), sz);
|
|
||||||
offset_ += sz;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief To string conversion.
|
|
||||||
|
|
||||||
bool to_string(fmt_context &ctx, char const *a) noexcept {
|
|
||||||
return to_string(ctx, a, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
bool to_string(fmt_context &ctx, std::string const &a) noexcept {
|
|
||||||
return ctx.append(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool to_string(fmt_context &ctx, char const *a, span<char const> fstr) noexcept {
|
|
||||||
if (a == nullptr) {
|
|
||||||
return ipc::sprintf(ctx, fmt_of, fstr, "s", "");
|
|
||||||
} else {
|
|
||||||
return ipc::sprintf(ctx, fmt_of, fstr, "s", a);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool to_string(fmt_context &ctx, char a) noexcept {
|
|
||||||
return ipc::sprintf(ctx, fmt_of, {}, "c", a);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool to_string(fmt_context &ctx, wchar_t a) noexcept {
|
|
||||||
LIBIPC_TRY {
|
|
||||||
std::string des;
|
|
||||||
cvt_sstr(std::wstring{a}, des);
|
|
||||||
return ctx.append(des);
|
|
||||||
} LIBIPC_CATCH(...) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool to_string(fmt_context &ctx, char16_t a) noexcept {
|
|
||||||
LIBIPC_TRY {
|
|
||||||
std::string des;
|
|
||||||
cvt_sstr(std::u16string{a}, des);
|
|
||||||
return ctx.append(des);
|
|
||||||
} LIBIPC_CATCH(...) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool to_string(fmt_context &ctx, char32_t a) noexcept {
|
|
||||||
LIBIPC_TRY {
|
|
||||||
std::string des;
|
|
||||||
cvt_sstr(std::u32string{a}, des);
|
|
||||||
return ctx.append(des);
|
|
||||||
} LIBIPC_CATCH(...) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool to_string(fmt_context &ctx, signed short a, span<char const> fstr) noexcept {
|
|
||||||
return ipc::sprintf(ctx, fmt_of_signed, fstr, "h", a);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool to_string(fmt_context &ctx, unsigned short a, span<char const> fstr) noexcept {
|
|
||||||
return ipc::sprintf(ctx, fmt_of_unsigned, fstr, "h", a);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool to_string(fmt_context &ctx, signed int a, span<char const> fstr) noexcept {
|
|
||||||
return ipc::sprintf(ctx, fmt_of_signed, fstr, "", a);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool to_string(fmt_context &ctx, unsigned int a, span<char const> fstr) noexcept {
|
|
||||||
return ipc::sprintf(ctx, fmt_of_unsigned, fstr, "", a);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool to_string(fmt_context &ctx, signed long a, span<char const> fstr) noexcept {
|
|
||||||
return ipc::sprintf(ctx, fmt_of_signed, fstr, "l", a);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool to_string(fmt_context &ctx, unsigned long a, span<char const> fstr) noexcept {
|
|
||||||
return ipc::sprintf(ctx, fmt_of_unsigned, fstr, "l", a);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool to_string(fmt_context &ctx, signed long long a, span<char const> fstr) noexcept {
|
|
||||||
return ipc::sprintf(ctx, fmt_of_signed, fstr, "ll", a);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool to_string(fmt_context &ctx, unsigned long long a, span<char const> fstr) noexcept {
|
|
||||||
return ipc::sprintf(ctx, fmt_of_unsigned, fstr, "ll", a);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool to_string(fmt_context &ctx, double a, span<char const> fstr) noexcept {
|
|
||||||
return ipc::sprintf(ctx, fmt_of_float, fstr, "", a);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool to_string(fmt_context &ctx, long double a, span<char const> fstr) noexcept {
|
|
||||||
return ipc::sprintf(ctx, fmt_of_float, fstr, "L", a);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool to_string(fmt_context &ctx, std::nullptr_t) noexcept {
|
|
||||||
return ctx.append("null");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool to_string(fmt_context &ctx, void const volatile *a) noexcept {
|
|
||||||
if (a == nullptr) {
|
|
||||||
return to_string(ctx, nullptr);
|
|
||||||
}
|
|
||||||
return ipc::sprintf(ctx, fmt_of, "", "p", a);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool to_string(fmt_context &ctx, std::tm const &a, span<char const> fstr) noexcept {
|
|
||||||
if (fstr.empty()) {
|
|
||||||
fstr = "%Y-%m-%d %H:%M:%S";
|
|
||||||
}
|
|
||||||
LIBIPC_TRY {
|
|
||||||
std::ostringstream ss;
|
|
||||||
ss << std::put_time(&a, as_cstr(fstr));
|
|
||||||
return ctx.append(ss.str());
|
|
||||||
} LIBIPC_CATCH(...) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ipc
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
|
|
||||||
#include "libipc/imp/detect_plat.h"
|
|
||||||
#if defined(LIBIPC_CC_GNUC)
|
|
||||||
# include "libipc/platform/gnuc/demangle.h"
|
|
||||||
#else
|
|
||||||
# include "libipc/platform/win/demangle.h"
|
|
||||||
#endif
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
|
|
||||||
#include "libipc/imp/detect_plat.h"
|
|
||||||
#if defined(LIBIPC_OS_WIN)
|
|
||||||
# include "libipc/platform/win/system.h"
|
|
||||||
#else
|
|
||||||
# include "libipc/platform/posix/system.h"
|
|
||||||
#endif
|
|
||||||
@ -14,18 +14,18 @@
|
|||||||
#include "libipc/ipc.h"
|
#include "libipc/ipc.h"
|
||||||
#include "libipc/def.h"
|
#include "libipc/def.h"
|
||||||
#include "libipc/shm.h"
|
#include "libipc/shm.h"
|
||||||
|
#include "libipc/pool_alloc.h"
|
||||||
#include "libipc/queue.h"
|
#include "libipc/queue.h"
|
||||||
#include "libipc/policy.h"
|
#include "libipc/policy.h"
|
||||||
#include "libipc/rw_lock.h"
|
#include "libipc/rw_lock.h"
|
||||||
#include "libipc/waiter.h"
|
#include "libipc/waiter.h"
|
||||||
|
|
||||||
#include "libipc/imp/log.h"
|
#include "libipc/utility/log.h"
|
||||||
#include "libipc/utility/id_pool.h"
|
#include "libipc/utility/id_pool.h"
|
||||||
#include "libipc/utility/scope_guard.h"
|
#include "libipc/utility/scope_guard.h"
|
||||||
#include "libipc/utility/utility.h"
|
#include "libipc/utility/utility.h"
|
||||||
|
|
||||||
#include "libipc/mem/resource.h"
|
#include "libipc/memory/resource.h"
|
||||||
#include "libipc/mem/new.h"
|
|
||||||
#include "libipc/platform/detail.h"
|
#include "libipc/platform/detail.h"
|
||||||
#include "libipc/circ/elem_array.h"
|
#include "libipc/circ/elem_array.h"
|
||||||
|
|
||||||
@ -65,30 +65,24 @@ struct msg_t : msg_t<0, AlignSize> {
|
|||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
ipc::buff_t make_cache(T& data, std::size_t size) {
|
ipc::buff_t make_cache(T& data, std::size_t size) {
|
||||||
auto *ptr = ipc::mem::$new<void>(size);
|
auto ptr = ipc::mem::alloc(size);
|
||||||
std::memcpy(ptr, &data, (ipc::detail::min)(sizeof(data), size));
|
std::memcpy(ptr, &data, (ipc::detail::min)(sizeof(data), size));
|
||||||
return {
|
return { ptr, size, ipc::mem::free };
|
||||||
ptr, size,
|
|
||||||
[](void *p, std::size_t) noexcept {
|
|
||||||
ipc::mem::$delete(p);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
acc_t *cc_acc(std::string const &pref) {
|
acc_t *cc_acc(ipc::string const &pref) {
|
||||||
LIBIPC_LOG();
|
static ipc::unordered_map<ipc::string, ipc::shm::handle> handles;
|
||||||
static auto *phs = new ipc::unordered_map<std::string, ipc::shm::handle>; // no delete
|
|
||||||
static std::mutex lock;
|
static std::mutex lock;
|
||||||
std::lock_guard<std::mutex> guard {lock};
|
std::lock_guard<std::mutex> guard {lock};
|
||||||
auto it = phs->find(pref);
|
auto it = handles.find(pref);
|
||||||
if (it == phs->end()) {
|
if (it == handles.end()) {
|
||||||
std::string shm_name {ipc::make_prefix(pref, "CA_CONN__")};
|
ipc::string shm_name {ipc::make_prefix(pref, {"CA_CONN__"})};
|
||||||
ipc::shm::handle h;
|
ipc::shm::handle h;
|
||||||
if (!h.acquire(shm_name.c_str(), sizeof(acc_t))) {
|
if (!h.acquire(shm_name.c_str(), sizeof(acc_t))) {
|
||||||
log.error("[cc_acc] acquire failed: ", shm_name);
|
ipc::error("[cc_acc] acquire failed: %s\n", shm_name.c_str());
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
it = phs->emplace(pref, std::move(h)).first;
|
it = handles.emplace(pref, std::move(h)).first;
|
||||||
}
|
}
|
||||||
return static_cast<acc_t *>(it->second.get());
|
return static_cast<acc_t *>(it->second.get());
|
||||||
}
|
}
|
||||||
@ -111,8 +105,8 @@ struct cache_t {
|
|||||||
|
|
||||||
struct conn_info_head {
|
struct conn_info_head {
|
||||||
|
|
||||||
std::string prefix_;
|
ipc::string prefix_;
|
||||||
std::string name_;
|
ipc::string name_;
|
||||||
msg_id_t cc_id_; // connection-info id
|
msg_id_t cc_id_; // connection-info id
|
||||||
ipc::detail::waiter cc_waiter_, wt_waiter_, rd_waiter_;
|
ipc::detail::waiter cc_waiter_, wt_waiter_, rd_waiter_;
|
||||||
ipc::shm::handle acc_h_;
|
ipc::shm::handle acc_h_;
|
||||||
@ -123,10 +117,10 @@ struct conn_info_head {
|
|||||||
, cc_id_ {} {}
|
, cc_id_ {} {}
|
||||||
|
|
||||||
void init() {
|
void init() {
|
||||||
if (!cc_waiter_.valid()) cc_waiter_.open(ipc::make_prefix(prefix_, "CC_CONN__", name_).c_str());
|
if (!cc_waiter_.valid()) cc_waiter_.open(ipc::make_prefix(prefix_, {"CC_CONN__", name_}).c_str());
|
||||||
if (!wt_waiter_.valid()) wt_waiter_.open(ipc::make_prefix(prefix_, "WT_CONN__", name_).c_str());
|
if (!wt_waiter_.valid()) wt_waiter_.open(ipc::make_prefix(prefix_, {"WT_CONN__", name_}).c_str());
|
||||||
if (!rd_waiter_.valid()) rd_waiter_.open(ipc::make_prefix(prefix_, "RD_CONN__", name_).c_str());
|
if (!rd_waiter_.valid()) rd_waiter_.open(ipc::make_prefix(prefix_, {"RD_CONN__", name_}).c_str());
|
||||||
if (!acc_h_.valid()) acc_h_.acquire(ipc::make_prefix(prefix_, "AC_CONN__", name_).c_str(), sizeof(acc_t));
|
if (!acc_h_.valid()) acc_h_.acquire(ipc::make_prefix(prefix_, {"AC_CONN__", name_}).c_str(), sizeof(acc_t));
|
||||||
if (cc_id_ != 0) {
|
if (cc_id_ != 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -152,10 +146,10 @@ struct conn_info_head {
|
|||||||
static void clear_storage(char const * prefix, char const * name) noexcept {
|
static void clear_storage(char const * prefix, char const * name) noexcept {
|
||||||
auto p = ipc::make_string(prefix);
|
auto p = ipc::make_string(prefix);
|
||||||
auto n = ipc::make_string(name);
|
auto n = ipc::make_string(name);
|
||||||
ipc::detail::waiter::clear_storage(ipc::make_prefix(p, "CC_CONN__", n).c_str());
|
ipc::detail::waiter::clear_storage(ipc::make_prefix(p, {"CC_CONN__", n}).c_str());
|
||||||
ipc::detail::waiter::clear_storage(ipc::make_prefix(p, "WT_CONN__", n).c_str());
|
ipc::detail::waiter::clear_storage(ipc::make_prefix(p, {"WT_CONN__", n}).c_str());
|
||||||
ipc::detail::waiter::clear_storage(ipc::make_prefix(p, "RD_CONN__", n).c_str());
|
ipc::detail::waiter::clear_storage(ipc::make_prefix(p, {"RD_CONN__", n}).c_str());
|
||||||
ipc::shm::handle::clear_storage(ipc::make_prefix(p, "AC_CONN__", n).c_str());
|
ipc::shm::handle::clear_storage(ipc::make_prefix(p, {"AC_CONN__", n}).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void quit_waiting() {
|
void quit_waiting() {
|
||||||
@ -214,15 +208,14 @@ struct chunk_info_t {
|
|||||||
|
|
||||||
auto& chunk_storages() {
|
auto& chunk_storages() {
|
||||||
class chunk_handle_t {
|
class chunk_handle_t {
|
||||||
ipc::unordered_map<std::string, ipc::shm::handle> handles_;
|
ipc::unordered_map<ipc::string, ipc::shm::handle> handles_;
|
||||||
std::mutex lock_;
|
std::mutex lock_;
|
||||||
|
|
||||||
static bool make_handle(ipc::shm::handle &h, std::string const &shm_name, std::size_t chunk_size) {
|
static bool make_handle(ipc::shm::handle &h, ipc::string const &shm_name, std::size_t chunk_size) {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (!h.valid() &&
|
if (!h.valid() &&
|
||||||
!h.acquire( shm_name.c_str(),
|
!h.acquire( shm_name.c_str(),
|
||||||
sizeof(chunk_info_t) + chunk_info_t::chunks_mem_size(chunk_size) )) {
|
sizeof(chunk_info_t) + chunk_info_t::chunks_mem_size(chunk_size) )) {
|
||||||
log.error("[chunk_storages] chunk_shm.id_info_.acquire failed: chunk_size = ", chunk_size);
|
ipc::error("[chunk_storages] chunk_shm.id_info_.acquire failed: chunk_size = %zd\n", chunk_size);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -230,9 +223,8 @@ auto& chunk_storages() {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
chunk_info_t *get_info(conn_info_head *inf, std::size_t chunk_size) {
|
chunk_info_t *get_info(conn_info_head *inf, std::size_t chunk_size) {
|
||||||
LIBIPC_LOG();
|
ipc::string pref {(inf == nullptr) ? ipc::string{} : inf->prefix_};
|
||||||
std::string pref {(inf == nullptr) ? std::string{} : inf->prefix_};
|
ipc::string shm_name {ipc::make_prefix(pref, {"CHUNK_INFO__", ipc::to_string(chunk_size)})};
|
||||||
std::string shm_name {ipc::make_prefix(pref, "CHUNK_INFO__", chunk_size)};
|
|
||||||
ipc::shm::handle *h;
|
ipc::shm::handle *h;
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> guard {lock_};
|
std::lock_guard<std::mutex> guard {lock_};
|
||||||
@ -243,7 +235,7 @@ auto& chunk_storages() {
|
|||||||
}
|
}
|
||||||
auto *info = static_cast<chunk_info_t*>(h->get());
|
auto *info = static_cast<chunk_info_t*>(h->get());
|
||||||
if (info == nullptr) {
|
if (info == nullptr) {
|
||||||
log.error("[chunk_storages] chunk_shm.id_info_.get failed: chunk_size = ", chunk_size);
|
ipc::error("[chunk_storages] chunk_shm.id_info_.get failed: chunk_size = %zd\n", chunk_size);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return info;
|
return info;
|
||||||
@ -251,8 +243,8 @@ auto& chunk_storages() {
|
|||||||
};
|
};
|
||||||
using deleter_t = void (*)(chunk_handle_t*);
|
using deleter_t = void (*)(chunk_handle_t*);
|
||||||
using chunk_handle_ptr_t = std::unique_ptr<chunk_handle_t, deleter_t>;
|
using chunk_handle_ptr_t = std::unique_ptr<chunk_handle_t, deleter_t>;
|
||||||
static auto *chunk_hs = new ipc::map<std::size_t, chunk_handle_ptr_t>; // no delete
|
static ipc::map<std::size_t, chunk_handle_ptr_t> chunk_hs;
|
||||||
return *chunk_hs;
|
return chunk_hs;
|
||||||
}
|
}
|
||||||
|
|
||||||
chunk_info_t *chunk_storage_info(conn_info_head *inf, std::size_t chunk_size) {
|
chunk_info_t *chunk_storage_info(conn_info_head *inf, std::size_t chunk_size) {
|
||||||
@ -260,15 +252,15 @@ chunk_info_t *chunk_storage_info(conn_info_head *inf, std::size_t chunk_size) {
|
|||||||
std::decay_t<decltype(storages)>::iterator it;
|
std::decay_t<decltype(storages)>::iterator it;
|
||||||
{
|
{
|
||||||
static ipc::rw_lock lock;
|
static ipc::rw_lock lock;
|
||||||
LIBIPC_UNUSED std::shared_lock<ipc::rw_lock> guard {lock};
|
IPC_UNUSED_ std::shared_lock<ipc::rw_lock> guard {lock};
|
||||||
if ((it = storages.find(chunk_size)) == storages.end()) {
|
if ((it = storages.find(chunk_size)) == storages.end()) {
|
||||||
using chunk_handle_ptr_t = std::decay_t<decltype(storages)>::value_type::second_type;
|
using chunk_handle_ptr_t = std::decay_t<decltype(storages)>::value_type::second_type;
|
||||||
using chunk_handle_t = chunk_handle_ptr_t::element_type;
|
using chunk_handle_t = chunk_handle_ptr_t::element_type;
|
||||||
guard.unlock();
|
guard.unlock();
|
||||||
LIBIPC_UNUSED std::lock_guard<ipc::rw_lock> guard {lock};
|
IPC_UNUSED_ std::lock_guard<ipc::rw_lock> guard {lock};
|
||||||
it = storages.emplace(chunk_size, chunk_handle_ptr_t{
|
it = storages.emplace(chunk_size, chunk_handle_ptr_t{
|
||||||
ipc::mem::$new<chunk_handle_t>(), [](chunk_handle_t *p) {
|
ipc::mem::alloc<chunk_handle_t>(), [](chunk_handle_t *p) {
|
||||||
ipc::mem::$delete(p);
|
ipc::mem::destruct(p);
|
||||||
}}).first;
|
}}).first;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -293,9 +285,8 @@ std::pair<ipc::storage_id_t, void*> acquire_storage(conn_info_head *inf, std::si
|
|||||||
}
|
}
|
||||||
|
|
||||||
void *find_storage(ipc::storage_id_t id, conn_info_head *inf, std::size_t size) {
|
void *find_storage(ipc::storage_id_t id, conn_info_head *inf, std::size_t size) {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (id < 0) {
|
if (id < 0) {
|
||||||
log.error("[find_storage] id is invalid: id = ", (long)id, ", size = ", size);
|
ipc::error("[find_storage] id is invalid: id = %ld, size = %zd\n", (long)id, size);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
std::size_t chunk_size = calc_chunk_size(size);
|
std::size_t chunk_size = calc_chunk_size(size);
|
||||||
@ -305,9 +296,8 @@ void *find_storage(ipc::storage_id_t id, conn_info_head *inf, std::size_t size)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void release_storage(ipc::storage_id_t id, conn_info_head *inf, std::size_t size) {
|
void release_storage(ipc::storage_id_t id, conn_info_head *inf, std::size_t size) {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (id < 0) {
|
if (id < 0) {
|
||||||
log.error("[release_storage] id is invalid: id = ", (long)id, ", size = ", size);
|
ipc::error("[release_storage] id is invalid: id = %ld, size = %zd\n", (long)id, size);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
std::size_t chunk_size = calc_chunk_size(size);
|
std::size_t chunk_size = calc_chunk_size(size);
|
||||||
@ -339,9 +329,8 @@ bool sub_rc(ipc::wr<Rp, Rc, ipc::trans::broadcast>,
|
|||||||
|
|
||||||
template <typename Flag>
|
template <typename Flag>
|
||||||
void recycle_storage(ipc::storage_id_t id, conn_info_head *inf, std::size_t size, ipc::circ::cc_t curr_conns, ipc::circ::cc_t conn_id) {
|
void recycle_storage(ipc::storage_id_t id, conn_info_head *inf, std::size_t size, ipc::circ::cc_t curr_conns, ipc::circ::cc_t conn_id) {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (id < 0) {
|
if (id < 0) {
|
||||||
log.error("[recycle_storage] id is invalid: id = ", (long)id, ", size = ", size);
|
ipc::error("[recycle_storage] id is invalid: id = %ld, size = %zd\n", (long)id, size);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
std::size_t chunk_size = calc_chunk_size(size);
|
std::size_t chunk_size = calc_chunk_size(size);
|
||||||
@ -361,12 +350,11 @@ void recycle_storage(ipc::storage_id_t id, conn_info_head *inf, std::size_t size
|
|||||||
|
|
||||||
template <typename MsgT>
|
template <typename MsgT>
|
||||||
bool clear_message(conn_info_head *inf, void* p) {
|
bool clear_message(conn_info_head *inf, void* p) {
|
||||||
LIBIPC_LOG();
|
|
||||||
auto msg = static_cast<MsgT*>(p);
|
auto msg = static_cast<MsgT*>(p);
|
||||||
if (msg->storage_) {
|
if (msg->storage_) {
|
||||||
std::int32_t r_size = static_cast<std::int32_t>(ipc::data_length) + msg->remain_;
|
std::int32_t r_size = static_cast<std::int32_t>(ipc::data_length) + msg->remain_;
|
||||||
if (r_size <= 0) {
|
if (r_size <= 0) {
|
||||||
log.error("[clear_message] invalid msg size: ", (int)r_size);
|
ipc::error("[clear_message] invalid msg size: %d\n", (int)r_size);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
release_storage(*reinterpret_cast<ipc::storage_id_t*>(&msg->data_),
|
release_storage(*reinterpret_cast<ipc::storage_id_t*>(&msg->data_),
|
||||||
@ -406,11 +394,11 @@ struct queue_generator {
|
|||||||
void init() {
|
void init() {
|
||||||
conn_info_head::init();
|
conn_info_head::init();
|
||||||
if (!que_.valid()) {
|
if (!que_.valid()) {
|
||||||
que_.open(ipc::make_prefix(prefix_,
|
que_.open(ipc::make_prefix(prefix_, {
|
||||||
"QU_CONN__",
|
"QU_CONN__",
|
||||||
this->name_,
|
this->name_,
|
||||||
"__", DataSize,
|
"__", ipc::to_string(DataSize),
|
||||||
"__", AlignSize).c_str());
|
"__", ipc::to_string(AlignSize)}).c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -420,11 +408,11 @@ struct queue_generator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void clear_storage(char const * prefix, char const * name) noexcept {
|
static void clear_storage(char const * prefix, char const * name) noexcept {
|
||||||
queue_t::clear_storage(ipc::make_prefix(prefix,
|
queue_t::clear_storage(ipc::make_prefix(ipc::make_string(prefix), {
|
||||||
"QU_CONN__",
|
"QU_CONN__",
|
||||||
name,
|
ipc::make_string(name),
|
||||||
"__", DataSize,
|
"__", ipc::to_string(DataSize),
|
||||||
"__", AlignSize).c_str());
|
"__", ipc::to_string(AlignSize)}).c_str());
|
||||||
conn_info_head::clear_storage(prefix, name);
|
conn_info_head::clear_storage(prefix, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -459,7 +447,7 @@ constexpr static queue_t* queue_of(ipc::handle_t h) noexcept {
|
|||||||
static bool connect(ipc::handle_t * ph, ipc::prefix pref, char const * name, bool start_to_recv) {
|
static bool connect(ipc::handle_t * ph, ipc::prefix pref, char const * name, bool start_to_recv) {
|
||||||
assert(ph != nullptr);
|
assert(ph != nullptr);
|
||||||
if (*ph == nullptr) {
|
if (*ph == nullptr) {
|
||||||
*ph = ipc::mem::$new<conn_info_t>(pref.str, name);
|
*ph = ipc::mem::alloc<conn_info_t>(pref.str, name);
|
||||||
}
|
}
|
||||||
return reconnect(ph, start_to_recv);
|
return reconnect(ph, start_to_recv);
|
||||||
}
|
}
|
||||||
@ -502,7 +490,7 @@ static bool reconnect(ipc::handle_t * ph, bool start_to_recv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void destroy(ipc::handle_t h) noexcept {
|
static void destroy(ipc::handle_t h) noexcept {
|
||||||
ipc::mem::$delete(info_of(h));
|
ipc::mem::free(info_of(h));
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::size_t recv_count(ipc::handle_t h) noexcept {
|
static std::size_t recv_count(ipc::handle_t h) noexcept {
|
||||||
@ -525,34 +513,33 @@ static bool wait_for_recv(ipc::handle_t h, std::size_t r_count, std::uint64_t tm
|
|||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
static bool send(F&& gen_push, ipc::handle_t h, void const * data, std::size_t size) {
|
static bool send(F&& gen_push, ipc::handle_t h, void const * data, std::size_t size) {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (data == nullptr || size == 0) {
|
if (data == nullptr || size == 0) {
|
||||||
log.error("fail: send(", data, ", ", size, ")");
|
ipc::error("fail: send(%p, %zd)\n", data, size);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto que = queue_of(h);
|
auto que = queue_of(h);
|
||||||
if (que == nullptr) {
|
if (que == nullptr) {
|
||||||
log.error("fail: send, queue_of(h) == nullptr");
|
ipc::error("fail: send, queue_of(h) == nullptr\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (que->elems() == nullptr) {
|
if (que->elems() == nullptr) {
|
||||||
log.error("fail: send, queue_of(h)->elems() == nullptr");
|
ipc::error("fail: send, queue_of(h)->elems() == nullptr\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!que->ready_sending()) {
|
if (!que->ready_sending()) {
|
||||||
log.error("fail: send, que->ready_sending() == false");
|
ipc::error("fail: send, que->ready_sending() == false\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ipc::circ::cc_t conns = que->elems()->connections(std::memory_order_relaxed);
|
ipc::circ::cc_t conns = que->elems()->connections(std::memory_order_relaxed);
|
||||||
if (conns == 0) {
|
if (conns == 0) {
|
||||||
log.error("fail: send, there is no receiver on this connection.");
|
ipc::error("fail: send, there is no receiver on this connection.\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// calc a new message id
|
// calc a new message id
|
||||||
conn_info_t *inf = info_of(h);
|
conn_info_t *inf = info_of(h);
|
||||||
auto acc = inf->acc();
|
auto acc = inf->acc();
|
||||||
if (acc == nullptr) {
|
if (acc == nullptr) {
|
||||||
log.error("fail: send, info_of(h)->acc() == nullptr");
|
ipc::error("fail: send, info_of(h)->acc() == nullptr\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto msg_id = acc->fetch_add(1, std::memory_order_relaxed);
|
auto msg_id = acc->fetch_add(1, std::memory_order_relaxed);
|
||||||
@ -566,7 +553,7 @@ static bool send(F&& gen_push, ipc::handle_t h, void const * data, std::size_t s
|
|||||||
static_cast<std::int32_t>(ipc::data_length), &(dat.first), 0);
|
static_cast<std::int32_t>(ipc::data_length), &(dat.first), 0);
|
||||||
}
|
}
|
||||||
// try using message fragment
|
// try using message fragment
|
||||||
//log.debug("fail: shm::handle for big message. msg_id: ", msg_id, ", size: ", size);
|
//ipc::log("fail: shm::handle for big message. msg_id: %zd, size: %zd\n", msg_id, size);
|
||||||
}
|
}
|
||||||
// push message fragment
|
// push message fragment
|
||||||
std::int32_t offset = 0;
|
std::int32_t offset = 0;
|
||||||
@ -589,15 +576,14 @@ static bool send(F&& gen_push, ipc::handle_t h, void const * data, std::size_t s
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool send(ipc::handle_t h, void const * data, std::size_t size, std::uint64_t tm) {
|
static bool send(ipc::handle_t h, void const * data, std::size_t size, std::uint64_t tm) {
|
||||||
LIBIPC_LOG();
|
return send([tm](auto *info, auto *que, auto msg_id) {
|
||||||
return send([tm, &log](auto *info, auto *que, auto msg_id) {
|
return [tm, info, que, msg_id](std::int32_t remain, void const * data, std::size_t size) {
|
||||||
return [tm, &log, info, que, msg_id](std::int32_t remain, void const * data, std::size_t size) {
|
|
||||||
if (!wait_for(info->wt_waiter_, [&] {
|
if (!wait_for(info->wt_waiter_, [&] {
|
||||||
return !que->push(
|
return !que->push(
|
||||||
[](void*) { return true; },
|
[](void*) { return true; },
|
||||||
info->cc_id_, msg_id, remain, data, size);
|
info->cc_id_, msg_id, remain, data, size);
|
||||||
}, tm)) {
|
}, tm)) {
|
||||||
log.debug("force_push: msg_id = ", msg_id, ", remain = ", remain, ", size = ", size);
|
ipc::log("force_push: msg_id = %zd, remain = %d, size = %zd\n", msg_id, remain, size);
|
||||||
if (!que->force_push(
|
if (!que->force_push(
|
||||||
[info](void* p) { return clear_message<typename queue_t::value_t>(info, p); },
|
[info](void* p) { return clear_message<typename queue_t::value_t>(info, p); },
|
||||||
info->cc_id_, msg_id, remain, data, size)) {
|
info->cc_id_, msg_id, remain, data, size)) {
|
||||||
@ -627,10 +613,9 @@ static bool try_send(ipc::handle_t h, void const * data, std::size_t size, std::
|
|||||||
}
|
}
|
||||||
|
|
||||||
static ipc::buff_t recv(ipc::handle_t h, std::uint64_t tm) {
|
static ipc::buff_t recv(ipc::handle_t h, std::uint64_t tm) {
|
||||||
LIBIPC_LOG();
|
|
||||||
auto que = queue_of(h);
|
auto que = queue_of(h);
|
||||||
if (que == nullptr) {
|
if (que == nullptr) {
|
||||||
log.error("fail: recv, queue_of(h) == nullptr");
|
ipc::error("fail: recv, queue_of(h) == nullptr\n");
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
if (!que->connected()) {
|
if (!que->connected()) {
|
||||||
@ -658,7 +643,7 @@ static ipc::buff_t recv(ipc::handle_t h, std::uint64_t tm) {
|
|||||||
// msg.remain_ may minus & abs(msg.remain_) < data_length
|
// msg.remain_ may minus & abs(msg.remain_) < data_length
|
||||||
std::int32_t r_size = static_cast<std::int32_t>(ipc::data_length) + msg.remain_;
|
std::int32_t r_size = static_cast<std::int32_t>(ipc::data_length) + msg.remain_;
|
||||||
if (r_size <= 0) {
|
if (r_size <= 0) {
|
||||||
log.error("fail: recv, r_size = ", (int)r_size);
|
ipc::error("fail: recv, r_size = %d\n", (int)r_size);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
std::size_t msg_size = static_cast<std::size_t>(r_size);
|
std::size_t msg_size = static_cast<std::size_t>(r_size);
|
||||||
@ -672,20 +657,20 @@ static ipc::buff_t recv(ipc::handle_t h, std::uint64_t tm) {
|
|||||||
conn_info_t * inf;
|
conn_info_t * inf;
|
||||||
ipc::circ::cc_t curr_conns;
|
ipc::circ::cc_t curr_conns;
|
||||||
ipc::circ::cc_t conn_id;
|
ipc::circ::cc_t conn_id;
|
||||||
} *r_info = ipc::mem::$new<recycle_t>(recycle_t{
|
} *r_info = ipc::mem::alloc<recycle_t>(recycle_t{
|
||||||
buf_id,
|
buf_id,
|
||||||
inf,
|
inf,
|
||||||
que->elems()->connections(std::memory_order_relaxed),
|
que->elems()->connections(std::memory_order_relaxed),
|
||||||
que->connected_id()
|
que->connected_id()
|
||||||
});
|
});
|
||||||
if (r_info == nullptr) {
|
if (r_info == nullptr) {
|
||||||
log.error("fail: ipc::mem::$new<recycle_t>.");
|
ipc::log("fail: ipc::mem::alloc<recycle_t>.\n");
|
||||||
return ipc::buff_t{buf, msg_size}; // no recycle
|
return ipc::buff_t{buf, msg_size}; // no recycle
|
||||||
} else {
|
} else {
|
||||||
return ipc::buff_t{buf, msg_size, [](void* p_info, std::size_t size) {
|
return ipc::buff_t{buf, msg_size, [](void* p_info, std::size_t size) {
|
||||||
auto r_info = static_cast<recycle_t *>(p_info);
|
auto r_info = static_cast<recycle_t *>(p_info);
|
||||||
LIBIPC_UNUSED auto finally = ipc::guard([r_info] {
|
IPC_UNUSED_ auto finally = ipc::guard([r_info] {
|
||||||
ipc::mem::$delete(r_info);
|
ipc::mem::free(r_info);
|
||||||
});
|
});
|
||||||
recycle_storage<flag_t>(r_info->storage_id,
|
recycle_storage<flag_t>(r_info->storage_id,
|
||||||
r_info->inf,
|
r_info->inf,
|
||||||
@ -695,7 +680,7 @@ static ipc::buff_t recv(ipc::handle_t h, std::uint64_t tm) {
|
|||||||
}, r_info};
|
}, r_info};
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.error("fail: shm::handle for large message. msg_id: ", msg.id_, ", buf_id: ", buf_id, ", size: ", msg_size);
|
ipc::log("fail: shm::handle for large message. msg_id: %zd, buf_id: %zd, size: %zd\n", msg.id_, buf_id, msg_size);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,53 +0,0 @@
|
|||||||
|
|
||||||
#include <algorithm> // std::swap
|
|
||||||
|
|
||||||
#include "libipc/imp/log.h"
|
|
||||||
#include "libipc/mem/bytes_allocator.h"
|
|
||||||
#include "libipc/mem/memory_resource.h"
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
namespace mem {
|
|
||||||
|
|
||||||
bytes_allocator::holder_mr_base &bytes_allocator::get_holder() noexcept {
|
|
||||||
return *reinterpret_cast<holder_mr_base *>(holder_.data());
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes_allocator::holder_mr_base const &bytes_allocator::get_holder() const noexcept {
|
|
||||||
return *reinterpret_cast<holder_mr_base const *>(holder_.data());
|
|
||||||
}
|
|
||||||
|
|
||||||
void bytes_allocator::init_default_resource() noexcept {
|
|
||||||
std::ignore = ipc::construct<holder_mr<new_delete_resource>>(holder_.data(), new_delete_resource::get());
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes_allocator::bytes_allocator() noexcept
|
|
||||||
: bytes_allocator(new_delete_resource::get()) {}
|
|
||||||
|
|
||||||
bytes_allocator::~bytes_allocator() noexcept {
|
|
||||||
ipc::destroy(&get_holder());
|
|
||||||
}
|
|
||||||
|
|
||||||
void bytes_allocator::swap(bytes_allocator &other) noexcept {
|
|
||||||
std::swap(this->holder_, other.holder_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *bytes_allocator::allocate(std::size_t s, std::size_t a) const {
|
|
||||||
LIBIPC_LOG();
|
|
||||||
if ((a & (a - 1)) != 0) {
|
|
||||||
log.error("failed: allocate alignment is not a power of 2.");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return get_holder().alloc(s, a);
|
|
||||||
}
|
|
||||||
|
|
||||||
void bytes_allocator::deallocate(void *p, std::size_t s, std::size_t a) const {
|
|
||||||
LIBIPC_LOG();
|
|
||||||
if ((a & (a - 1)) != 0) {
|
|
||||||
log.error("failed: allocate alignment is not a power of 2.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
get_holder().dealloc(p, s, a);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace mem
|
|
||||||
} // namespace ipc
|
|
||||||
@ -1,47 +0,0 @@
|
|||||||
|
|
||||||
#include <mutex>
|
|
||||||
#include <array>
|
|
||||||
#include <cstddef>
|
|
||||||
|
|
||||||
#include "libipc/def.h"
|
|
||||||
#include "libipc/imp/detect_plat.h"
|
|
||||||
#include "libipc/imp/byte.h"
|
|
||||||
#include "libipc/mem/bytes_allocator.h"
|
|
||||||
#include "libipc/mem/memory_resource.h"
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
namespace mem {
|
|
||||||
|
|
||||||
class thread_safe_resource : public monotonic_buffer_resource {
|
|
||||||
public:
|
|
||||||
thread_safe_resource(span<byte> buffer) noexcept
|
|
||||||
: monotonic_buffer_resource(buffer) {}
|
|
||||||
|
|
||||||
~thread_safe_resource() noexcept {
|
|
||||||
LIBIPC_UNUSED std::lock_guard<std::mutex> lock(mutex_);
|
|
||||||
monotonic_buffer_resource::release();
|
|
||||||
}
|
|
||||||
|
|
||||||
void *allocate(std::size_t bytes, std::size_t alignment) noexcept {
|
|
||||||
LIBIPC_UNUSED std::lock_guard<std::mutex> lock(mutex_);
|
|
||||||
return monotonic_buffer_resource::allocate(bytes, alignment);
|
|
||||||
}
|
|
||||||
|
|
||||||
void deallocate(void *p, std::size_t bytes, std::size_t alignment) noexcept {
|
|
||||||
LIBIPC_UNUSED std::lock_guard<std::mutex> lock(mutex_);
|
|
||||||
monotonic_buffer_resource::deallocate(p, bytes, alignment);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::mutex mutex_;
|
|
||||||
};
|
|
||||||
|
|
||||||
bytes_allocator ¢ral_cache_allocator() noexcept {
|
|
||||||
static std::array<byte, central_cache_default_size> buf;
|
|
||||||
static thread_safe_resource res(buf);
|
|
||||||
static bytes_allocator a(&res);
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace mem
|
|
||||||
} // namespace ipc
|
|
||||||
@ -1,140 +0,0 @@
|
|||||||
|
|
||||||
#include <utility>
|
|
||||||
#include <memory>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#include "libipc/imp/log.h"
|
|
||||||
#include "libipc/imp/aligned.h"
|
|
||||||
#include "libipc/imp/detect_plat.h"
|
|
||||||
#include "libipc/mem/memory_resource.h"
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
namespace mem {
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
template <typename Node>
|
|
||||||
Node *make_node(bytes_allocator const &upstream, std::size_t initial_size, std::size_t alignment) noexcept {
|
|
||||||
LIBIPC_LOG();
|
|
||||||
auto sz = ipc::round_up(sizeof(Node), alignment) + initial_size;
|
|
||||||
LIBIPC_TRY {
|
|
||||||
auto *node = static_cast<Node *>(upstream.allocate(sz));
|
|
||||||
if (node == nullptr) {
|
|
||||||
log.error("failed: allocate memory for `monotonic_buffer_resource`'s node.",
|
|
||||||
" bytes = ", initial_size, ", alignment = ", alignment);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
node->next = nullptr;
|
|
||||||
node->size = sz;
|
|
||||||
return node;
|
|
||||||
} LIBIPC_CATCH(...) {
|
|
||||||
log.error("failed: allocate memory for `monotonic_buffer_resource`'s node.",
|
|
||||||
" bytes = ", initial_size, ", alignment = ", alignment,
|
|
||||||
"\n\texception: ", ipc::log::exception_string(std::current_exception()));
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t next_buffer_size(std::size_t size) noexcept {
|
|
||||||
return size * 3 / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
monotonic_buffer_resource::monotonic_buffer_resource() noexcept
|
|
||||||
: monotonic_buffer_resource(bytes_allocator{}) {}
|
|
||||||
|
|
||||||
monotonic_buffer_resource::monotonic_buffer_resource(bytes_allocator upstream) noexcept
|
|
||||||
: monotonic_buffer_resource(0, std::move(upstream)) {}
|
|
||||||
|
|
||||||
monotonic_buffer_resource::monotonic_buffer_resource(std::size_t initial_size) noexcept
|
|
||||||
: monotonic_buffer_resource(initial_size, bytes_allocator{}) {}
|
|
||||||
|
|
||||||
monotonic_buffer_resource::monotonic_buffer_resource(std::size_t initial_size, bytes_allocator upstream) noexcept
|
|
||||||
: upstream_ (std::move(upstream))
|
|
||||||
, free_list_ (nullptr)
|
|
||||||
, head_ (nullptr)
|
|
||||||
, tail_ (nullptr)
|
|
||||||
, next_size_ (initial_size)
|
|
||||||
, initial_buffer_(nullptr)
|
|
||||||
, initial_size_ (initial_size) {}
|
|
||||||
|
|
||||||
monotonic_buffer_resource::monotonic_buffer_resource(ipc::span<ipc::byte> buffer) noexcept
|
|
||||||
: monotonic_buffer_resource(buffer, bytes_allocator{}) {}
|
|
||||||
|
|
||||||
monotonic_buffer_resource::monotonic_buffer_resource(ipc::span<ipc::byte> buffer, bytes_allocator upstream) noexcept
|
|
||||||
: upstream_ (std::move(upstream))
|
|
||||||
, free_list_ (nullptr)
|
|
||||||
, head_ (buffer.begin())
|
|
||||||
, tail_ (buffer.end())
|
|
||||||
, next_size_ (next_buffer_size(buffer.size()))
|
|
||||||
, initial_buffer_(buffer.begin())
|
|
||||||
, initial_size_ (buffer.size()) {}
|
|
||||||
|
|
||||||
monotonic_buffer_resource::~monotonic_buffer_resource() noexcept {
|
|
||||||
release();
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes_allocator monotonic_buffer_resource::upstream_resource() const noexcept {
|
|
||||||
return upstream_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void monotonic_buffer_resource::release() noexcept {
|
|
||||||
LIBIPC_LOG();
|
|
||||||
LIBIPC_TRY {
|
|
||||||
while (free_list_ != nullptr) {
|
|
||||||
auto *next = free_list_->next;
|
|
||||||
upstream_.deallocate(free_list_, free_list_->size);
|
|
||||||
free_list_ = next;
|
|
||||||
}
|
|
||||||
} LIBIPC_CATCH(...) {
|
|
||||||
log.error("failed: deallocate memory for `monotonic_buffer_resource`.",
|
|
||||||
"\n\texception: ", ipc::log::exception_string(std::current_exception()));
|
|
||||||
}
|
|
||||||
// reset to initial state at contruction
|
|
||||||
if ((head_ = initial_buffer_) != nullptr) {
|
|
||||||
tail_ = head_ + initial_size_;
|
|
||||||
next_size_ = next_buffer_size(initial_size_);
|
|
||||||
} else {
|
|
||||||
tail_ = nullptr;
|
|
||||||
next_size_ = initial_size_;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void *monotonic_buffer_resource::allocate(std::size_t bytes, std::size_t alignment) noexcept {
|
|
||||||
LIBIPC_LOG();
|
|
||||||
if (bytes == 0) {
|
|
||||||
log.error("failed: allocate bytes = 0.");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
void *p = head_;
|
|
||||||
auto s = static_cast<std::size_t>(tail_ - head_);
|
|
||||||
if (std::align(alignment, bytes, p, s) == nullptr) {
|
|
||||||
next_size_ = (std::max)(next_size_, bytes);
|
|
||||||
auto *node = make_node<monotonic_buffer_resource::node>(upstream_, next_size_, alignment);
|
|
||||||
if (node == nullptr) return nullptr;
|
|
||||||
node->next = free_list_;
|
|
||||||
free_list_ = node;
|
|
||||||
next_size_ = next_buffer_size(next_size_);
|
|
||||||
// try again
|
|
||||||
s = node->size - sizeof(monotonic_buffer_resource::node);
|
|
||||||
p = std::align(alignment, bytes, (p = node + 1), s);
|
|
||||||
if (p == nullptr) {
|
|
||||||
log.error("failed: allocate memory for `monotonic_buffer_resource`.",
|
|
||||||
" bytes = ", bytes, ", alignment = ", alignment);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
tail_ = static_cast<ipc::byte *>(p) + s;
|
|
||||||
}
|
|
||||||
head_ = static_cast<ipc::byte *>(p) + bytes;
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
void monotonic_buffer_resource::deallocate(void *p, std::size_t bytes, std::size_t alignment) noexcept {
|
|
||||||
static_cast<void>(p);
|
|
||||||
static_cast<void>(bytes);
|
|
||||||
static_cast<void>(alignment);
|
|
||||||
// Do nothing.
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace mem
|
|
||||||
} // namespace ipc
|
|
||||||
@ -1,121 +0,0 @@
|
|||||||
|
|
||||||
#include "libipc/mem/new.h"
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
namespace mem {
|
|
||||||
|
|
||||||
/// \brief Select the incremental level based on the size.
|
|
||||||
constexpr inline std::size_t regular_level(std::size_t s) noexcept {
|
|
||||||
return (s <= 128 ) ? 0 :
|
|
||||||
(s <= 1024 ) ? 1 :
|
|
||||||
(s <= 8192 ) ? 2 :
|
|
||||||
(s <= 65536) ? 3 : 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief Use block pools to handle memory less than 64K.
|
|
||||||
template <std::size_t BlockSize, std::size_t BlockPoolExpansion>
|
|
||||||
class block_resource_base : public block_pool<BlockSize, BlockPoolExpansion>
|
|
||||||
, public block_collector {
|
|
||||||
public:
|
|
||||||
void *allocate(std::size_t /*bytes*/) noexcept override {
|
|
||||||
return block_pool<BlockSize, BlockPoolExpansion>::allocate();
|
|
||||||
}
|
|
||||||
|
|
||||||
void deallocate(void *p, std::size_t /*bytes*/) noexcept override {
|
|
||||||
block_pool<BlockSize, BlockPoolExpansion>::deallocate(p);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// \brief Use `new`/`delete` to handle memory larger than 64K.
|
|
||||||
template <>
|
|
||||||
class block_resource_base<0, 0> : public new_delete_resource
|
|
||||||
, public block_collector {
|
|
||||||
public:
|
|
||||||
void *allocate(std::size_t bytes) noexcept override {
|
|
||||||
return new_delete_resource::allocate(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
void deallocate(void *p, std::size_t bytes) noexcept override {
|
|
||||||
new_delete_resource::deallocate(p, bytes);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// \brief Defines block pool memory resource based on block pool.
|
|
||||||
template <std::size_t BlockSize, std::size_t BlockPoolExpansion>
|
|
||||||
class block_pool_resource : public block_resource_base<BlockSize, BlockPoolExpansion> {
|
|
||||||
public:
|
|
||||||
static block_collector &get() noexcept {
|
|
||||||
thread_local block_pool_resource instance;
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// \brief Matches the appropriate memory block resource based on a specified size.
|
|
||||||
block_collector &get_regular_resource(std::size_t s) noexcept {
|
|
||||||
std::size_t l = regular_level(s);
|
|
||||||
switch (l) {
|
|
||||||
case 0:
|
|
||||||
switch (round_up<std::size_t>(s, 16)) {
|
|
||||||
case 16 : return block_pool_resource<16 , 512>::get();
|
|
||||||
case 32 : return block_pool_resource<32 , 512>::get();
|
|
||||||
case 48 : return block_pool_resource<48 , 512>::get();
|
|
||||||
case 64 : return block_pool_resource<64 , 512>::get();
|
|
||||||
case 80 : return block_pool_resource<80 , 512>::get();
|
|
||||||
case 96 : return block_pool_resource<96 , 512>::get();
|
|
||||||
case 112: return block_pool_resource<112, 512>::get();
|
|
||||||
case 128: return block_pool_resource<128, 512>::get();
|
|
||||||
default : break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
switch (round_up<std::size_t>(s, 128)) {
|
|
||||||
case 256 : return block_pool_resource<256 , 256>::get();
|
|
||||||
case 384 : return block_pool_resource<384 , 256>::get();
|
|
||||||
case 512 : return block_pool_resource<512 , 256>::get();
|
|
||||||
case 640 : return block_pool_resource<640 , 256>::get();
|
|
||||||
case 768 : return block_pool_resource<768 , 256>::get();
|
|
||||||
case 896 : return block_pool_resource<896 , 256>::get();
|
|
||||||
case 1024: return block_pool_resource<1024, 256>::get();
|
|
||||||
default : break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
switch (round_up<std::size_t>(s, 1024)) {
|
|
||||||
case 2048: return block_pool_resource<2048, 128>::get();
|
|
||||||
case 3072: return block_pool_resource<3072, 128>::get();
|
|
||||||
case 4096: return block_pool_resource<4096, 128>::get();
|
|
||||||
case 5120: return block_pool_resource<5120, 128>::get();
|
|
||||||
case 6144: return block_pool_resource<6144, 128>::get();
|
|
||||||
case 7168: return block_pool_resource<7168, 128>::get();
|
|
||||||
case 8192: return block_pool_resource<8192, 128>::get();
|
|
||||||
default : break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
switch (round_up<std::size_t>(s, 8192)) {
|
|
||||||
case 16384: return block_pool_resource<16384, 64>::get();
|
|
||||||
case 24576: return block_pool_resource<24576, 64>::get();
|
|
||||||
case 32768: return block_pool_resource<32768, 64>::get();
|
|
||||||
case 40960: return block_pool_resource<40960, 64>::get();
|
|
||||||
case 49152: return block_pool_resource<49152, 64>::get();
|
|
||||||
case 57344: return block_pool_resource<57344, 64>::get();
|
|
||||||
case 65536: return block_pool_resource<65536, 64>::get();
|
|
||||||
default : break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return block_pool_resource<0, 0>::get();
|
|
||||||
}
|
|
||||||
|
|
||||||
void *alloc(std::size_t bytes) noexcept {
|
|
||||||
return get_regular_resource(bytes).allocate(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
void free(void *p, std::size_t bytes) noexcept {
|
|
||||||
return get_regular_resource(bytes).deallocate(p, bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace mem
|
|
||||||
} // namespace ipc
|
|
||||||
@ -1,101 +0,0 @@
|
|||||||
|
|
||||||
#include <cstdlib> // std::aligned_alloc
|
|
||||||
|
|
||||||
#include "libipc/imp/detect_plat.h"
|
|
||||||
#include "libipc/imp/aligned.h"
|
|
||||||
#include "libipc/imp/system.h"
|
|
||||||
#include "libipc/imp/log.h"
|
|
||||||
#include "libipc/mem/memory_resource.h"
|
|
||||||
#include "libipc/mem/verify_args.h"
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
namespace mem {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Returns a pointer to a new_delete_resource.
|
|
||||||
*
|
|
||||||
* \return new_delete_resource*
|
|
||||||
*/
|
|
||||||
new_delete_resource *new_delete_resource::get() noexcept {
|
|
||||||
static new_delete_resource mem_res;
|
|
||||||
return &mem_res;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Allocates storage with a size of at least bytes bytes, aligned to the specified alignment.
|
|
||||||
* Alignment shall be a power of two.
|
|
||||||
*
|
|
||||||
* \see https://en.cppreference.com/w/cpp/memory/memory_resource/do_allocate
|
|
||||||
* https://www.cppstories.com/2019/08/newnew-align/
|
|
||||||
*
|
|
||||||
* \return void * - nullptr if storage of the requested size and alignment cannot be obtained.
|
|
||||||
*/
|
|
||||||
void *new_delete_resource::allocate(std::size_t bytes, std::size_t alignment) noexcept {
|
|
||||||
LIBIPC_LOG();
|
|
||||||
if (!verify_args(bytes, alignment)) {
|
|
||||||
log.error("invalid bytes = ", bytes, ", alignment = ", alignment);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
#if defined(LIBIPC_CPP_17)
|
|
||||||
/// \see https://en.cppreference.com/w/cpp/memory/c/aligned_alloc
|
|
||||||
/// \remark The size parameter must be an integral multiple of alignment.
|
|
||||||
return std::aligned_alloc(alignment, ipc::round_up(bytes, alignment));
|
|
||||||
#else
|
|
||||||
if (alignment <= alignof(std::max_align_t)) {
|
|
||||||
/// \see https://en.cppreference.com/w/cpp/memory/c/malloc
|
|
||||||
return std::malloc(bytes);
|
|
||||||
}
|
|
||||||
#if defined(LIBIPC_OS_WIN)
|
|
||||||
/// \see https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/aligned-malloc
|
|
||||||
return ::_aligned_malloc(bytes, alignment);
|
|
||||||
#else // try posix
|
|
||||||
/// \see https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_memalign.html
|
|
||||||
void *p = nullptr;
|
|
||||||
int ret = ::posix_memalign(&p, alignment, bytes);
|
|
||||||
if (ret != 0) {
|
|
||||||
log.error("failed: posix_memalign(alignment = ", alignment,
|
|
||||||
", bytes = ", bytes,
|
|
||||||
"). error = ", sys::error(ret));
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return p;
|
|
||||||
#endif
|
|
||||||
#endif // defined(LIBIPC_CPP_17)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Deallocates the storage pointed to by p.
|
|
||||||
* The storage it points to must not yet have been deallocated, otherwise the behavior is undefined.
|
|
||||||
*
|
|
||||||
* \see https://en.cppreference.com/w/cpp/memory/memory_resource/do_deallocate
|
|
||||||
*
|
|
||||||
* \param p must have been returned by a prior call to new_delete_resource::do_allocate(bytes, alignment).
|
|
||||||
*/
|
|
||||||
void new_delete_resource::deallocate(void *p, std::size_t bytes, std::size_t alignment) noexcept {
|
|
||||||
LIBIPC_LOG();
|
|
||||||
if (p == nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!verify_args(bytes, alignment)) {
|
|
||||||
log.error("invalid bytes = ", bytes, ", alignment = ", alignment);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#if defined(LIBIPC_CPP_17)
|
|
||||||
/// \see https://en.cppreference.com/w/cpp/memory/c/free
|
|
||||||
std::free(p);
|
|
||||||
#else
|
|
||||||
if (alignment <= alignof(std::max_align_t)) {
|
|
||||||
std::free(p);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#if defined(LIBIPC_OS_WIN)
|
|
||||||
/// \see https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/aligned-free
|
|
||||||
::_aligned_free(p);
|
|
||||||
#else // try posix
|
|
||||||
::free(p);
|
|
||||||
#endif
|
|
||||||
#endif // defined(LIBIPC_CPP_17)
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace mem
|
|
||||||
} // namespace ipc
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "libipc/def.h"
|
|
||||||
#include "libipc/imp/fmt.h"
|
|
||||||
#include "libipc/mem/container_allocator.h"
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
|
|
||||||
template <typename Key, typename T>
|
|
||||||
using unordered_map = std::unordered_map<
|
|
||||||
Key, T, std::hash<Key>, std::equal_to<Key>, ipc::mem::container_allocator<std::pair<Key const, T>>
|
|
||||||
>;
|
|
||||||
|
|
||||||
template <typename Key, typename T>
|
|
||||||
using map = std::map<
|
|
||||||
Key, T, std::less<Key>, ipc::mem::container_allocator<std::pair<Key const, T>>
|
|
||||||
>;
|
|
||||||
|
|
||||||
/// \brief Check string validity.
|
|
||||||
constexpr bool is_valid_string(char const *str) noexcept {
|
|
||||||
return (str != nullptr) && (str[0] != '\0');
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief Make a valid string.
|
|
||||||
inline std::string make_string(char const *str) {
|
|
||||||
return is_valid_string(str) ? std::string{str} : std::string{};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief Combine prefix from a list of strings.
|
|
||||||
template <typename A1, typename... A>
|
|
||||||
inline std::string make_prefix(A1 &&prefix, A &&...args) {
|
|
||||||
return ipc::fmt(std::forward<A1>(prefix), "__IPC_SHM__", std::forward<A>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ipc
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
namespace mem {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Check that bytes is not 0 and that the alignment is a power of two.
|
|
||||||
*/
|
|
||||||
inline constexpr bool verify_args(std::size_t bytes, std::size_t alignment) noexcept {
|
|
||||||
return (bytes > 0) && (alignment > 0) && ((alignment & (alignment - 1)) == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace mem
|
|
||||||
} // namespace ipc
|
|
||||||
424
src/libipc/memory/alloc.h
Executable file
424
src/libipc/memory/alloc.h
Executable file
@ -0,0 +1,424 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <utility>
|
||||||
|
#include <iterator>
|
||||||
|
#include <limits> // std::numeric_limits
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cassert> // assert
|
||||||
|
|
||||||
|
#include "libipc/def.h"
|
||||||
|
#include "libipc/rw_lock.h"
|
||||||
|
|
||||||
|
#include "libipc/utility/concept.h"
|
||||||
|
#include "libipc/memory/allocator_wrapper.h"
|
||||||
|
#include "libipc/platform/detail.h"
|
||||||
|
|
||||||
|
namespace ipc {
|
||||||
|
namespace mem {
|
||||||
|
|
||||||
|
class static_alloc {
|
||||||
|
public:
|
||||||
|
static void swap(static_alloc&) noexcept {}
|
||||||
|
|
||||||
|
static void* alloc(std::size_t size) noexcept {
|
||||||
|
return size ? std::malloc(size) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free(void* p) noexcept {
|
||||||
|
std::free(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free(void* p, std::size_t /*size*/) noexcept {
|
||||||
|
free(p);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
/// Scope allocation -- The destructor will release all allocated blocks.
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
constexpr std::size_t aligned(std::size_t size, size_t alignment) noexcept {
|
||||||
|
return ( (size - 1) & ~(alignment - 1) ) + alignment;
|
||||||
|
}
|
||||||
|
|
||||||
|
IPC_CONCEPT_(has_take, take(std::move(std::declval<Type>())));
|
||||||
|
|
||||||
|
class scope_alloc_base {
|
||||||
|
protected:
|
||||||
|
struct block_t {
|
||||||
|
std::size_t size_;
|
||||||
|
block_t * next_;
|
||||||
|
} * head_ = nullptr, * tail_ = nullptr;
|
||||||
|
|
||||||
|
enum : std::size_t {
|
||||||
|
aligned_block_size = aligned(sizeof(block_t), alignof(std::max_align_t))
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
void swap(scope_alloc_base & rhs) {
|
||||||
|
std::swap(head_, rhs.head_);
|
||||||
|
std::swap(tail_, rhs.tail_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const noexcept {
|
||||||
|
return head_ == nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void take(scope_alloc_base && rhs) {
|
||||||
|
if (rhs.empty()) return;
|
||||||
|
if (empty()) swap(rhs);
|
||||||
|
else {
|
||||||
|
std::swap(tail_->next_, rhs.head_);
|
||||||
|
// rhs.head_ should be nullptr here
|
||||||
|
tail_ = rhs.tail_;
|
||||||
|
rhs.tail_ = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void free(void* /*p*/) {}
|
||||||
|
void free(void* /*p*/, std::size_t) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
template <typename AllocP = static_alloc>
|
||||||
|
class scope_alloc : public detail::scope_alloc_base {
|
||||||
|
public:
|
||||||
|
using base_t = detail::scope_alloc_base;
|
||||||
|
using alloc_policy = AllocP;
|
||||||
|
|
||||||
|
private:
|
||||||
|
alloc_policy alloc_;
|
||||||
|
|
||||||
|
void free_all() {
|
||||||
|
while (!empty()) {
|
||||||
|
auto curr = head_;
|
||||||
|
head_ = head_->next_;
|
||||||
|
alloc_.free(curr, curr->size_);
|
||||||
|
}
|
||||||
|
// now head_ is nullptr
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
scope_alloc() = default;
|
||||||
|
|
||||||
|
scope_alloc(scope_alloc && rhs) { swap(rhs); }
|
||||||
|
scope_alloc& operator=(scope_alloc rhs) { swap(rhs); return (*this); }
|
||||||
|
|
||||||
|
~scope_alloc() { free_all(); }
|
||||||
|
|
||||||
|
void swap(scope_alloc& rhs) {
|
||||||
|
alloc_.swap(rhs.alloc_);
|
||||||
|
base_t::swap(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename A = AllocP>
|
||||||
|
auto take(scope_alloc && rhs) -> ipc::require<detail::has_take<A>::value> {
|
||||||
|
base_t::take(std::move(rhs));
|
||||||
|
alloc_.take(std::move(rhs.alloc_));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename A = AllocP>
|
||||||
|
auto take(scope_alloc && rhs) -> ipc::require<!detail::has_take<A>::value> {
|
||||||
|
base_t::take(std::move(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
void* alloc(std::size_t size) {
|
||||||
|
std::size_t real_size = aligned_block_size + size;
|
||||||
|
auto curr = static_cast<block_t*>(alloc_.alloc(real_size));
|
||||||
|
curr->size_ = real_size;
|
||||||
|
curr->next_ = head_;
|
||||||
|
head_ = curr;
|
||||||
|
if (tail_ == nullptr) {
|
||||||
|
tail_ = curr;
|
||||||
|
}
|
||||||
|
return (reinterpret_cast<byte_t*>(curr) + aligned_block_size);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
/// Fixed-size blocks allocation
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
class fixed_alloc_base {
|
||||||
|
protected:
|
||||||
|
std::size_t block_size_;
|
||||||
|
std::size_t init_expand_;
|
||||||
|
void * cursor_;
|
||||||
|
|
||||||
|
void init(std::size_t block_size, std::size_t init_expand) {
|
||||||
|
block_size_ = block_size;
|
||||||
|
init_expand_ = init_expand;
|
||||||
|
cursor_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void** node_p(void* node) {
|
||||||
|
return reinterpret_cast<void**>(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto& next(void* node) {
|
||||||
|
return *node_p(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool operator<(fixed_alloc_base const & right) const {
|
||||||
|
return init_expand_ < right.init_expand_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_block_size(std::size_t block_size) {
|
||||||
|
block_size_ = block_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void swap(fixed_alloc_base& rhs) {
|
||||||
|
std::swap(block_size_ , rhs.block_size_);
|
||||||
|
std::swap(init_expand_, rhs.init_expand_);
|
||||||
|
std::swap(cursor_ , rhs.cursor_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const noexcept {
|
||||||
|
return cursor_ == nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void take(fixed_alloc_base && rhs) {
|
||||||
|
assert(block_size_ == rhs.block_size_);
|
||||||
|
init_expand_ = (ipc::detail::max)(init_expand_, rhs.init_expand_);
|
||||||
|
if (rhs.empty()) return;
|
||||||
|
auto curr = cursor_;
|
||||||
|
if (curr != nullptr) while (1) {
|
||||||
|
auto next_cur = next(curr);
|
||||||
|
if (next_cur == nullptr) {
|
||||||
|
std::swap(next(curr), rhs.cursor_);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// next_cur != nullptr
|
||||||
|
else curr = next_cur;
|
||||||
|
}
|
||||||
|
// curr == nullptr, means cursor_ == nullptr
|
||||||
|
else std::swap(cursor_, rhs.cursor_);
|
||||||
|
// rhs.cursor_ must be nullptr
|
||||||
|
}
|
||||||
|
|
||||||
|
void free(void* p) {
|
||||||
|
if (p == nullptr) return;
|
||||||
|
next(p) = cursor_;
|
||||||
|
cursor_ = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void free(void* p, std::size_t) {
|
||||||
|
free(p);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename AllocP, typename ExpandP>
|
||||||
|
class fixed_alloc : public detail::fixed_alloc_base {
|
||||||
|
public:
|
||||||
|
using base_t = detail::fixed_alloc_base;
|
||||||
|
using alloc_policy = AllocP;
|
||||||
|
|
||||||
|
private:
|
||||||
|
alloc_policy alloc_;
|
||||||
|
|
||||||
|
void* try_expand() {
|
||||||
|
if (empty()) {
|
||||||
|
auto size = ExpandP::next(block_size_, init_expand_);
|
||||||
|
auto p = node_p(cursor_ = alloc_.alloc(size));
|
||||||
|
for (std::size_t i = 0; i < (size / block_size_) - 1; ++i)
|
||||||
|
p = node_p((*p) = reinterpret_cast<byte_t*>(p) + block_size_);
|
||||||
|
(*p) = nullptr;
|
||||||
|
}
|
||||||
|
return cursor_;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit fixed_alloc(std::size_t block_size, std::size_t init_expand = 1) {
|
||||||
|
init(block_size, init_expand);
|
||||||
|
}
|
||||||
|
|
||||||
|
fixed_alloc(fixed_alloc && rhs) {
|
||||||
|
init(0, 0);
|
||||||
|
swap(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
fixed_alloc& operator=(fixed_alloc rhs) {
|
||||||
|
swap(rhs);
|
||||||
|
return (*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void swap(fixed_alloc& rhs) {
|
||||||
|
alloc_.swap(rhs.alloc_);
|
||||||
|
base_t::swap(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename A = AllocP>
|
||||||
|
auto take(fixed_alloc && rhs) -> ipc::require<detail::has_take<A>::value> {
|
||||||
|
base_t::take(std::move(rhs));
|
||||||
|
alloc_.take(std::move(rhs.alloc_));
|
||||||
|
}
|
||||||
|
|
||||||
|
void* alloc() {
|
||||||
|
void* p = try_expand();
|
||||||
|
cursor_ = next(p);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* alloc(std::size_t) {
|
||||||
|
return alloc();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
template <std::size_t BaseSize = sizeof(void*) * 1024,
|
||||||
|
std::size_t LimitSize = (std::numeric_limits<std::uint32_t>::max)()>
|
||||||
|
struct fixed_expand_policy {
|
||||||
|
|
||||||
|
enum : std::size_t {
|
||||||
|
base_size = BaseSize,
|
||||||
|
limit_size = LimitSize
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr static std::size_t prev(std::size_t e) noexcept {
|
||||||
|
return ((e / 2) == 0) ? 1 : (e / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr static std::size_t next(std::size_t e) noexcept {
|
||||||
|
return e * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::size_t next(std::size_t block_size, std::size_t & e) {
|
||||||
|
auto n = ipc::detail::max<std::size_t>(block_size, base_size) * e;
|
||||||
|
e = ipc::detail::min<std::size_t>(limit_size, next(e));
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t BlockSize,
|
||||||
|
typename AllocP = scope_alloc<>,
|
||||||
|
typename ExpandP = fixed_expand_policy<>>
|
||||||
|
class fixed_alloc : public detail::fixed_alloc<AllocP, ExpandP> {
|
||||||
|
public:
|
||||||
|
using base_t = detail::fixed_alloc<AllocP, ExpandP>;
|
||||||
|
|
||||||
|
enum : std::size_t {
|
||||||
|
block_size = ipc::detail::max<std::size_t>(BlockSize, sizeof(void*))
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit fixed_alloc(std::size_t init_expand)
|
||||||
|
: base_t(block_size, init_expand) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fixed_alloc() : fixed_alloc(1) {}
|
||||||
|
|
||||||
|
fixed_alloc(fixed_alloc && rhs)
|
||||||
|
: base_t(std::move(rhs)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fixed_alloc& operator=(fixed_alloc rhs) {
|
||||||
|
swap(rhs);
|
||||||
|
return (*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void swap(fixed_alloc& rhs) {
|
||||||
|
base_t::swap(rhs);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
/// Variable-size blocks allocation (without alignment)
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
class variable_alloc_base {
|
||||||
|
protected:
|
||||||
|
byte_t * head_ = nullptr, * tail_ = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void swap(variable_alloc_base & rhs) {
|
||||||
|
std::swap(head_, rhs.head_);
|
||||||
|
std::swap(tail_, rhs.tail_);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t remain() const noexcept {
|
||||||
|
return static_cast<std::size_t>(tail_ - head_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const noexcept {
|
||||||
|
return remain() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void take(variable_alloc_base && rhs) {
|
||||||
|
if (remain() < rhs.remain()) {
|
||||||
|
// replace this by rhs
|
||||||
|
head_ = rhs.head_;
|
||||||
|
tail_ = rhs.tail_;
|
||||||
|
}
|
||||||
|
// discard rhs
|
||||||
|
rhs.head_ = rhs.tail_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void free(void* /*p*/) {}
|
||||||
|
void free(void* /*p*/, std::size_t) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
template <std::size_t ChunkSize = (sizeof(void*) * 1024), typename AllocP = scope_alloc<>>
|
||||||
|
class variable_alloc : public detail::variable_alloc_base {
|
||||||
|
public:
|
||||||
|
using base_t = detail::variable_alloc_base;
|
||||||
|
using alloc_policy = AllocP;
|
||||||
|
|
||||||
|
enum : std::size_t {
|
||||||
|
aligned_chunk_size = detail::aligned(ChunkSize, alignof(std::max_align_t))
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
alloc_policy alloc_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
variable_alloc() = default;
|
||||||
|
|
||||||
|
variable_alloc(variable_alloc && rhs) { swap(rhs); }
|
||||||
|
variable_alloc& operator=(variable_alloc rhs) { swap(rhs); return (*this); }
|
||||||
|
|
||||||
|
void swap(variable_alloc& rhs) {
|
||||||
|
alloc_.swap(rhs.alloc_);
|
||||||
|
base_t::swap(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename A = AllocP>
|
||||||
|
auto take(variable_alloc && rhs) -> ipc::require<detail::has_take<A>::value> {
|
||||||
|
base_t::take(std::move(rhs));
|
||||||
|
alloc_.take(std::move(rhs.alloc_));
|
||||||
|
}
|
||||||
|
|
||||||
|
void* alloc(std::size_t size) {
|
||||||
|
/*
|
||||||
|
* byte alignment is always alignof(std::max_align_t).
|
||||||
|
*/
|
||||||
|
size = detail::aligned(size, alignof(std::max_align_t));
|
||||||
|
void* ptr;
|
||||||
|
// size would never be 0 here
|
||||||
|
if (remain() < size) {
|
||||||
|
std::size_t chunk_size = ipc::detail::max<std::size_t>(aligned_chunk_size, size);
|
||||||
|
ptr = alloc_.alloc(chunk_size);
|
||||||
|
tail_ = static_cast<byte_t*>(ptr) + chunk_size;
|
||||||
|
head_ = tail_ - (chunk_size - size);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ptr = head_;
|
||||||
|
head_ += size;
|
||||||
|
}
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mem
|
||||||
|
} // namespace ipc
|
||||||
121
src/libipc/memory/allocator_wrapper.h
Executable file
121
src/libipc/memory/allocator_wrapper.h
Executable file
@ -0,0 +1,121 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <limits> // std::numeric_limits
|
||||||
|
#include <utility> // std::forward
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
#include "libipc/pool_alloc.h"
|
||||||
|
|
||||||
|
namespace ipc {
|
||||||
|
namespace mem {
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
/// The allocator wrapper class for STL
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <typename T, typename AllocP>
|
||||||
|
struct rebind {
|
||||||
|
template <typename U>
|
||||||
|
using alloc_t = AllocP;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, template <typename> class AllocT>
|
||||||
|
struct rebind<T, AllocT<T>> {
|
||||||
|
template <typename U>
|
||||||
|
using alloc_t = AllocT<U>;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
template <typename T, typename AllocP>
|
||||||
|
class allocator_wrapper {
|
||||||
|
|
||||||
|
template <typename U, typename AllocU>
|
||||||
|
friend class allocator_wrapper;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// type definitions
|
||||||
|
typedef T value_type;
|
||||||
|
typedef value_type* pointer;
|
||||||
|
typedef const value_type* const_pointer;
|
||||||
|
typedef value_type& reference;
|
||||||
|
typedef const value_type& const_reference;
|
||||||
|
typedef std::size_t size_type;
|
||||||
|
typedef std::ptrdiff_t difference_type;
|
||||||
|
typedef AllocP alloc_policy;
|
||||||
|
|
||||||
|
private:
|
||||||
|
alloc_policy alloc_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
allocator_wrapper() noexcept {}
|
||||||
|
|
||||||
|
// construct by copying (do nothing)
|
||||||
|
allocator_wrapper (const allocator_wrapper<T, AllocP>&) noexcept {}
|
||||||
|
allocator_wrapper& operator=(const allocator_wrapper<T, AllocP>&) noexcept { return *this; }
|
||||||
|
|
||||||
|
// construct from a related allocator (do nothing)
|
||||||
|
template <typename U, typename AllocU> allocator_wrapper (const allocator_wrapper<U, AllocU>&) noexcept {}
|
||||||
|
template <typename U, typename AllocU> allocator_wrapper& operator=(const allocator_wrapper<U, AllocU>&) noexcept { return *this; }
|
||||||
|
|
||||||
|
allocator_wrapper (allocator_wrapper && rhs) noexcept : alloc_ ( std::move(rhs.alloc_) ) {}
|
||||||
|
allocator_wrapper& operator=(allocator_wrapper && rhs) noexcept { alloc_ = std::move(rhs.alloc_); return *this; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
// the other type of std_allocator
|
||||||
|
template <typename U>
|
||||||
|
struct rebind {
|
||||||
|
using other = allocator_wrapper< U, typename detail::rebind<T, AllocP>::template alloc_t<U> >;
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr size_type max_size(void) const noexcept {
|
||||||
|
return (std::numeric_limits<size_type>::max)() / sizeof(value_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
pointer allocate(size_type count) noexcept {
|
||||||
|
if (count == 0) return nullptr;
|
||||||
|
if (count > this->max_size()) return nullptr;
|
||||||
|
return static_cast<pointer>(alloc_.alloc(count * sizeof(value_type)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void deallocate(pointer p, size_type count) noexcept {
|
||||||
|
alloc_.free(p, count * sizeof(value_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... P>
|
||||||
|
static void construct(pointer p, P && ... params) {
|
||||||
|
ipc::mem::construct(p, std::forward<P>(params)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void destroy(pointer p) {
|
||||||
|
ipc::mem::destruct(p);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class AllocP>
|
||||||
|
class allocator_wrapper<void, AllocP> {
|
||||||
|
public:
|
||||||
|
// type definitions
|
||||||
|
typedef void value_type;
|
||||||
|
typedef value_type* pointer;
|
||||||
|
typedef const value_type* const_pointer;
|
||||||
|
typedef std::size_t size_type;
|
||||||
|
typedef std::ptrdiff_t difference_type;
|
||||||
|
typedef AllocP alloc_policy;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U, class AllocP>
|
||||||
|
constexpr bool operator==(const allocator_wrapper<T, AllocP>&, const allocator_wrapper<U, AllocP>&) noexcept {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename U, class AllocP>
|
||||||
|
constexpr bool operator!=(const allocator_wrapper<T, AllocP>&, const allocator_wrapper<U, AllocP>&) noexcept {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace mem
|
||||||
|
} // namespace ipc
|
||||||
110
src/libipc/memory/resource.h
Executable file
110
src/libipc/memory/resource.h
Executable file
@ -0,0 +1,110 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
#include <limits>
|
||||||
|
#include <utility>
|
||||||
|
#include <functional>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
#include "libipc/def.h"
|
||||||
|
|
||||||
|
#include "libipc/memory/alloc.h"
|
||||||
|
#include "libipc/memory/wrapper.h"
|
||||||
|
#include "libipc/platform/detail.h"
|
||||||
|
|
||||||
|
namespace ipc {
|
||||||
|
namespace mem {
|
||||||
|
|
||||||
|
//using async_pool_alloc = static_wrapper<variable_wrapper<async_wrapper<
|
||||||
|
// detail::fixed_alloc<
|
||||||
|
// variable_alloc <sizeof(void*) * 1024 * 256>,
|
||||||
|
// fixed_expand_policy<sizeof(void*) * 1024, sizeof(void*) * 1024 * 256>
|
||||||
|
// >,
|
||||||
|
// default_recycler >>>;
|
||||||
|
using async_pool_alloc = ipc::mem::static_alloc;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using allocator = allocator_wrapper<T, async_pool_alloc>;
|
||||||
|
|
||||||
|
} // namespace mem
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr char const * pf(int) { return "%d" ; }
|
||||||
|
constexpr char const * pf(long) { return "%ld" ; }
|
||||||
|
constexpr char const * pf(long long) { return "%lld"; }
|
||||||
|
constexpr char const * pf(unsigned int) { return "%u" ; }
|
||||||
|
constexpr char const * pf(unsigned long) { return "%lu" ; }
|
||||||
|
constexpr char const * pf(unsigned long long) { return "%llu"; }
|
||||||
|
constexpr char const * pf(float) { return "%f" ; }
|
||||||
|
constexpr char const * pf(double) { return "%f" ; }
|
||||||
|
constexpr char const * pf(long double) { return "%Lf" ; }
|
||||||
|
|
||||||
|
} // internal-linkage
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct hash : public std::hash<T> {};
|
||||||
|
|
||||||
|
template <typename Key, typename T>
|
||||||
|
using unordered_map = std::unordered_map<
|
||||||
|
Key, T, ipc::hash<Key>, std::equal_to<Key>, ipc::mem::allocator<std::pair<Key const, T>>
|
||||||
|
>;
|
||||||
|
|
||||||
|
template <typename Key, typename T>
|
||||||
|
using map = std::map<
|
||||||
|
Key, T, std::less<Key>, ipc::mem::allocator<std::pair<Key const, T>>
|
||||||
|
>;
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
using basic_string = std::basic_string<
|
||||||
|
Char, std::char_traits<Char>, ipc::mem::allocator<Char>
|
||||||
|
>;
|
||||||
|
|
||||||
|
using string = basic_string<char>;
|
||||||
|
using wstring = basic_string<wchar_t>;
|
||||||
|
|
||||||
|
template <> struct hash<string> {
|
||||||
|
std::size_t operator()(string const &val) const noexcept {
|
||||||
|
return std::hash<char const *>{}(val.c_str());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <> struct hash<wstring> {
|
||||||
|
std::size_t operator()(wstring const &val) const noexcept {
|
||||||
|
return std::hash<wchar_t const *>{}(val.c_str());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
ipc::string to_string(T val) {
|
||||||
|
char buf[std::numeric_limits<T>::digits10 + 1] {};
|
||||||
|
if (std::snprintf(buf, sizeof(buf), pf(val), val) > 0) {
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Check string validity.
|
||||||
|
constexpr bool is_valid_string(char const *str) noexcept {
|
||||||
|
return (str != nullptr) && (str[0] != '\0');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Make a valid string.
|
||||||
|
inline ipc::string make_string(char const *str) {
|
||||||
|
return is_valid_string(str) ? ipc::string{str} : ipc::string{};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Combine prefix from a list of strings.
|
||||||
|
inline ipc::string make_prefix(ipc::string prefix, std::initializer_list<ipc::string> args) {
|
||||||
|
prefix += "__IPC_SHM__";
|
||||||
|
for (auto const &txt: args) {
|
||||||
|
if (txt.empty()) continue;
|
||||||
|
prefix += txt;
|
||||||
|
}
|
||||||
|
return prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ipc
|
||||||
327
src/libipc/memory/wrapper.h
Executable file
327
src/libipc/memory/wrapper.h
Executable file
@ -0,0 +1,327 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <tuple>
|
||||||
|
#include <thread>
|
||||||
|
#include <deque> // std::deque
|
||||||
|
#include <functional> // std::function
|
||||||
|
#include <utility> // std::forward
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cassert> // assert
|
||||||
|
#include <type_traits> // std::aligned_storage_t
|
||||||
|
|
||||||
|
#include "libipc/def.h"
|
||||||
|
#include "libipc/rw_lock.h"
|
||||||
|
#include "libipc/pool_alloc.h"
|
||||||
|
|
||||||
|
#include "libipc/utility/concept.h"
|
||||||
|
#include "libipc/memory/alloc.h"
|
||||||
|
#include "libipc/platform/detail.h"
|
||||||
|
|
||||||
|
namespace ipc {
|
||||||
|
namespace mem {
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
/// Thread-safe allocation wrapper
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
IPC_CONCEPT_(is_comparable, operator<(std::declval<Type>()));
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
template <typename AllocP, bool = detail::is_comparable<AllocP>::value>
|
||||||
|
class limited_recycler;
|
||||||
|
|
||||||
|
template <typename AllocP>
|
||||||
|
class limited_recycler<AllocP, true> {
|
||||||
|
public:
|
||||||
|
using alloc_policy = AllocP;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::deque<alloc_policy> master_allocs_;
|
||||||
|
ipc::spin_lock master_lock_;
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
void take_first_do(F && pred) {
|
||||||
|
auto it = master_allocs_.begin();
|
||||||
|
pred(const_cast<alloc_policy&>(*it));
|
||||||
|
master_allocs_.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
void try_recover(alloc_policy & alc) {
|
||||||
|
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_);
|
||||||
|
if (master_allocs_.empty()) return;
|
||||||
|
take_first_do([&alc](alloc_policy & first) { alc.swap(first); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void collect(alloc_policy && alc) {
|
||||||
|
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(master_lock_);
|
||||||
|
if (master_allocs_.size() >= 32) {
|
||||||
|
take_first_do([](alloc_policy &) {}); // erase first
|
||||||
|
}
|
||||||
|
master_allocs_.emplace_back(std::move(alc));
|
||||||
|
}
|
||||||
|
|
||||||
|
IPC_CONSTEXPR_ auto try_replenish(alloc_policy&, std::size_t) noexcept {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename AllocP>
|
||||||
|
class default_recycler : public limited_recycler<AllocP> {
|
||||||
|
|
||||||
|
IPC_CONCEPT_(has_remain, remain());
|
||||||
|
IPC_CONCEPT_(has_empty , empty());
|
||||||
|
|
||||||
|
template <typename A>
|
||||||
|
void try_fill(A & alc) {
|
||||||
|
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(this->master_lock_);
|
||||||
|
if (this->master_allocs_.empty()) return;
|
||||||
|
this->take_first_do([&alc](alloc_policy & first) { alc.take(std::move(first)); });
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
using alloc_policy = typename limited_recycler<AllocP>::alloc_policy;
|
||||||
|
|
||||||
|
template <typename A = AllocP>
|
||||||
|
auto try_replenish(alloc_policy & alc, std::size_t size)
|
||||||
|
-> ipc::require<detail::has_take<A>::value && has_remain<A>::value> {
|
||||||
|
if (alc.remain() >= size) return;
|
||||||
|
this->try_fill(alc);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename A = AllocP>
|
||||||
|
auto try_replenish(alloc_policy & alc, std::size_t /*size*/)
|
||||||
|
-> ipc::require<detail::has_take<A>::value && !has_remain<A>::value && has_empty<A>::value> {
|
||||||
|
if (!alc.empty()) return;
|
||||||
|
this->try_fill(alc);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename A = AllocP>
|
||||||
|
auto try_replenish(alloc_policy & alc, std::size_t /*size*/)
|
||||||
|
-> ipc::require<!detail::has_take<A>::value && has_empty<A>::value> {
|
||||||
|
if (!alc.empty()) return;
|
||||||
|
this->try_recover(alc);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename A = AllocP>
|
||||||
|
IPC_CONSTEXPR_ auto try_replenish(alloc_policy & /*alc*/, std::size_t /*size*/) noexcept
|
||||||
|
-> ipc::require<(!detail::has_take<A>::value || !has_remain<A>::value) && !has_empty<A>::value> {
|
||||||
|
// Do Nothing.
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename AllocP>
|
||||||
|
class empty_recycler {
|
||||||
|
public:
|
||||||
|
using alloc_policy = AllocP;
|
||||||
|
|
||||||
|
IPC_CONSTEXPR_ void try_recover(alloc_policy&) noexcept {}
|
||||||
|
IPC_CONSTEXPR_ auto try_replenish(alloc_policy&, std::size_t) noexcept {}
|
||||||
|
IPC_CONSTEXPR_ void collect(alloc_policy&&) noexcept {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename AllocP,
|
||||||
|
template <typename> class RecyclerP = default_recycler>
|
||||||
|
class async_wrapper {
|
||||||
|
public:
|
||||||
|
using alloc_policy = AllocP;
|
||||||
|
|
||||||
|
private:
|
||||||
|
RecyclerP<alloc_policy> recycler_;
|
||||||
|
|
||||||
|
class alloc_proxy : public AllocP {
|
||||||
|
async_wrapper * w_ = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
alloc_proxy(alloc_proxy && rhs) = default;
|
||||||
|
|
||||||
|
template <typename ... P>
|
||||||
|
alloc_proxy(async_wrapper* w, P && ... pars)
|
||||||
|
: AllocP(std::forward<P>(pars) ...), w_(w) {
|
||||||
|
assert(w_ != nullptr);
|
||||||
|
w_->recycler_.try_recover(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
~alloc_proxy() {
|
||||||
|
w_->recycler_.collect(std::move(*this));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto alloc(std::size_t size) {
|
||||||
|
w_->recycler_.try_replenish(*this, size);
|
||||||
|
return AllocP::alloc(size);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
friend class alloc_proxy;
|
||||||
|
using ref_t = alloc_proxy&;
|
||||||
|
|
||||||
|
std::function<ref_t()> get_alloc_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template <typename ... P>
|
||||||
|
async_wrapper(P ... pars) {
|
||||||
|
get_alloc_ = [this, pars ...]()->ref_t {
|
||||||
|
thread_local alloc_proxy tls(pars ...);
|
||||||
|
return tls;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void* alloc(std::size_t size) {
|
||||||
|
return get_alloc_().alloc(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void free(void* p, std::size_t size) {
|
||||||
|
get_alloc_().free(p, size);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
/// Thread-safe allocation wrapper (with spin_lock)
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
template <typename AllocP, typename MutexT = ipc::spin_lock>
|
||||||
|
class sync_wrapper {
|
||||||
|
public:
|
||||||
|
using alloc_policy = AllocP;
|
||||||
|
using mutex_type = MutexT;
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutex_type lock_;
|
||||||
|
alloc_policy alloc_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template <typename ... P>
|
||||||
|
sync_wrapper(P && ... pars)
|
||||||
|
: alloc_(std::forward<P>(pars) ...)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void swap(sync_wrapper& rhs) {
|
||||||
|
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
|
||||||
|
alloc_.swap(rhs.alloc_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* alloc(std::size_t size) {
|
||||||
|
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
|
||||||
|
return alloc_.alloc(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void free(void* p, std::size_t size) {
|
||||||
|
IPC_UNUSED_ auto guard = ipc::detail::unique_lock(lock_);
|
||||||
|
alloc_.free(p, size);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
/// Variable memory allocation wrapper
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
template <std::size_t BaseSize = 0, std::size_t IterSize = sizeof(void*)>
|
||||||
|
struct default_mapping_policy {
|
||||||
|
|
||||||
|
enum : std::size_t {
|
||||||
|
base_size = BaseSize,
|
||||||
|
iter_size = IterSize,
|
||||||
|
classes_size = 64
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename F, typename ... P>
|
||||||
|
IPC_CONSTEXPR_ static void foreach(F && f, P && ... params) {
|
||||||
|
for (std::size_t i = 0; i < classes_size; ++i) {
|
||||||
|
f(i, std::forward<P>(params)...);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IPC_CONSTEXPR_ static std::size_t block_size(std::size_t id) noexcept {
|
||||||
|
return (id < classes_size) ? (base_size + (id + 1) * iter_size) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F, typename D, typename ... P>
|
||||||
|
IPC_CONSTEXPR_ static auto classify(F && f, D && d, std::size_t size, P && ... params) {
|
||||||
|
std::size_t id = (size - base_size - 1) / iter_size;
|
||||||
|
return (id < classes_size) ?
|
||||||
|
f(id, size, std::forward<P>(params)...) :
|
||||||
|
d(size, std::forward<P>(params)...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename FixedAlloc,
|
||||||
|
typename DefaultAlloc = mem::static_alloc,
|
||||||
|
typename MappingP = default_mapping_policy<>>
|
||||||
|
class variable_wrapper {
|
||||||
|
|
||||||
|
struct initiator {
|
||||||
|
|
||||||
|
using falc_t = std::aligned_storage_t<sizeof(FixedAlloc), alignof(FixedAlloc)>;
|
||||||
|
falc_t arr_[MappingP::classes_size];
|
||||||
|
|
||||||
|
initiator() {
|
||||||
|
MappingP::foreach([](std::size_t id, falc_t * a) {
|
||||||
|
ipc::mem::construct(&initiator::at(a, id), MappingP::block_size(id));
|
||||||
|
}, arr_);
|
||||||
|
}
|
||||||
|
|
||||||
|
~initiator() {
|
||||||
|
MappingP::foreach([](std::size_t id, falc_t * a) {
|
||||||
|
ipc::mem::destruct(&initiator::at(a, id));
|
||||||
|
}, arr_);
|
||||||
|
}
|
||||||
|
|
||||||
|
static FixedAlloc & at(falc_t * arr, std::size_t id) noexcept {
|
||||||
|
return reinterpret_cast<FixedAlloc&>(arr[id]);
|
||||||
|
}
|
||||||
|
} init_;
|
||||||
|
|
||||||
|
using falc_t = typename initiator::falc_t;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void swap(variable_wrapper & other) {
|
||||||
|
MappingP::foreach([](std::size_t id, falc_t * in, falc_t * ot) {
|
||||||
|
initiator::at(in, id).swap(initiator::at(ot, id));
|
||||||
|
}, init_.arr_, other.init_.arr_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* alloc(std::size_t size) {
|
||||||
|
return MappingP::classify([](std::size_t id, std::size_t size, falc_t * a) {
|
||||||
|
return initiator::at(a, id).alloc(size);
|
||||||
|
}, [](std::size_t size, falc_t *) {
|
||||||
|
return DefaultAlloc::alloc(size);
|
||||||
|
}, size, init_.arr_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void free(void* p, std::size_t size) {
|
||||||
|
MappingP::classify([](std::size_t id, std::size_t size, void* p, falc_t * a) {
|
||||||
|
initiator::at(a, id).free(p, size);
|
||||||
|
}, [](std::size_t size, void* p, falc_t *) {
|
||||||
|
DefaultAlloc::free(p, size);
|
||||||
|
}, size, p, init_.arr_);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
/// Static allocation wrapper
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
template <typename AllocP>
|
||||||
|
class static_wrapper {
|
||||||
|
public:
|
||||||
|
using alloc_policy = AllocP;
|
||||||
|
|
||||||
|
static alloc_policy& instance() {
|
||||||
|
static alloc_policy alloc;
|
||||||
|
return alloc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void swap(static_wrapper&) {}
|
||||||
|
|
||||||
|
static void* alloc(std::size_t size) {
|
||||||
|
return instance().alloc(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free(void* p, std::size_t size) {
|
||||||
|
instance().free(p, size);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mem
|
||||||
|
} // namespace ipc
|
||||||
@ -1,8 +1,22 @@
|
|||||||
#ifndef LIBIPC_SRC_PLATFORM_DETAIL_H_
|
#ifndef LIBIPC_SRC_PLATFORM_DETAIL_H_
|
||||||
#define LIBIPC_SRC_PLATFORM_DETAIL_H_
|
#define LIBIPC_SRC_PLATFORM_DETAIL_H_
|
||||||
|
|
||||||
#include "libipc/imp/detect_plat.h"
|
// detect platform
|
||||||
|
|
||||||
|
#if defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || \
|
||||||
|
defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || \
|
||||||
|
defined(WINCE) || defined(_WIN32_WCE)
|
||||||
|
# define IPC_OS_WINDOWS_
|
||||||
|
#elif defined(__linux__) || defined(__linux)
|
||||||
|
# define IPC_OS_LINUX_
|
||||||
|
#elif defined(__FreeBSD__)
|
||||||
|
# define IPC_OS_FREEBSD_
|
||||||
|
#elif defined(__QNX__)
|
||||||
|
# define IPC_OS_QNX_
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
#elif defined(__ANDROID__)
|
||||||
|
// TBD
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(__cplusplus)
|
#if defined(__cplusplus)
|
||||||
|
|
||||||
@ -15,6 +29,12 @@
|
|||||||
|
|
||||||
// pre-defined
|
// pre-defined
|
||||||
|
|
||||||
|
#ifdef IPC_UNUSED_
|
||||||
|
# error "IPC_UNUSED_ has been defined."
|
||||||
|
#endif
|
||||||
|
#ifdef IPC_FALLTHROUGH_
|
||||||
|
# error "IPC_FALLTHROUGH_ has been defined."
|
||||||
|
#endif
|
||||||
#ifdef IPC_STBIND_
|
#ifdef IPC_STBIND_
|
||||||
# error "IPC_STBIND_ has been defined."
|
# error "IPC_STBIND_ has been defined."
|
||||||
#endif
|
#endif
|
||||||
@ -24,11 +44,23 @@
|
|||||||
|
|
||||||
#if __cplusplus >= 201703L
|
#if __cplusplus >= 201703L
|
||||||
|
|
||||||
|
#define IPC_UNUSED_ [[maybe_unused]]
|
||||||
|
#define IPC_FALLTHROUGH_ [[fallthrough]]
|
||||||
#define IPC_STBIND_(A, B, ...) auto [A, B] = __VA_ARGS__
|
#define IPC_STBIND_(A, B, ...) auto [A, B] = __VA_ARGS__
|
||||||
#define IPC_CONSTEXPR_ constexpr
|
#define IPC_CONSTEXPR_ constexpr
|
||||||
|
|
||||||
#else /*__cplusplus < 201703L*/
|
#else /*__cplusplus < 201703L*/
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
# define IPC_UNUSED_ __pragma(warning(suppress: 4100 4101 4189))
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
# define IPC_UNUSED_ __attribute__((__unused__))
|
||||||
|
#else
|
||||||
|
# define IPC_UNUSED_
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define IPC_FALLTHROUGH_
|
||||||
|
|
||||||
#define IPC_STBIND_(A, B, ...) \
|
#define IPC_STBIND_(A, B, ...) \
|
||||||
auto tp___ = __VA_ARGS__ \
|
auto tp___ = __VA_ARGS__ \
|
||||||
auto A = std::get<0>(tp___); \
|
auto A = std::get<0>(tp___); \
|
||||||
@ -98,5 +130,3 @@ constexpr const T& (min)(const T& a, const T& b) {
|
|||||||
|
|
||||||
#endif // defined(__cplusplus)
|
#endif // defined(__cplusplus)
|
||||||
#endif // LIBIPC_SRC_PLATFORM_DETAIL_H_
|
#endif // LIBIPC_SRC_PLATFORM_DETAIL_H_
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,43 +0,0 @@
|
|||||||
/**
|
|
||||||
* \file libipc/platform/gnuc/demangle.h
|
|
||||||
* \author mutouyun (orz@orzz.org)
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cxxabi.h> // abi::__cxa_demangle
|
|
||||||
#include <cstdlib> // std::malloc
|
|
||||||
|
|
||||||
#include "libipc/imp/nameof.h"
|
|
||||||
#include "libipc/imp/scope_exit.h"
|
|
||||||
#include "libipc/imp/detect_plat.h"
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief The conventional way to obtain demangled symbol name.
|
|
||||||
* \see https://www.boost.org/doc/libs/1_80_0/libs/core/doc/html/core/demangle.html
|
|
||||||
*
|
|
||||||
* \param name the mangled name
|
|
||||||
* \return std::string a human-readable demangled type name
|
|
||||||
*/
|
|
||||||
std::string demangle(std::string name) noexcept {
|
|
||||||
/// \see https://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.3/a01696.html
|
|
||||||
std::size_t sz = name.size() + 1;
|
|
||||||
char *buffer = static_cast<char *>(std::malloc(sz));
|
|
||||||
int status = 0;
|
|
||||||
char *realname = abi::__cxa_demangle(name.data(), buffer, &sz, &status);
|
|
||||||
if (realname == nullptr) {
|
|
||||||
std::free(buffer);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
LIBIPC_SCOPE_EXIT(guard) = [realname] {
|
|
||||||
std::free(realname);
|
|
||||||
};
|
|
||||||
LIBIPC_TRY {
|
|
||||||
return std::move(name.assign(realname, sz));
|
|
||||||
} LIBIPC_CATCH(...) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ipc
|
|
||||||
@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "libipc/imp/log.h"
|
#include "libipc/utility/log.h"
|
||||||
#include "libipc/mutex.h"
|
#include "libipc/mutex.h"
|
||||||
|
|
||||||
#include "get_wait_time.h"
|
#include "get_wait_time.h"
|
||||||
@ -19,12 +19,11 @@ public:
|
|||||||
~condition() = default;
|
~condition() = default;
|
||||||
|
|
||||||
bool wait(ipc::sync::mutex &mtx, std::uint64_t tm) noexcept {
|
bool wait(ipc::sync::mutex &mtx, std::uint64_t tm) noexcept {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (!valid()) return false;
|
if (!valid()) return false;
|
||||||
if (tm == invalid_value) {
|
if (tm == invalid_value) {
|
||||||
int eno = A0_SYSERR(a0_cnd_wait(native(), static_cast<a0_mtx_t *>(mtx.native())));
|
int eno = A0_SYSERR(a0_cnd_wait(native(), static_cast<a0_mtx_t *>(mtx.native())));
|
||||||
if (eno != 0) {
|
if (eno != 0) {
|
||||||
log.error("fail condition wait[", eno, "]");
|
ipc::error("fail condition wait[%d]\n", eno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -32,7 +31,8 @@ public:
|
|||||||
int eno = A0_SYSERR(a0_cnd_timedwait(native(), static_cast<a0_mtx_t *>(mtx.native()), {ts}));
|
int eno = A0_SYSERR(a0_cnd_timedwait(native(), static_cast<a0_mtx_t *>(mtx.native()), {ts}));
|
||||||
if (eno != 0) {
|
if (eno != 0) {
|
||||||
if (eno != ETIMEDOUT) {
|
if (eno != ETIMEDOUT) {
|
||||||
log.error("fail condition timedwait[", eno, "]: tm = ", tm, ", tv_sec = ", ts.tv_sec, ", tv_nsec = ", ts.tv_nsec);
|
ipc::error("fail condition timedwait[%d]: tm = %zd, tv_sec = %ld, tv_nsec = %ld\n",
|
||||||
|
eno, tm, ts.tv_sec, ts.tv_nsec);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -41,22 +41,20 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool notify(ipc::sync::mutex &mtx) noexcept {
|
bool notify(ipc::sync::mutex &mtx) noexcept {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (!valid()) return false;
|
if (!valid()) return false;
|
||||||
int eno = A0_SYSERR(a0_cnd_signal(native(), static_cast<a0_mtx_t *>(mtx.native())));
|
int eno = A0_SYSERR(a0_cnd_signal(native(), static_cast<a0_mtx_t *>(mtx.native())));
|
||||||
if (eno != 0) {
|
if (eno != 0) {
|
||||||
log.error("fail condition notify[", eno, "]");
|
ipc::error("fail condition notify[%d]\n", eno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool broadcast(ipc::sync::mutex &mtx) noexcept {
|
bool broadcast(ipc::sync::mutex &mtx) noexcept {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (!valid()) return false;
|
if (!valid()) return false;
|
||||||
int eno = A0_SYSERR(a0_cnd_broadcast(native(), static_cast<a0_mtx_t *>(mtx.native())));
|
int eno = A0_SYSERR(a0_cnd_broadcast(native(), static_cast<a0_mtx_t *>(mtx.native())));
|
||||||
if (eno != 0) {
|
if (eno != 0) {
|
||||||
log.error("fail condition broadcast[", eno, "]");
|
ipc::error("fail condition broadcast[%d]\n", eno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
#include <system_error>
|
#include <system_error>
|
||||||
|
|
||||||
#include "libipc/imp/log.h"
|
#include "libipc/utility/log.h"
|
||||||
|
|
||||||
#include "a0/time.h"
|
#include "a0/time.h"
|
||||||
#include "a0/err_macro.h"
|
#include "a0/err_macro.h"
|
||||||
@ -14,31 +14,30 @@ namespace linux_ {
|
|||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
inline bool calc_wait_time(timespec &ts, std::uint64_t tm /*ms*/) noexcept {
|
inline bool calc_wait_time(timespec &ts, std::uint64_t tm /*ms*/) noexcept {
|
||||||
LIBIPC_LOG();
|
|
||||||
std::int64_t add_ns = static_cast<std::int64_t>(tm * 1000000ull);
|
std::int64_t add_ns = static_cast<std::int64_t>(tm * 1000000ull);
|
||||||
if (add_ns < 0) {
|
if (add_ns < 0) {
|
||||||
log.error("invalid time = ", tm);
|
ipc::error("invalid time = " PRIu64 "\n", tm);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
a0_time_mono_t now;
|
a0_time_mono_t now;
|
||||||
int eno = A0_SYSERR(a0_time_mono_now(&now));
|
int eno = A0_SYSERR(a0_time_mono_now(&now));
|
||||||
if (eno != 0) {
|
if (eno != 0) {
|
||||||
log.error("fail get time[", eno, "]");
|
ipc::error("fail get time[%d]\n", eno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
a0_time_mono_t *target = reinterpret_cast<a0_time_mono_t *>(&ts);
|
a0_time_mono_t *target = reinterpret_cast<a0_time_mono_t *>(&ts);
|
||||||
if ((eno = A0_SYSERR(a0_time_mono_add(now, add_ns, target))) != 0) {
|
if ((eno = A0_SYSERR(a0_time_mono_add(now, add_ns, target))) != 0) {
|
||||||
log.error("fail get time[", eno, "]");
|
ipc::error("fail get time[%d]\n", eno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline timespec make_timespec(std::uint64_t tm /*ms*/) noexcept(false) {
|
inline timespec make_timespec(std::uint64_t tm /*ms*/) noexcept(false) {
|
||||||
LIBIPC_LOG();
|
|
||||||
timespec ts {};
|
timespec ts {};
|
||||||
if (!calc_wait_time(ts, tm)) {
|
if (!calc_wait_time(ts, tm)) {
|
||||||
log.error("fail calc_wait_time: tm = ", tm, ", tv_sec = ", ts.tv_sec, ", tv_nsec = ", ts.tv_nsec);
|
ipc::error("fail calc_wait_time: tm = %zd, tv_sec = %ld, tv_nsec = %ld\n",
|
||||||
|
tm, ts.tv_sec, ts.tv_nsec);
|
||||||
throw std::system_error{static_cast<int>(errno), std::system_category()};
|
throw std::system_error{static_cast<int>(errno), std::system_category()};
|
||||||
}
|
}
|
||||||
return ts;
|
return ts;
|
||||||
|
|||||||
@ -6,8 +6,8 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
||||||
#include "libipc/platform/detail.h"
|
#include "libipc/platform/detail.h"
|
||||||
#include "libipc/imp/log.h"
|
#include "libipc/utility/log.h"
|
||||||
#include "libipc/mem/resource.h"
|
#include "libipc/memory/resource.h"
|
||||||
#include "libipc/shm.h"
|
#include "libipc/shm.h"
|
||||||
|
|
||||||
#include "get_wait_time.h"
|
#include "get_wait_time.h"
|
||||||
@ -23,7 +23,6 @@ namespace sync {
|
|||||||
class robust_mutex : public sync::obj_impl<a0_mtx_t> {
|
class robust_mutex : public sync::obj_impl<a0_mtx_t> {
|
||||||
public:
|
public:
|
||||||
bool lock(std::uint64_t tm) noexcept {
|
bool lock(std::uint64_t tm) noexcept {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (!valid()) return false;
|
if (!valid()) return false;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
auto ts = linux_::detail::make_timespec(tm);
|
auto ts = linux_::detail::make_timespec(tm);
|
||||||
@ -38,25 +37,24 @@ public:
|
|||||||
case EOWNERDEAD: {
|
case EOWNERDEAD: {
|
||||||
int eno2 = A0_SYSERR(a0_mtx_consistent(native()));
|
int eno2 = A0_SYSERR(a0_mtx_consistent(native()));
|
||||||
if (eno2 != 0) {
|
if (eno2 != 0) {
|
||||||
log.error("fail mutex lock[", eno, "] -> consistent[", eno2, "]");
|
ipc::error("fail mutex lock[%d] -> consistent[%d]\n", eno, eno2);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
int eno3 = A0_SYSERR(a0_mtx_unlock(native()));
|
int eno3 = A0_SYSERR(a0_mtx_unlock(native()));
|
||||||
if (eno3 != 0) {
|
if (eno3 != 0) {
|
||||||
log.error("fail mutex lock[", eno, "] -> unlock[", eno3, "]");
|
ipc::error("fail mutex lock[%d] -> unlock[%d]\n", eno, eno3);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break; // loop again
|
break; // loop again
|
||||||
default:
|
default:
|
||||||
log.error("fail mutex lock[", eno, "]");
|
ipc::error("fail mutex lock[%d]\n", eno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool try_lock() noexcept(false) {
|
bool try_lock() noexcept(false) {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (!valid()) return false;
|
if (!valid()) return false;
|
||||||
int eno = A0_SYSERR(a0_mtx_timedlock(native(), {linux_::detail::make_timespec(0)}));
|
int eno = A0_SYSERR(a0_mtx_timedlock(native(), {linux_::detail::make_timespec(0)}));
|
||||||
switch (eno) {
|
switch (eno) {
|
||||||
@ -67,29 +65,28 @@ public:
|
|||||||
case EOWNERDEAD: {
|
case EOWNERDEAD: {
|
||||||
int eno2 = A0_SYSERR(a0_mtx_consistent(native()));
|
int eno2 = A0_SYSERR(a0_mtx_consistent(native()));
|
||||||
if (eno2 != 0) {
|
if (eno2 != 0) {
|
||||||
log.error("fail mutex try_lock[", eno, "] -> consistent[", eno2, "]");
|
ipc::error("fail mutex try_lock[%d] -> consistent[%d]\n", eno, eno2);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
int eno3 = A0_SYSERR(a0_mtx_unlock(native()));
|
int eno3 = A0_SYSERR(a0_mtx_unlock(native()));
|
||||||
if (eno3 != 0) {
|
if (eno3 != 0) {
|
||||||
log.error("fail mutex try_lock[", eno, "] -> unlock[", eno3, "]");
|
ipc::error("fail mutex try_lock[%d] -> unlock[%d]\n", eno, eno3);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
log.error("fail mutex try_lock[", eno, "]");
|
ipc::error("fail mutex try_lock[%d]\n", eno);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
throw std::system_error{eno, std::system_category()};
|
throw std::system_error{eno, std::system_category()};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool unlock() noexcept {
|
bool unlock() noexcept {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (!valid()) return false;
|
if (!valid()) return false;
|
||||||
int eno = A0_SYSERR(a0_mtx_unlock(native()));
|
int eno = A0_SYSERR(a0_mtx_unlock(native()));
|
||||||
if (eno != 0) {
|
if (eno != 0) {
|
||||||
log.error("fail mutex unlock[", eno, "]");
|
ipc::error("fail mutex unlock[%d]\n", eno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -111,7 +108,7 @@ class mutex {
|
|||||||
shm_data(init arg)
|
shm_data(init arg)
|
||||||
: mtx{}, ref{0} { mtx.open(arg.name); }
|
: mtx{}, ref{0} { mtx.open(arg.name); }
|
||||||
};
|
};
|
||||||
ipc::map<std::string, shm_data> mutex_handles;
|
ipc::map<ipc::string, shm_data> mutex_handles;
|
||||||
std::mutex lock;
|
std::mutex lock;
|
||||||
|
|
||||||
static curr_prog &get() {
|
static curr_prog &get() {
|
||||||
@ -125,7 +122,7 @@ class mutex {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto &info = curr_prog::get();
|
auto &info = curr_prog::get();
|
||||||
LIBIPC_UNUSED std::lock_guard<std::mutex> guard {info.lock};
|
IPC_UNUSED_ std::lock_guard<std::mutex> guard {info.lock};
|
||||||
auto it = info.mutex_handles.find(name);
|
auto it = info.mutex_handles.find(name);
|
||||||
if (it == info.mutex_handles.end()) {
|
if (it == info.mutex_handles.end()) {
|
||||||
it = info.mutex_handles
|
it = info.mutex_handles
|
||||||
@ -139,10 +136,10 @@ class mutex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
static void release_mutex(std::string const &name, F &&clear) {
|
static void release_mutex(ipc::string const &name, F &&clear) {
|
||||||
if (name.empty()) return;
|
if (name.empty()) return;
|
||||||
auto &info = curr_prog::get();
|
auto &info = curr_prog::get();
|
||||||
LIBIPC_UNUSED std::lock_guard<std::mutex> guard {info.lock};
|
IPC_UNUSED_ std::lock_guard<std::mutex> guard {info.lock};
|
||||||
auto it = info.mutex_handles.find(name);
|
auto it = info.mutex_handles.find(name);
|
||||||
if (it == info.mutex_handles.end()) {
|
if (it == info.mutex_handles.end()) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "libipc/imp/log.h"
|
#include "libipc/utility/log.h"
|
||||||
#include "libipc/shm.h"
|
#include "libipc/shm.h"
|
||||||
|
|
||||||
#include "a0/empty.h"
|
#include "a0/empty.h"
|
||||||
@ -19,9 +19,8 @@ protected:
|
|||||||
sync_t *h_ = nullptr;
|
sync_t *h_ = nullptr;
|
||||||
|
|
||||||
sync_t *acquire_handle(char const *name) {
|
sync_t *acquire_handle(char const *name) {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (!shm_.acquire(name, sizeof(sync_t))) {
|
if (!shm_.acquire(name, sizeof(sync_t))) {
|
||||||
log.error("[acquire_handle] fail shm.acquire: ", name);
|
ipc::error("[acquire_handle] fail shm.acquire: %s\n", name);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return static_cast<sync_t *>(shm_.get());
|
return static_cast<sync_t *>(shm_.get());
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
|
|
||||||
#include "libipc/platform/detail.h"
|
#include "libipc/platform/detail.h"
|
||||||
#if defined(LIBIPC_OS_WIN)
|
#if defined(IPC_OS_WINDOWS_)
|
||||||
#elif defined(LIBIPC_OS_LINUX)
|
#elif defined(IPC_OS_LINUX_)
|
||||||
#include "libipc/platform/linux/a0/err.c"
|
#include "libipc/platform/linux/a0/err.c"
|
||||||
#include "libipc/platform/linux/a0/mtx.c"
|
#include "libipc/platform/linux/a0/mtx.c"
|
||||||
#include "libipc/platform/linux/a0/strconv.c"
|
#include "libipc/platform/linux/a0/strconv.c"
|
||||||
#include "libipc/platform/linux/a0/tid.c"
|
#include "libipc/platform/linux/a0/tid.c"
|
||||||
#include "libipc/platform/linux/a0/time.c"
|
#include "libipc/platform/linux/a0/time.c"
|
||||||
#elif defined(LIBIPC_OS_QNX) || defined(LIBIPC_OS_FREEBSD)
|
#elif defined(IPC_OS_QNX_) || defined(IPC_OS_FREEBSD_)
|
||||||
#else/*IPC_OS*/
|
#else/*IPC_OS*/
|
||||||
# error "Unsupported platform."
|
# error "Unsupported platform."
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
|
|
||||||
#include "libipc/platform/detail.h"
|
#include "libipc/platform/detail.h"
|
||||||
#if defined(LIBIPC_OS_WIN)
|
#if defined(IPC_OS_WINDOWS_)
|
||||||
#include "libipc/platform/win/shm_win.cpp"
|
#include "libipc/platform/win/shm_win.cpp"
|
||||||
#elif defined(LIBIPC_OS_LINUX) || defined(LIBIPC_OS_QNX) || defined(LIBIPC_OS_FREEBSD)
|
#elif defined(IPC_OS_LINUX_) || defined(IPC_OS_QNX_) || defined(IPC_OS_FREEBSD_)
|
||||||
#include "libipc/platform/posix/shm_posix.cpp"
|
#include "libipc/platform/posix/shm_posix.cpp"
|
||||||
#else/*IPC_OS*/
|
#else/*IPC_OS*/
|
||||||
# error "Unsupported platform."
|
# error "Unsupported platform."
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
|
||||||
#include "libipc/imp/log.h"
|
#include "libipc/utility/log.h"
|
||||||
#include "libipc/utility/scope_guard.h"
|
#include "libipc/utility/scope_guard.h"
|
||||||
#include "libipc/mutex.h"
|
#include "libipc/mutex.h"
|
||||||
#include "libipc/shm.h"
|
#include "libipc/shm.h"
|
||||||
@ -21,9 +21,8 @@ class condition {
|
|||||||
pthread_cond_t *cond_ = nullptr;
|
pthread_cond_t *cond_ = nullptr;
|
||||||
|
|
||||||
pthread_cond_t *acquire_cond(char const *name) {
|
pthread_cond_t *acquire_cond(char const *name) {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (!shm_.acquire(name, sizeof(pthread_cond_t))) {
|
if (!shm_.acquire(name, sizeof(pthread_cond_t))) {
|
||||||
log.error("[acquire_cond] fail shm.acquire: ", name);
|
ipc::error("[acquire_cond] fail shm.acquire: %s\n", name);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return static_cast<pthread_cond_t *>(shm_.get());
|
return static_cast<pthread_cond_t *>(shm_.get());
|
||||||
@ -48,7 +47,6 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool open(char const *name) noexcept {
|
bool open(char const *name) noexcept {
|
||||||
LIBIPC_LOG();
|
|
||||||
close();
|
close();
|
||||||
if ((cond_ = acquire_cond(name)) == nullptr) {
|
if ((cond_ = acquire_cond(name)) == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
@ -62,17 +60,17 @@ public:
|
|||||||
int eno;
|
int eno;
|
||||||
pthread_condattr_t cond_attr;
|
pthread_condattr_t cond_attr;
|
||||||
if ((eno = ::pthread_condattr_init(&cond_attr)) != 0) {
|
if ((eno = ::pthread_condattr_init(&cond_attr)) != 0) {
|
||||||
log.error("fail pthread_condattr_init[", eno, "]");
|
ipc::error("fail pthread_condattr_init[%d]\n", eno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
LIBIPC_UNUSED auto guard_cond_attr = guard([&cond_attr] { ::pthread_condattr_destroy(&cond_attr); });
|
IPC_UNUSED_ auto guard_cond_attr = guard([&cond_attr] { ::pthread_condattr_destroy(&cond_attr); });
|
||||||
if ((eno = ::pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED)) != 0) {
|
if ((eno = ::pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED)) != 0) {
|
||||||
log.error("fail pthread_condattr_setpshared[", eno, "]");
|
ipc::error("fail pthread_condattr_setpshared[%d]\n", eno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
*cond_ = PTHREAD_COND_INITIALIZER;
|
*cond_ = PTHREAD_COND_INITIALIZER;
|
||||||
if ((eno = ::pthread_cond_init(cond_, &cond_attr)) != 0) {
|
if ((eno = ::pthread_cond_init(cond_, &cond_attr)) != 0) {
|
||||||
log.error("fail pthread_cond_init[", eno, "]");
|
ipc::error("fail pthread_cond_init[%d]\n", eno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
finally.dismiss();
|
finally.dismiss();
|
||||||
@ -80,11 +78,10 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void close() noexcept {
|
void close() noexcept {
|
||||||
LIBIPC_LOG();
|
|
||||||
if ((shm_.ref() <= 1) && cond_ != nullptr) {
|
if ((shm_.ref() <= 1) && cond_ != nullptr) {
|
||||||
int eno;
|
int eno;
|
||||||
if ((eno = ::pthread_cond_destroy(cond_)) != 0) {
|
if ((eno = ::pthread_cond_destroy(cond_)) != 0) {
|
||||||
log.error("fail pthread_cond_destroy[", eno, "]");
|
ipc::error("fail pthread_cond_destroy[%d]\n", eno);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
shm_.release();
|
shm_.release();
|
||||||
@ -95,7 +92,7 @@ public:
|
|||||||
if ((shm_.ref() <= 1) && cond_ != nullptr) {
|
if ((shm_.ref() <= 1) && cond_ != nullptr) {
|
||||||
int eno;
|
int eno;
|
||||||
if ((eno = ::pthread_cond_destroy(cond_)) != 0) {
|
if ((eno = ::pthread_cond_destroy(cond_)) != 0) {
|
||||||
log.error("fail pthread_cond_destroy[", eno, "]");
|
ipc::error("fail pthread_cond_destroy[%d]\n", eno);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
shm_.clear(); // Make sure the storage is cleaned up.
|
shm_.clear(); // Make sure the storage is cleaned up.
|
||||||
@ -107,13 +104,12 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool wait(ipc::sync::mutex &mtx, std::uint64_t tm) noexcept {
|
bool wait(ipc::sync::mutex &mtx, std::uint64_t tm) noexcept {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (!valid()) return false;
|
if (!valid()) return false;
|
||||||
switch (tm) {
|
switch (tm) {
|
||||||
case invalid_value: {
|
case invalid_value: {
|
||||||
int eno;
|
int eno;
|
||||||
if ((eno = ::pthread_cond_wait(cond_, static_cast<pthread_mutex_t *>(mtx.native()))) != 0) {
|
if ((eno = ::pthread_cond_wait(cond_, static_cast<pthread_mutex_t *>(mtx.native()))) != 0) {
|
||||||
log.error("fail pthread_cond_wait[", eno, "]");
|
ipc::error("fail pthread_cond_wait[%d]\n", eno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -123,7 +119,8 @@ public:
|
|||||||
int eno;
|
int eno;
|
||||||
if ((eno = ::pthread_cond_timedwait(cond_, static_cast<pthread_mutex_t *>(mtx.native()), &ts)) != 0) {
|
if ((eno = ::pthread_cond_timedwait(cond_, static_cast<pthread_mutex_t *>(mtx.native()), &ts)) != 0) {
|
||||||
if (eno != ETIMEDOUT) {
|
if (eno != ETIMEDOUT) {
|
||||||
log.error("fail pthread_cond_timedwait[", eno, "]: tm = ", tm, ", tv_sec = ", ts.tv_sec, ", tv_nsec = ", ts.tv_nsec);
|
ipc::error("fail pthread_cond_timedwait[%d]: tm = %zd, tv_sec = %ld, tv_nsec = %ld\n",
|
||||||
|
eno, tm, ts.tv_sec, ts.tv_nsec);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -137,7 +134,7 @@ public:
|
|||||||
if (!valid()) return false;
|
if (!valid()) return false;
|
||||||
int eno;
|
int eno;
|
||||||
if ((eno = ::pthread_cond_signal(cond_)) != 0) {
|
if ((eno = ::pthread_cond_signal(cond_)) != 0) {
|
||||||
log.error("fail pthread_cond_signal[", eno, "]");
|
ipc::error("fail pthread_cond_signal[%d]\n", eno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -147,7 +144,7 @@ public:
|
|||||||
if (!valid()) return false;
|
if (!valid()) return false;
|
||||||
int eno;
|
int eno;
|
||||||
if ((eno = ::pthread_cond_broadcast(cond_)) != 0) {
|
if ((eno = ::pthread_cond_broadcast(cond_)) != 0) {
|
||||||
log.error("fail pthread_cond_broadcast[", eno, "]");
|
ipc::error("fail pthread_cond_broadcast[%d]\n", eno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@ -7,18 +7,17 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#include "libipc/imp/log.h"
|
#include "libipc/utility/log.h"
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
namespace posix_ {
|
namespace posix_ {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
inline bool calc_wait_time(timespec &ts, std::uint64_t tm /*ms*/) noexcept {
|
inline bool calc_wait_time(timespec &ts, std::uint64_t tm /*ms*/) noexcept {
|
||||||
LIBIPC_LOG();
|
|
||||||
timeval now;
|
timeval now;
|
||||||
int eno = ::gettimeofday(&now, NULL);
|
int eno = ::gettimeofday(&now, NULL);
|
||||||
if (eno != 0) {
|
if (eno != 0) {
|
||||||
log.error("fail gettimeofday [", eno, "]");
|
ipc::error("fail gettimeofday [%d]\n", eno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ts.tv_nsec = (now.tv_usec + (tm % 1000) * 1000) * 1000;
|
ts.tv_nsec = (now.tv_usec + (tm % 1000) * 1000) * 1000;
|
||||||
@ -28,10 +27,10 @@ inline bool calc_wait_time(timespec &ts, std::uint64_t tm /*ms*/) noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline timespec make_timespec(std::uint64_t tm /*ms*/) noexcept(false) {
|
inline timespec make_timespec(std::uint64_t tm /*ms*/) noexcept(false) {
|
||||||
LIBIPC_LOG();
|
|
||||||
timespec ts {};
|
timespec ts {};
|
||||||
if (!calc_wait_time(ts, tm)) {
|
if (!calc_wait_time(ts, tm)) {
|
||||||
log.error("fail calc_wait_time: tm = ", tm, ", tv_sec = ", ts.tv_sec, ", tv_nsec = ", ts.tv_nsec);
|
ipc::error("fail calc_wait_time: tm = %zd, tv_sec = %ld, tv_nsec = %ld\n",
|
||||||
|
tm, ts.tv_sec, ts.tv_nsec);
|
||||||
throw std::system_error{static_cast<int>(errno), std::system_category()};
|
throw std::system_error{static_cast<int>(errno), std::system_category()};
|
||||||
}
|
}
|
||||||
return ts;
|
return ts;
|
||||||
|
|||||||
@ -10,9 +10,9 @@
|
|||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
|
||||||
#include "libipc/platform/detail.h"
|
#include "libipc/platform/detail.h"
|
||||||
#include "libipc/imp/log.h"
|
#include "libipc/utility/log.h"
|
||||||
#include "libipc/utility/scope_guard.h"
|
#include "libipc/utility/scope_guard.h"
|
||||||
#include "libipc/mem/resource.h"
|
#include "libipc/memory/resource.h"
|
||||||
#include "libipc/shm.h"
|
#include "libipc/shm.h"
|
||||||
|
|
||||||
#include "get_wait_time.h"
|
#include "get_wait_time.h"
|
||||||
@ -38,7 +38,7 @@ class mutex {
|
|||||||
shm_data(init arg)
|
shm_data(init arg)
|
||||||
: shm{arg.name, arg.size}, ref{0} {}
|
: shm{arg.name, arg.size}, ref{0} {}
|
||||||
};
|
};
|
||||||
ipc::map<std::string, shm_data> mutex_handles;
|
ipc::map<ipc::string, shm_data> mutex_handles;
|
||||||
std::mutex lock;
|
std::mutex lock;
|
||||||
|
|
||||||
static curr_prog &get() {
|
static curr_prog &get() {
|
||||||
@ -52,7 +52,7 @@ class mutex {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
auto &info = curr_prog::get();
|
auto &info = curr_prog::get();
|
||||||
LIBIPC_UNUSED std::lock_guard<std::mutex> guard {info.lock};
|
IPC_UNUSED_ std::lock_guard<std::mutex> guard {info.lock};
|
||||||
auto it = info.mutex_handles.find(name);
|
auto it = info.mutex_handles.find(name);
|
||||||
if (it == info.mutex_handles.end()) {
|
if (it == info.mutex_handles.end()) {
|
||||||
it = info.mutex_handles
|
it = info.mutex_handles
|
||||||
@ -71,10 +71,10 @@ class mutex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
static void release_mutex(std::string const &name, F &&clear) {
|
static void release_mutex(ipc::string const &name, F &&clear) {
|
||||||
if (name.empty()) return;
|
if (name.empty()) return;
|
||||||
auto &info = curr_prog::get();
|
auto &info = curr_prog::get();
|
||||||
LIBIPC_UNUSED std::lock_guard<std::mutex> guard {info.lock};
|
IPC_UNUSED_ std::lock_guard<std::mutex> guard {info.lock};
|
||||||
auto it = info.mutex_handles.find(name);
|
auto it = info.mutex_handles.find(name);
|
||||||
if (it == info.mutex_handles.end()) {
|
if (it == info.mutex_handles.end()) {
|
||||||
return;
|
return;
|
||||||
@ -127,21 +127,21 @@ public:
|
|||||||
int eno;
|
int eno;
|
||||||
pthread_mutexattr_t mutex_attr;
|
pthread_mutexattr_t mutex_attr;
|
||||||
if ((eno = ::pthread_mutexattr_init(&mutex_attr)) != 0) {
|
if ((eno = ::pthread_mutexattr_init(&mutex_attr)) != 0) {
|
||||||
log.error("fail pthread_mutexattr_init[", eno, "]");
|
ipc::error("fail pthread_mutexattr_init[%d]\n", eno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
LIBIPC_UNUSED auto guard_mutex_attr = guard([&mutex_attr] { ::pthread_mutexattr_destroy(&mutex_attr); });
|
IPC_UNUSED_ auto guard_mutex_attr = guard([&mutex_attr] { ::pthread_mutexattr_destroy(&mutex_attr); });
|
||||||
if ((eno = ::pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED)) != 0) {
|
if ((eno = ::pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED)) != 0) {
|
||||||
log.error("fail pthread_mutexattr_setpshared[", eno, "]");
|
ipc::error("fail pthread_mutexattr_setpshared[%d]\n", eno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if ((eno = ::pthread_mutexattr_setrobust(&mutex_attr, PTHREAD_MUTEX_ROBUST)) != 0) {
|
if ((eno = ::pthread_mutexattr_setrobust(&mutex_attr, PTHREAD_MUTEX_ROBUST)) != 0) {
|
||||||
log.error("fail pthread_mutexattr_setrobust[", eno, "]");
|
ipc::error("fail pthread_mutexattr_setrobust[%d]\n", eno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
*mutex_ = PTHREAD_MUTEX_INITIALIZER;
|
*mutex_ = PTHREAD_MUTEX_INITIALIZER;
|
||||||
if ((eno = ::pthread_mutex_init(mutex_, &mutex_attr)) != 0) {
|
if ((eno = ::pthread_mutex_init(mutex_, &mutex_attr)) != 0) {
|
||||||
log.error("fail pthread_mutex_init[", eno, "]");
|
ipc::error("fail pthread_mutex_init[%d]\n", eno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
finally.dismiss();
|
finally.dismiss();
|
||||||
@ -149,7 +149,6 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void close() noexcept {
|
void close() noexcept {
|
||||||
LIBIPC_LOG();
|
|
||||||
if ((ref_ != nullptr) && (shm_ != nullptr) && (mutex_ != nullptr)) {
|
if ((ref_ != nullptr) && (shm_ != nullptr) && (mutex_ != nullptr)) {
|
||||||
if (shm_->name() != nullptr) {
|
if (shm_->name() != nullptr) {
|
||||||
release_mutex(shm_->name(), [this] {
|
release_mutex(shm_->name(), [this] {
|
||||||
@ -166,7 +165,7 @@ public:
|
|||||||
|
|
||||||
int eno;
|
int eno;
|
||||||
if ((eno = ::pthread_mutex_destroy(mutex_)) != 0) {
|
if ((eno = ::pthread_mutex_destroy(mutex_)) != 0) {
|
||||||
log.error("fail pthread_mutex_destroy[", eno, "]");
|
ipc::error("fail pthread_mutex_destroy[%d]\n", eno);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -180,7 +179,6 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void clear() noexcept {
|
void clear() noexcept {
|
||||||
LIBIPC_LOG();
|
|
||||||
if ((shm_ != nullptr) && (mutex_ != nullptr)) {
|
if ((shm_ != nullptr) && (mutex_ != nullptr)) {
|
||||||
if (shm_->name() != nullptr) {
|
if (shm_->name() != nullptr) {
|
||||||
release_mutex(shm_->name(), [this] {
|
release_mutex(shm_->name(), [this] {
|
||||||
@ -189,7 +187,7 @@ public:
|
|||||||
|
|
||||||
int eno;
|
int eno;
|
||||||
if ((eno = ::pthread_mutex_destroy(mutex_)) != 0) {
|
if ((eno = ::pthread_mutex_destroy(mutex_)) != 0) {
|
||||||
log.error("fail pthread_mutex_destroy[", eno, "]");
|
ipc::error("fail pthread_mutex_destroy[%d]\n", eno);
|
||||||
}
|
}
|
||||||
shm_->clear();
|
shm_->clear();
|
||||||
return true;
|
return true;
|
||||||
@ -208,7 +206,6 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool lock(std::uint64_t tm) noexcept {
|
bool lock(std::uint64_t tm) noexcept {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (!valid()) return false;
|
if (!valid()) return false;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
auto ts = posix_::detail::make_timespec(tm);
|
auto ts = posix_::detail::make_timespec(tm);
|
||||||
@ -225,7 +222,7 @@ public:
|
|||||||
// but the previous owner died. We need to make it consistent.
|
// but the previous owner died. We need to make it consistent.
|
||||||
int eno2 = ::pthread_mutex_consistent(mutex_);
|
int eno2 = ::pthread_mutex_consistent(mutex_);
|
||||||
if (eno2 != 0) {
|
if (eno2 != 0) {
|
||||||
log.error("fail pthread_mutex_lock[", eno, "], pthread_mutex_consistent[", eno2, "]");
|
ipc::error("fail pthread_mutex_lock[%d], pthread_mutex_consistent[%d]\n", eno, eno2);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// After calling pthread_mutex_consistent(), the mutex is now in a
|
// After calling pthread_mutex_consistent(), the mutex is now in a
|
||||||
@ -233,7 +230,7 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
log.error("fail pthread_mutex_lock[", eno, "]");
|
ipc::error("fail pthread_mutex_lock[%d]\n", eno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -253,7 +250,7 @@ public:
|
|||||||
// but the previous owner died. We need to make it consistent.
|
// but the previous owner died. We need to make it consistent.
|
||||||
int eno2 = ::pthread_mutex_consistent(mutex_);
|
int eno2 = ::pthread_mutex_consistent(mutex_);
|
||||||
if (eno2 != 0) {
|
if (eno2 != 0) {
|
||||||
log.error("fail pthread_mutex_timedlock[", eno, "], pthread_mutex_consistent[", eno2, "]");
|
ipc::error("fail pthread_mutex_timedlock[%d], pthread_mutex_consistent[%d]\n", eno, eno2);
|
||||||
throw std::system_error{eno2, std::system_category()};
|
throw std::system_error{eno2, std::system_category()};
|
||||||
}
|
}
|
||||||
// After calling pthread_mutex_consistent(), the mutex is now in a
|
// After calling pthread_mutex_consistent(), the mutex is now in a
|
||||||
@ -261,18 +258,17 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
log.error("fail pthread_mutex_timedlock[", eno, "]");
|
ipc::error("fail pthread_mutex_timedlock[%d]\n", eno);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
throw std::system_error{eno, std::system_category()};
|
throw std::system_error{eno, std::system_category()};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool unlock() noexcept {
|
bool unlock() noexcept {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (!valid()) return false;
|
if (!valid()) return false;
|
||||||
int eno;
|
int eno;
|
||||||
if ((eno = ::pthread_mutex_unlock(mutex_)) != 0) {
|
if ((eno = ::pthread_mutex_unlock(mutex_)) != 0) {
|
||||||
log.error("fail pthread_mutex_unlock[", eno, "]");
|
ipc::error("fail pthread_mutex_unlock[%d]\n", eno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
#include <semaphore.h>
|
#include <semaphore.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#include "libipc/imp/log.h"
|
#include "libipc/utility/log.h"
|
||||||
#include "libipc/shm.h"
|
#include "libipc/shm.h"
|
||||||
|
|
||||||
#include "get_wait_time.h"
|
#include "get_wait_time.h"
|
||||||
@ -34,10 +34,9 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool open(char const *name, std::uint32_t count) noexcept {
|
bool open(char const *name, std::uint32_t count) noexcept {
|
||||||
LIBIPC_LOG();
|
|
||||||
close();
|
close();
|
||||||
if (!shm_.acquire(name, 1)) {
|
if (!shm_.acquire(name, 1)) {
|
||||||
log.error("[open_semaphore] fail shm.acquire: ", name);
|
ipc::error("[open_semaphore] fail shm.acquire: %s\n", name);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// POSIX semaphore names must start with "/" on some platforms (e.g., FreeBSD)
|
// POSIX semaphore names must start with "/" on some platforms (e.g., FreeBSD)
|
||||||
@ -49,23 +48,22 @@ public:
|
|||||||
}
|
}
|
||||||
h_ = ::sem_open(sem_name_.c_str(), O_CREAT, 0666, static_cast<unsigned>(count));
|
h_ = ::sem_open(sem_name_.c_str(), O_CREAT, 0666, static_cast<unsigned>(count));
|
||||||
if (h_ == SEM_FAILED) {
|
if (h_ == SEM_FAILED) {
|
||||||
log.error("fail sem_open[", errno, "]: ", sem_name_);
|
ipc::error("fail sem_open[%d]: %s\n", errno, sem_name_.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void close() noexcept {
|
void close() noexcept {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (!valid()) return;
|
if (!valid()) return;
|
||||||
if (::sem_close(h_) != 0) {
|
if (::sem_close(h_) != 0) {
|
||||||
log.error("fail sem_close[", errno, "]");
|
ipc::error("fail sem_close[%d]: %s\n", errno);
|
||||||
}
|
}
|
||||||
h_ = SEM_FAILED;
|
h_ = SEM_FAILED;
|
||||||
if (!sem_name_.empty() && shm_.name() != nullptr) {
|
if (!sem_name_.empty() && shm_.name() != nullptr) {
|
||||||
if (shm_.release() <= 1) {
|
if (shm_.release() <= 1) {
|
||||||
if (::sem_unlink(sem_name_.c_str()) != 0) {
|
if (::sem_unlink(sem_name_.c_str()) != 0) {
|
||||||
log.error("fail sem_unlink[", errno, "]: ", sem_name_);
|
ipc::error("fail sem_unlink[%d]: %s, name: %s\n", errno, sem_name_.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -73,10 +71,9 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void clear() noexcept {
|
void clear() noexcept {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (valid()) {
|
if (valid()) {
|
||||||
if (::sem_close(h_) != 0) {
|
if (::sem_close(h_) != 0) {
|
||||||
log.error("fail sem_close[", errno, "]");
|
ipc::error("fail sem_close[%d]: %s\n", errno);
|
||||||
}
|
}
|
||||||
h_ = SEM_FAILED;
|
h_ = SEM_FAILED;
|
||||||
}
|
}
|
||||||
@ -100,18 +97,18 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool wait(std::uint64_t tm) noexcept {
|
bool wait(std::uint64_t tm) noexcept {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (!valid()) return false;
|
if (!valid()) return false;
|
||||||
if (tm == invalid_value) {
|
if (tm == invalid_value) {
|
||||||
if (::sem_wait(h_) != 0) {
|
if (::sem_wait(h_) != 0) {
|
||||||
log.error("fail sem_wait[", errno, "]");
|
ipc::error("fail sem_wait[%d]: %s\n", errno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
auto ts = posix_::detail::make_timespec(tm);
|
auto ts = posix_::detail::make_timespec(tm);
|
||||||
if (::sem_timedwait(h_, &ts) != 0) {
|
if (::sem_timedwait(h_, &ts) != 0) {
|
||||||
if (errno != ETIMEDOUT) {
|
if (errno != ETIMEDOUT) {
|
||||||
log.error("fail sem_timedwait[", errno, "]: tm = ", tm, ", tv_sec = ", ts.tv_sec, ", tv_nsec = ", ts.tv_nsec);
|
ipc::error("fail sem_timedwait[%d]: tm = %zd, tv_sec = %ld, tv_nsec = %ld\n",
|
||||||
|
errno, tm, ts.tv_sec, ts.tv_nsec);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -120,11 +117,10 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool post(std::uint32_t count) noexcept {
|
bool post(std::uint32_t count) noexcept {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (!valid()) return false;
|
if (!valid()) return false;
|
||||||
for (std::uint32_t i = 0; i < count; ++i) {
|
for (std::uint32_t i = 0; i < count; ++i) {
|
||||||
if (::sem_post(h_) != 0) {
|
if (::sem_post(h_) != 0) {
|
||||||
log.error("fail sem_post[", errno, "]");
|
ipc::error("fail sem_post[%d]: %s\n", errno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,10 +13,10 @@
|
|||||||
|
|
||||||
#include "libipc/shm.h"
|
#include "libipc/shm.h"
|
||||||
#include "libipc/def.h"
|
#include "libipc/def.h"
|
||||||
|
#include "libipc/pool_alloc.h"
|
||||||
|
|
||||||
#include "libipc/imp/log.h"
|
#include "libipc/utility/log.h"
|
||||||
#include "libipc/mem/resource.h"
|
#include "libipc/memory/resource.h"
|
||||||
#include "libipc/mem/new.h"
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@ -28,7 +28,7 @@ struct id_info_t {
|
|||||||
int fd_ = -1;
|
int fd_ = -1;
|
||||||
void* mem_ = nullptr;
|
void* mem_ = nullptr;
|
||||||
std::size_t size_ = 0;
|
std::size_t size_ = 0;
|
||||||
std::string name_;
|
ipc::string name_;
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr std::size_t calc_size(std::size_t size) {
|
constexpr std::size_t calc_size(std::size_t size) {
|
||||||
@ -45,18 +45,17 @@ namespace ipc {
|
|||||||
namespace shm {
|
namespace shm {
|
||||||
|
|
||||||
id_t acquire(char const * name, std::size_t size, unsigned mode) {
|
id_t acquire(char const * name, std::size_t size, unsigned mode) {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (!is_valid_string(name)) {
|
if (!is_valid_string(name)) {
|
||||||
log.error("fail acquire: name is empty");
|
ipc::error("fail acquire: name is empty\n");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
// For portable use, a shared memory object should be identified by name of the form /somename.
|
// For portable use, a shared memory object should be identified by name of the form /somename.
|
||||||
// see: https://man7.org/linux/man-pages/man3/shm_open.3.html
|
// see: https://man7.org/linux/man-pages/man3/shm_open.3.html
|
||||||
std::string op_name;
|
ipc::string op_name;
|
||||||
if (name[0] == '/') {
|
if (name[0] == '/') {
|
||||||
op_name = name;
|
op_name = name;
|
||||||
} else {
|
} else {
|
||||||
op_name = std::string{"/"} + name;
|
op_name = ipc::string{"/"} + name;
|
||||||
}
|
}
|
||||||
// Open the object for read-write access.
|
// Open the object for read-write access.
|
||||||
int flag = O_RDWR;
|
int flag = O_RDWR;
|
||||||
@ -80,14 +79,14 @@ id_t acquire(char const * name, std::size_t size, unsigned mode) {
|
|||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
// only open shm not log error when file not exist
|
// only open shm not log error when file not exist
|
||||||
if (open != mode || ENOENT != errno) {
|
if (open != mode || ENOENT != errno) {
|
||||||
log.error("fail shm_open[", errno, "]: ", op_name);
|
ipc::error("fail shm_open[%d]: %s\n", errno, op_name.c_str());
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
::fchmod(fd, S_IRUSR | S_IWUSR |
|
::fchmod(fd, S_IRUSR | S_IWUSR |
|
||||||
S_IRGRP | S_IWGRP |
|
S_IRGRP | S_IWGRP |
|
||||||
S_IROTH | S_IWOTH);
|
S_IROTH | S_IWOTH);
|
||||||
auto ii = mem::$new<id_info_t>();
|
auto ii = mem::alloc<id_info_t>();
|
||||||
ii->fd_ = fd;
|
ii->fd_ = fd;
|
||||||
ii->size_ = size;
|
ii->size_ = size;
|
||||||
ii->name_ = std::move(op_name);
|
ii->name_ = std::move(op_name);
|
||||||
@ -106,23 +105,21 @@ std::int32_t get_ref(id_t id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void sub_ref(id_t id) {
|
void sub_ref(id_t id) {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (id == nullptr) {
|
if (id == nullptr) {
|
||||||
log.error("fail sub_ref: invalid id (null)");
|
ipc::error("fail sub_ref: invalid id (null)\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto ii = static_cast<id_info_t*>(id);
|
auto ii = static_cast<id_info_t*>(id);
|
||||||
if (ii->mem_ == nullptr || ii->size_ == 0) {
|
if (ii->mem_ == nullptr || ii->size_ == 0) {
|
||||||
log.error("fail sub_ref: invalid id (mem = ", ii->mem_, ", size = ", ii->size_, ")");
|
ipc::error("fail sub_ref: invalid id (mem = %p, size = %zd)\n", ii->mem_, ii->size_);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
acc_of(ii->mem_, ii->size_).fetch_sub(1, std::memory_order_acq_rel);
|
acc_of(ii->mem_, ii->size_).fetch_sub(1, std::memory_order_acq_rel);
|
||||||
}
|
}
|
||||||
|
|
||||||
void * get_mem(id_t id, std::size_t * size) {
|
void * get_mem(id_t id, std::size_t * size) {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (id == nullptr) {
|
if (id == nullptr) {
|
||||||
log.error("fail get_mem: invalid id (null)");
|
ipc::error("fail get_mem: invalid id (null)\n");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
auto ii = static_cast<id_info_t*>(id);
|
auto ii = static_cast<id_info_t*>(id);
|
||||||
@ -132,31 +129,31 @@ void * get_mem(id_t id, std::size_t * size) {
|
|||||||
}
|
}
|
||||||
int fd = ii->fd_;
|
int fd = ii->fd_;
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
log.error("fail get_mem: invalid id (fd = -1)");
|
ipc::error("fail get_mem: invalid id (fd = -1)\n");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
if (ii->size_ == 0) {
|
if (ii->size_ == 0) {
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (::fstat(fd, &st) != 0) {
|
if (::fstat(fd, &st) != 0) {
|
||||||
log.error("fail fstat[", errno, "]: ", ii->name_, ", size = ", ii->size_);
|
ipc::error("fail fstat[%d]: %s, size = %zd\n", errno, ii->name_.c_str(), ii->size_);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
ii->size_ = static_cast<std::size_t>(st.st_size);
|
ii->size_ = static_cast<std::size_t>(st.st_size);
|
||||||
if ((ii->size_ <= sizeof(info_t)) || (ii->size_ % sizeof(info_t))) {
|
if ((ii->size_ <= sizeof(info_t)) || (ii->size_ % sizeof(info_t))) {
|
||||||
log.error("fail get_mem: ", ii->name_, ", invalid size = ", ii->size_);
|
ipc::error("fail get_mem: %s, invalid size = %zd\n", ii->name_.c_str(), ii->size_);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ii->size_ = calc_size(ii->size_);
|
ii->size_ = calc_size(ii->size_);
|
||||||
if (::ftruncate(fd, static_cast<off_t>(ii->size_)) != 0) {
|
if (::ftruncate(fd, static_cast<off_t>(ii->size_)) != 0) {
|
||||||
log.error("fail ftruncate[", errno, "]: ", ii->name_, ", size = ", ii->size_);
|
ipc::error("fail ftruncate[%d]: %s, size = %zd\n", errno, ii->name_.c_str(), ii->size_);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void* mem = ::mmap(nullptr, ii->size_, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
void* mem = ::mmap(nullptr, ii->size_, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||||
if (mem == MAP_FAILED) {
|
if (mem == MAP_FAILED) {
|
||||||
log.error("fail mmap[", errno, "]: ", ii->name_, ", size = ", ii->size_);
|
ipc::error("fail mmap[%d]: %s, size = %zd\n", errno, ii->name_.c_str(), ii->size_);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
::close(fd);
|
::close(fd);
|
||||||
@ -168,34 +165,33 @@ void * get_mem(id_t id, std::size_t * size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::int32_t release(id_t id) noexcept {
|
std::int32_t release(id_t id) noexcept {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (id == nullptr) {
|
if (id == nullptr) {
|
||||||
log.error("fail release: invalid id (null)");
|
ipc::error("fail release: invalid id (null)\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
std::int32_t ret = -1;
|
std::int32_t ret = -1;
|
||||||
auto ii = static_cast<id_info_t*>(id);
|
auto ii = static_cast<id_info_t*>(id);
|
||||||
if (ii->mem_ == nullptr || ii->size_ == 0) {
|
if (ii->mem_ == nullptr || ii->size_ == 0) {
|
||||||
log.error("fail release: invalid id (mem = ", ii->mem_, ", size = ", ii->size_, "), name = ", ii->name_);
|
ipc::error("fail release: invalid id (mem = %p, size = %zd), name = %s\n",
|
||||||
|
ii->mem_, ii->size_, ii->name_.c_str());
|
||||||
}
|
}
|
||||||
else if ((ret = acc_of(ii->mem_, ii->size_).fetch_sub(1, std::memory_order_acq_rel)) <= 1) {
|
else if ((ret = acc_of(ii->mem_, ii->size_).fetch_sub(1, std::memory_order_acq_rel)) <= 1) {
|
||||||
::munmap(ii->mem_, ii->size_);
|
::munmap(ii->mem_, ii->size_);
|
||||||
if (!ii->name_.empty()) {
|
if (!ii->name_.empty()) {
|
||||||
int unlink_ret = ::shm_unlink(ii->name_.c_str());
|
int unlink_ret = ::shm_unlink(ii->name_.c_str());
|
||||||
if (unlink_ret == -1) {
|
if (unlink_ret == -1) {
|
||||||
log.error("fail shm_unlink[", errno, "]: ", ii->name_);
|
ipc::error("fail shm_unlink[%d]: %s\n", errno, ii->name_.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else ::munmap(ii->mem_, ii->size_);
|
else ::munmap(ii->mem_, ii->size_);
|
||||||
mem::$delete(ii);
|
mem::free(ii);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void remove(id_t id) noexcept {
|
void remove(id_t id) noexcept {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (id == nullptr) {
|
if (id == nullptr) {
|
||||||
log.error("fail remove: invalid id (null)");
|
ipc::error("fail remove: invalid id (null)\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto ii = static_cast<id_info_t*>(id);
|
auto ii = static_cast<id_info_t*>(id);
|
||||||
@ -204,27 +200,26 @@ void remove(id_t id) noexcept {
|
|||||||
if (!name.empty()) {
|
if (!name.empty()) {
|
||||||
int unlink_ret = ::shm_unlink(name.c_str());
|
int unlink_ret = ::shm_unlink(name.c_str());
|
||||||
if (unlink_ret == -1) {
|
if (unlink_ret == -1) {
|
||||||
log.error("fail shm_unlink[", errno, "]: ", name);
|
ipc::error("fail shm_unlink[%d]: %s\n", errno, name.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void remove(char const * name) noexcept {
|
void remove(char const * name) noexcept {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (!is_valid_string(name)) {
|
if (!is_valid_string(name)) {
|
||||||
log.error("fail remove: name is empty");
|
ipc::error("fail remove: name is empty\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// For portable use, a shared memory object should be identified by name of the form /somename.
|
// For portable use, a shared memory object should be identified by name of the form /somename.
|
||||||
std::string op_name;
|
ipc::string op_name;
|
||||||
if (name[0] == '/') {
|
if (name[0] == '/') {
|
||||||
op_name = name;
|
op_name = name;
|
||||||
} else {
|
} else {
|
||||||
op_name = std::string{"/"} + name;
|
op_name = ipc::string{"/"} + name;
|
||||||
}
|
}
|
||||||
int unlink_ret = ::shm_unlink(op_name.c_str());
|
int unlink_ret = ::shm_unlink(op_name.c_str());
|
||||||
if (unlink_ret == -1) {
|
if (unlink_ret == -1) {
|
||||||
log.error("fail shm_unlink[", errno, "]: ", op_name);
|
ipc::error("fail shm_unlink[%d]: %s\n", errno, op_name.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,49 +0,0 @@
|
|||||||
/**
|
|
||||||
* \file libipc/platform/posix/system.h
|
|
||||||
* \author mutouyun (orz@orzz.org)
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "libipc/imp/system.h"
|
|
||||||
#include "libipc/imp/log.h"
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
namespace sys {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Get the system error number.
|
|
||||||
* \see https://en.cppreference.com/w/cpp/error/generic_category
|
|
||||||
* https://man7.org/linux/man-pages/man3/errno.3.html
|
|
||||||
*/
|
|
||||||
std::error_code error() noexcept {
|
|
||||||
return std::error_code(errno, std::generic_category());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Gets configuration information at run time
|
|
||||||
* https://man7.org/linux/man-pages/man2/getpagesize.2.html
|
|
||||||
* https://man7.org/linux/man-pages/man3/sysconf.3.html
|
|
||||||
*/
|
|
||||||
result<std::int64_t> conf(info r) noexcept {
|
|
||||||
LIBIPC_LOG();
|
|
||||||
switch (r) {
|
|
||||||
case info::page_size: {
|
|
||||||
auto val = ::sysconf(_SC_PAGESIZE);
|
|
||||||
if (val >= 0) return static_cast<std::int64_t>(val);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
log.error("invalid info = ", underlyof(r));
|
|
||||||
return std::make_error_code(std::errc::invalid_argument);
|
|
||||||
}
|
|
||||||
auto err = sys::error();
|
|
||||||
log.error("info = ", underlyof(r), ", error = ", err);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace sys
|
|
||||||
} // namespace ipc
|
|
||||||
@ -1,95 +0,0 @@
|
|||||||
/**
|
|
||||||
* \file libipc/platform/win/codecvt.h
|
|
||||||
* \author mutouyun (orz@orzz.org)
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Windows.h>
|
|
||||||
|
|
||||||
#include "libipc/imp/codecvt.h"
|
|
||||||
#include "libipc/imp/detect_plat.h"
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \see https://docs.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-multibytetowidechar
|
|
||||||
* https://docs.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-widechartomultibyte
|
|
||||||
*
|
|
||||||
* CP_ACP : The system default Windows ANSI code page.
|
|
||||||
* CP_MACCP : The current system Macintosh code page.
|
|
||||||
* CP_OEMCP : The current system OEM code page.
|
|
||||||
* CP_SYMBOL : Symbol code page (42).
|
|
||||||
* CP_THREAD_ACP: The Windows ANSI code page for the current thread.
|
|
||||||
* CP_UTF7 : UTF-7. Use this value only when forced by a 7-bit transport mechanism. Use of UTF-8 is preferred.
|
|
||||||
* CP_UTF8 : UTF-8.
|
|
||||||
*/
|
|
||||||
|
|
||||||
template <>
|
|
||||||
std::size_t cvt_cstr(char const *src, std::size_t slen, wchar_t *des, std::size_t dlen) noexcept {
|
|
||||||
if ((src == nullptr) || ((*src) == 0) || (slen == 0)) {
|
|
||||||
// source string is empty
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int cch_wc = (des == nullptr) ? 0 : (int)dlen;
|
|
||||||
int size_needed = ::MultiByteToWideChar(CP_ACP, 0, src, (int)slen, des, cch_wc);
|
|
||||||
if (size_needed <= 0) {
|
|
||||||
// failed: MultiByteToWideChar(CP_ACP).
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return size_needed;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
std::size_t cvt_cstr(wchar_t const *src, std::size_t slen, char *des, std::size_t dlen) noexcept {
|
|
||||||
if ((src == nullptr) || ((*src) == 0) || (slen == 0)) {
|
|
||||||
// source string is empty
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int cb_mb = (des == nullptr) ? 0 : (int)dlen;
|
|
||||||
int size_needed = ::WideCharToMultiByte(CP_ACP, 0, src, (int)slen, des, cb_mb, NULL, NULL);
|
|
||||||
if (size_needed <= 0) {
|
|
||||||
// failed: WideCharToMultiByte(CP_ACP).
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return size_needed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Used for char8_t (since C++20) to wchar_t conversion.
|
|
||||||
*
|
|
||||||
* There is no ut to guarantee correctness (I'm a little lazy here),
|
|
||||||
* so if there are any bugs, please contact me in time.
|
|
||||||
*/
|
|
||||||
#if defined(LIBIMP_CPP_20)
|
|
||||||
template <>
|
|
||||||
std::size_t cvt_cstr(char8_t const *src, std::size_t slen, wchar_t *des, std::size_t dlen) noexcept {
|
|
||||||
if ((src == nullptr) || ((*src) == 0) || (slen == 0)) {
|
|
||||||
// source string is empty
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int cch_wc = (des == nullptr) ? 0 : (int)dlen;
|
|
||||||
int size_needed = ::MultiByteToWideChar(CP_UTF8, 0, (char *)src, (int)slen, des, cch_wc);
|
|
||||||
if (size_needed <= 0) {
|
|
||||||
// failed: MultiByteToWideChar(CP_UTF8).
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return size_needed;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
std::size_t cvt_cstr(wchar_t const *src, std::size_t slen, char8_t *des, std::size_t dlen) noexcept {
|
|
||||||
if ((src == nullptr) || ((*src) == 0) || (slen == 0)) {
|
|
||||||
// source string is empty
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int cb_mb = (des == nullptr) ? 0 : (int)dlen;
|
|
||||||
int size_needed = ::WideCharToMultiByte(CP_UTF8, 0, src, (int)slen, (char *)des, cb_mb, NULL, NULL);
|
|
||||||
if (size_needed <= 0) {
|
|
||||||
// failed: WideCharToMultiByte(CP_UTF8).
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return size_needed;
|
|
||||||
}
|
|
||||||
#endif // defined(LIBIMP_CPP_20)
|
|
||||||
|
|
||||||
} // namespace ipc
|
|
||||||
@ -10,7 +10,7 @@
|
|||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "libipc/imp/log.h"
|
#include "libipc/utility/log.h"
|
||||||
#include "libipc/utility/scope_guard.h"
|
#include "libipc/utility/scope_guard.h"
|
||||||
#include "libipc/platform/detail.h"
|
#include "libipc/platform/detail.h"
|
||||||
#include "libipc/mutex.h"
|
#include "libipc/mutex.h"
|
||||||
@ -85,7 +85,7 @@ public:
|
|||||||
if (!valid()) return false;
|
if (!valid()) return false;
|
||||||
auto &cnt = counter();
|
auto &cnt = counter();
|
||||||
{
|
{
|
||||||
LIBIPC_UNUSED std::lock_guard<ipc::sync::mutex> guard {lock_};
|
IPC_UNUSED_ std::lock_guard<ipc::sync::mutex> guard {lock_};
|
||||||
cnt = (cnt < 0) ? 1 : cnt + 1;
|
cnt = (cnt < 0) ? 1 : cnt + 1;
|
||||||
}
|
}
|
||||||
DWORD ms = (tm == invalid_value) ? INFINITE : static_cast<DWORD>(tm);
|
DWORD ms = (tm == invalid_value) ? INFINITE : static_cast<DWORD>(tm);
|
||||||
@ -97,7 +97,7 @@ public:
|
|||||||
bool rs = ::SignalObjectAndWait(mtx.native(), sem_.native(), ms, FALSE) == WAIT_OBJECT_0;
|
bool rs = ::SignalObjectAndWait(mtx.native(), sem_.native(), ms, FALSE) == WAIT_OBJECT_0;
|
||||||
bool rl = mtx.lock(); // INFINITE
|
bool rl = mtx.lock(); // INFINITE
|
||||||
if (!rs) {
|
if (!rs) {
|
||||||
LIBIPC_UNUSED std::lock_guard<ipc::sync::mutex> guard {lock_};
|
IPC_UNUSED_ std::lock_guard<ipc::sync::mutex> guard {lock_};
|
||||||
cnt -= 1;
|
cnt -= 1;
|
||||||
}
|
}
|
||||||
return rs && rl;
|
return rs && rl;
|
||||||
|
|||||||
@ -1,15 +0,0 @@
|
|||||||
/**
|
|
||||||
* \file libipc/platform/win/demangle.h
|
|
||||||
* \author mutouyun (orz@orzz.org)
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "libipc/imp/nameof.h"
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
|
|
||||||
std::string demangle(std::string name) noexcept {
|
|
||||||
return std::move(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ipc
|
|
||||||
@ -5,19 +5,21 @@
|
|||||||
namespace ipc {
|
namespace ipc {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
struct sa_initiator {
|
inline LPSECURITY_ATTRIBUTES get_sa() {
|
||||||
|
static struct initiator {
|
||||||
|
|
||||||
SECURITY_DESCRIPTOR sd_;
|
SECURITY_DESCRIPTOR sd_;
|
||||||
SECURITY_ATTRIBUTES sa_;
|
SECURITY_ATTRIBUTES sa_;
|
||||||
|
|
||||||
bool succ_ = false;
|
bool succ_ = false;
|
||||||
|
|
||||||
template <typename Logger>
|
initiator() {
|
||||||
sa_initiator(Logger const &log) {
|
|
||||||
if (!::InitializeSecurityDescriptor(&sd_, SECURITY_DESCRIPTOR_REVISION)) {
|
if (!::InitializeSecurityDescriptor(&sd_, SECURITY_DESCRIPTOR_REVISION)) {
|
||||||
log.error("fail InitializeSecurityDescriptor[", static_cast<int>(::GetLastError()), "]");
|
ipc::error("fail InitializeSecurityDescriptor[%d]\n", static_cast<int>(::GetLastError()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!::SetSecurityDescriptorDacl(&sd_, TRUE, NULL, FALSE)) {
|
if (!::SetSecurityDescriptorDacl(&sd_, TRUE, NULL, FALSE)) {
|
||||||
log.error("fail SetSecurityDescriptorDacl[", static_cast<int>(::GetLastError()), "]");
|
ipc::error("fail SetSecurityDescriptorDacl[%d]\n", static_cast<int>(::GetLastError()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sa_.nLength = sizeof(SECURITY_ATTRIBUTES);
|
sa_.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||||
@ -25,11 +27,7 @@ struct sa_initiator {
|
|||||||
sa_.lpSecurityDescriptor = &sd_;
|
sa_.lpSecurityDescriptor = &sd_;
|
||||||
succ_ = true;
|
succ_ = true;
|
||||||
}
|
}
|
||||||
};
|
} handle;
|
||||||
|
|
||||||
inline LPSECURITY_ATTRIBUTES get_sa() {
|
|
||||||
LIBIPC_LOG();
|
|
||||||
static sa_initiator handle(log);
|
|
||||||
return handle.succ_ ? &handle.sa_ : nullptr;
|
return handle.succ_ ? &handle.sa_ : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "libipc/imp/log.h"
|
#include "libipc/utility/log.h"
|
||||||
|
|
||||||
#include "to_tchar.h"
|
#include "to_tchar.h"
|
||||||
#include "get_sa.h"
|
#include "get_sa.h"
|
||||||
@ -36,11 +36,10 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool open(char const *name) noexcept {
|
bool open(char const *name) noexcept {
|
||||||
LIBIPC_LOG();
|
|
||||||
close();
|
close();
|
||||||
h_ = ::CreateMutex(detail::get_sa(), FALSE, detail::to_tchar(name).c_str());
|
h_ = ::CreateMutex(detail::get_sa(), FALSE, detail::to_tchar(name).c_str());
|
||||||
if (h_ == NULL) {
|
if (h_ == NULL) {
|
||||||
log.error("fail CreateMutex[", static_cast<unsigned long>(::GetLastError()), "]: ", name);
|
ipc::error("fail CreateMutex[%lu]: %s\n", ::GetLastError(), name);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -60,7 +59,6 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool lock(std::uint64_t tm) noexcept {
|
bool lock(std::uint64_t tm) noexcept {
|
||||||
LIBIPC_LOG();
|
|
||||||
DWORD ret, ms = (tm == invalid_value) ? INFINITE : static_cast<DWORD>(tm);
|
DWORD ret, ms = (tm == invalid_value) ? INFINITE : static_cast<DWORD>(tm);
|
||||||
for(;;) {
|
for(;;) {
|
||||||
switch ((ret = ::WaitForSingleObject(h_, ms))) {
|
switch ((ret = ::WaitForSingleObject(h_, ms))) {
|
||||||
@ -69,20 +67,19 @@ public:
|
|||||||
case WAIT_TIMEOUT:
|
case WAIT_TIMEOUT:
|
||||||
return false;
|
return false;
|
||||||
case WAIT_ABANDONED:
|
case WAIT_ABANDONED:
|
||||||
log.warning("fail WaitForSingleObject[", ::GetLastError(), "]: WAIT_ABANDONED, try again.");
|
ipc::log("fail WaitForSingleObject[%lu]: WAIT_ABANDONED, try again.\n", ::GetLastError());
|
||||||
if (!unlock()) {
|
if (!unlock()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break; // loop again
|
break; // loop again
|
||||||
default:
|
default:
|
||||||
log.error("fail WaitForSingleObject[", ::GetLastError(), "]: 0x", std::hex, ret, std::dec);
|
ipc::error("fail WaitForSingleObject[%lu]: 0x%08X\n", ::GetLastError(), ret);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool try_lock() noexcept(false) {
|
bool try_lock() noexcept(false) {
|
||||||
LIBIPC_LOG();
|
|
||||||
DWORD ret = ::WaitForSingleObject(h_, 0);
|
DWORD ret = ::WaitForSingleObject(h_, 0);
|
||||||
switch (ret) {
|
switch (ret) {
|
||||||
case WAIT_OBJECT_0:
|
case WAIT_OBJECT_0:
|
||||||
@ -91,17 +88,16 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
case WAIT_ABANDONED:
|
case WAIT_ABANDONED:
|
||||||
unlock();
|
unlock();
|
||||||
LIBIPC_FALLTHROUGH;
|
IPC_FALLTHROUGH_;
|
||||||
default:
|
default:
|
||||||
log.error("fail WaitForSingleObject[", ::GetLastError(), "]: 0x", std::hex, ret, std::dec);
|
ipc::error("fail WaitForSingleObject[%lu]: 0x%08X\n", ::GetLastError(), ret);
|
||||||
throw std::system_error{static_cast<int>(ret), std::system_category()};
|
throw std::system_error{static_cast<int>(ret), std::system_category()};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool unlock() noexcept {
|
bool unlock() noexcept {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (!::ReleaseMutex(h_)) {
|
if (!::ReleaseMutex(h_)) {
|
||||||
log.error("fail ReleaseMutex[", ::GetLastError(), "]");
|
ipc::error("fail ReleaseMutex[%lu]\n", ::GetLastError());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "libipc/imp/log.h"
|
#include "libipc/utility/log.h"
|
||||||
|
|
||||||
#include "to_tchar.h"
|
#include "to_tchar.h"
|
||||||
#include "get_sa.h"
|
#include "get_sa.h"
|
||||||
@ -33,13 +33,12 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool open(char const *name, std::uint32_t count) noexcept {
|
bool open(char const *name, std::uint32_t count) noexcept {
|
||||||
LIBIPC_LOG();
|
|
||||||
close();
|
close();
|
||||||
h_ = ::CreateSemaphore(detail::get_sa(),
|
h_ = ::CreateSemaphore(detail::get_sa(),
|
||||||
static_cast<LONG>(count), LONG_MAX,
|
static_cast<LONG>(count), LONG_MAX,
|
||||||
detail::to_tchar(name).c_str());
|
detail::to_tchar(name).c_str());
|
||||||
if (h_ == NULL) {
|
if (h_ == NULL) {
|
||||||
log.error("fail CreateSemaphore[", static_cast<unsigned long>(::GetLastError()), "]: ", name);
|
ipc::error("fail CreateSemaphore[%lu]: %s\n", ::GetLastError(), name);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -59,7 +58,6 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool wait(std::uint64_t tm) noexcept {
|
bool wait(std::uint64_t tm) noexcept {
|
||||||
LIBIPC_LOG();
|
|
||||||
DWORD ret, ms = (tm == invalid_value) ? INFINITE : static_cast<DWORD>(tm);
|
DWORD ret, ms = (tm == invalid_value) ? INFINITE : static_cast<DWORD>(tm);
|
||||||
switch ((ret = ::WaitForSingleObject(h_, ms))) {
|
switch ((ret = ::WaitForSingleObject(h_, ms))) {
|
||||||
case WAIT_OBJECT_0:
|
case WAIT_OBJECT_0:
|
||||||
@ -68,15 +66,14 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
case WAIT_ABANDONED:
|
case WAIT_ABANDONED:
|
||||||
default:
|
default:
|
||||||
log.error("fail WaitForSingleObject[", ::GetLastError(), "]: 0x", std::hex, ret, std::dec);
|
ipc::error("fail WaitForSingleObject[%lu]: 0x%08X\n", ::GetLastError(), ret);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool post(std::uint32_t count) noexcept {
|
bool post(std::uint32_t count) noexcept {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (!::ReleaseSemaphore(h_, static_cast<LONG>(count), NULL)) {
|
if (!::ReleaseSemaphore(h_, static_cast<LONG>(count), NULL)) {
|
||||||
log.error("fail ReleaseSemaphore[", ::GetLastError(), "]");
|
ipc::error("fail ReleaseSemaphore[%lu]\n", ::GetLastError());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@ -11,10 +11,10 @@
|
|||||||
|
|
||||||
#include "libipc/shm.h"
|
#include "libipc/shm.h"
|
||||||
#include "libipc/def.h"
|
#include "libipc/def.h"
|
||||||
|
#include "libipc/pool_alloc.h"
|
||||||
|
|
||||||
#include "libipc/imp/log.h"
|
#include "libipc/utility/log.h"
|
||||||
#include "libipc/mem/resource.h"
|
#include "libipc/memory/resource.h"
|
||||||
#include "libipc/mem/new.h"
|
|
||||||
|
|
||||||
#include "to_tchar.h"
|
#include "to_tchar.h"
|
||||||
#include "get_sa.h"
|
#include "get_sa.h"
|
||||||
@ -45,9 +45,8 @@ namespace ipc {
|
|||||||
namespace shm {
|
namespace shm {
|
||||||
|
|
||||||
id_t acquire(char const * name, std::size_t size, unsigned mode) {
|
id_t acquire(char const * name, std::size_t size, unsigned mode) {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (!is_valid_string(name)) {
|
if (!is_valid_string(name)) {
|
||||||
log.error("fail acquire: name is empty");
|
ipc::error("fail acquire: name is empty\n");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
HANDLE h;
|
HANDLE h;
|
||||||
@ -56,7 +55,7 @@ id_t acquire(char const * name, std::size_t size, unsigned mode) {
|
|||||||
if (mode == open) {
|
if (mode == open) {
|
||||||
h = ::OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, fmt_name.c_str());
|
h = ::OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, fmt_name.c_str());
|
||||||
if (h == NULL) {
|
if (h == NULL) {
|
||||||
log.error("fail OpenFileMapping[", static_cast<int>(::GetLastError()), "]: ", name);
|
ipc::error("fail OpenFileMapping[%d]: %s\n", static_cast<int>(::GetLastError()), name);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -73,11 +72,11 @@ id_t acquire(char const * name, std::size_t size, unsigned mode) {
|
|||||||
h = NULL;
|
h = NULL;
|
||||||
}
|
}
|
||||||
if (h == NULL) {
|
if (h == NULL) {
|
||||||
log.error("fail CreateFileMapping[", static_cast<int>(err), "]: ", name);
|
ipc::error("fail CreateFileMapping[%d]: %s\n", static_cast<int>(err), name);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto ii = mem::$new<id_info_t>();
|
auto ii = mem::alloc<id_info_t>();
|
||||||
ii->h_ = h;
|
ii->h_ = h;
|
||||||
ii->size_ = size;
|
ii->size_ = size;
|
||||||
return ii;
|
return ii;
|
||||||
@ -95,23 +94,21 @@ std::int32_t get_ref(id_t id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void sub_ref(id_t id) {
|
void sub_ref(id_t id) {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (id == nullptr) {
|
if (id == nullptr) {
|
||||||
log.error("fail sub_ref: invalid id (null)");
|
ipc::error("fail sub_ref: invalid id (null)\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto ii = static_cast<id_info_t*>(id);
|
auto ii = static_cast<id_info_t*>(id);
|
||||||
if (ii->mem_ == nullptr || ii->size_ == 0) {
|
if (ii->mem_ == nullptr || ii->size_ == 0) {
|
||||||
log.error("fail sub_ref: invalid id (mem = ", ii->mem_, ", size = ", ii->size_, ")");
|
ipc::error("fail sub_ref: invalid id (mem = %p, size = %zd)\n", ii->mem_, ii->size_);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
acc_of(ii->mem_, calc_size(ii->size_)).fetch_sub(1, std::memory_order_acq_rel);
|
acc_of(ii->mem_, calc_size(ii->size_)).fetch_sub(1, std::memory_order_acq_rel);
|
||||||
}
|
}
|
||||||
|
|
||||||
void * get_mem(id_t id, std::size_t * size) {
|
void * get_mem(id_t id, std::size_t * size) {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (id == nullptr) {
|
if (id == nullptr) {
|
||||||
log.error("fail get_mem: invalid id (null)");
|
ipc::error("fail get_mem: invalid id (null)\n");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
auto ii = static_cast<id_info_t*>(id);
|
auto ii = static_cast<id_info_t*>(id);
|
||||||
@ -120,17 +117,17 @@ void * get_mem(id_t id, std::size_t * size) {
|
|||||||
return ii->mem_;
|
return ii->mem_;
|
||||||
}
|
}
|
||||||
if (ii->h_ == NULL) {
|
if (ii->h_ == NULL) {
|
||||||
log.error("fail to_mem: invalid id (h = null)");
|
ipc::error("fail to_mem: invalid id (h = null)\n");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
LPVOID mem = ::MapViewOfFile(ii->h_, FILE_MAP_ALL_ACCESS, 0, 0, 0);
|
LPVOID mem = ::MapViewOfFile(ii->h_, FILE_MAP_ALL_ACCESS, 0, 0, 0);
|
||||||
if (mem == NULL) {
|
if (mem == NULL) {
|
||||||
log.error("fail MapViewOfFile[", static_cast<int>(::GetLastError()), "]");
|
ipc::error("fail MapViewOfFile[%d]\n", static_cast<int>(::GetLastError()));
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
MEMORY_BASIC_INFORMATION mem_info;
|
MEMORY_BASIC_INFORMATION mem_info;
|
||||||
if (::VirtualQuery(mem, &mem_info, sizeof(mem_info)) == 0) {
|
if (::VirtualQuery(mem, &mem_info, sizeof(mem_info)) == 0) {
|
||||||
log.error("fail VirtualQuery[", static_cast<int>(::GetLastError()), "]");
|
ipc::error("fail VirtualQuery[%d]\n", static_cast<int>(::GetLastError()));
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
std::size_t actual_size = static_cast<std::size_t>(mem_info.RegionSize);
|
std::size_t actual_size = static_cast<std::size_t>(mem_info.RegionSize);
|
||||||
@ -147,41 +144,38 @@ void * get_mem(id_t id, std::size_t * size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::int32_t release(id_t id) noexcept {
|
std::int32_t release(id_t id) noexcept {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (id == nullptr) {
|
if (id == nullptr) {
|
||||||
log.error("fail release: invalid id (null)");
|
ipc::error("fail release: invalid id (null)\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
std::int32_t ret = -1;
|
std::int32_t ret = -1;
|
||||||
auto ii = static_cast<id_info_t*>(id);
|
auto ii = static_cast<id_info_t*>(id);
|
||||||
if (ii->mem_ == nullptr || ii->size_ == 0) {
|
if (ii->mem_ == nullptr || ii->size_ == 0) {
|
||||||
log.error("fail release: invalid id (mem = ", ii->mem_, ", size = ", ii->size_, ")");
|
ipc::error("fail release: invalid id (mem = %p, size = %zd)\n", ii->mem_, ii->size_);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ret = acc_of(ii->mem_, calc_size(ii->size_)).fetch_sub(1, std::memory_order_acq_rel);
|
ret = acc_of(ii->mem_, calc_size(ii->size_)).fetch_sub(1, std::memory_order_acq_rel);
|
||||||
::UnmapViewOfFile(static_cast<LPCVOID>(ii->mem_));
|
::UnmapViewOfFile(static_cast<LPCVOID>(ii->mem_));
|
||||||
}
|
}
|
||||||
if (ii->h_ == NULL) {
|
if (ii->h_ == NULL) {
|
||||||
log.error("fail release: invalid id (h = null)");
|
ipc::error("fail release: invalid id (h = null)\n");
|
||||||
}
|
}
|
||||||
else ::CloseHandle(ii->h_);
|
else ::CloseHandle(ii->h_);
|
||||||
mem::$delete(ii);
|
mem::free(ii);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void remove(id_t id) noexcept {
|
void remove(id_t id) noexcept {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (id == nullptr) {
|
if (id == nullptr) {
|
||||||
log.error("fail release: invalid id (null)");
|
ipc::error("fail release: invalid id (null)\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
release(id);
|
release(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void remove(char const * name) noexcept {
|
void remove(char const * name) noexcept {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (!is_valid_string(name)) {
|
if (!is_valid_string(name)) {
|
||||||
log.error("fail remove: name is empty");
|
ipc::error("fail remove: name is empty\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Do Nothing.
|
// Do Nothing.
|
||||||
@ -189,4 +183,3 @@ void remove(char const * name) noexcept {
|
|||||||
|
|
||||||
} // namespace shm
|
} // namespace shm
|
||||||
} // namespace ipc
|
} // namespace ipc
|
||||||
|
|
||||||
|
|||||||
@ -1,88 +0,0 @@
|
|||||||
/**
|
|
||||||
* \file libipc/platform/win/system.h
|
|
||||||
* \author mutouyun (orz@orzz.org)
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <exception>
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
#include <Windows.h>
|
|
||||||
#include <tchar.h>
|
|
||||||
|
|
||||||
#include "libipc/imp/system.h"
|
|
||||||
#include "libipc/imp/log.h"
|
|
||||||
#include "libipc/imp/codecvt.h"
|
|
||||||
#include "libipc/imp/generic.h"
|
|
||||||
#include "libipc/imp/detect_plat.h"
|
|
||||||
#include "libipc/imp/scope_exit.h"
|
|
||||||
|
|
||||||
namespace ipc {
|
|
||||||
namespace sys {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Gets a text description of the system error
|
|
||||||
* \see https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-formatmessage
|
|
||||||
*/
|
|
||||||
std::string error_string(DWORD code) noexcept {
|
|
||||||
LIBIPC_LOG();
|
|
||||||
LIBIPC_TRY {
|
|
||||||
LPTSTR lpErrText = NULL;
|
|
||||||
if (::FormatMessage(
|
|
||||||
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
||||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
|
||||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
||||||
NULL,
|
|
||||||
code,
|
|
||||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
||||||
(LPTSTR)&lpErrText,
|
|
||||||
0, NULL) == 0) {
|
|
||||||
log.error("failed: FormatMessage(dwMessageId = ", code, "). error = ", ::GetLastError());
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
LIBIPC_SCOPE_EXIT(finally) = [lpErrText] { ::LocalFree(lpErrText); };
|
|
||||||
std::size_t msg_len = ::_tcslen(lpErrText);
|
|
||||||
std::size_t len = cvt_cstr(lpErrText, msg_len, (char *)nullptr, 0);
|
|
||||||
if (len == 0) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
std::string ret(len, '\0');
|
|
||||||
cvt_cstr(lpErrText, msg_len, &ret[0], ret.size());
|
|
||||||
return ret;
|
|
||||||
} LIBIPC_CATCH(...) {
|
|
||||||
log.error("failed: FormatMessage(dwMessageId = ", code, ").",
|
|
||||||
"\n\texception: ", log::exception_string(std::current_exception()));
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Get the system error number.
|
|
||||||
* \see https://en.cppreference.com/w/cpp/error/system_category
|
|
||||||
* https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror
|
|
||||||
*/
|
|
||||||
std::error_code error() noexcept {
|
|
||||||
return std::error_code(::GetLastError(), std::system_category());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Retrieves information about the current system.
|
|
||||||
* \see https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsysteminfo
|
|
||||||
* https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getnativesysteminfo
|
|
||||||
*/
|
|
||||||
result<std::int64_t> conf(info r) noexcept {
|
|
||||||
LIBIPC_LOG();
|
|
||||||
switch (r) {
|
|
||||||
case info::page_size: {
|
|
||||||
::SYSTEM_INFO info{};
|
|
||||||
::GetNativeSystemInfo(&info);
|
|
||||||
return (std::int64_t)info.dwPageSize;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
log.error("invalid info = ", underlyof(r));
|
|
||||||
return std::make_error_code(std::errc::invalid_argument);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace sys
|
|
||||||
} // namespace ipc
|
|
||||||
@ -15,7 +15,7 @@
|
|||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
|
||||||
#include "libipc/utility/concept.h"
|
#include "libipc/utility/concept.h"
|
||||||
#include "libipc/mem/resource.h"
|
#include "libipc/memory/resource.h"
|
||||||
#include "libipc/platform/detail.h"
|
#include "libipc/platform/detail.h"
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
@ -40,7 +40,7 @@ using IsSameChar = ipc::require<is_same_char<T, S>::value, R>;
|
|||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
template <typename T = TCHAR>
|
template <typename T = TCHAR>
|
||||||
constexpr auto to_tchar(std::string &&str) -> IsSameChar<T, std::string, std::string &&> {
|
constexpr auto to_tchar(ipc::string &&str) -> IsSameChar<T, ipc::string, ipc::string &&> {
|
||||||
return std::move(str); // noconv
|
return std::move(str); // noconv
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ constexpr auto to_tchar(std::string &&str) -> IsSameChar<T, std::string, std::st
|
|||||||
* https://docs.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-multibytetowidechar
|
* https://docs.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-multibytetowidechar
|
||||||
*/
|
*/
|
||||||
template <typename T = TCHAR>
|
template <typename T = TCHAR>
|
||||||
auto to_tchar(std::string &&external) -> IsSameChar<T, std::wstring> {
|
auto to_tchar(ipc::string &&external) -> IsSameChar<T, ipc::wstring> {
|
||||||
if (external.empty()) {
|
if (external.empty()) {
|
||||||
return {}; // noconv
|
return {}; // noconv
|
||||||
}
|
}
|
||||||
@ -69,7 +69,7 @@ auto to_tchar(std::string &&external) -> IsSameChar<T, std::wstring> {
|
|||||||
if (size_needed <= 0) {
|
if (size_needed <= 0) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
std::wstring internal(size_needed, L'\0');
|
ipc::wstring internal(size_needed, L'\0');
|
||||||
::MultiByteToWideChar(CP_UTF8, 0, &external[0], (int)external.size(), &internal[0], size_needed);
|
::MultiByteToWideChar(CP_UTF8, 0, &external[0], (int)external.size(), &internal[0], size_needed);
|
||||||
return internal;
|
return internal;
|
||||||
}
|
}
|
||||||
|
|||||||
17
src/libipc/pool_alloc.cpp
Executable file
17
src/libipc/pool_alloc.cpp
Executable file
@ -0,0 +1,17 @@
|
|||||||
|
#include "libipc/pool_alloc.h"
|
||||||
|
|
||||||
|
#include "libipc/memory/resource.h"
|
||||||
|
|
||||||
|
namespace ipc {
|
||||||
|
namespace mem {
|
||||||
|
|
||||||
|
void* pool_alloc::alloc(std::size_t size) noexcept {
|
||||||
|
return async_pool_alloc::alloc(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pool_alloc::free(void* p, std::size_t size) noexcept {
|
||||||
|
async_pool_alloc::free(p, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace mem
|
||||||
|
} // namespace ipc
|
||||||
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
#include "libipc/platform/detail.h"
|
#include "libipc/platform/detail.h"
|
||||||
#include "libipc/circ/elem_def.h"
|
#include "libipc/circ/elem_def.h"
|
||||||
#include "libipc/imp/log.h"
|
#include "libipc/utility/log.h"
|
||||||
#include "libipc/utility/utility.h"
|
#include "libipc/utility/utility.h"
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
@ -242,7 +242,6 @@ struct prod_cons_impl<wr<relat::single, relat::multi, trans::broadcast>> {
|
|||||||
|
|
||||||
template <typename W, typename F, typename E>
|
template <typename W, typename F, typename E>
|
||||||
bool force_push(W* wrapper, F&& f, E* elems) {
|
bool force_push(W* wrapper, F&& f, E* elems) {
|
||||||
LIBIPC_LOG();
|
|
||||||
E* el;
|
E* el;
|
||||||
epoch_ += ep_incr;
|
epoch_ += ep_incr;
|
||||||
for (unsigned k = 0;;) {
|
for (unsigned k = 0;;) {
|
||||||
@ -253,7 +252,7 @@ struct prod_cons_impl<wr<relat::single, relat::multi, trans::broadcast>> {
|
|||||||
auto cur_rc = el->rc_.load(std::memory_order_acquire);
|
auto cur_rc = el->rc_.load(std::memory_order_acquire);
|
||||||
circ::cc_t rem_cc = cur_rc & ep_mask;
|
circ::cc_t rem_cc = cur_rc & ep_mask;
|
||||||
if (cc & rem_cc) {
|
if (cc & rem_cc) {
|
||||||
log.debug("force_push: k = ", k, ", cc = ", cc, ", rem_cc = ", rem_cc);
|
ipc::log("force_push: k = %u, cc = %u, rem_cc = %u\n", k, cc, rem_cc);
|
||||||
cc = wrapper->elems()->disconnect_receiver(rem_cc); // disconnect all invalid readers
|
cc = wrapper->elems()->disconnect_receiver(rem_cc); // disconnect all invalid readers
|
||||||
if (cc == 0) return false; // no reader
|
if (cc == 0) return false; // no reader
|
||||||
}
|
}
|
||||||
@ -365,7 +364,6 @@ struct prod_cons_impl<wr<relat::multi, relat::multi, trans::broadcast>> {
|
|||||||
|
|
||||||
template <typename W, typename F, typename E>
|
template <typename W, typename F, typename E>
|
||||||
bool force_push(W* wrapper, F&& f, E* elems) {
|
bool force_push(W* wrapper, F&& f, E* elems) {
|
||||||
LIBIPC_LOG();
|
|
||||||
E* el;
|
E* el;
|
||||||
circ::u2_t cur_ct;
|
circ::u2_t cur_ct;
|
||||||
rc_t epoch = epoch_.fetch_add(ep_incr, std::memory_order_release) + ep_incr;
|
rc_t epoch = epoch_.fetch_add(ep_incr, std::memory_order_release) + ep_incr;
|
||||||
@ -377,7 +375,7 @@ struct prod_cons_impl<wr<relat::multi, relat::multi, trans::broadcast>> {
|
|||||||
auto cur_rc = el->rc_.load(std::memory_order_acquire);
|
auto cur_rc = el->rc_.load(std::memory_order_acquire);
|
||||||
circ::cc_t rem_cc = cur_rc & rc_mask;
|
circ::cc_t rem_cc = cur_rc & rc_mask;
|
||||||
if (cc & rem_cc) {
|
if (cc & rem_cc) {
|
||||||
log.debug("force_push: k = ", k, ", cc = ", cc, ", rem_cc = ", rem_cc);
|
ipc::log("force_push: k = %u, cc = %u, rem_cc = %u\n", k, cc, rem_cc);
|
||||||
cc = wrapper->elems()->disconnect_receiver(rem_cc); // disconnect all invalid readers
|
cc = wrapper->elems()->disconnect_receiver(rem_cc); // disconnect all invalid readers
|
||||||
if (cc == 0) return false; // no reader
|
if (cc == 0) return false; // no reader
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,10 +15,10 @@
|
|||||||
#include "libipc/shm.h"
|
#include "libipc/shm.h"
|
||||||
#include "libipc/rw_lock.h"
|
#include "libipc/rw_lock.h"
|
||||||
|
|
||||||
#include "libipc/imp/log.h"
|
#include "libipc/utility/log.h"
|
||||||
#include "libipc/platform/detail.h"
|
#include "libipc/platform/detail.h"
|
||||||
#include "libipc/circ/elem_def.h"
|
#include "libipc/circ/elem_def.h"
|
||||||
#include "libipc/mem/resource.h"
|
#include "libipc/memory/resource.h"
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
@ -30,9 +30,8 @@ protected:
|
|||||||
|
|
||||||
template <typename Elems>
|
template <typename Elems>
|
||||||
Elems* open(char const * name) {
|
Elems* open(char const * name) {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (!is_valid_string(name)) {
|
if (!is_valid_string(name)) {
|
||||||
log.error("fail open waiter: name is empty!");
|
ipc::error("fail open waiter: name is empty!\n");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
if (!elems_h_.acquire(name, sizeof(Elems))) {
|
if (!elems_h_.acquire(name, sizeof(Elems))) {
|
||||||
@ -40,7 +39,7 @@ protected:
|
|||||||
}
|
}
|
||||||
auto elems = static_cast<Elems*>(elems_h_.get());
|
auto elems = static_cast<Elems*>(elems_h_.get());
|
||||||
if (elems == nullptr) {
|
if (elems == nullptr) {
|
||||||
log.error("fail acquire elems: ", name);
|
ipc::error("fail acquire elems: %s\n", name);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
elems->init();
|
elems->init();
|
||||||
|
|||||||
@ -5,8 +5,8 @@
|
|||||||
#include "libipc/shm.h"
|
#include "libipc/shm.h"
|
||||||
|
|
||||||
#include "libipc/utility/pimpl.h"
|
#include "libipc/utility/pimpl.h"
|
||||||
#include "libipc/imp/log.h"
|
#include "libipc/utility/log.h"
|
||||||
#include "libipc/mem/resource.h"
|
#include "libipc/memory/resource.h"
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
namespace shm {
|
namespace shm {
|
||||||
@ -16,7 +16,7 @@ public:
|
|||||||
shm::id_t id_ = nullptr;
|
shm::id_t id_ = nullptr;
|
||||||
void* m_ = nullptr;
|
void* m_ = nullptr;
|
||||||
|
|
||||||
std::string n_;
|
ipc::string n_;
|
||||||
std::size_t s_ = 0;
|
std::size_t s_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -69,13 +69,12 @@ void handle::sub_ref() noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool handle::acquire(char const * name, std::size_t size, unsigned mode) {
|
bool handle::acquire(char const * name, std::size_t size, unsigned mode) {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (!is_valid_string(name)) {
|
if (!is_valid_string(name)) {
|
||||||
log.error("fail acquire: name is empty");
|
ipc::error("fail acquire: name is empty\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (size == 0) {
|
if (size == 0) {
|
||||||
log.error("fail acquire: size is 0");
|
ipc::error("fail acquire: size is 0\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
release();
|
release();
|
||||||
|
|||||||
@ -2,14 +2,14 @@
|
|||||||
#include "libipc/condition.h"
|
#include "libipc/condition.h"
|
||||||
|
|
||||||
#include "libipc/utility/pimpl.h"
|
#include "libipc/utility/pimpl.h"
|
||||||
#include "libipc/imp/log.h"
|
#include "libipc/utility/log.h"
|
||||||
#include "libipc/mem/resource.h"
|
#include "libipc/memory/resource.h"
|
||||||
#include "libipc/platform/detail.h"
|
#include "libipc/platform/detail.h"
|
||||||
#if defined(LIBIPC_OS_WIN)
|
#if defined(IPC_OS_WINDOWS_)
|
||||||
#include "libipc/platform/win/condition.h"
|
#include "libipc/platform/win/condition.h"
|
||||||
#elif defined(LIBIPC_OS_LINUX)
|
#elif defined(IPC_OS_LINUX_)
|
||||||
#include "libipc/platform/linux/condition.h"
|
#include "libipc/platform/linux/condition.h"
|
||||||
#elif defined(LIBIPC_OS_QNX) || defined(LIBIPC_OS_FREEBSD)
|
#elif defined(IPC_OS_QNX_) || defined(IPC_OS_FREEBSD_)
|
||||||
#include "libipc/platform/posix/condition.h"
|
#include "libipc/platform/posix/condition.h"
|
||||||
#else/*IPC_OS*/
|
#else/*IPC_OS*/
|
||||||
# error "Unsupported platform."
|
# error "Unsupported platform."
|
||||||
@ -50,9 +50,8 @@ bool condition::valid() const noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool condition::open(char const *name) noexcept {
|
bool condition::open(char const *name) noexcept {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (!is_valid_string(name)) {
|
if (!is_valid_string(name)) {
|
||||||
log.error("fail condition open: name is empty");
|
ipc::error("fail condition open: name is empty\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return impl(p_)->cond_.open(name);
|
return impl(p_)->cond_.open(name);
|
||||||
|
|||||||
@ -2,14 +2,14 @@
|
|||||||
#include "libipc/mutex.h"
|
#include "libipc/mutex.h"
|
||||||
|
|
||||||
#include "libipc/utility/pimpl.h"
|
#include "libipc/utility/pimpl.h"
|
||||||
#include "libipc/imp/log.h"
|
#include "libipc/utility/log.h"
|
||||||
#include "libipc/mem/resource.h"
|
#include "libipc/memory/resource.h"
|
||||||
#include "libipc/platform/detail.h"
|
#include "libipc/platform/detail.h"
|
||||||
#if defined(LIBIPC_OS_WIN)
|
#if defined(IPC_OS_WINDOWS_)
|
||||||
#include "libipc/platform/win/mutex.h"
|
#include "libipc/platform/win/mutex.h"
|
||||||
#elif defined(LIBIPC_OS_LINUX)
|
#elif defined(IPC_OS_LINUX_)
|
||||||
#include "libipc/platform/linux/mutex.h"
|
#include "libipc/platform/linux/mutex.h"
|
||||||
#elif defined(LIBIPC_OS_QNX) || defined(LIBIPC_OS_FREEBSD)
|
#elif defined(IPC_OS_QNX_) || defined(IPC_OS_FREEBSD_)
|
||||||
#include "libipc/platform/posix/mutex.h"
|
#include "libipc/platform/posix/mutex.h"
|
||||||
#else/*IPC_OS*/
|
#else/*IPC_OS*/
|
||||||
# error "Unsupported platform."
|
# error "Unsupported platform."
|
||||||
@ -50,9 +50,8 @@ bool mutex::valid() const noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool mutex::open(char const *name) noexcept {
|
bool mutex::open(char const *name) noexcept {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (!is_valid_string(name)) {
|
if (!is_valid_string(name)) {
|
||||||
log.error("fail mutex open: name is empty");
|
ipc::error("fail mutex open: name is empty\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return impl(p_)->lock_.open(name);
|
return impl(p_)->lock_.open(name);
|
||||||
|
|||||||
@ -2,13 +2,12 @@
|
|||||||
#include "libipc/semaphore.h"
|
#include "libipc/semaphore.h"
|
||||||
|
|
||||||
#include "libipc/utility/pimpl.h"
|
#include "libipc/utility/pimpl.h"
|
||||||
#include "libipc/imp/log.h"
|
#include "libipc/utility/log.h"
|
||||||
#include "libipc/mem/resource.h"
|
#include "libipc/memory/resource.h"
|
||||||
#include "libipc/platform/detail.h"
|
#include "libipc/platform/detail.h"
|
||||||
#if defined(LIBIPC_OS_WIN)
|
#if defined(IPC_OS_WINDOWS_)
|
||||||
#include "libipc/platform/win/semaphore.h"
|
#include "libipc/platform/win/semaphore.h"
|
||||||
#elif defined(LIBIPC_OS_QNX) || defined(LIBIPC_OS_FREEBSD)
|
#elif defined(IPC_OS_LINUX_) || defined(IPC_OS_QNX_) || defined(IPC_OS_FREEBSD_)
|
||||||
#elif defined(LIBIPC_OS_LINUX) || defined(LIBIPC_OS_QNX)
|
|
||||||
#include "libipc/platform/posix/semaphore_impl.h"
|
#include "libipc/platform/posix/semaphore_impl.h"
|
||||||
#else/*IPC_OS*/
|
#else/*IPC_OS*/
|
||||||
# error "Unsupported platform."
|
# error "Unsupported platform."
|
||||||
@ -49,9 +48,8 @@ bool semaphore::valid() const noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool semaphore::open(char const *name, std::uint32_t count) noexcept {
|
bool semaphore::open(char const *name, std::uint32_t count) noexcept {
|
||||||
LIBIPC_LOG();
|
|
||||||
if (!is_valid_string(name)) {
|
if (!is_valid_string(name)) {
|
||||||
log.error("fail semaphore open: name is empty");
|
ipc::error("fail semaphore open: name is empty\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return impl(p_)->sem_.open(name, count);
|
return impl(p_)->sem_.open(name, count);
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
#include "libipc/waiter.h"
|
#include "libipc/waiter.h"
|
||||||
|
|
||||||
#include "libipc/platform/detail.h"
|
#include "libipc/platform/detail.h"
|
||||||
#if defined(LIBIPC_OS_WIN)
|
#if defined(IPC_OS_WINDOWS_)
|
||||||
#include "libipc/platform/win/mutex.h"
|
#include "libipc/platform/win/mutex.h"
|
||||||
#elif defined(LIBIPC_OS_LINUX)
|
#elif defined(IPC_OS_LINUX_)
|
||||||
#include "libipc/platform/linux/mutex.h"
|
#include "libipc/platform/linux/mutex.h"
|
||||||
#elif defined(LIBIPC_OS_QNX) || defined(LIBIPC_OS_FREEBSD)
|
#elif defined(IPC_OS_QNX_) || defined(IPC_OS_FREEBSD_)
|
||||||
#include "libipc/platform/posix/mutex.h"
|
#include "libipc/platform/posix/mutex.h"
|
||||||
#else/*IPC_OS*/
|
#else/*IPC_OS*/
|
||||||
# error "Unsupported platform."
|
# error "Unsupported platform."
|
||||||
|
|||||||
39
src/libipc/utility/log.h
Executable file
39
src/libipc/utility/log.h
Executable file
@ -0,0 +1,39 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace ipc {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <typename O>
|
||||||
|
void print(O out, char const * str) {
|
||||||
|
std::fprintf(out, "%s", str);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename O, typename P1, typename... P>
|
||||||
|
void print(O out, char const * fmt, P1&& p1, P&&... params) {
|
||||||
|
std::fprintf(out, fmt, std::forward<P1>(p1), std::forward<P>(params)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
inline void log(char const * fmt) {
|
||||||
|
ipc::detail::print(stdout, fmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename P1, typename... P>
|
||||||
|
void log(char const * fmt, P1&& p1, P&&... params) {
|
||||||
|
ipc::detail::print(stdout, fmt, std::forward<P1>(p1), std::forward<P>(params)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void error(char const * str) {
|
||||||
|
ipc::detail::print(stderr, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename P1, typename... P>
|
||||||
|
void error(char const * fmt, P1&& p1, P&&... params) {
|
||||||
|
ipc::detail::print(stderr, fmt, std::forward<P1>(p1), std::forward<P>(params)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ipc
|
||||||
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
#include "libipc/platform/detail.h"
|
#include "libipc/platform/detail.h"
|
||||||
#include "libipc/utility/concept.h"
|
#include "libipc/utility/concept.h"
|
||||||
#include "libipc/mem/new.h"
|
#include "libipc/pool_alloc.h"
|
||||||
|
|
||||||
namespace ipc {
|
namespace ipc {
|
||||||
|
|
||||||
@ -36,12 +36,12 @@ IPC_CONSTEXPR_ auto clear_impl(T* p) -> IsImplComfortable<T, void> {
|
|||||||
|
|
||||||
template <typename T, typename... P>
|
template <typename T, typename... P>
|
||||||
IPC_CONSTEXPR_ auto make_impl(P&&... params) -> IsImplUncomfortable<T> {
|
IPC_CONSTEXPR_ auto make_impl(P&&... params) -> IsImplUncomfortable<T> {
|
||||||
return mem::$new<T>(std::forward<P>(params)...);
|
return mem::alloc<T>(std::forward<P>(params)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
IPC_CONSTEXPR_ auto clear_impl(T* p) -> IsImplUncomfortable<T, void> {
|
IPC_CONSTEXPR_ auto clear_impl(T* p) -> IsImplUncomfortable<T, void> {
|
||||||
mem::$delete(p);
|
mem::free(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
|||||||
@ -27,7 +27,7 @@ constexpr decltype(auto) static_switch(std::size_t i, F&& f, D&& def) {
|
|||||||
|
|
||||||
template <typename F, std::size_t...I>
|
template <typename F, std::size_t...I>
|
||||||
IPC_CONSTEXPR_ void static_for(std::index_sequence<I...>, F&& f) {
|
IPC_CONSTEXPR_ void static_for(std::index_sequence<I...>, F&& f) {
|
||||||
LIBIPC_UNUSED auto expand = { (std::forward<F>(f)(std::integral_constant<std::size_t, I>{}), 0)... };
|
IPC_UNUSED_ auto expand = { (std::forward<F>(f)(std::integral_constant<std::size_t, I>{}), 0)... };
|
||||||
}
|
}
|
||||||
|
|
||||||
template <std::size_t N, typename F>
|
template <std::size_t N, typename F>
|
||||||
@ -44,6 +44,18 @@ enum {
|
|||||||
// #endif/*__cplusplus < 201703L*/
|
// #endif/*__cplusplus < 201703L*/
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
auto horrible_cast(U rhs) noexcept
|
||||||
|
-> typename std::enable_if<std::is_trivially_copyable<T>::value
|
||||||
|
&& std::is_trivially_copyable<U>::value, T>::type {
|
||||||
|
union {
|
||||||
|
T t;
|
||||||
|
U u;
|
||||||
|
} r = {};
|
||||||
|
r.u = rhs;
|
||||||
|
return r.t;
|
||||||
|
}
|
||||||
|
|
||||||
IPC_CONSTEXPR_ std::size_t make_align(std::size_t align, std::size_t size) {
|
IPC_CONSTEXPR_ std::size_t make_align(std::size_t align, std::size_t size) {
|
||||||
// align must be 2^n
|
// align must be 2^n
|
||||||
return (size + align - 1) & ~(align - 1);
|
return (size + align - 1) & ~(align - 1);
|
||||||
|
|||||||
@ -63,7 +63,7 @@ public:
|
|||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
bool wait_if(F &&pred, std::uint64_t tm = ipc::invalid_value) noexcept {
|
bool wait_if(F &&pred, std::uint64_t tm = ipc::invalid_value) noexcept {
|
||||||
LIBIPC_UNUSED std::lock_guard<ipc::sync::mutex> guard {lock_};
|
IPC_UNUSED_ std::lock_guard<ipc::sync::mutex> guard {lock_};
|
||||||
while ([this, &pred] {
|
while ([this, &pred] {
|
||||||
return !quit_.load(std::memory_order_relaxed)
|
return !quit_.load(std::memory_order_relaxed)
|
||||||
&& std::forward<F>(pred)();
|
&& std::forward<F>(pred)();
|
||||||
@ -75,14 +75,14 @@ public:
|
|||||||
|
|
||||||
bool notify() noexcept {
|
bool notify() noexcept {
|
||||||
{
|
{
|
||||||
LIBIPC_UNUSED std::lock_guard<ipc::sync::mutex> barrier{lock_}; // barrier
|
IPC_UNUSED_ std::lock_guard<ipc::sync::mutex> barrier{lock_}; // barrier
|
||||||
}
|
}
|
||||||
return cond_.notify(lock_);
|
return cond_.notify(lock_);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool broadcast() noexcept {
|
bool broadcast() noexcept {
|
||||||
{
|
{
|
||||||
LIBIPC_UNUSED std::lock_guard<ipc::sync::mutex> barrier{lock_}; // barrier
|
IPC_UNUSED_ std::lock_guard<ipc::sync::mutex> barrier{lock_}; // barrier
|
||||||
}
|
}
|
||||||
return cond_.broadcast(lock_);
|
return cond_.broadcast(lock_);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,10 +18,6 @@ include_directories(
|
|||||||
# Collect only new test files (exclude archive directory)
|
# Collect only new test files (exclude archive directory)
|
||||||
file(GLOB SRC_FILES
|
file(GLOB SRC_FILES
|
||||||
${LIBIPC_PROJECT_DIR}/test/test_*.cpp
|
${LIBIPC_PROJECT_DIR}/test/test_*.cpp
|
||||||
${LIBIPC_PROJECT_DIR}/test/imp/*.cpp
|
|
||||||
${LIBIPC_PROJECT_DIR}/test/mem/*.cpp
|
|
||||||
${LIBIPC_PROJECT_DIR}/test/concur/*.cpp
|
|
||||||
# ${LIBIPC_PROJECT_DIR}/test/profiler/*.cpp
|
|
||||||
)
|
)
|
||||||
file(GLOB HEAD_FILES ${LIBIPC_PROJECT_DIR}/test/test_*.h)
|
file(GLOB HEAD_FILES ${LIBIPC_PROJECT_DIR}/test/test_*.h)
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,7 @@
|
|||||||
#include "thread_pool.h"
|
#include "thread_pool.h"
|
||||||
|
|
||||||
#include "libipc/platform/detail.h"
|
#include "libipc/platform/detail.h"
|
||||||
#ifdef LIBIPC_OS_LINUX
|
#ifdef IPC_OS_LINUX_
|
||||||
#include <fcntl.h> // ::open
|
#include <fcntl.h> // ::open
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -88,7 +88,7 @@ inline static thread_pool & reader() {
|
|||||||
return pool;
|
return pool;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef LIBIPC_OS_LINUX
|
#ifdef IPC_OS_LINUX_
|
||||||
inline bool check_exist(char const *name) noexcept {
|
inline bool check_exist(char const *name) noexcept {
|
||||||
int fd = ::open((std::string{"/dev/shm/"} + name).c_str(), O_RDONLY);
|
int fd = ::open((std::string{"/dev/shm/"} + name).c_str(), O_RDONLY);
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
@ -100,7 +100,7 @@ inline bool check_exist(char const *name) noexcept {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
inline bool expect_exist(char const *name, bool expected) noexcept {
|
inline bool expect_exist(char const *name, bool expected) noexcept {
|
||||||
#ifdef LIBIPC_OS_LINUX
|
#ifdef IPC_OS_LINUX_
|
||||||
return ipc_ut::check_exist(name) == expected;
|
return ipc_ut::check_exist(name) == expected;
|
||||||
#else
|
#else
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@ -4,11 +4,10 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <new>
|
|
||||||
|
|
||||||
#include "libipc/ipc.h"
|
#include "libipc/ipc.h"
|
||||||
#include "libipc/buffer.h"
|
#include "libipc/buffer.h"
|
||||||
#include "libipc/mem/resource.h"
|
#include "libipc/memory/resource.h"
|
||||||
|
|
||||||
#include "test.h"
|
#include "test.h"
|
||||||
#include "thread_pool.h"
|
#include "thread_pool.h"
|
||||||
@ -85,31 +84,18 @@ void test_basic(char const * name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class data_set {
|
class data_set {
|
||||||
alignas(rand_buf) char datas_[sizeof(rand_buf[LoopCount])];
|
std::vector<rand_buf> datas_;
|
||||||
rand_buf *d_;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
data_set() {
|
data_set() {
|
||||||
// Use individual placement new instead of array placement new
|
datas_.resize(LoopCount);
|
||||||
// Array placement new adds a cookie (array size) before the array,
|
|
||||||
// which would overflow the buffer. MSVC's implementation stores this
|
|
||||||
// cookie, causing buffer overflow and subsequent memory corruption.
|
|
||||||
d_ = reinterpret_cast<rand_buf *>(datas_);
|
|
||||||
for (int i = 0; i < LoopCount; ++i) {
|
for (int i = 0; i < LoopCount; ++i) {
|
||||||
::new(d_ + i) rand_buf;
|
datas_[i].set_id(i);
|
||||||
d_[i].set_id(i);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
~data_set() {
|
std::vector<rand_buf> const &get() const noexcept {
|
||||||
// Manually destroy each element since we used individual placement new
|
return datas_;
|
||||||
for (int i = 0; i < LoopCount; ++i) {
|
|
||||||
d_[i].~rand_buf();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rand_buf const *get() const noexcept {
|
|
||||||
return d_;
|
|
||||||
}
|
}
|
||||||
} const data_set__;
|
} const data_set__;
|
||||||
|
|
||||||
@ -126,7 +112,7 @@ void test_sr(char const * name, int s_cnt, int r_cnt) {
|
|||||||
Que que { name, ipc::sender };
|
Que que { name, ipc::sender };
|
||||||
ASSERT_TRUE(que.wait_for_recv(r_cnt));
|
ASSERT_TRUE(que.wait_for_recv(r_cnt));
|
||||||
sw.start();
|
sw.start();
|
||||||
for (int i = 0; i < LoopCount; ++i) {
|
for (int i = 0; i < (int)data_set__.get().size(); ++i) {
|
||||||
ASSERT_TRUE(que.send(data_set__.get()[i]));
|
ASSERT_TRUE(que.send(data_set__.get()[i]));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -142,7 +128,7 @@ void test_sr(char const * name, int s_cnt, int r_cnt) {
|
|||||||
if (i == -1) {
|
if (i == -1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ASSERT_TRUE((i >= 0) && (i < LoopCount));
|
ASSERT_TRUE((i >= 0) && (i < (int)data_set__.get().size()));
|
||||||
auto const &data_set = data_set__.get()[i];
|
auto const &data_set = data_set__.get()[i];
|
||||||
if (data_set != got) {
|
if (data_set != got) {
|
||||||
printf("data_set__.get()[%d] != got, size = %zd/%zd\n",
|
printf("data_set__.get()[%d] != got, size = %zd/%zd\n",
|
||||||
@ -160,7 +146,7 @@ void test_sr(char const * name, int s_cnt, int r_cnt) {
|
|||||||
que.send(rand_buf{msg_head{-1}});
|
que.send(rand_buf{msg_head{-1}});
|
||||||
}
|
}
|
||||||
ipc_ut::reader().wait_for_done();
|
ipc_ut::reader().wait_for_done();
|
||||||
sw.print_elapsed<std::chrono::microseconds>(s_cnt, r_cnt, LoopCount, name);
|
sw.print_elapsed<std::chrono::microseconds>(s_cnt, r_cnt, (int)data_set__.get().size(), name);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // internal-linkage
|
} // internal-linkage
|
||||||
|
|||||||
218
test/archive/test_mem.cpp
Executable file
218
test/archive/test_mem.cpp
Executable file
@ -0,0 +1,218 @@
|
|||||||
|
#include <vector>
|
||||||
|
#include <array>
|
||||||
|
#include <thread>
|
||||||
|
#include <atomic>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
#include "capo/random.hpp"
|
||||||
|
|
||||||
|
#include "libipc/memory/resource.h"
|
||||||
|
#include "libipc/pool_alloc.h"
|
||||||
|
|
||||||
|
// #include "gperftools/tcmalloc.h"
|
||||||
|
|
||||||
|
#include "test.h"
|
||||||
|
#include "thread_pool.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr int DataMin = 4;
|
||||||
|
constexpr int DataMax = 256;
|
||||||
|
constexpr int LoopCount = 8388608;
|
||||||
|
constexpr int ThreadMax = 8;
|
||||||
|
|
||||||
|
// constexpr int DataMin = 256;
|
||||||
|
// constexpr int DataMax = 512;
|
||||||
|
// constexpr int LoopCount = 2097152;
|
||||||
|
|
||||||
|
std::vector<std::size_t> sizes__;
|
||||||
|
std::vector<void*> ptr_cache__[ThreadMax];
|
||||||
|
|
||||||
|
template <typename M>
|
||||||
|
struct alloc_ix_t {
|
||||||
|
static std::vector<int> ix_;
|
||||||
|
static bool inited_;
|
||||||
|
|
||||||
|
alloc_ix_t() {
|
||||||
|
if (inited_) return;
|
||||||
|
inited_ = true;
|
||||||
|
M::init();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int ThreadsN>
|
||||||
|
static int index(std::size_t /*pid*/, std::size_t /*k*/, std::size_t n) {
|
||||||
|
return ix_[n];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename M>
|
||||||
|
std::vector<int> alloc_ix_t<M>::ix_(LoopCount);
|
||||||
|
template <typename M>
|
||||||
|
bool alloc_ix_t<M>::inited_ = false;
|
||||||
|
|
||||||
|
struct alloc_FIFO : alloc_ix_t<alloc_FIFO> {
|
||||||
|
static void init() {
|
||||||
|
for (int i = 0; i < LoopCount; ++i) {
|
||||||
|
ix_[static_cast<std::size_t>(i)] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct alloc_LIFO : alloc_ix_t<alloc_LIFO> {
|
||||||
|
static void init() {
|
||||||
|
for (int i = 0; i < LoopCount; ++i) {
|
||||||
|
ix_[static_cast<std::size_t>(i)] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int ThreadsN>
|
||||||
|
static int index(std::size_t pid, std::size_t k, std::size_t n) {
|
||||||
|
constexpr static int CacheSize = LoopCount / ThreadsN;
|
||||||
|
if (k) {
|
||||||
|
return ix_[(CacheSize * (2 * pid + 1)) - 1 - n];
|
||||||
|
}
|
||||||
|
else return ix_[n];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct alloc_Random : alloc_ix_t<alloc_Random> {
|
||||||
|
static void init() {
|
||||||
|
capo::random<> rdm_index(0, LoopCount - 1);
|
||||||
|
for (int i = 0; i < LoopCount; ++i) {
|
||||||
|
ix_[static_cast<std::size_t>(i)] = rdm_index();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Init {
|
||||||
|
Init() {
|
||||||
|
capo::random<> rdm{ DataMin, DataMax };
|
||||||
|
for (int i = 0; i < LoopCount; ++i) {
|
||||||
|
sizes__.emplace_back(static_cast<std::size_t>(rdm()));
|
||||||
|
}
|
||||||
|
for (auto& vec : ptr_cache__) {
|
||||||
|
vec.resize(LoopCount, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} init__;
|
||||||
|
|
||||||
|
template <typename AllocT, int ThreadsN>
|
||||||
|
void benchmark_alloc(char const * message) {
|
||||||
|
std::string msg = std::to_string(ThreadsN) + "\t" + message;
|
||||||
|
|
||||||
|
constexpr static int CacheSize = LoopCount / ThreadsN;
|
||||||
|
ipc_ut::sender().start(static_cast<std::size_t>(ThreadsN));
|
||||||
|
ipc_ut::test_stopwatch sw;
|
||||||
|
|
||||||
|
for (int pid = 0; pid < ThreadsN; ++pid) {
|
||||||
|
ipc_ut::sender() << [&, pid] {
|
||||||
|
sw.start();
|
||||||
|
for (int n = (CacheSize * pid); n < (CacheSize * (pid + 1)); ++n) {
|
||||||
|
std::size_t s = sizes__[n];
|
||||||
|
AllocT::free(AllocT::alloc(s), s);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ipc_ut::sender().wait_for_done();
|
||||||
|
sw.print_elapsed<1>(DataMin, DataMax, LoopCount, msg.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename AllocT, typename ModeT, int ThreadsN>
|
||||||
|
void benchmark_alloc(char const * message) {
|
||||||
|
std::string msg = std::to_string(ThreadsN) + "\t" + message;
|
||||||
|
|
||||||
|
constexpr static int CacheSize = LoopCount / ThreadsN;
|
||||||
|
ModeT mode;
|
||||||
|
ipc_ut::sender().start(static_cast<std::size_t>(ThreadsN));
|
||||||
|
ipc_ut::test_stopwatch sw;
|
||||||
|
|
||||||
|
for (int pid = 0; pid < ThreadsN; ++pid) {
|
||||||
|
ipc_ut::sender() << [&, pid] {
|
||||||
|
auto& vec = ptr_cache__[pid];
|
||||||
|
sw.start();
|
||||||
|
for (std::size_t k = 0; k < 2; ++k)
|
||||||
|
for (int n = (CacheSize * pid); n < (CacheSize * (pid + 1)); ++n) {
|
||||||
|
int m = mode.template index<ThreadsN>(pid, k, n);
|
||||||
|
void*& p = vec[static_cast<std::size_t>(m)];
|
||||||
|
std::size_t s = sizes__[static_cast<std::size_t>(m)];
|
||||||
|
if (p == nullptr) {
|
||||||
|
p = AllocT::alloc(s);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
AllocT::free(p, s);
|
||||||
|
p = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ipc_ut::sender().wait_for_done();
|
||||||
|
sw.print_elapsed<1>(DataMin, DataMax, LoopCount, msg.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename AllocT, typename ModeT, int ThreadsN>
|
||||||
|
struct test_performance {
|
||||||
|
static void start(char const * message) {
|
||||||
|
test_performance<AllocT, ModeT, ThreadsN / 2>::start(message);
|
||||||
|
benchmark_alloc<AllocT, ModeT, ThreadsN>(message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename AllocT, typename ModeT>
|
||||||
|
struct test_performance<AllocT, ModeT, 1> {
|
||||||
|
static void start(char const * message) {
|
||||||
|
benchmark_alloc<AllocT, ModeT, 1>(message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename AllocT, int ThreadsN>
|
||||||
|
struct test_performance<AllocT, void, ThreadsN> {
|
||||||
|
static void start(char const * message) {
|
||||||
|
test_performance<AllocT, void, ThreadsN / 2>::start(message);
|
||||||
|
benchmark_alloc<AllocT, ThreadsN>(message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename AllocT>
|
||||||
|
struct test_performance<AllocT, void, 1> {
|
||||||
|
static void start(char const * message) {
|
||||||
|
benchmark_alloc<AllocT, 1>(message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// class tc_alloc {
|
||||||
|
// public:
|
||||||
|
// static void clear() {}
|
||||||
|
|
||||||
|
// static void* alloc(std::size_t size) {
|
||||||
|
// return size ? tc_malloc(size) : nullptr;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// static void free(void* p, std::size_t size) {
|
||||||
|
// tc_free_sized(p, size);
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
/*
|
||||||
|
TEST(Memory, static_alloc) {
|
||||||
|
test_performance<ipc::mem::static_alloc, void , ThreadMax>::start("alloc-free");
|
||||||
|
test_performance<ipc::mem::static_alloc, alloc_FIFO , ThreadMax>::start("alloc-FIFO");
|
||||||
|
test_performance<ipc::mem::static_alloc, alloc_LIFO , ThreadMax>::start("alloc-LIFO");
|
||||||
|
test_performance<ipc::mem::static_alloc, alloc_Random, ThreadMax>::start("alloc-Rand");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Memory, pool_alloc) {
|
||||||
|
test_performance<ipc::mem::async_pool_alloc, void , ThreadMax>::start("alloc-free");
|
||||||
|
test_performance<ipc::mem::async_pool_alloc, alloc_FIFO , ThreadMax>::start("alloc-FIFO");
|
||||||
|
test_performance<ipc::mem::async_pool_alloc, alloc_LIFO , ThreadMax>::start("alloc-LIFO");
|
||||||
|
test_performance<ipc::mem::async_pool_alloc, alloc_Random, ThreadMax>::start("alloc-Rand");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// TEST(Memory, tc_alloc) {
|
||||||
|
// test_performance<tc_alloc, void , ThreadMax>::start();
|
||||||
|
// test_performance<tc_alloc, alloc_FIFO , ThreadMax>::start();
|
||||||
|
// test_performance<tc_alloc, alloc_LIFO , ThreadMax>::start();
|
||||||
|
// test_performance<tc_alloc, alloc_Random, ThreadMax>::start();
|
||||||
|
// }
|
||||||
|
|
||||||
|
} // internal-linkage
|
||||||
@ -17,11 +17,11 @@ TEST(Platform, to_tchar) {
|
|||||||
"\x81\xab\xe3\x81\xa1\xe3\x81\xaf";
|
"\x81\xab\xe3\x81\xa1\xe3\x81\xaf";
|
||||||
wchar_t const *utf16 = L"hello world, \u4f60\u597d\uff0c\u3053\u3093\u306b\u3061\u306f";
|
wchar_t const *utf16 = L"hello world, \u4f60\u597d\uff0c\u3053\u3093\u306b\u3061\u306f";
|
||||||
{
|
{
|
||||||
std::string str = ipc::detail::to_tchar<char>(utf8);
|
ipc::string str = ipc::detail::to_tchar<char>(utf8);
|
||||||
EXPECT_STREQ(str.c_str(), utf8);
|
EXPECT_STREQ(str.c_str(), utf8);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
std::wstring wtr = ipc::detail::to_tchar<wchar_t>(utf8);
|
ipc::wstring wtr = ipc::detail::to_tchar<wchar_t>(utf8);
|
||||||
EXPECT_STREQ(wtr.c_str(), utf16);
|
EXPECT_STREQ(wtr.c_str(), utf16);
|
||||||
//std::ofstream out("out.txt", std::ios::binary|std::ios::out);
|
//std::ofstream out("out.txt", std::ios::binary|std::ios::out);
|
||||||
//out.write((char const *)wtr.c_str(), wtr.size() * sizeof(wchar_t));
|
//out.write((char const *)wtr.c_str(), wtr.size() * sizeof(wchar_t));
|
||||||
|
|||||||
@ -10,7 +10,7 @@
|
|||||||
#include "test.h"
|
#include "test.h"
|
||||||
|
|
||||||
#include "libipc/platform/detail.h"
|
#include "libipc/platform/detail.h"
|
||||||
#if defined(LIBIPC_OS_LINUX)
|
#if defined(IPC_OS_LINUX_)
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
@ -36,8 +36,10 @@ TEST(PThread, Robust) {
|
|||||||
pthread_mutex_unlock(&mutex);
|
pthread_mutex_unlock(&mutex);
|
||||||
pthread_mutex_destroy(&mutex);
|
pthread_mutex_destroy(&mutex);
|
||||||
}
|
}
|
||||||
#elif defined(LIBIPC_OS_WIN)
|
#elif defined(IPC_OS_WINDOWS_)
|
||||||
#include <Windows.h>
|
#if defined(__MINGW32__)
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#endif
|
#endif
|
||||||
#include <tchar.h>
|
#include <tchar.h>
|
||||||
|
|||||||
@ -1,103 +0,0 @@
|
|||||||
|
|
||||||
#include "../archive/test.h"
|
|
||||||
|
|
||||||
#define private public
|
|
||||||
#include "libipc/concur/intrusive_stack.h"
|
|
||||||
|
|
||||||
using namespace ipc;
|
|
||||||
|
|
||||||
TEST(intrusive_stack, construct) {
|
|
||||||
concur::intrusive_stack<int> s;
|
|
||||||
EXPECT_TRUE(s.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(intrusive_stack, construct_node) {
|
|
||||||
concur::intrusive_stack<int>::node n{};
|
|
||||||
EXPECT_TRUE(n.next.load(std::memory_order_relaxed) == nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(intrusive_stack, copyable) {
|
|
||||||
EXPECT_FALSE(std::is_copy_constructible<concur::intrusive_stack<int>>::value);
|
|
||||||
EXPECT_FALSE(std::is_copy_assignable<concur::intrusive_stack<int>>::value);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(intrusive_stack, moveable) {
|
|
||||||
EXPECT_FALSE(std::is_move_constructible<concur::intrusive_stack<int>>::value);
|
|
||||||
EXPECT_FALSE(std::is_move_assignable<concur::intrusive_stack<int>>::value);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(intrusive_stack, push_one) {
|
|
||||||
concur::intrusive_stack<int> s;
|
|
||||||
concur::intrusive_stack<int>::node n{123};
|
|
||||||
s.push(&n);
|
|
||||||
EXPECT_FALSE(s.empty());
|
|
||||||
EXPECT_TRUE(s.top_.load(std::memory_order_relaxed) == &n);
|
|
||||||
EXPECT_TRUE(n.next.load(std::memory_order_relaxed) == nullptr);
|
|
||||||
EXPECT_EQ(n.value, 123);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(intrusive_stack, push_many) {
|
|
||||||
concur::intrusive_stack<int> s;
|
|
||||||
concur::intrusive_stack<int>::node n1{111111};
|
|
||||||
concur::intrusive_stack<int>::node n2{222222};
|
|
||||||
concur::intrusive_stack<int>::node n3{333333};
|
|
||||||
s.push(&n1);
|
|
||||||
s.push(&n2);
|
|
||||||
s.push(&n3);
|
|
||||||
EXPECT_FALSE(s.empty());
|
|
||||||
EXPECT_TRUE(s.top_.load(std::memory_order_relaxed) == &n3);
|
|
||||||
EXPECT_TRUE(n3.next.load(std::memory_order_relaxed) == &n2);
|
|
||||||
EXPECT_TRUE(n2.next.load(std::memory_order_relaxed) == &n1);
|
|
||||||
EXPECT_TRUE(n1.next.load(std::memory_order_relaxed) == nullptr);
|
|
||||||
EXPECT_EQ(n1.value, 111111);
|
|
||||||
EXPECT_EQ(n2.value, 222222);
|
|
||||||
EXPECT_EQ(n3.value, 333333);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(intrusive_stack, push_same) {
|
|
||||||
concur::intrusive_stack<int> s;
|
|
||||||
concur::intrusive_stack<int>::node n{321};
|
|
||||||
s.push(&n);
|
|
||||||
s.push(&n);
|
|
||||||
EXPECT_FALSE(s.empty());
|
|
||||||
EXPECT_TRUE(s.top_.load(std::memory_order_relaxed) == &n);
|
|
||||||
EXPECT_TRUE(n.next.load(std::memory_order_relaxed) == &n);
|
|
||||||
EXPECT_EQ(n.value, 321);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(intrusive_stack, pop_empty) {
|
|
||||||
concur::intrusive_stack<int> s;
|
|
||||||
EXPECT_TRUE(s.pop() == nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(intrusive_stack, pop_one) {
|
|
||||||
concur::intrusive_stack<int> s;
|
|
||||||
concur::intrusive_stack<int>::node n{112233};
|
|
||||||
s.push(&n);
|
|
||||||
EXPECT_TRUE(s.pop() == &n);
|
|
||||||
EXPECT_TRUE(s.empty());
|
|
||||||
EXPECT_TRUE(s.top_.load(std::memory_order_relaxed) == nullptr);
|
|
||||||
EXPECT_TRUE(n.next.load(std::memory_order_relaxed) == nullptr);
|
|
||||||
EXPECT_EQ(n.value, 112233);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(intrusive_stack, pop_many) {
|
|
||||||
concur::intrusive_stack<int> s;
|
|
||||||
concur::intrusive_stack<int>::node n1{111111};
|
|
||||||
concur::intrusive_stack<int>::node n2{222222};
|
|
||||||
concur::intrusive_stack<int>::node n3{333333};
|
|
||||||
s.push(&n1);
|
|
||||||
s.push(&n2);
|
|
||||||
s.push(&n3);
|
|
||||||
EXPECT_TRUE(s.pop() == &n3);
|
|
||||||
EXPECT_TRUE(s.pop() == &n2);
|
|
||||||
EXPECT_TRUE(s.pop() == &n1);
|
|
||||||
EXPECT_TRUE(s.empty());
|
|
||||||
EXPECT_TRUE(s.top_.load(std::memory_order_relaxed) == nullptr);
|
|
||||||
EXPECT_TRUE(n3.next.load(std::memory_order_relaxed) == &n2);
|
|
||||||
EXPECT_TRUE(n2.next.load(std::memory_order_relaxed) == &n1);
|
|
||||||
EXPECT_TRUE(n1.next.load(std::memory_order_relaxed) == nullptr);
|
|
||||||
EXPECT_EQ(n1.value, 111111);
|
|
||||||
EXPECT_EQ(n2.value, 222222);
|
|
||||||
EXPECT_EQ(n3.value, 333333);
|
|
||||||
}
|
|
||||||
@ -1,57 +0,0 @@
|
|||||||
|
|
||||||
#include "../archive/test.h"
|
|
||||||
|
|
||||||
#include "libipc/imp/byte.h"
|
|
||||||
#include "libipc/imp/span.h"
|
|
||||||
#include "libipc/imp/detect_plat.h"
|
|
||||||
|
|
||||||
TEST(byte, construct) {
|
|
||||||
{
|
|
||||||
LIBIPC_UNUSED ipc::byte b;
|
|
||||||
SUCCEED();
|
|
||||||
}
|
|
||||||
{
|
|
||||||
ipc::byte b{};
|
|
||||||
EXPECT_EQ(int(b), 0);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
ipc::byte b{123};
|
|
||||||
EXPECT_EQ(int(b), 123);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
ipc::byte b{65535};
|
|
||||||
EXPECT_EQ(int(b), 255);
|
|
||||||
EXPECT_EQ(std::int8_t(b), -1);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
ipc::byte b{65536};
|
|
||||||
EXPECT_EQ(int(b), 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(byte, compare) {
|
|
||||||
{
|
|
||||||
ipc::byte b1{}, b2{};
|
|
||||||
EXPECT_EQ(b1, b2);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
ipc::byte b1{}, b2(321);
|
|
||||||
EXPECT_NE(b1, b2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(byte, byte_cast) {
|
|
||||||
int a = 654321;
|
|
||||||
int *pa = &a;
|
|
||||||
|
|
||||||
// int * => byte *
|
|
||||||
ipc::byte *pb = ipc::byte_cast(pa);
|
|
||||||
EXPECT_EQ((std::size_t)pb, (std::size_t)pa);
|
|
||||||
|
|
||||||
// byte * => int32_t *
|
|
||||||
std::int32_t *pc = ipc::byte_cast<std::int32_t>(pb);
|
|
||||||
EXPECT_EQ(*pc, a);
|
|
||||||
|
|
||||||
// byte alignment check
|
|
||||||
EXPECT_EQ(ipc::byte_cast<int>(pb + 1), nullptr);
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user