inline void setState(qi::Atomic<int>& state, TaskState from, TaskState to, TaskState from2, TaskState to2) { for (unsigned i=0; i<1000; ++i) if (state.setIfEquals(from, to) || state.setIfEquals(from2, to2)) return; while (true) { for (unsigned i=0; i<1000; ++i) { if (state.setIfEquals(from, to) || state.setIfEquals(from2, to2)) return; qi::os::msleep(1); // TODO: 1ms is probably too long } qiLogWarning() << "PeriodicTask is stuck " << from << ' ' << to << ' ' << from2 << ' ' << to2 << ' '<< *state; } }
void PeriodicTaskPrivate::_reschedule(qi::int64_t delay) { qiLogDebug() << _name <<" rescheduling in " << delay; _task = getEventLoop()->async(boost::bind(&PeriodicTaskPrivate::_wrap, shared_from_this()), delay); if (!_state.setIfEquals(Task_Rescheduling, Task_Scheduled)) qiLogError() << "PeriodicTask forbidden state change while rescheduling " << *_state; }
void PeriodicTaskPrivate::_trigger(qi::Future<void> future) { // if future was not canceled, the task already ran, don't retrigger if (!future.isCanceled()) return; // else, start the task now if we are still triggering if (_state.setIfEquals(Task_Triggering, Task_Rescheduling)) _reschedule(0); }
void Application::stop() { static qi::Atomic<bool> atStopHandlerCall = false; if (atStopHandlerCall.setIfEquals(false, true)) { FunctionList& fl = lazyGet(globalAtStop); qiLogDebug() << "Executing " << fl.size() << " atStop handlers"; for (FunctionList::iterator i = fl.begin(); i!= fl.end(); ++i) { try { (*i)(); } catch (std::exception& e) { qiLogError() << "Application atStop callback throw the following error: " << e.what(); } } globalIsStop = true; globalCond.notify_all(); } }
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)); } }