Skip to content

Commit

Permalink
iox-eclipse-iceoryx#1036 Create Shared memory builder
Browse files Browse the repository at this point in the history
Signed-off-by: Christian Eltzschig <me@elchris.org>
  • Loading branch information
elfenpiff committed Jan 26, 2022
1 parent 13b0717 commit 44b6c6b
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 106 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@
#ifndef IOX_HOOFS_POSIX_WRAPPER_SHARED_MEMORY_OBJECT_SHARED_MEMORY_HPP
#define IOX_HOOFS_POSIX_WRAPPER_SHARED_MEMORY_OBJECT_SHARED_MEMORY_HPP

#include "iceoryx_hoofs/cxx/expected.hpp"
#include "iceoryx_hoofs/cxx/filesystem.hpp"
#include "iceoryx_hoofs/cxx/helplets.hpp"
#include "iceoryx_hoofs/cxx/optional.hpp"
#include "iceoryx_hoofs/cxx/string.hpp"
#include "iceoryx_hoofs/design_pattern/creation.hpp"
#include "iceoryx_hoofs/platform/mman.hpp"

#include <cstdint>

Expand Down Expand Up @@ -54,7 +55,7 @@ static constexpr const char* OPEN_MODE_STRING[] = {
enum class SharedMemoryError
{
EMPTY_NAME,
NAME_WITHOUT_LEADING_SLASH,
INVALID_FILE_NAME,
INSUFFICIENT_PERMISSIONS,
DOES_EXIST,
PROCESS_LIMIT_OF_OPEN_FILES_REACHED,
Expand All @@ -74,7 +75,7 @@ enum class SharedMemoryError
/// shm_open, shm_unlink etc.
/// It must be used in combination with MemoryMap (or manual mmap calls)
// to gain access to the created/opened shared memory
class SharedMemory : public DesignPattern::Creation<SharedMemory, SharedMemoryError>
class SharedMemory
{
public:
static constexpr uint64_t NAME_SIZE = platform::IOX_MAX_SHM_NAME_LENGTH;
Expand Down Expand Up @@ -103,35 +104,35 @@ class SharedMemory : public DesignPattern::Creation<SharedMemory, SharedMemoryEr
/// SharedMemoryError when the underlying shm_unlink call failed.
static cxx::expected<bool, SharedMemoryError> unlinkIfExist(const Name_t& name) noexcept;

friend class DesignPattern::Creation<SharedMemory, SharedMemoryError>;
friend class SharedMemoryBuilder;

private:
/// @brief constructs or opens existing shared memory
/// @param[in] name the name of the shared memory, must start with a leading /
/// @param[in] accessMode defines if the shared memory is mapped read only or with read write rights
/// @param[in] openMode states how the shared memory is created/opened
/// @param[in] permissions the permissions the shared memory should have
/// @param[in] size the size in bytes of the shared memory
SharedMemory(const Name_t& name,
const AccessMode accessMode,
const OpenMode openMode,
const mode_t permissions,
const uint64_t size) noexcept;

bool
open(const AccessMode accessMode, const OpenMode openMode, const mode_t permissions, const uint64_t size) noexcept;
SharedMemory(const Name_t& name, const int handle, const bool hasOwnership) noexcept;

bool unlink() noexcept;
bool close() noexcept;
void destroy() noexcept;
void reset() noexcept;
static int getOflagsFor(const AccessMode accessMode, const OpenMode openMode) noexcept;

static SharedMemoryError errnoToEnum(const int32_t errnum) noexcept;

Name_t m_name;
int m_handle{INVALID_HANDLE};
bool m_hasOwnership{false};
};

class SharedMemoryBuilder
{
IOX_BUILDER_PARAMETER(SharedMemory::Name_t, name, "")
IOX_BUILDER_PARAMETER(AccessMode, accessMode, AccessMode::READ_WRITE)
IOX_BUILDER_PARAMETER(OpenMode, openMode, OpenMode::OPEN_OR_CREATE)
IOX_BUILDER_PARAMETER(cxx::perms, filePermissions, cxx::perms::owner_all | cxx::perms::group_read)
IOX_BUILDER_PARAMETER(uint64_t, size, 0U)

public:
cxx::expected<SharedMemory, SharedMemoryError> create() noexcept;
};

} // namespace posix
} // namespace iox

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,30 +33,99 @@ namespace iox
{
namespace posix
{
// NOLINTNEXTLINE(readability-function-size) todo(iox-#832): make a struct out of arguments
SharedMemory::SharedMemory(const Name_t& name,
const AccessMode accessMode,
const OpenMode openMode,
const mode_t permissions,
const uint64_t size) noexcept
static int getOflagsFor(const AccessMode accessMode, const OpenMode openMode) noexcept
{
int oflags = 0;
oflags |= (accessMode == AccessMode::READ_ONLY) ? O_RDONLY : O_RDWR;
oflags |= (openMode != OpenMode::OPEN_EXISTING) ? O_CREAT : 0;
oflags |= (openMode == OpenMode::EXCLUSIVE_CREATE) ? O_EXCL : 0;
return oflags;
}

cxx::expected<SharedMemory, SharedMemoryError> SharedMemoryBuilder::create() noexcept
{
m_isInitialized = true;
// on qnx the current working directory will be added to the /dev/shmem path if the leading slash is missing
if (name.empty())
if (m_name.empty())
{
std::cerr << "No shared memory name specified!" << std::endl;
m_isInitialized = false;
m_errorValue = SharedMemoryError::EMPTY_NAME;
return cxx::error<SharedMemoryError>(SharedMemoryError::EMPTY_NAME);
}

if (!cxx::isValidFileName(m_name))
{
std::cerr << "Shared memory requires a valid file name (not path) as name and \"" << m_name
<< "\" is not a valid file name" << std::endl;
return cxx::error<SharedMemoryError>(SharedMemoryError::INVALID_FILE_NAME);
}

cxx::string<SharedMemory::Name_t::capacity() + 1> nameWithLeadingSlash = "/";
nameWithLeadingSlash.append(cxx::TruncateToCapacity, m_name);

bool hasOwnership = (m_openMode == OpenMode::EXCLUSIVE_CREATE || m_openMode == OpenMode::PURGE_AND_CREATE
|| m_openMode == OpenMode::OPEN_OR_CREATE);
// the mask will be applied to the permissions, therefore we need to set it to 0
int sharedMemoryFileHandle = SharedMemory::INVALID_HANDLE;
mode_t umaskSaved = umask(0U);
{
cxx::GenericRAII umaskGuard([&] { umask(umaskSaved); });

if (m_openMode == OpenMode::PURGE_AND_CREATE)
{
IOX_DISCARD_RESULT(posixCall(iox_shm_unlink)(nameWithLeadingSlash.c_str())
.failureReturnValue(SharedMemory::INVALID_HANDLE)
.ignoreErrnos(ENOENT)
.evaluate());
}

auto result =
posixCall(iox_shm_open)(
nameWithLeadingSlash.c_str(),
getOflagsFor(m_accessMode,
(m_openMode == OpenMode::OPEN_OR_CREATE) ? OpenMode::EXCLUSIVE_CREATE : m_openMode),
static_cast<mode_t>(m_filePermissions))
.failureReturnValue(SharedMemory::INVALID_HANDLE)
.suppressErrorMessagesForErrnos((m_openMode == OpenMode::OPEN_OR_CREATE) ? EEXIST : 0)
.evaluate();
if (result.has_error())
{
// if it was not possible to create the shm exclusively someone else has the
// ownership and we just try to open it
if (m_openMode == OpenMode::OPEN_OR_CREATE && result.get_error().errnum == EEXIST)
{
hasOwnership = false;
result = posixCall(iox_shm_open)(nameWithLeadingSlash.c_str(),
getOflagsFor(m_accessMode, OpenMode::OPEN_EXISTING),
static_cast<mode_t>(m_filePermissions))
.failureReturnValue(SharedMemory::INVALID_HANDLE)
.evaluate();
if (!result.has_error())
{
sharedMemoryFileHandle = result->value;
return true;
}
}

m_errorValue = errnoToEnum(result.get_error().errnum);
return false;
}
m_handle = result->value;
}
// empty case handled above, so it's fine to get first element
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
else if (name.c_str()[0] != '/')

if (hasOwnership)
{
std::cerr << "Shared memory name must start with a leading slash!" << std::endl;
m_isInitialized = false;
m_errorValue = SharedMemoryError::NAME_WITHOUT_LEADING_SLASH;
if (posixCall(ftruncate)(sharedMemoryFileHandle, static_cast<int64_t>(m_size))
.failureReturnValue(SharedMemory::INVALID_HANDLE)
.evaluate()
.or_else([this](auto& r) { m_errorValue = errnoToEnum(r.errnum); })
.has_error())
{
return false;
}
}

return true;


if (m_isInitialized)
{
m_name = name;
Expand All @@ -74,18 +143,17 @@ SharedMemory::SharedMemory(const Name_t& name,
}
}

SharedMemory::~SharedMemory() noexcept
// NOLINTNEXTLINE(readability-function-size) todo(iox-#832): make a struct out of arguments
SharedMemory::SharedMemory(const Name_t& name, const int handle, const bool hasOwnership) noexcept
: m_name{name}
, m_handle{handle}
, m_hasOwnership{hasOwnership}
{
destroy();
}

int SharedMemory::getOflagsFor(const AccessMode accessMode, const OpenMode openMode) noexcept
SharedMemory::~SharedMemory() noexcept
{
int oflags = 0;
oflags |= (accessMode == AccessMode::READ_ONLY) ? O_RDONLY : O_RDWR;
oflags |= (openMode != OpenMode::OPEN_EXISTING) ? O_CREAT : 0;
oflags |= (openMode == OpenMode::EXCLUSIVE_CREATE) ? O_EXCL : 0;
return oflags;
destroy();
}

void SharedMemory::destroy() noexcept
Expand Down Expand Up @@ -143,68 +211,6 @@ bool SharedMemory::open(const AccessMode accessMode,
const mode_t permissions,
const uint64_t size) noexcept
{
cxx::Expects(size <= static_cast<uint64_t>(std::numeric_limits<int64_t>::max()));

m_hasOwnership = (openMode == OpenMode::EXCLUSIVE_CREATE || openMode == OpenMode::PURGE_AND_CREATE
|| openMode == OpenMode::OPEN_OR_CREATE);
// the mask will be applied to the permissions, therefore we need to set it to 0
mode_t umaskSaved = umask(0U);
{
cxx::GenericRAII umaskGuard([&] { umask(umaskSaved); });

if (openMode == OpenMode::PURGE_AND_CREATE)
{
IOX_DISCARD_RESULT(posixCall(iox_shm_unlink)(m_name.c_str())
.failureReturnValue(INVALID_HANDLE)
.ignoreErrnos(ENOENT)
.evaluate());
}

auto result = posixCall(iox_shm_open)(
m_name.c_str(),
getOflagsFor(accessMode,
(openMode == OpenMode::OPEN_OR_CREATE) ? OpenMode::EXCLUSIVE_CREATE : openMode),
permissions)
.failureReturnValue(INVALID_HANDLE)
.suppressErrorMessagesForErrnos((openMode == OpenMode::OPEN_OR_CREATE) ? EEXIST : 0)
.evaluate();
if (result.has_error())
{
// if it was not possible to create the shm exclusively someone else has the
// ownership and we just try to open it
if (openMode == OpenMode::OPEN_OR_CREATE && result.get_error().errnum == EEXIST)
{
m_hasOwnership = false;
result = posixCall(iox_shm_open)(
m_name.c_str(), getOflagsFor(accessMode, OpenMode::OPEN_EXISTING), permissions)
.failureReturnValue(INVALID_HANDLE)
.evaluate();
if (!result.has_error())
{
m_handle = result->value;
return true;
}
}

m_errorValue = errnoToEnum(result.get_error().errnum);
return false;
}
m_handle = result->value;
}

if (m_hasOwnership)
{
if (posixCall(ftruncate)(m_handle, static_cast<int64_t>(size))
.failureReturnValue(INVALID_HANDLE)
.evaluate()
.or_else([this](auto& r) { m_errorValue = errnoToEnum(r.errnum); })
.has_error())
{
return false;
}
}

return true;
}

cxx::expected<bool, SharedMemoryError> SharedMemory::unlinkIfExist(const Name_t& name) noexcept
Expand Down

0 comments on commit 44b6c6b

Please sign in to comment.