| #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 |