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