Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,17 @@ add_library(laghu_test_support STATIC tests/support/laghu_test_support.cpp)
laghu_apply_first_party_contract(laghu_test_support)
target_include_directories(laghu_test_support PUBLIC "${CMAKE_SOURCE_DIR}/tests/support")

# Fault controls are build-local test support. Its complete interface is
# deliberately private to Laghu's test targets because the wrapper contracts
# name internal core and OS seams.
add_library(laghu_test_faults STATIC tests/support/laghu_test_faults.cpp)
laghu_apply_first_party_contract(laghu_test_faults)
target_include_directories(laghu_test_faults PUBLIC
"${CMAKE_SOURCE_DIR}/tests/support"
"${CMAKE_SOURCE_DIR}/src/core/contract"
"${CMAKE_SOURCE_DIR}/src/core/private"
"${CMAKE_SOURCE_DIR}/src/os/private")

add_executable(laghu_test_support_fixtures_test tests/support/fixtures.cpp)
laghu_apply_first_party_contract(laghu_test_support_fixtures_test)
target_link_libraries(laghu_test_support_fixtures_test PRIVATE laghu_test_support Threads::Threads)
Expand All @@ -68,12 +79,20 @@ add_executable(laghu_test_support_runner_contract_test tests/support/runner_cont
laghu_apply_first_party_contract(laghu_test_support_runner_contract_test)
target_link_libraries(laghu_test_support_runner_contract_test PRIVATE laghu_test_support)
laghu_add_native_test(laghu.test_support.runner_contract laghu_test_support_runner_contract_test)

add_executable(laghu_test_fault_injection_test tests/support/fault_injection.cpp)
laghu_apply_first_party_contract(laghu_test_fault_injection_test)
laghu_configure_api_consumer(laghu_test_fault_injection_test os)
target_link_libraries(laghu_test_fault_injection_test PRIVATE
laghu_test_support laghu_test_faults laghu_core laghu_os)
laghu_add_native_test(laghu.test_support.fault_injection laghu_test_fault_injection_test)
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
# POSIX requires fixed C buffers for sockaddr, mkdtemp, and directory APIs.
# Keep the narrow diagnostic waiver confined to this non-installed test support.
set_source_files_properties(
tests/support/laghu_test_support.cpp
tests/support/fixtures.cpp
tests/support/fault_injection.cpp
PROPERTIES COMPILE_OPTIONS -Wno-unsafe-buffer-usage)
endif()

Expand Down Expand Up @@ -270,8 +289,8 @@ set(laghu_verify_targets
laghu_core_state_transitions_test laghu_core_deadlines_cancellation_test
laghu_core_shared_offsets_test laghu_core_mapped_regions_test
laghu_core_binary_envelope_test laghu_core_digest_primitives_test
laghu_os_iovec_translation_test laghu_test_support laghu_test_support_fixtures_test
laghu_test_support_runner_contract_test laghu
laghu_os_iovec_translation_test laghu_test_support laghu_test_faults laghu_test_support_fixtures_test
laghu_test_support_runner_contract_test laghu_test_fault_injection_test laghu
laghu_capability_header_parity laghu_visibility_probe visibility_negative_fixture)
if(TARGET laghu_crypto_provider_test)
list(APPEND laghu_verify_targets laghu_crypto laghu_crypto_provider_test)
Expand Down
2 changes: 2 additions & 0 deletions cmake/LaghuBuildIdentity.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ function(laghu_build_identity_input_hashes output)
src/core/private/laghu/core/internal/descriptor_operations.hpp
src/core/private/laghu/core/internal/fingerprints.hpp
src/core/private/laghu/core/internal/mapping_operations.hpp
src/os/io_operations.cpp
src/os/private/laghu/os/internal/io_operations.hpp
tests/warnings/suppressions.tsv)
list(FIND LAGHU_EFFECTIVE_FEATURES tls tls_feature_index)
if(NOT tls_feature_index EQUAL -1)
Expand Down
2 changes: 1 addition & 1 deletion cmake/LaghuDependencyDag.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ function(laghu_declare_subsystem_graph)
laghu_register_subsystem_target(laghu_core core)
foreach(node IN ITEMS config os protocol tls cache observability proxy control cli adapters)
if(node STREQUAL "os")
add_library(laghu_os STATIC src/os/io_slices.cpp)
add_library(laghu_os STATIC src/os/io_slices.cpp src/os/io_operations.cpp)
else()
add_library("laghu_${node}" INTERFACE)
endif()
Expand Down
6 changes: 3 additions & 3 deletions src/core/handles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
namespace laghu::core {
namespace {

[[nodiscard]] int close_descriptor(int descriptor) noexcept { return ::close(descriptor); }
[[nodiscard]] int close_descriptor(void*, int descriptor) noexcept { return ::close(descriptor); }

constexpr internal::DescriptorOperations default_operations{close_descriptor};
constexpr internal::DescriptorOperations default_operations{nullptr, close_descriptor};

[[nodiscard]] Error invalid_descriptor_error() noexcept {
return Error{ErrorDomain::core, ErrorCode::invalid_input, EINVAL,
Expand All @@ -25,7 +25,7 @@ constexpr internal::DescriptorOperations default_operations{close_descriptor};
return std::unexpected{Error{ErrorDomain::core, ErrorCode::invalid_state, 0,
"descriptor has no close operation"}};
}
if (operations->close(descriptor) == 0) {
if (operations->close(operations->context, descriptor) == 0) {
return {};
}
return std::unexpected{Error::from_errno(errno, "descriptor close failed")};
Expand Down
32 changes: 16 additions & 16 deletions src/core/mapped_regions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,29 +28,29 @@ namespace {
return mode == MappingFlush::synchronous || mode == MappingFlush::asynchronous;
}

[[nodiscard]] void* map_region(int descriptor, std::size_t size, std::uint64_t offset,
[[nodiscard]] void* map_region(void*, int descriptor, std::size_t size, std::uint64_t offset,
MappingAccess access) noexcept {
const int protection = access == MappingAccess::read_write ? PROT_READ | PROT_WRITE : PROT_READ;
return ::mmap(nullptr, size, protection, MAP_SHARED, descriptor, static_cast<off_t>(offset));
}

[[nodiscard]] int unmap_region(void* address, std::size_t size) noexcept {
[[nodiscard]] int unmap_region(void*, void* address, std::size_t size) noexcept {
return ::munmap(address, size);
}

[[nodiscard]] int flush_region(void* address, std::size_t size, MappingFlush mode) noexcept {
[[nodiscard]] int flush_region(void*, void* address, std::size_t size, MappingFlush mode) noexcept {
const int flags = mode == MappingFlush::synchronous ? MS_SYNC : MS_ASYNC;
return ::msync(address, size, flags);
}

[[nodiscard]] int protect_region(void* address, std::size_t size, MappingAccess access) noexcept {
[[nodiscard]] int protect_region(void*, void* address, std::size_t size, MappingAccess access) noexcept {
const int protection = access == MappingAccess::read_write ? PROT_READ | PROT_WRITE : PROT_READ;
return ::mprotect(address, size, protection);
}

[[nodiscard]] long mapping_page_size() noexcept { return ::sysconf(_SC_PAGESIZE); }
[[nodiscard]] long mapping_page_size(void*) noexcept { return ::sysconf(_SC_PAGESIZE); }

[[nodiscard]] int mapping_file_size(int descriptor, std::uint64_t* output) noexcept {
[[nodiscard]] int mapping_file_size(void*, int descriptor, std::uint64_t* output) noexcept {
struct stat information {};
if (::fstat(descriptor, &information) != 0) {
return -1;
Expand All @@ -63,13 +63,13 @@ namespace {
return 0;
}

[[nodiscard]] int open_shared_memory(const char* name, MappingAccess access) noexcept {
[[nodiscard]] int open_shared_memory(void*, const char* name, MappingAccess access) noexcept {
const int flags = access == MappingAccess::read_write ? O_RDWR : O_RDONLY;
return ::shm_open(name, flags, 0);
}

const internal::MappingOperations default_operations{
map_region, unmap_region, flush_region, protect_region,
nullptr, map_region, unmap_region, flush_region, protect_region,
mapping_page_size, mapping_file_size, open_shared_memory, MAP_FAILED,
};

Expand All @@ -94,7 +94,7 @@ const internal::MappingOperations default_operations{
return std::unexpected{mapping_error(ErrorCode::invalid_state,
"mapping operations are incomplete")};
}
const long page_size = operations.page_size();
const long page_size = operations.page_size(operations.context);
if (page_size <= 0) {
return std::unexpected{Error::from_errno(errno, "mapping page size query failed")};
}
Expand All @@ -111,7 +111,7 @@ const internal::MappingOperations default_operations{
"mapping offset exceeds POSIX offset range")};
}
std::uint64_t source_size{};
if (operations.file_size(file.native_handle(), &source_size) != 0) {
if (operations.file_size(operations.context, file.native_handle(), &source_size) != 0) {
return std::unexpected{Error::from_errno(errno, "mapping source size query failed")};
}
const auto size_u64 = checked_narrow<std::uint64_t>(size);
Expand Down Expand Up @@ -144,7 +144,7 @@ const internal::MappingOperations default_operations{
return std::unexpected{range.error()};
}

const long page_size = operations->page_size();
const long page_size = operations->page_size(operations->context);
if (page_size <= 0) {
return std::unexpected{Error::from_errno(errno, "mapping page size query failed")};
}
Expand Down Expand Up @@ -275,7 +275,7 @@ Result<MappedRegion> MappedRegion::map_shared_memory_name(const char* name, std:
"mapping access is invalid")};
}
const internal::MappingOperations& operations = internal::default_mapping_operations();
const int descriptor = operations.open_shared_memory(name, access);
const int descriptor = operations.open_shared_memory(operations.context, name, access);
if (descriptor < 0) {
return std::unexpected{Error::from_errno(errno, "shared memory open failed")};
}
Expand All @@ -294,7 +294,7 @@ Result<MappedRegion> MappedRegion::map_with_operations(
!validation.has_value()) {
return std::unexpected{validation.error()};
}
void* const address = operations.map(file.native_handle(), size, offset, access);
void* const address = operations.map(operations.context, file.native_handle(), size, offset, access);
if (address == nullptr || address == operations.failed_mapping) {
return std::unexpected{Error::from_errno(errno, "mapping creation failed")};
}
Expand Down Expand Up @@ -322,7 +322,7 @@ Result<void> MappedRegion::flush(std::size_t offset, std::size_t length,
if (!range.has_value()) {
return std::unexpected{range.error()};
}
if (operations_->flush(range->first, range->second, mode) != 0) {
if (operations_->flush(operations_->context, range->first, range->second, mode) != 0) {
return std::unexpected{Error::from_errno(errno, "mapping flush failed")};
}
return {};
Expand All @@ -340,7 +340,7 @@ Result<void> MappedRegion::protect(MappingAccess access) noexcept {
return std::unexpected{mapping_error(ErrorCode::invalid_state,
"mapping has no protection operation")};
}
if (operations_->protect(address_, size_, access) != 0) {
if (operations_->protect(operations_->context, address_, size_, access) != 0) {
return std::unexpected{Error::from_errno(errno, "mapping protection change failed")};
}
access_ = access;
Expand All @@ -355,7 +355,7 @@ Result<FileHandle> MappedRegion::release() noexcept {
return std::unexpected{mapping_error(ErrorCode::invalid_state,
"mapping has no unmap operation")};
}
if (operations_->unmap(address_, size_) != 0) {
if (operations_->unmap(operations_->context, address_, size_) != 0) {
return std::unexpected{Error::from_errno(errno, "mapping release failed")};
}
address_ = nullptr;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@

namespace laghu::core::internal {

using DescriptorCloseFunction = int (*)(int) noexcept;
using DescriptorCloseFunction = int (*)(void* context, int) noexcept;

struct DescriptorOperations final {
void* context;
DescriptorCloseFunction close;
};

Expand Down
16 changes: 9 additions & 7 deletions src/core/private/laghu/core/internal/mapping_operations.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,17 @@

namespace laghu::core::internal {

using MappingMapFunction = void* (*)(int, std::size_t, std::uint64_t, MappingAccess) noexcept;
using MappingUnmapFunction = int (*)(void*, std::size_t) noexcept;
using MappingFlushFunction = int (*)(void*, std::size_t, MappingFlush) noexcept;
using MappingProtectFunction = int (*)(void*, std::size_t, MappingAccess) noexcept;
using MappingPageSizeFunction = long (*)() noexcept;
using MappingFileSizeFunction = int (*)(int, std::uint64_t*) noexcept;
using SharedMemoryOpenFunction = int (*)(const char*, MappingAccess) noexcept;
using MappingMapFunction = void* (*)(void* context, int, std::size_t, std::uint64_t,
MappingAccess) noexcept;
using MappingUnmapFunction = int (*)(void* context, void*, std::size_t) noexcept;
using MappingFlushFunction = int (*)(void* context, void*, std::size_t, MappingFlush) noexcept;
using MappingProtectFunction = int (*)(void* context, void*, std::size_t, MappingAccess) noexcept;
using MappingPageSizeFunction = long (*)(void* context) noexcept;
using MappingFileSizeFunction = int (*)(void* context, int, std::uint64_t*) noexcept;
using SharedMemoryOpenFunction = int (*)(void* context, const char*, MappingAccess) noexcept;

struct MappingOperations final {
void* context;
MappingMapFunction map;
MappingUnmapFunction unmap;
MappingFlushFunction flush;
Expand Down
55 changes: 55 additions & 0 deletions src/os/io_operations.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// SPDX-License-Identifier: AGPL-3.0-only
#include <cerrno>

#include <unistd.h>

#include <laghu/os/internal/io_operations.hpp>

namespace laghu::os::internal {
namespace {

[[nodiscard]] ssize_t system_read(void*, int descriptor, void* output,
std::size_t capacity) noexcept {
return ::read(descriptor, output, capacity);
}

[[nodiscard]] ssize_t system_write(void*, int descriptor, const void* input,
std::size_t size) noexcept {
return ::write(descriptor, input, size);
}

constexpr IoOperations default_operations{nullptr, system_read, system_write};

[[nodiscard]] core::Result<std::size_t> transfer_result(ssize_t result,
const char* diagnostic) noexcept {
if (result < 0) {
return std::unexpected{core::Error::from_errno(errno, diagnostic)};
}
return static_cast<std::size_t>(result);
}

} // namespace

const IoOperations& default_io_operations() noexcept { return default_operations; }

core::Result<std::size_t> read_once(int descriptor, core::MutableByteView output,
const IoOperations& operations) noexcept {
if (descriptor < 0 || operations.read == nullptr) {
return std::unexpected{core::Error{core::ErrorDomain::core, core::ErrorCode::invalid_input, 0,
"read operation is invalid"}};
}
return transfer_result(operations.read(operations.context, descriptor, output.data(), output.size()),
"read operation failed");
}

core::Result<std::size_t> write_once(int descriptor, core::ByteView input,
const IoOperations& operations) noexcept {
if (descriptor < 0 || operations.write == nullptr) {
return std::unexpected{core::Error{core::ErrorDomain::core, core::ErrorCode::invalid_input, 0,
"write operation is invalid"}};
}
return transfer_result(operations.write(operations.context, descriptor, input.data(), input.size()),
"write operation failed");
}

} // namespace laghu::os::internal
29 changes: 29 additions & 0 deletions src/os/private/laghu/os/internal/io_operations.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: AGPL-3.0-only
#pragma once

#include <cstddef>

#include <sys/types.h>

#include <laghu/core/bounded_buffer.hpp>

namespace laghu::os::internal {

using IoReadFunction = ssize_t (*)(void* context, int descriptor, void* output,
std::size_t capacity) noexcept;
using IoWriteFunction = ssize_t (*)(void* context, int descriptor, const void* input,
std::size_t size) noexcept;

struct IoOperations final {
void* context;
IoReadFunction read;
IoWriteFunction write;
};

[[nodiscard]] const IoOperations& default_io_operations() noexcept;
[[nodiscard]] core::Result<std::size_t> read_once(int descriptor, core::MutableByteView output,
const IoOperations& operations) noexcept;
[[nodiscard]] core::Result<std::size_t> write_once(int descriptor, core::ByteView input,
const IoOperations& operations) noexcept;

} // namespace laghu::os::internal
Loading
Loading