Quit event loop when a heavy event is encountered. This is required so that slow events such as Store rebuilding step do not force the event loop to run for a very long time, without any chance for the select loop to run. === modified file 'src/AsyncEngine.h' --- src/AsyncEngine.h 2012-09-01 14:38:36 +0000 +++ src/AsyncEngine.h 2012-12-03 22:26:44 +0000 @@ -34,40 +34,43 @@ /* Abstract interface for async engines which an event loop can utilise. * * Some implementations will be truely async, others like the event engine * will be pseudo async. */ class AsyncEngine { public: /* error codes returned from checkEvents. If the return value is not * negative, then it is the requested delay until the next call. If it is * negative, it is one of the following codes: */ enum CheckError { /* this engine is completely idle: it has no pending events, and nothing * registered with it that can create events */ EVENT_IDLE = -1, + /// found a slow event; we should end current main loop iteration ASAP + /// TODO: move after EVENT_ERROR before commit + EVENT_HEAVY = -3, /* some error has occured in this engine */ EVENT_ERROR = -2 }; virtual ~AsyncEngine() {} /* Check the engine for events. If there are events that have completed, * the engine should at this point hand them off to their dispatcher. * Engines that operate asynchronously - i.e. the DiskThreads engine - * should hand events off to their dispatcher as they arrive rather than * waiting for checkEvents to be called. Engines like poll and select should * use this call as the time to perform their checks with the OS for new * events. * * The return value is the status code of the event checking. If its a * non-negative value then it is used as hint for the minimum requested * time before checkEvents is called again. I.e. the event engine knows * how long it is until the next event will be scheduled - so it will * return that time (in milliseconds). * === modified file 'src/EventLoop.cc' --- src/EventLoop.cc 2012-09-25 16:38:36 +0000 +++ src/EventLoop.cc 2012-12-03 22:51:43 +0000 @@ -21,123 +21,129 @@ * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * */ #include "squid.h" #include "AsyncEngine.h" #include "Debug.h" #include "EventLoop.h" #include "base/AsyncCallQueue.h" #include "SquidTime.h" EventLoop::EventLoop() : errcount(0), last_loop(false), timeService(NULL), - primaryEngine(NULL) + primaryEngine(NULL), sawHeavyEvent(false) {} void EventLoop::checkEngine(AsyncEngine * engine, bool const primary) { int requested_delay; if (!primary) requested_delay = engine->checkEvents(0); else requested_delay = engine->checkEvents(loop_delay); if (requested_delay < 0) switch (requested_delay) { case AsyncEngine::EVENT_IDLE: debugs(1, 9, "Engine " << engine << " is idle."); break; case AsyncEngine::EVENT_ERROR: runOnceResult = false; error = true; break; + case AsyncEngine::EVENT_HEAVY: + debugs(1, 9, "Engine " << engine << " is busy."); + sawHeavyEvent = true; + break; + default: fatal_dump("unknown AsyncEngine result"); } else { - /* not idle or error */ + // a time delay was returned runOnceResult = false; if (requested_delay < loop_delay) loop_delay = requested_delay; } } void EventLoop::prepareToRun() { last_loop = false; errcount = 0; } void EventLoop::registerEngine(AsyncEngine *engine) { engines.push_back(engine); } void EventLoop::run() { prepareToRun(); while (!runOnce()); } bool EventLoop::runOnce() { bool sawActivity = false; runOnceResult = true; error = false; loop_delay = EVENT_LOOP_TIMEOUT; + sawHeavyEvent = false; AsyncEngine *waitingEngine = primaryEngine; if (!waitingEngine && !engines.empty()) waitingEngine = engines.back(); do { // generate calls and events typedef engine_vector::iterator EVI; for (EVI i = engines.begin(); i != engines.end(); ++i) { if (*i != waitingEngine) checkEngine(*i, false); } // dispatch calls accumulated so far sawActivity = dispatchCalls(); if (sawActivity) runOnceResult = false; - } while (sawActivity); + } while (sawActivity && !sawHeavyEvent); if (waitingEngine != NULL) checkEngine(waitingEngine, true); if (timeService != NULL) timeService->tick(); // dispatch calls scheduled by waitingEngine and timeService sawActivity = dispatchCalls(); if (sawActivity) runOnceResult = false; if (error) { ++errcount; debugs(1, DBG_CRITICAL, "Select loop Error. Retry " << errcount); } else errcount = 0; if (errcount == 10) return true; === modified file 'src/EventLoop.h' --- src/EventLoop.h 2012-09-20 11:28:21 +0000 +++ src/EventLoop.h 2012-12-03 22:18:32 +0000 @@ -90,23 +90,24 @@ int errcount; private: /** setup state variables prior to running */ void prepareToRun(); /** check an individual engine */ void checkEngine(AsyncEngine * engine, bool const primary); /** dispatch calls and events scheduled during checkEngine() */ bool dispatchCalls(); bool last_loop; typedef Vector engine_vector; engine_vector engines; TimeEngine * timeService; AsyncEngine * primaryEngine; int loop_delay; /**< the delay to be given to the primary engine */ bool error; /**< has an error occured in this loop */ bool runOnceResult; /**< the result from runOnce */ + bool sawHeavyEvent; ///< must stop processing events ASAP and do I/O }; #endif /* SQUID_EVENTLOOP_H */ === modified file 'src/event.cc' --- src/event.cc 2012-09-01 14:38:36 +0000 +++ src/event.cc 2012-12-03 22:23:18 +0000 @@ -248,44 +248,44 @@ PROF_start(eventRun); debugs(41, 5, HERE << "checkEvents"); while ((event = tasks)) { if (event->when > current_dtime) break; /* XXX assumes event->name is static memory! */ AsyncCall::Pointer call = asyncCall(41,5, event->name, EventDialer(event->func, event->arg, event->cbdata)); ScheduleCallHere(call); last_event_ran = event->name; // XXX: move this to AsyncCallQueue const bool heavy = event->weight && (!event->cbdata || cbdataReferenceValid(event->arg)); tasks = event->next; delete event; - // XXX: We may be called again during the same event loop iteration. - // Is there a point in breaking now? - if (heavy) - break; // do not dequeue events following a heavy event + if (heavy) { + PROF_stop(eventRun); + return EVENT_HEAVY; // do not dequeue events after a heavy event + } } PROF_stop(eventRun); return checkDelay(); } void EventScheduler::clean() { while (ev_entry * event = tasks) { tasks = event->next; delete event; } tasks = NULL; } void EventScheduler::dump(StoreEntry * sentry) {