void PeriodicTaskPrivate::_wrap() { if (*_state == Task_Stopped) qiLogError() << "PeriodicTask inconsistency: stopped from callback"; /* To avoid being stuck because of unhandled transition, the rule is * that any other thread playing with our state can only do so * to stop us, and must eventualy reach the Stopping state */ if (_state.setIfEquals(Task_Stopping, Task_Stopped)) return; /* reschedule() needs to call async() before reseting state from rescheduling * to scheduled, to protect the _task object. So we might still be * in rescheduling state here. */ while (*_state == Task_Rescheduling) boost::this_thread::yield(); // order matters! check scheduled state first as the state cannot change // from triggering to scheduled but can change in the other way if (!_state.setIfEquals(Task_Scheduled, Task_Running) && !_state.setIfEquals(Task_Triggering, Task_Running)) { setState(_state, Task_Stopping, Task_Stopped); return; } bool shouldAbort = false; qi::int64_t wall = 0, now=0, delta=0; qi::int64_t usr, sys; bool compensate = _compensateCallTime; // we don't want that bool to change in the middle try { wall = qi::os::ustime(); std::pair<qi::int64_t, qi::int64_t> cpu = qi::os::cputime(); _tid = os::gettid(); _callback(); _tid = invalidThreadId; now = qi::os::ustime(); wall = now - wall; std::pair<qi::int64_t, qi::int64_t> cpu2 = qi::os::cputime(); usr = cpu2.first - cpu.first; sys = cpu2.second - cpu.second; if (compensate) delta = wall; } catch (const std::exception& e) { qiLogInfo() << "Exception in task " << _name << ": " << e.what(); shouldAbort = true; } catch(...) { qiLogInfo() << "Unknown exception in task callback."; shouldAbort = true; } if (shouldAbort) { setState(_state, Task_Stopping, Task_Stopped, Task_Running, Task_Stopped); return; } else { _callStats.push((float)wall / 1e6f, (float)usr / 1e6f, (float)sys / 1e6f); if (now - _statsDisplayTime >= 20000000) { float secTime = float(now - _statsDisplayTime) / 1e6f; _statsDisplayTime = now; unsigned int count = _callStats.count(); std::string catName = "stats." + _name; qiLogVerbose(catName.c_str()) << (_callStats.user().cumulatedValue() * 100.0 / secTime) << "% " << count << " " << _callStats.wall().asString(count) << " " << _callStats.user().asString(count) << " " << _callStats.system().asString(count) ; _callStats.reset(); } if (!_state.setIfEquals(Task_Running, Task_Rescheduling)) { // If we are not in running state anymore, someone switched us // to stopping setState(_state, Task_Stopping, Task_Stopped); return; } _reschedule(std::max((qi::int64_t)0, _usPeriod - delta)); } }
void PeriodicTaskPrivate::_wrap() { qiLogDebug() << "callback start"; { boost::mutex::scoped_lock l(_mutex); QI_ASSERT(_state != TaskState::Stopped); /* To avoid being stuck because of unhandled transition, the rule is * that any other thread playing with our state can only do so * to stop us, and must eventualy reach the Stopping state */ if (_state == TaskState::Stopping) { _state = TaskState::Stopped; _cond.notify_all(); return; } QI_ASSERT(_state == TaskState::Scheduled || _state == TaskState::Triggering); _state = TaskState::Running; _cond.notify_all(); } bool shouldAbort = false; qi::SteadyClockTimePoint now; qi::Duration delta; qi::int64_t usr, sys; bool compensate = _compensateCallTime; // we don't want that bool to change in the middle try { qi::SteadyClockTimePoint start = qi::SteadyClock::now(); std::pair<qi::int64_t, qi::int64_t> cpu = qi::os::cputime(); _tid = os::gettid(); _callback(); _tid = invalidThreadId; now = qi::SteadyClock::now(); delta = now - start; std::pair<qi::int64_t, qi::int64_t> cpu2 = qi::os::cputime(); usr = cpu2.first - cpu.first; sys = cpu2.second - cpu.second; } catch (const std::exception& e) { qiLogInfo() << "Exception in task " << _name << ": " << e.what(); shouldAbort = true; } catch(...) { qiLogInfo() << "Unknown exception in task callback."; shouldAbort = true; } if (shouldAbort) { qiLogDebug() << "should abort, bye"; boost::mutex::scoped_lock l(_mutex); _state = TaskState::Stopped; _cond.notify_all(); return; } else { _callStats.push( (float)boost::chrono::duration_cast<qi::MicroSeconds>(delta).count() / 1e6f, (float)usr / 1e6f, (float)sys / 1e6f); if (now - _statsDisplayTime >= qi::Seconds(20)) { float secTime = float(boost::chrono::duration_cast<qi::MicroSeconds>(now - _statsDisplayTime).count()) / 1e6f; _statsDisplayTime = now; unsigned int count = _callStats.count(); std::string catName = "stats." + _name; qiLogVerbose(catName.c_str()) << (_callStats.user().cumulatedValue() * 100.0 / secTime) << "% " << count << " " << _callStats.wall().asString(count) << " " << _callStats.user().asString(count) << " " << _callStats.system().asString(count) ; _callStats.reset(); } qiLogDebug() << "continuing"; { boost::mutex::scoped_lock l(_mutex); if (_state != TaskState::Running) { qiLogDebug() << "continuing " << static_cast<int>(_state); QI_ASSERT(_state == TaskState::Stopping); _state = TaskState::Stopped; _cond.notify_all(); return; } _reschedule(std::max(qi::Duration(0), _period - (compensate ? delta : qi::Duration(0)))); } } }