blob: 72b885c33c779fd895fb766a6297512e75bed91b [file]
#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