xref: /openbmc/phosphor-hwmon/sensor.cpp (revision 94539af60991235bc921c848ab1aba47485265b3)
1043d3230SPatrick Venture #include "config.h"
2043d3230SPatrick Venture 
3043d3230SPatrick Venture #include "sensor.hpp"
4043d3230SPatrick Venture 
5043d3230SPatrick Venture #include "env.hpp"
6b28f432aSPatrick Venture #include "gpio_handle.hpp"
7043d3230SPatrick Venture #include "hwmon.hpp"
8043d3230SPatrick Venture #include "sensorset.hpp"
9043d3230SPatrick Venture #include "sysfs.hpp"
10043d3230SPatrick Venture 
11e8771fd4SPatrick Williams #include <phosphor-logging/elog-errors.hpp>
12e8771fd4SPatrick Williams #include <xyz/openbmc_project/Common/error.hpp>
13e8771fd4SPatrick Williams #include <xyz/openbmc_project/Sensor/Device/error.hpp>
14e8771fd4SPatrick Williams 
1586dcac85SBrandon Kim #include <cassert>
162227bd52SWilliam A. Kennington III #include <chrono>
17ee73f5bdSJames Feist #include <cmath>
18cb3daafbSMatthew Barth #include <cstring>
199e997b4dSPatrick Venture #include <filesystem>
2064129937SPatrick Williams #include <format>
216d50c3e9SBrandon Kim #include <future>
22b28f432aSPatrick Venture #include <thread>
2335819381SMatthew Barth 
2435819381SMatthew Barth namespace sensor
2535819381SMatthew Barth {
2635819381SMatthew Barth 
27cb3daafbSMatthew Barth using namespace phosphor::logging;
28b28f432aSPatrick Venture using namespace sdbusplus::xyz::openbmc_project::Common::Error;
29cb3daafbSMatthew Barth 
30ee73f5bdSJames Feist // todo: this can be simplified once we move to the double interface
Sensor(const SensorSet::key_type & sensor,const hwmonio::HwmonIOInterface * ioAccess,const std::string & devPath)312e41b13fSMatthew Barth Sensor::Sensor(const SensorSet::key_type& sensor,
322864b063SPatrick Venture                const hwmonio::HwmonIOInterface* ioAccess,
332864b063SPatrick Venture                const std::string& devPath) :
3402e598abSPatrick Williams     _sensor(sensor), _ioAccess(ioAccess), _devPath(devPath), _scale(0),
3502e598abSPatrick Williams     _hasFaultFile(false)
369c43106cSMatthew Barth {
37b28f432aSPatrick Venture     auto chip = env::getEnv("GPIOCHIP", sensor);
38b28f432aSPatrick Venture     auto access = env::getEnv("GPIO", sensor);
39b28f432aSPatrick Venture     if (!access.empty() && !chip.empty())
40b28f432aSPatrick Venture     {
4112659aa1SPatrick Venture         _handle = gpio::BuildGpioHandle(chip, access);
42b28f432aSPatrick Venture 
4312659aa1SPatrick Venture         if (!_handle)
44b28f432aSPatrick Venture         {
45b28f432aSPatrick Venture             log<level::ERR>("Unable to set up gpio locking");
46b28f432aSPatrick Venture             elog<InternalFailure>();
47b28f432aSPatrick Venture         }
48b28f432aSPatrick Venture     }
49b28f432aSPatrick Venture 
50ac47309fSMatthew Barth     auto gain = env::getEnv("GAIN", sensor);
51ac47309fSMatthew Barth     if (!gain.empty())
52ac47309fSMatthew Barth     {
5312659aa1SPatrick Venture         _sensorAdjusts.gain = std::stod(gain);
54ac47309fSMatthew Barth     }
55ac47309fSMatthew Barth 
56ac47309fSMatthew Barth     auto offset = env::getEnv("OFFSET", sensor);
57ac47309fSMatthew Barth     if (!offset.empty())
58ac47309fSMatthew Barth     {
5912659aa1SPatrick Venture         _sensorAdjusts.offset = std::stoi(offset);
60ac47309fSMatthew Barth     }
61ac47309fSMatthew Barth     auto senRmRCs = env::getEnv("REMOVERCS", sensor);
62ac47309fSMatthew Barth     // Add sensor removal return codes defined per sensor
63ac47309fSMatthew Barth     addRemoveRCs(senRmRCs);
649c43106cSMatthew Barth }
659c43106cSMatthew Barth 
addRemoveRCs(const std::string & rcList)66cb3daafbSMatthew Barth void Sensor::addRemoveRCs(const std::string& rcList)
67cb3daafbSMatthew Barth {
68cb3daafbSMatthew Barth     if (rcList.empty())
69cb3daafbSMatthew Barth     {
70cb3daafbSMatthew Barth         return;
71cb3daafbSMatthew Barth     }
72cb3daafbSMatthew Barth 
73cb3daafbSMatthew Barth     // Convert to a char* for strtok
74043d3230SPatrick Venture     std::vector<char> rmRCs(rcList.c_str(), rcList.c_str() + rcList.size() + 1);
75cb3daafbSMatthew Barth     auto rmRC = std::strtok(&rmRCs[0], ", ");
76cb3daafbSMatthew Barth     while (rmRC != nullptr)
77cb3daafbSMatthew Barth     {
78cb3daafbSMatthew Barth         try
79cb3daafbSMatthew Barth         {
8012659aa1SPatrick Venture             _sensorAdjusts.rmRCs.insert(std::stoi(rmRC));
81cb3daafbSMatthew Barth         }
82cb3daafbSMatthew Barth         catch (const std::logic_error& le)
83cb3daafbSMatthew Barth         {
84cb3daafbSMatthew Barth             // Unable to convert to int, continue to next token
8512659aa1SPatrick Venture             std::string name = _sensor.first + "_" + _sensor.second;
86cb3daafbSMatthew Barth             log<level::INFO>("Unable to convert sensor removal return code",
87cb3daafbSMatthew Barth                              entry("SENSOR=%s", name.c_str()),
88cb3daafbSMatthew Barth                              entry("RC=%s", rmRC),
89cb3daafbSMatthew Barth                              entry("EXCEPTION=%s", le.what()));
90cb3daafbSMatthew Barth         }
91cb3daafbSMatthew Barth         rmRC = std::strtok(nullptr, ", ");
92cb3daafbSMatthew Barth     }
93cb3daafbSMatthew Barth }
94cb3daafbSMatthew Barth 
adjustValue(SensorValueType value)95ee73f5bdSJames Feist SensorValueType Sensor::adjustValue(SensorValueType value)
96cb3daafbSMatthew Barth {
97cb3daafbSMatthew Barth // Because read doesn't have an out pointer to store errors.
98cb3daafbSMatthew Barth // let's assume negative values are errors if they have this
99cb3daafbSMatthew Barth // set.
100d8cacfd4SMatt Spinler #if NEGATIVE_ERRNO_ON_FAIL
101cb3daafbSMatthew Barth     if (value < 0)
102cb3daafbSMatthew Barth     {
103cb3daafbSMatthew Barth         return value;
104cb3daafbSMatthew Barth     }
105cb3daafbSMatthew Barth #endif
106cb3daafbSMatthew Barth 
107cb3daafbSMatthew Barth     // Adjust based on gain and offset
10802e598abSPatrick Williams     value = static_cast<decltype(value)>(
10902e598abSPatrick Williams         static_cast<double>(value) * _sensorAdjusts.gain +
11012659aa1SPatrick Venture         _sensorAdjusts.offset);
111cb3daafbSMatthew Barth 
112ee73f5bdSJames Feist     if constexpr (std::is_same<SensorValueType, double>::value)
113ee73f5bdSJames Feist     {
11412659aa1SPatrick Venture         value *= std::pow(10, _scale);
115ee73f5bdSJames Feist     }
116ee73f5bdSJames Feist 
117cb3daafbSMatthew Barth     return value;
118cb3daafbSMatthew Barth }
119cb3daafbSMatthew Barth 
addValue(const RetryIO & retryIO,ObjectInfo & info,TimedoutMap & timedoutMap)12002e598abSPatrick Williams std::shared_ptr<ValueObject> Sensor::addValue(
12102e598abSPatrick Williams     const RetryIO& retryIO, ObjectInfo& info, TimedoutMap& timedoutMap)
122cb3daafbSMatthew Barth {
123cb3daafbSMatthew Barth     // Get the initial value for the value interface.
124ad6043f6SPatrick Williams     auto& bus = *std::get<sdbusplus::bus_t*>(info);
1256206723dSPatrick Venture     auto& obj = std::get<InterfaceMap>(info);
126cb3daafbSMatthew Barth     auto& objPath = std::get<std::string>(info);
127cb3daafbSMatthew Barth 
128ee73f5bdSJames Feist     SensorValueType val = 0;
129cb3daafbSMatthew Barth 
13086dcac85SBrandon Kim     auto& statusIface = std::any_cast<std::shared_ptr<StatusObject>&>(
13186dcac85SBrandon Kim         obj[InterfaceType::STATUS]);
13286dcac85SBrandon Kim     // As long as addStatus is called before addValue, statusIface
13386dcac85SBrandon Kim     // should never be nullptr
13486dcac85SBrandon Kim     assert(statusIface);
13586dcac85SBrandon Kim 
13686dcac85SBrandon Kim     // Only read the input value if the status is functional
13786dcac85SBrandon Kim     if (statusIface->functional())
138cb3daafbSMatthew Barth     {
139d8cacfd4SMatt Spinler #if UPDATE_FUNCTIONAL_ON_FAIL
14079205b2cSBrandon Kim         try
14179205b2cSBrandon Kim #endif
14279205b2cSBrandon Kim         {
143db76d49cSBrandon Kim             // RAII object for GPIO unlock / lock
1442227bd52SWilliam A. Kennington III             auto locker = gpioUnlock(getGpio());
145b28f432aSPatrick Venture 
1466d50c3e9SBrandon Kim             // For sensors with attribute ASYNC_READ_TIMEOUT,
1476d50c3e9SBrandon Kim             // spawn a thread with timeout
1486d50c3e9SBrandon Kim             auto asyncReadTimeout = env::getEnv("ASYNC_READ_TIMEOUT", _sensor);
1496d50c3e9SBrandon Kim             if (!asyncReadTimeout.empty())
1506d50c3e9SBrandon Kim             {
1516d50c3e9SBrandon Kim                 std::chrono::milliseconds asyncTimeout{
1526d50c3e9SBrandon Kim                     std::stoi(asyncReadTimeout)};
1536d50c3e9SBrandon Kim                 val = asyncRead(_sensor, _ioAccess, asyncTimeout, timedoutMap,
1546d50c3e9SBrandon Kim                                 _sensor.first, _sensor.second,
15512659aa1SPatrick Venture                                 hwmon::entry::cinput, std::get<size_t>(retryIO),
156cb3daafbSMatthew Barth                                 std::get<std::chrono::milliseconds>(retryIO));
157cb3daafbSMatthew Barth             }
1586d50c3e9SBrandon Kim             else
1596d50c3e9SBrandon Kim             {
1606d50c3e9SBrandon Kim                 // Retry for up to a second if device is busy
1616d50c3e9SBrandon Kim                 // or has a transient error.
1626d50c3e9SBrandon Kim                 val = _ioAccess->read(
1636d50c3e9SBrandon Kim                     _sensor.first, _sensor.second, hwmon::entry::cinput,
1646d50c3e9SBrandon Kim                     std::get<size_t>(retryIO),
1656d50c3e9SBrandon Kim                     std::get<std::chrono::milliseconds>(retryIO));
1666d50c3e9SBrandon Kim             }
1676d50c3e9SBrandon Kim         }
168d8cacfd4SMatt Spinler #if UPDATE_FUNCTIONAL_ON_FAIL
16979205b2cSBrandon Kim         catch (const std::system_error& e)
17079205b2cSBrandon Kim         {
17179205b2cSBrandon Kim             // Catch the exception here and update the functional property.
17279205b2cSBrandon Kim             // By catching the exception, it will not propagate it up the stack
17379205b2cSBrandon Kim             // and thus the code will skip the "Remove RCs" check in
17479205b2cSBrandon Kim             // MainLoop::getObject and will not exit on failure.
17579205b2cSBrandon Kim             statusIface->functional(false);
17679205b2cSBrandon Kim         }
17779205b2cSBrandon Kim #endif
17879205b2cSBrandon Kim     }
179cb3daafbSMatthew Barth 
180d273b1e0SPatrick Williams     auto iface = std::make_shared<ValueObject>(bus, objPath.c_str(),
181d273b1e0SPatrick Williams                                                ValueObject::action::defer_emit);
182cb3daafbSMatthew Barth 
183cb3daafbSMatthew Barth     hwmon::Attributes attrs;
18412659aa1SPatrick Venture     if (hwmon::getAttributes(_sensor.first, attrs))
185cb3daafbSMatthew Barth     {
186cb3daafbSMatthew Barth         iface->unit(hwmon::getUnit(attrs));
187ee73f5bdSJames Feist 
18812659aa1SPatrick Venture         _scale = hwmon::getScale(attrs);
189cb3daafbSMatthew Barth     }
190cb3daafbSMatthew Barth 
1917ab1b25bSMatt Spinler     val = adjustValue(val);
1927ab1b25bSMatt Spinler     iface->value(val);
1937ab1b25bSMatt Spinler 
19412659aa1SPatrick Venture     auto maxValue = env::getEnv("MAXVALUE", _sensor);
195cb3daafbSMatthew Barth     if (!maxValue.empty())
196cb3daafbSMatthew Barth     {
197cb3daafbSMatthew Barth         iface->maxValue(std::stoll(maxValue));
198cb3daafbSMatthew Barth     }
19912659aa1SPatrick Venture     auto minValue = env::getEnv("MINVALUE", _sensor);
200cb3daafbSMatthew Barth     if (!minValue.empty())
201cb3daafbSMatthew Barth     {
202cb3daafbSMatthew Barth         iface->minValue(std::stoll(minValue));
203cb3daafbSMatthew Barth     }
204cb3daafbSMatthew Barth 
205cb3daafbSMatthew Barth     obj[InterfaceType::VALUE] = iface;
206cb3daafbSMatthew Barth     return iface;
207cb3daafbSMatthew Barth }
208cb3daafbSMatthew Barth 
addStatus(ObjectInfo & info)2092e41b13fSMatthew Barth std::shared_ptr<StatusObject> Sensor::addStatus(ObjectInfo& info)
21035819381SMatthew Barth {
2119e997b4dSPatrick Venture     namespace fs = std::filesystem;
21235819381SMatthew Barth 
21335819381SMatthew Barth     std::shared_ptr<StatusObject> iface = nullptr;
21435819381SMatthew Barth     auto& objPath = std::get<std::string>(info);
2156206723dSPatrick Venture     auto& obj = std::get<InterfaceMap>(info);
21635819381SMatthew Barth 
21735819381SMatthew Barth     // Check if fault sysfs file exists
21812659aa1SPatrick Venture     std::string faultName = _sensor.first;
21912659aa1SPatrick Venture     std::string faultID = _sensor.second;
22035819381SMatthew Barth     std::string entry = hwmon::entry::fault;
22135819381SMatthew Barth 
22286dcac85SBrandon Kim     bool functional = true;
22302e598abSPatrick Williams     auto sysfsFullPath =
22402e598abSPatrick Williams         sysfs::make_sysfs_path(_ioAccess->path(), faultName, faultID, entry);
22535819381SMatthew Barth     if (fs::exists(sysfsFullPath))
22635819381SMatthew Barth     {
22786dcac85SBrandon Kim         _hasFaultFile = true;
22835819381SMatthew Barth         try
22935819381SMatthew Barth         {
23012659aa1SPatrick Venture             uint32_t fault = _ioAccess->read(faultName, faultID, entry,
231685efa16SPatrick Venture                                              hwmonio::retries, hwmonio::delay);
23235819381SMatthew Barth             if (fault != 0)
23335819381SMatthew Barth             {
23435819381SMatthew Barth                 functional = false;
23535819381SMatthew Barth             }
23635819381SMatthew Barth         }
23735819381SMatthew Barth         catch (const std::system_error& e)
23835819381SMatthew Barth         {
239043d3230SPatrick Venture             using namespace sdbusplus::xyz::openbmc_project::Sensor::Device::
240043d3230SPatrick Venture                 Error;
241043d3230SPatrick Venture             using metadata = xyz::openbmc_project::Sensor::Device::ReadFailure;
24235819381SMatthew Barth 
24312659aa1SPatrick Venture             report<ReadFailure>(
24412659aa1SPatrick Venture                 metadata::CALLOUT_ERRNO(e.code().value()),
24512659aa1SPatrick Venture                 metadata::CALLOUT_DEVICE_PATH(_devPath.c_str()));
24635819381SMatthew Barth 
24764129937SPatrick Williams             log<level::INFO>(std::format("Failing sysfs file: {} errno {}",
2486a391de4SMatt Spinler                                          sysfsFullPath, e.code().value())
2496a391de4SMatt Spinler                                  .c_str());
25035819381SMatthew Barth         }
25186dcac85SBrandon Kim     }
25235819381SMatthew Barth 
253ad6043f6SPatrick Williams     auto& bus = *std::get<sdbusplus::bus_t*>(info);
254685efa16SPatrick Venture 
255d273b1e0SPatrick Williams     iface = std::make_shared<StatusObject>(
256d273b1e0SPatrick Williams         bus, objPath.c_str(), StatusObject::action::emit_no_signals);
25735819381SMatthew Barth     // Set functional property
25835819381SMatthew Barth     iface->functional(functional);
25935819381SMatthew Barth 
26035819381SMatthew Barth     obj[InterfaceType::STATUS] = iface;
26135819381SMatthew Barth 
26235819381SMatthew Barth     return iface;
26335819381SMatthew Barth }
26435819381SMatthew Barth 
addAccuracy(ObjectInfo & info,double accuracy)265*94539af6SPatrick Williams std::shared_ptr<AccuracyObject> Sensor::addAccuracy(ObjectInfo& info,
266*94539af6SPatrick Williams                                                     double accuracy)
267c9d61613SGeorge Liu {
268c9d61613SGeorge Liu     auto& objPath = std::get<std::string>(info);
269c9d61613SGeorge Liu     auto& obj = std::get<InterfaceMap>(info);
270c9d61613SGeorge Liu 
271c9d61613SGeorge Liu     auto& bus = *std::get<sdbusplus::bus_t*>(info);
272c9d61613SGeorge Liu     auto iface = std::make_shared<AccuracyObject>(
273c9d61613SGeorge Liu         bus, objPath.c_str(), AccuracyObject::action::emit_no_signals);
274c9d61613SGeorge Liu 
275c9d61613SGeorge Liu     iface->accuracy(accuracy);
276c9d61613SGeorge Liu     obj[InterfaceType::ACCURACY] = iface;
277c9d61613SGeorge Liu 
278c9d61613SGeorge Liu     return iface;
279c9d61613SGeorge Liu }
280c9d61613SGeorge Liu 
addPriority(ObjectInfo & info,size_t priority)281*94539af6SPatrick Williams std::shared_ptr<PriorityObject> Sensor::addPriority(ObjectInfo& info,
282*94539af6SPatrick Williams                                                     size_t priority)
28347fb49acSLakshmi Yadlapati {
28447fb49acSLakshmi Yadlapati     auto& objPath = std::get<std::string>(info);
28547fb49acSLakshmi Yadlapati     auto& obj = std::get<InterfaceMap>(info);
28647fb49acSLakshmi Yadlapati 
28747fb49acSLakshmi Yadlapati     auto& bus = *std::get<sdbusplus::bus_t*>(info);
28847fb49acSLakshmi Yadlapati     auto iface = std::make_shared<PriorityObject>(
28947fb49acSLakshmi Yadlapati         bus, objPath.c_str(), PriorityObject::action::emit_no_signals);
29047fb49acSLakshmi Yadlapati 
29147fb49acSLakshmi Yadlapati     iface->priority(priority);
29247fb49acSLakshmi Yadlapati     obj[InterfaceType::PRIORITY] = iface;
29347fb49acSLakshmi Yadlapati 
29447fb49acSLakshmi Yadlapati     return iface;
29547fb49acSLakshmi Yadlapati }
29647fb49acSLakshmi Yadlapati 
gpioLock(const gpioplus::HandleInterface * && handle)2972227bd52SWilliam A. Kennington III void gpioLock(const gpioplus::HandleInterface*&& handle)
298db76d49cSBrandon Kim {
2992227bd52SWilliam A. Kennington III     handle->setValues({0});
300db76d49cSBrandon Kim }
301db76d49cSBrandon Kim 
gpioUnlock(const gpioplus::HandleInterface * handle)3022227bd52SWilliam A. Kennington III std::optional<GpioLocker> gpioUnlock(const gpioplus::HandleInterface* handle)
303db76d49cSBrandon Kim {
3042227bd52SWilliam A. Kennington III     if (handle == nullptr)
3052227bd52SWilliam A. Kennington III     {
3062227bd52SWilliam A. Kennington III         return std::nullopt;
307db76d49cSBrandon Kim     }
308db76d49cSBrandon Kim 
3092227bd52SWilliam A. Kennington III     handle->setValues({1});
3102227bd52SWilliam A. Kennington III     // Default pause needed to guarantee sensors are ready
3112227bd52SWilliam A. Kennington III     std::this_thread::sleep_for(std::chrono::milliseconds(500));
3122227bd52SWilliam A. Kennington III     return GpioLocker(std::move(handle));
313b28f432aSPatrick Venture }
314b28f432aSPatrick Venture 
asyncRead(const SensorSet::key_type & sensorSetKey,const hwmonio::HwmonIOInterface * ioAccess,std::chrono::milliseconds asyncTimeout,TimedoutMap & timedoutMap,const std::string & type,const std::string & id,const std::string & sensor,const size_t retries,const std::chrono::milliseconds delay)31502e598abSPatrick Williams SensorValueType asyncRead(
31602e598abSPatrick Williams     const SensorSet::key_type& sensorSetKey,
3176d50c3e9SBrandon Kim     const hwmonio::HwmonIOInterface* ioAccess,
31802e598abSPatrick Williams     std::chrono::milliseconds asyncTimeout, TimedoutMap& timedoutMap,
31902e598abSPatrick Williams     const std::string& type, const std::string& id, const std::string& sensor,
32002e598abSPatrick Williams     const size_t retries, const std::chrono::milliseconds delay)
3216d50c3e9SBrandon Kim {
3226d50c3e9SBrandon Kim     // Default async read timeout
3236d50c3e9SBrandon Kim     bool valueIsValid = false;
3246d50c3e9SBrandon Kim     std::future<int64_t> asyncThread;
3256d50c3e9SBrandon Kim 
3266d50c3e9SBrandon Kim     auto asyncIter = timedoutMap.find(sensorSetKey);
3276d50c3e9SBrandon Kim     if (asyncIter == timedoutMap.end())
3286d50c3e9SBrandon Kim     {
3296d50c3e9SBrandon Kim         // If sensor not found in timedoutMap, spawn an async thread
33002e598abSPatrick Williams         asyncThread =
33102e598abSPatrick Williams             std::async(std::launch::async, &hwmonio::HwmonIOInterface::read,
33202e598abSPatrick Williams                        ioAccess, type, id, sensor, retries, delay);
3336d50c3e9SBrandon Kim         valueIsValid = true;
3346d50c3e9SBrandon Kim     }
3356d50c3e9SBrandon Kim     else
3366d50c3e9SBrandon Kim     {
3376d50c3e9SBrandon Kim         // If we already have the async thread in the timedoutMap, it means this
3386d50c3e9SBrandon Kim         // sensor has already timed out in the previous reads. No need to wait
3396d50c3e9SBrandon Kim         // on subsequent reads - proceed to check the future_status to see when
3406d50c3e9SBrandon Kim         // the async thread finishes
3416d50c3e9SBrandon Kim         asyncTimeout = std::chrono::seconds(0);
3426d50c3e9SBrandon Kim         asyncThread = std::move(asyncIter->second);
3436d50c3e9SBrandon Kim     }
3446d50c3e9SBrandon Kim 
3456d50c3e9SBrandon Kim     // TODO: This is still not a true asynchronous read as it still blocks the
3466d50c3e9SBrandon Kim     // main thread for asyncTimeout amount of time. To make this completely
3476d50c3e9SBrandon Kim     // asynchronous, schedule a read and register a callback to update the
3486d50c3e9SBrandon Kim     // sensor value
3496d50c3e9SBrandon Kim     std::future_status status = asyncThread.wait_for(asyncTimeout);
3506d50c3e9SBrandon Kim     switch (status)
3516d50c3e9SBrandon Kim     {
3526d50c3e9SBrandon Kim         case std::future_status::ready:
3536d50c3e9SBrandon Kim             // Read has finished
3546d50c3e9SBrandon Kim             if (valueIsValid)
3556d50c3e9SBrandon Kim             {
3566d50c3e9SBrandon Kim                 return asyncThread.get();
3576d50c3e9SBrandon Kim                 // Good sensor reads should skip the code below
3586d50c3e9SBrandon Kim             }
3596d50c3e9SBrandon Kim             // Async read thread has completed but had previously timed out (was
3606d50c3e9SBrandon Kim             // found in the timedoutMap). Erase from timedoutMap and throw to
3616d50c3e9SBrandon Kim             // allow retry in the next read cycle. Not returning the read value
3626d50c3e9SBrandon Kim             // as the sensor reading may be bad / corrupted if it took so long.
3636d50c3e9SBrandon Kim             timedoutMap.erase(sensorSetKey);
3646d50c3e9SBrandon Kim             throw AsyncSensorReadTimeOut();
3656d50c3e9SBrandon Kim         default:
3666d50c3e9SBrandon Kim             // Read timed out so add the thread to the timedoutMap (if the entry
3676d50c3e9SBrandon Kim             // already exists, operator[] updates it).
3686d50c3e9SBrandon Kim             //
3696d50c3e9SBrandon Kim             // Keeping the timed out futures in a map is required to prevent
3706d50c3e9SBrandon Kim             // their destructor from being called when returning from this
3716d50c3e9SBrandon Kim             // stack. The destructor will otherwise block until the read
3726d50c3e9SBrandon Kim             // completes due to the limitation of std::async.
3736d50c3e9SBrandon Kim             timedoutMap[sensorSetKey] = std::move(asyncThread);
3746d50c3e9SBrandon Kim             throw AsyncSensorReadTimeOut();
3756d50c3e9SBrandon Kim     }
3766d50c3e9SBrandon Kim }
3776d50c3e9SBrandon Kim 
37835819381SMatthew Barth } // namespace sensor
379