| #include "tlbmc/hal/shared_mem/static_client_impl.h" |
| |
| #include <cstdint> |
| #include <filesystem> // NOLINT |
| #include <limits> |
| #include <memory> |
| #include <string> |
| #include <system_error> // NOLINT |
| #include <utility> |
| |
| #include "absl/base/no_destructor.h" |
| #include "absl/log/log.h" |
| #include "absl/status/status.h" |
| #include "absl/status/statusor.h" |
| #include "absl/synchronization/mutex.h" |
| #include "boost/interprocess/managed_shared_memory.hpp" //NOLINT |
| #include "g3/macros.h" |
| #include "tlbmc/hal/shared_mem/client_interface.h" |
| #include "tlbmc/hal/shared_mem/client_segment.h" |
| // copybara:strip_begin(g3-shared-libs) |
| #include "tlbmc/hal/shared_mem/metrics.h" |
| // copybara:strip_end |
| #include "tlbmc/hal/shared_mem/sensors.h" |
| #include "tlbmc/hal/shared_mem/static_client_segment.h" |
| #include "tlbmc/hal/shared_mem/static_shm_common.h" |
| namespace { |
| |
| /* The lifecycle of real shared memory client is one instance per process. |
| * However, in many tests tear down and recreate the Tlbmc app instance multiple |
| * times for testing multiple configurations. Since it's not feasible possible |
| * to have tests spawn a new process for each test, we need to inject a testing |
| * instance to have the client lifecycle match the test lifecycle. |
| */ |
| std::unique_ptr<milotic_tlbmc::StaticSharedMemoryClient>& TestingInstance() { |
| static absl::NoDestructor< |
| std::unique_ptr<milotic_tlbmc::StaticSharedMemoryClient>> |
| testing_instance; |
| return *testing_instance; |
| } |
| |
| } // namespace |
| namespace milotic_tlbmc { |
| |
| IpcClient& StaticSharedMemoryClient::GetInstance( |
| const std::string& initialized_file_path, |
| const std::string& shared_memory_name) { |
| static absl::NoDestructor<StaticSharedMemoryClient> instance( |
| Token(), initialized_file_path, shared_memory_name); |
| if (TestingInstance() == nullptr) { |
| return *instance; |
| } |
| return *TestingInstance(); |
| } |
| std::unique_ptr<StaticSharedMemoryClient> |
| StaticSharedMemoryClient::CreateInstanceForTesting( |
| const std::string& initialized_file_path, |
| const std::string& shared_memory_name) { |
| return std::make_unique<StaticSharedMemoryClient>( |
| Token(), initialized_file_path, shared_memory_name); |
| } |
| void StaticSharedMemoryClient::SetupInstanceForUnitTest() { |
| TestingInstance() = CreateInstanceForTesting( |
| "/tmp/tlbmc/static_shm_initialized", kStaticShmName); |
| } |
| IpcClient& StaticSharedMemoryClient::GetInstance() { |
| return GetInstance(kStaticShmInitializedFile, kStaticShmName); |
| } |
| |
| absl::StatusOr<ClientSegment*> |
| StaticSharedMemoryClient::GetOrCreateClientSegment() { |
| absl::MutexLock const lock(client_segment_mutex_); |
| if (client_segment_ == nullptr) { |
| std::error_code ec; |
| if (std::filesystem::exists(initialized_file_path_, ec)) { |
| std::unique_ptr<StaticClientSegment> static_segment = |
| StaticClientSegment::Create(shared_memory_name_); |
| if (static_segment == nullptr) { |
| return absl::InternalError("Failed to load static shared memory."); |
| } |
| client_segment_ = std::move(static_segment); |
| } else if (ec) { |
| LOG(ERROR) << "Filesystem error while checking for static shm: " |
| << ec.message(); |
| return absl::InternalError(ec.message()); |
| } else { |
| return absl::UnavailableError("Shared memory not yet initialized"); |
| } |
| } |
| return client_segment_.get(); |
| } |
| |
| bool StaticSharedMemoryClient::IsSharedMemoryReady() { |
| return GetOrCreateClientSegment().ok(); |
| } |
| |
| absl::StatusOr<IpcSensor*> StaticSharedMemoryClient::FindSensor( |
| const std::string& sensor_name) { |
| { |
| absl::MutexLock const lock(sensors_mutex_); |
| auto it = sensors_.find(sensor_name); |
| if (it != sensors_.end()) { |
| return it->second; |
| } |
| } |
| |
| ECCLESIA_ASSIGN_OR_RETURN(const ClientSegment* client_segment, |
| GetOrCreateClientSegment()); |
| auto* sensor = client_segment->FindSensor(sensor_name); |
| if (sensor == nullptr) { |
| return absl::NotFoundError("Sensor not found in shared memory"); |
| } |
| absl::MutexLock const lock(sensors_mutex_); |
| sensors_[sensor_name] = sensor; |
| |
| return sensor; |
| } |
| |
| absl::StatusOr<std::pair<float, uint64_t>> |
| StaticSharedMemoryClient::ReadSensorValue(const std::string& sensor_name) { |
| ECCLESIA_ASSIGN_OR_RETURN(IpcSensor * sensor, FindSensor(sensor_name)); |
| |
| std::pair<float, uint64_t> value = sensor->GetValue(); |
| if (value.first == std::numeric_limits<float>::infinity() || |
| value.second == 0) { |
| return absl::UnavailableError( |
| "Sensor value is still invalid due to no update from client."); |
| } |
| return value; |
| } |
| |
| absl::Status StaticSharedMemoryClient::UpdateSensorValue( |
| const std::string& sensor_name, float value) { |
| ECCLESIA_ASSIGN_OR_RETURN(IpcSensor * sensor, FindSensor(sensor_name)); |
| sensor->SetValue(value); |
| return absl::OkStatus(); |
| } |
| |
| absl::Status StaticSharedMemoryClient::UpdateSensorValue( |
| const std::string& sensor_name, float value, uint64_t timestamp) { |
| ECCLESIA_ASSIGN_OR_RETURN(IpcSensor * sensor, FindSensor(sensor_name)); |
| sensor->SetValue(value, timestamp); |
| return absl::OkStatus(); |
| } |
| // copybara:strip_begin(g3-shared-libs) |
| const TlbmcMetrics* StaticSharedMemoryClient::GetMetrics() { |
| return StaticSharedMemoryMetrics::GetInProcessMetrics(); |
| } |
| // copybara:strip_end |
| } // namespace milotic_tlbmc |