mcap/modules/Shared_Memory_Channel/posix.jai

193 lines
7.5 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Header_Os_Specific :: struct {
magic: u32;
}
Read_Channel_Os_Specific :: struct {
shm: s32;
}
Write_Channel_Os_Specific :: struct {
shm: s32;
}
//
// Reader stuff:
//
// O_EXCL: the caller unlinks any stale object first, so an existing one belongs to a live reader.
reader_create_channel_os_specific :: (name: string, bytes: s64) -> (success: bool, channel: Read_Channel, already_exists: bool = false) {
P :: Posix;
total_bytes := bytes + OFFSET_TO_MESSAGE_DATA;
c_name := Basic.temp_c_string(name);
shm := P.shm_open(c_name, P.O_CREAT | P.O_RDWR | P.O_EXCL, CHANNEL_FILE_MODE);
if shm == -1 {
error_code, error_string := System.get_error_value_and_string();
if error_code == P.EEXIST return false, .{}, true;
Basic.log_error("Unable to map a view of '%' for % bytes (which is the user-requested % bytes, plus overhead). Error code %, %", name, total_bytes, bytes, error_code, error_string);
return false, .{};
}
do_truncate := true;
#if OS == .MACOS {
// On macOS, ftruncate can only be called once after creation of the shared memory object.
// Every subsequent call fails with EINVAL. So let’s check the size and only call ftruncate if it doesn’t match.
// -rluba, 2023-05-15
mem_stat: P.stat_t;
stat_result := P.fstat(shm, *mem_stat);
if stat_result == -1 {
error_code, error_string := System.get_error_value_and_string();
Basic.log_error("Unable to stat '%'. Error code %, %. The file descriptor was %.", name, error_code, error_string, shm);
P.close(shm);
return false, .{};
}
if mem_stat.st_size != 0 {
if mem_stat.st_size >= total_bytes {
// Already has the right size, don’t try to ftruncate again. It would fail on macOS.
// We’re comparing with ">=" instead of "=" because the actual size seems to always be slightly bigger than the one requested via ftruncate.
do_truncate = false;
} else {
// We need to remove the old memory object and re-create it to change its size.
P.close(shm);
unlink_result := P.shm_unlink(c_name);
if unlink_result != 0 {
error_code, error_string := System.get_error_value_and_string();
Basic.log_error("Unable to remove old '%' to re-create it with the right size. Error code %, %.", name, error_code, error_string);
return false, .{};
}
shm = P.shm_open(c_name, P.O_CREAT | P.O_RDWR | P.O_EXCL, CHANNEL_FILE_MODE);
if shm == -1 {
error_code, error_string := System.get_error_value_and_string();
Basic.log_error("Unable to re-create a view of '%' for % bytes (which is the user-requested % bytes, plus overhead). Error code %, %", name, total_bytes, bytes, error_code, error_string);
return false, .{};
}
}
}
}
if do_truncate {
truncate_result := P.ftruncate(shm, total_bytes);
if truncate_result == -1 {
error_code, error_string := System.get_error_value_and_string();
Basic.log_error("Unable to ftruncate '%' for % bytes (which is the user-requested % bytes, plus overhead). Error code %, %. The file descriptor was %.\n", name, total_bytes, bytes, error_code, error_string, shm);
return false, .{};
}
}
// Remove the name now, object stays alive while fd/mmap refs exist
// if (P.shm_unlink(c_name) < 0) {
// Basic.log_error("shm_unlink failed.");
// return false, .{};
// }
flags: s32 = P.MAP_SHARED;
#if OS == .LINUX {
flags |= P.MAP_POPULATE; // Not available on macOS.
}
pointer := P.mmap(null, cast(u64) total_bytes, P.PROT_READ | P.PROT_WRITE, flags, shm, 0);
if pointer == P.MAP_FAILED {
error_code, error_string := System.get_error_value_and_string();
Basic.log_error("Unable to mmap for the channel's memory segment, name '%' bytes '%'. Error code %, %. The file descriptor was %.\n", name, bytes, error_code, error_string, shm);
P.close(shm);
return false, .{};
}
data := cast(*u8) pointer;
header_os := cast(*Header_Os_Specific)(data + OFFSET_TO_HEADER_OS_SPECIFIC);
header_os.magic = (.*) cast(*u32) HEADER_OS_MAGIC.data; // @Endian
Basic.Initialize(header_os);
channel: Read_Channel;
channel.header = cast(*Header) pointer;
channel.os_specific.shm = shm;
return true, channel;
}
reader_release_channel :: (channel: *Read_Channel, channel_name: string) -> bool {
Posix.shm_unlink(Basic.temp_c_string(channel_name)); // closes kernel object.
data := cast(*u8) channel.header;
if !data return false;
Posix.munmap(data, cast(u64) channel.header.full_memory_region_size_in_bytes);
os := *channel.os_specific;
if os.shm Posix.close(os.shm);
return true; // We currently don't check for errors.
}
//
// Writer stuff:
//
writer_connect :: (name: string, name_not_found_is_quiet := false) -> (success: bool, channel: Write_Channel) {
P :: Posix;
c_name := Basic.temp_c_string(name);
shm := P.shm_open(c_name, P.O_RDWR, 0);
if shm == -1 {
error_code, error_string := System.get_error_value_and_string();
Basic.log_error("In writer_connect(), unable to map a view of name '%'. Error code %, %\n", name, error_code, error_string);
return false, .{};
}
header_pointer := P.mmap(null, size_of(Header), P.PROT_READ | P.PROT_WRITE, P.MAP_SHARED, shm, 0);
if header_pointer == P.MAP_FAILED {
error_code, error_string := System.get_error_value_and_string();
Basic.log_error("In writer_connect(), unable to mmap for the channel's memory segment, name '%'. Error code %, %. The file descriptor was %.\n", name, error_code, error_string, shm);
P.close(shm);
return false, .{};
}
// Look inside the header to see how big our view should be.
header_success, actual_bytes := get_size_of_full_mapping_from_stub_header(header_pointer);
P.munmap(header_pointer, size_of(Header));
if !header_success {
P.close(shm);
return false, .{};
}
// Now make a new pointer that is the full size.
header_pointer = P.mmap(null, cast(u64) actual_bytes, P.PROT_READ | P.PROT_WRITE, P.MAP_SHARED, shm, 0);
if header_pointer == P.MAP_FAILED {
error_code, error_string := System.get_error_value_and_string();
Basic.log_error("In writer_connect(), unable to mmap for the channel's memory segment, name '%'. Error code %, %\n", name, error_code, error_string);
P.close(shm);
return false, .{};
}
data := cast(*u8) header_pointer;
header_os := cast(*Header_Os_Specific)(data + OFFSET_TO_HEADER_OS_SPECIFIC);
Basic.Initialize(header_os);
header_os.magic = (.*) cast(*u32) HEADER_OS_MAGIC.data; // @Endian
channel: Write_Channel;
channel.header = cast(*Header) header_pointer;
channel.os_specific.shm = shm;
return true, channel;
}
writer_disconnect :: (channel: *Write_Channel) -> bool {
Posix.munmap(channel.header, cast(u64) channel.header.full_memory_region_size_in_bytes);
Posix.close(channel.os_specific.shm);
return true;
}
#scope_module
Posix :: #import "POSIX";
System :: #import "System";
HEADER_OS_MAGIC :: "ShmP";
CHANNEL_FILE_MODE :: 0x180; // 0600: the owner only; the name already carries the uid