blob: 119d8f2d8c07e16316c9720909fec12948cc724d [file] [log] [blame]
#include "tlbmc/hal/shared_mem/segment_manager.h"
#include <cstddef>
#include <exception>
#include <filesystem> // NOLINT
#include <fstream>
#include <iostream>
#include <memory>
#include <new>
#include <string>
#include <string_view>
#include <system_error> // NOLINT
#include <utility>
#include "absl/log/log.h"
#include "absl/memory/memory.h"
#include "boost/interprocess/errors.hpp" // NOLINT
#include "boost/interprocess/exceptions.hpp" // NOLINT
#include "boost/interprocess/managed_shared_memory.hpp" // NOLINT
#include "tlbmc/hal/shared_mem/metrics.h"
#include "tlbmc/hal/shared_mem/sensors.h"
namespace milotic_tlbmc {
namespace {
constexpr const char* kSharedMemoryName = "TlbmcSharedMemory";
constexpr const char* kMetricsObjectName = "TlbmcMetrics";
constexpr const char* kDummyObjectName = "DummyObject";
constexpr int kMaximumSensorCount = 1024;
// A dummy object to occupy the shared memory. This is to ensure the shared
// memory segment is healthy after creation.
struct DummyObject {
char ch = 0;
};
} // namespace
bool SegmentManager::IsInitialized() {
try {
boost::interprocess::managed_shared_memory sensors_memory(
boost::interprocess::open_only, kSharedMemoryName);
return true;
} catch (const boost::interprocess::interprocess_exception& e) {
LOG(ERROR) << "Failed to open shared memory: " << e.what();
return false;
}
}
std::unique_ptr<SegmentManager> SegmentManager::Create(
std::string_view initialized_file_path) {
std::unique_ptr<SegmentManager> segment_manager =
CreateHelper(initialized_file_path);
if (segment_manager == nullptr) {
return nullptr;
}
try {
segment_manager->GetMemory().find_or_construct<DummyObject>(
kDummyObjectName, std::nothrow)();
} catch (const boost::interprocess::lock_exception& e) {
if (e.get_error_code() ==
boost::interprocess::error_code_t::not_recoverable) {
LOG(ERROR) << "Not recoverable lock exception happened: " << e.what()
<< ". Will remove the init file and shared memory, then "
"create a new one.";
// Remove the shared memory object and the initialized file
std::error_code ec;
if (std::filesystem::remove(initialized_file_path, ec); ec) {
LOG(ERROR) << "Failed to remove the init file: " << ec.message()
<< ". Will return nullptr.";
return nullptr;
}
boost::interprocess::shared_memory_object::remove(kSharedMemoryName);
return CreateHelper(initialized_file_path);
}
LOG(ERROR) << "Lock exception happened: " << e.what()
<< ". Will return nullptr.";
return nullptr;
} catch (const std::exception& e) {
LOG(ERROR) << "Unknown exception happened: " << e.what()
<< ". Will return nullptr.";
return nullptr;
}
return segment_manager;
}
std::unique_ptr<SegmentManager> SegmentManager::CreateHelper(
std::string_view initialized_file_path) {
if (std::filesystem::exists(initialized_file_path)) {
try {
boost::interprocess::managed_shared_memory sensors_memory(
boost::interprocess::open_only, kSharedMemoryName);
return absl::WrapUnique(new SegmentManager(std::move(sensors_memory)));
} catch (const boost::interprocess::interprocess_exception& e) {
LOG(ERROR) << "Failed to open shared memory: " << e.what();
return nullptr;
}
}
std::filesystem::path path(initialized_file_path);
std::filesystem::path directory = path.parent_path();
if (!std::filesystem::exists(directory) &&
!std::filesystem::create_directories(directory)) {
LOG(ERROR) << "Failed to create directory: " << directory;
return nullptr;
}
LOG(WARNING)
<< "Creating shared memory; this should only happen once per boot.";
// Times 2 for accounting for overhead.
constexpr std::size_t kSharedMemorySize =
(kMaximumSensorCount * sizeof(IpcSensor) + sizeof(TlbmcMetrics)) * 2;
try {
// Creates the segment.
boost::interprocess::managed_shared_memory memory(
boost::interprocess::create_only, kSharedMemoryName, kSharedMemorySize);
std::ofstream file(std::string{initialized_file_path});
if (!file.is_open()) {
LOG(ERROR) << "Failed to open file: " << initialized_file_path;
return nullptr;
}
file << 1;
file.flush();
LOG(WARNING) << "Created shared memory object; created file at : "
<< initialized_file_path;
return absl::WrapUnique(new SegmentManager(std::move(memory)));
} catch (const boost::interprocess::interprocess_exception& e) {
LOG(ERROR) << "Failed to create shared memory once: " << e.what();
if (e.get_error_code() ==
boost::interprocess::error_code_t::already_exists_error) {
LOG(WARNING)
<< "Shared memory already exists without a initialization file. In "
"this case, we will clear the previous shared memory.";
try {
boost::interprocess::shared_memory_object::remove(kSharedMemoryName);
boost::interprocess::managed_shared_memory memory(
boost::interprocess::create_only, kSharedMemoryName,
kSharedMemorySize);
std::ofstream file(std::string{initialized_file_path});
if (!file.is_open()) {
LOG(ERROR) << "Failed to open file: " << initialized_file_path;
return nullptr;
}
file << 1;
file.flush();
LOG(WARNING) << "Created shared memory object; created file at : "
<< initialized_file_path;
return absl::WrapUnique(new SegmentManager(std::move(memory)));
} catch (const boost::interprocess::interprocess_exception& e) {
LOG(ERROR) << "Failed to create shared memory twice: " << e.what();
return nullptr;
}
LOG(WARNING) << "All other errors are not handled will return nullptr "
"segment manager.";
return nullptr;
}
return nullptr;
}
}
std::unique_ptr<SegmentManager> SegmentManager::Get(
std::string_view initialized_file_path) {
if (!std::filesystem::exists(initialized_file_path)) {
DLOG(WARNING) << "File " << initialized_file_path
<< " not found. Shared memory is not initialized yet.";
return nullptr;
}
try {
boost::interprocess::managed_shared_memory memory(
boost::interprocess::open_only, kSharedMemoryName);
return absl::WrapUnique(new SegmentManager(std::move(memory)));
} catch (const boost::interprocess::interprocess_exception& e) {
DLOG(ERROR) << "Failed to open shared memory: " << e.what();
return nullptr;
}
}
template <typename T>
T* SafeGetOrCreate(boost::interprocess::managed_shared_memory& memory,
const char* name) {
try {
return memory.find_or_construct<T>(name, std::nothrow)();
} catch (const boost::interprocess::lock_exception& e) {
LOG(ERROR) << "Lock exception happened: " << e.what()
<< ". Will return nullptr.";
return nullptr;
}
}
IpcSensor* SegmentManager::GetOrCreateSensor(const char* sensor_name) {
return SafeGetOrCreate<IpcSensor>(memory_, sensor_name);
}
TlbmcMetrics* SegmentManager::GetOrCreateMetrics() {
return SafeGetOrCreate<TlbmcMetrics>(memory_, kMetricsObjectName);
}
} // namespace milotic_tlbmc