From: robertc <> Date: Sat, 12 Aug 2006 07:43:10 +0000 (+0000) Subject: Add AsyncEngine and TimeEngine support to the EventLoop, allowing it to X-Git-Tag: SQUID_3_0_PRE5~194 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8ff3fa2e10cb85fb2e2edefdc4f00c935a4ca92f;p=thirdparty%2Fsquid.git Add AsyncEngine and TimeEngine support to the EventLoop, allowing it to completely replace the old one embedded in main.cc - convert loop using tests to use an event loop instance. --- diff --git a/src/AsyncEngine.cc b/src/AsyncEngine.cc new file mode 100644 index 0000000000..9887fd3bf5 --- /dev/null +++ b/src/AsyncEngine.cc @@ -0,0 +1,34 @@ + +/* + * $Id: AsyncEngine.cc,v 1.1 2006/08/12 01:43:10 robertc Exp $ + * + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 "AsyncEngine.h" diff --git a/src/AsyncEngine.h b/src/AsyncEngine.h new file mode 100644 index 0000000000..c41fd9ad53 --- /dev/null +++ b/src/AsyncEngine.h @@ -0,0 +1,86 @@ + +/* + * $Id: AsyncEngine.h,v 1.1 2006/08/12 01:43:10 robertc Exp $ + * + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + */ + +#ifndef SQUID_ASYNCENGINE_H +#define SQUID_ASYNCENGINE_H + +#include "squid.h" + + +/* 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, + /* 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). + * + * The timeout value is a requested timeout for this engine - the engine + * should not block for more than this period. (If it takes longer than the + * timeout to do actual checks thats fine though undesirable). + */ + virtual int checkEvents(int timeout) = 0; +}; + +#endif /* SQUID_ASYNCENGINE_H */ diff --git a/src/CompletionDispatcher.h b/src/CompletionDispatcher.h index 3d49451bce..afbff9f5d4 100644 --- a/src/CompletionDispatcher.h +++ b/src/CompletionDispatcher.h @@ -1,6 +1,6 @@ /* - * $Id: CompletionDispatcher.h,v 1.1 2006/08/07 02:28:22 robertc Exp $ + * $Id: CompletionDispatcher.h,v 1.2 2006/08/12 01:43:10 robertc Exp $ * * * SQUID Web Proxy Cache http://www.squid-cache.org/ @@ -49,7 +49,10 @@ public: virtual ~CompletionDispatcher() {} - virtual void dispatch() = 0; + /* dispatch events. This should return true if there were events dispatched + * between the last call to dispatch() returning and this call returning. + */ + virtual bool dispatch() = 0; }; #endif /* SQUID_COMPLETIONDISPATCHER_H */ diff --git a/src/EventLoop.cc b/src/EventLoop.cc index 23e85d5483..ddf74bda2b 100644 --- a/src/EventLoop.cc +++ b/src/EventLoop.cc @@ -1,6 +1,6 @@ /* - * $Id: EventLoop.cc,v 1.1 2006/08/07 02:28:22 robertc Exp $ + * $Id: EventLoop.cc,v 1.2 2006/08/12 01:43:10 robertc Exp $ * * DEBUG: section 1 Main Loop * AUTHOR: Harvest Derived @@ -33,12 +33,9 @@ * */ -#include "squid.h" -#include "event.h" #include "EventLoop.h" -#include "comm.h" -EventLoop::EventLoop() : errcount(0), last_loop(false) +EventLoop::EventLoop() : errcount(0), last_loop(false), timeService(NULL) {} void @@ -54,68 +51,87 @@ EventLoop::registerDispatcher(CompletionDispatcher *dispatcher) dispatchers.push_back(dispatcher); } +void +EventLoop::registerEngine(AsyncEngine *engine) +{ + engines.push_back(engine); +} + void EventLoop::run() { prepareToRun(); - while (!last_loop) - runOnce(); + while (!runOnce()) + + ; } -void +bool EventLoop::runOnce() { - int loop_delay = EventScheduler::GetInstance()->checkEvents(); + bool result = true; + bool error = false; + int loop_delay = 10; /* 10 ms default delay */ + + for (engine_vector::iterator i = engines.begin(); + i != engines.end(); ++i) { + int requested_delay; + /* special case the last engine */ + + if (i - engines.end() != -1) + requested_delay = (*i)->checkEvents(0); + else /* last engine gets the delay */ + requested_delay = (*i)->checkEvents(loop_delay); + + if (requested_delay < 0) + switch (requested_delay) { + + case AsyncEngine::EVENT_IDLE: + debugs(1, 9, "Engine " << *i << " is idle."); + break; + + case AsyncEngine::EVENT_ERROR: + result = false; + error = true; + break; + + default: + fatal_dump("unknown AsyncEngine result"); + } + else if (requested_delay < loop_delay) { + loop_delay = requested_delay; + result = false; + } + } + + if (timeService != NULL) + timeService->tick(); for (dispatcher_vector::iterator i = dispatchers.begin(); i != dispatchers.end(); ++i) - (*i)->dispatch(); - - if (loop_delay < 0) - loop_delay = 0; - - switch (comm_select(loop_delay)) { - - case COMM_OK: - errcount = 0; /* reset if successful */ - break; - - case COMM_IDLE: - /* TODO: rather than busy loop, if everything has returned IDLE we should - * wait for a reasonable timeout period, - if everything returned IDLE - * then not only is there no work to do, there is no work coming in - - * all the comm loops have no fds registered, and all the other - * async engines have no work active or pending. - * ... perhaps we can have a query method to say 'when could there be - * work' - i.e. the event dispatcher can return the next event in its - * queue, and everything else can return -1. - */ - errcount = 0; - break; + if ((*i)->dispatch()) + result = false; - case COMM_ERROR: - errcount++; + if (error) { + ++errcount; debugs(1, 0, "Select loop Error. Retry " << errcount); + } else + errcount = 0; - if (errcount == 10) - fatal_dump("Select Loop failed 10 times.!"); - - break; - - case COMM_TIMEOUT: - break; - - case COMM_SHUTDOWN: - stop(); + if (errcount == 10) + return true; - break; + if (last_loop) + return true; - default: - fatal_dump("MAIN: Internal error -- this should never happen."); + return result; +} - break; - } +void +EventLoop::setTimeService(TimeEngine *engine) +{ + timeService = engine; } void diff --git a/src/EventLoop.h b/src/EventLoop.h index c1cbd96bc5..e7500c62e9 100644 --- a/src/EventLoop.h +++ b/src/EventLoop.h @@ -1,6 +1,6 @@ /* - * $Id: EventLoop.h,v 1.1 2006/08/07 02:28:22 robertc Exp $ + * $Id: EventLoop.h,v 1.2 2006/08/12 01:43:10 robertc Exp $ * * * SQUID Web Proxy Cache http://www.squid-cache.org/ @@ -36,7 +36,9 @@ #include "squid.h" #include "Array.h" +#include "AsyncEngine.h" #include "CompletionDispatcher.h" +#include "SquidTime.h" /* An event loop. An event loop is the core inner loop of squid. * The event loop can be run until exit, or once. After it finishes control @@ -52,26 +54,41 @@ public: EventLoop(); /* register an event dispatcher to be invoked on each event loop. */ void registerDispatcher(CompletionDispatcher *dispatcher); - /* start the event loop running */ + /* register an async engine which will be given the opportunity to perform + * in-main-thread tasks each event loop. + */ + void registerEngine(AsyncEngine *engine); + /* start the event loop running. The loop will run until it is stopped by + * calling stop(), or when the loop is completely idle - nothing + * dispatched in a loop, and all engines idle. + */ void run(); /* run the loop once. This may not complete all events! It should therefor * be used with care. * TODO: signal in runOnce whether or not the loop is over - IDLE vs OK vs * TIMEOUT? */ - void runOnce(); + bool runOnce(); + /* set the time service. There can be only one time service set at any + * time. The time service is invoked on each loop + */ + void setTimeService(TimeEngine *engine); /* stop the event loop - it will finish the current loop and then return to the * caller of run(). */ void stop(); + int errcount; + private: /* setup state variables prior to running */ void prepareToRun(); - int errcount; bool last_loop; typedef Vector dispatcher_vector; dispatcher_vector dispatchers; + typedef Vector engine_vector; + engine_vector engines; + TimeEngine * timeService; }; diff --git a/src/Makefile.am b/src/Makefile.am index 03bc25dbee..f561157cd9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,7 +1,7 @@ # # Makefile for the Squid Object Cache server # -# $Id: Makefile.am,v 1.159 2006/08/07 02:28:22 robertc Exp $ +# $Id: Makefile.am,v 1.160 2006/08/12 01:43:10 robertc Exp $ # # Uncomment and customize the following to suit your needs: # @@ -381,6 +381,8 @@ squid_SOURCES = \ ACLChecklist.h \ $(squid_ACLSOURCES) \ asn.cc \ + AsyncEngine.cc \ + AsyncEngine.h \ authenticate.cc \ authenticate.h \ cache_cf.cc \ @@ -1643,6 +1645,7 @@ tests_testEventLoop_DEPENDENCIES = $(top_builddir)/lib/libmiscutil.a \ ## RBC 20060422. HEADERS_TO_TEST = \ tests/testHeader_ACL.cc \ + tests/testHeader_AsyncEngine.cc \ tests/testHeader_CompletionDispatcher.cc \ tests/testHeader_ConfigParser.cc \ tests/testHeader_client_side_request.cc \ @@ -1654,6 +1657,7 @@ HEADERS_TO_TEST = \ tests/testHeader_HttpHeaderRange.cc \ tests/testHeader_HttpReply.cc \ tests/testHeader_HttpRequestMethod.cc \ + tests/testHeader_SquidTime.cc \ tests/testHeader_Store.cc \ tests/testHeader_StoreEntryStream.cc \ tests/testHeader_URL.cc \ @@ -1964,6 +1968,8 @@ STORE_TEST_SOURCES=\ $(DELAY_POOL_SOURCE) \ CacheDigest.cc \ ConfigParser.cc \ + EventLoop.cc \ + event.cc \ HttpMsg.cc \ store_dir.cc \ store.cc \ @@ -1982,7 +1988,7 @@ STORE_TEST_SOURCES=\ mem_node.cc \ stmem.cc \ tests/stub_mime.cc \ - HttpHeaderTools.cc HttpHeader.cc acl.cc event.cc mem.cc \ + HttpHeaderTools.cc HttpHeader.cc acl.cc mem.cc \ acl_noncore.cc \ MemBuf.cc HttpHdrContRange.cc Packer.cc ACLChecklist.cc HttpHdrCc.cc HttpHdrSc.cc \ HttpHdrScTarget.cc url.cc ACLProxyAuth.cc ACLRegexData.cc ACLUserData.cc \ @@ -1991,6 +1997,8 @@ STORE_TEST_SOURCES=\ refresh.cc \ tests/stub_store_client.cc \ tests/stub_tools.cc \ + tests/testStoreSupport.cc \ + tests/testStoreSupport.h \ time.cc \ URLScheme.cc \ wordlist.cc diff --git a/src/SquidTime.h b/src/SquidTime.h index 45954e633c..23cb493952 100644 --- a/src/SquidTime.h +++ b/src/SquidTime.h @@ -1,6 +1,6 @@ /* - * $Id: SquidTime.h,v 1.1 2006/05/08 23:38:33 robertc Exp $ + * $Id: SquidTime.h,v 1.2 2006/08/12 01:43:11 robertc Exp $ * * DEBUG: section 21 Time Functions * AUTHOR: Harvest Derived @@ -42,4 +42,16 @@ extern time_t squid_curtime; /* 0 */ time_t getCurrentTime(void); +/* event class for doing synthetic time etc */ + +class TimeEngine +{ + +public: + virtual ~TimeEngine(); + /* tick the clock - update from the OS or other time source, */ + virtual void tick(); +}; + + #endif /* SQUID_TIME_H */ diff --git a/src/comm.cc b/src/comm.cc index 8df6963b29..d5e4b84cf0 100644 --- a/src/comm.cc +++ b/src/comm.cc @@ -1,6 +1,6 @@ /* - * $Id: comm.cc,v 1.420 2006/08/07 02:28:22 robertc Exp $ + * $Id: comm.cc,v 1.421 2006/08/12 01:43:11 robertc Exp $ * * DEBUG: section 5 Socket Functions * AUTHOR: Harvest Derived @@ -2640,3 +2640,38 @@ ConnectionDetail::ConnectionDetail() { memset(&me, 0, sizeof(me)); memset(&peer, 0, sizeof(peer)); } + +bool +CommDispatcher::dispatch() { + bool result = comm_iocallbackpending(); + comm_calliocallback(); + /* and again to deal with indirectly queued events + * resulting from the first call. These are usually + * callbacks and should be dealt with immediately. + */ + comm_calliocallback(); + return result; +} + +int +CommSelectEngine::checkEvents(int timeout) { + switch (comm_select(timeout)) { + + case COMM_OK: + + case COMM_TIMEOUT: + return 0; + + case COMM_IDLE: + + case COMM_SHUTDOWN: + return EVENT_IDLE; + + case COMM_ERROR: + return EVENT_ERROR; + + default: + fatal_dump("comm.cc: Internal error -- this should never happen."); + return EVENT_ERROR; + }; +} diff --git a/src/comm.h b/src/comm.h index dd91d402be..402084e0f8 100644 --- a/src/comm.h +++ b/src/comm.h @@ -2,6 +2,8 @@ #define __COMM_H__ #include "squid.h" +#include "AsyncEngine.h" +#include "CompletionDispatcher.h" #include "StoreIOBuffer.h" #include "Array.h" @@ -179,4 +181,22 @@ private: void removeCheck (int const); }; +/* a dispatcher for comms events */ + +class CommDispatcher : public CompletionDispatcher +{ + +public: + virtual bool dispatch(); +}; + +/* A comm engine that calls comm_select */ + +class CommSelectEngine : public AsyncEngine +{ + +public: + virtual int checkEvents(int timeout); +}; + #endif diff --git a/src/comm_poll.cc b/src/comm_poll.cc index eca9f8a636..5e7d4b3c01 100644 --- a/src/comm_poll.cc +++ b/src/comm_poll.cc @@ -1,6 +1,6 @@ /* - * $Id: comm_poll.cc,v 1.17 2006/08/07 02:28:22 robertc Exp $ + * $Id: comm_poll.cc,v 1.18 2006/08/12 01:43:11 robertc Exp $ * * DEBUG: section 5 Socket Functions * @@ -433,7 +433,11 @@ comm_select(int msec) if (msec > MAX_POLL_TIME) msec = MAX_POLL_TIME; - /* nothing to do */ + /* nothing to do + * + * Note that this will only ever trigger when there are no log files + * and stdout/err/in are all closed too. + */ if (nfds == 0 && !npending) { if (shutting_down) return COMM_SHUTDOWN; diff --git a/src/event.cc b/src/event.cc index 3deb693794..74ee38322f 100644 --- a/src/event.cc +++ b/src/event.cc @@ -1,6 +1,6 @@ /* - * $Id: event.cc,v 1.41 2006/08/07 02:28:22 robertc Exp $ + * $Id: event.cc,v 1.42 2006/08/12 01:43:11 robertc Exp $ * * DEBUG: section 41 Event Processing * AUTHOR: Henrik Nordstrom @@ -111,9 +111,11 @@ EventDispatcher::add queue.push_back(event); } -void +bool EventDispatcher::dispatch() { + bool result = queue.size() != 0; + for (Vector::iterator i = queue.begin(); i != queue.end(); ++i) { ev_entry * event = *i; EVH *callback; @@ -132,6 +134,7 @@ EventDispatcher::dispatch() } queue.clean(); + return result; } EventDispatcher * @@ -182,11 +185,16 @@ EventScheduler::checkDelay() if (!tasks) return (int) 10; - return (int) ((tasks->when - current_dtime) * 1000); + int result = (int) ((tasks->when - current_dtime) * 1000); + + if (result < 0) + return 0; + + return result; } int -EventScheduler::checkEvents() +EventScheduler::checkEvents(int timeout) { struct ev_entry *event = NULL; diff --git a/src/event.h b/src/event.h index 2831feb89c..66cd16ae01 100644 --- a/src/event.h +++ b/src/event.h @@ -1,6 +1,6 @@ /* - * $Id: event.h,v 1.1 2006/08/07 02:28:22 robertc Exp $ + * $Id: event.h,v 1.2 2006/08/12 01:43:11 robertc Exp $ * * * SQUID Web Proxy Cache http://www.squid-cache.org/ @@ -36,6 +36,7 @@ #include "squid.h" #include "Array.h" +#include "AsyncEngine.h" #include "CompletionDispatcher.h" /* forward decls */ @@ -86,7 +87,7 @@ public: void add (const char *name, EVH * func, void *arg, double when, int, bool cbdata=true); - void dispatch(); + bool dispatch(); static EventDispatcher *GetInstance(); @@ -96,12 +97,16 @@ private: static EventDispatcher _instance; }; -class EventScheduler +class EventScheduler : public AsyncEngine { public: /* Create an event scheduler that will hand its ready to run callbacks to - * an EventDispatcher */ + * an EventDispatcher + * + * TODO: add should include a dispatcher to use perhaps? then it would be + * more decoupled.. + */ EventScheduler(EventDispatcher *); ~EventScheduler(); /* cancel a scheduled but not dispatched event */ @@ -116,7 +121,7 @@ public: bool find(EVH * func, void * arg); /* schedule a callback function to run in when seconds */ void schedule(const char *name, EVH * func, void *arg, double when, int weight, bool cbdata=true); - int checkEvents(); + int checkEvents(int timeout); static EventScheduler *GetInstance(); private: diff --git a/src/main.cc b/src/main.cc index c829130bb3..493ce02655 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1,6 +1,6 @@ /* - * $Id: main.cc,v 1.428 2006/08/07 02:28:22 robertc Exp $ + * $Id: main.cc,v 1.429 2006/08/12 01:43:11 robertc Exp $ * * DEBUG: section 1 Startup and Main Loop * AUTHOR: Harvest Derived @@ -114,7 +114,7 @@ static void setEffectiveUser(void); extern void log_trace_done(); extern void log_trace_init(char *); #endif -static EVH SquidShutdown; +static void SquidShutdown(void); static void mainSetCwd(void); static int checkRunningPid(void); @@ -128,14 +128,27 @@ static const char *squid_start_script = "squid_start"; #include "test_access.c" #endif +/* temporary thunk across to the unrefactored store interface */ + +class StoreRootEngine : public AsyncEngine +{ + +public: + int checkEvents(int timeout) + { + Store::Root().callback(); + return EVENT_IDLE; + }; +}; + class SignalDispatcher : public CompletionDispatcher { public: - SignalDispatcher(EventLoop &loop) : loop(loop) {} + SignalDispatcher(EventLoop &loop) : loop(loop), events_dispatched(false) {} void addEventLoop(EventLoop * loop); - virtual void dispatch(); + virtual bool dispatch(); private: static void StopEventLoop(void * data) @@ -144,9 +157,10 @@ private: } EventLoop &loop; + bool events_dispatched; }; -void +bool SignalDispatcher::dispatch() { if (do_reconfigure) { @@ -176,9 +190,12 @@ SignalDispatcher::dispatch() #endif serverConnectionsClose(); - //eventAdd("SquidShutdown", StopEventLoop, this, (double) (wait + 1), 1, false); - eventAdd("SquidShutdown", SquidShutdown, NULL, (double) (wait + 1), 1, false); + eventAdd("SquidShutdown", StopEventLoop, this, (double) (wait + 1), 1, false); } + + bool result = events_dispatched; + events_dispatched = false; + return result; } static void @@ -1301,25 +1318,36 @@ main(int argc, char **argv) /* TODO: stop requiring the singleton here */ mainLoop.registerDispatcher(EventDispatcher::GetInstance()); - for (;;) - { - /* Attempt any pending storedir IO - * Note: the storedir is roughly a reactor of its own. - */ - Store::Root().callback(); + /* TODO: stop requiring the singleton here */ + mainLoop.registerEngine(EventScheduler::GetInstance()); - comm_calliocallback(); + StoreRootEngine store_engine; - /* and again to deal with indirectly queued events - * resulting from the first call. These are usually - * callbacks and should be dealt with immediately. - */ + mainLoop.registerEngine(&store_engine); - if (comm_iocallbackpending()) - comm_calliocallback(); + CommDispatcher comm_dispatcher; - mainLoop.runOnce(); - } + mainLoop.registerDispatcher(&comm_dispatcher); + + CommSelectEngine comm_engine; + + /* must be last - its the only engine that implements timeouts properly + * at the moment. + */ + mainLoop.registerEngine(&comm_engine); + + /* use the standard time service */ + TimeEngine time_engine; + + mainLoop.setTimeService(&time_engine); + + mainLoop.run(); + + if (mainLoop.errcount == 10) + fatal_dump("Event loop exited with failure."); + + /* shutdown squid now */ + SquidShutdown(); /* NOTREACHED */ return 0; @@ -1587,7 +1615,7 @@ watch_child(char *argv[]) } static void -SquidShutdown(void *unused) +SquidShutdown() { #if USE_WIN32_SERVICE WIN32_svcstatusupdate(SERVICE_STOP_PENDING, 10000); diff --git a/src/tests/testCoss.cc b/src/tests/testCoss.cc index 501422ff2e..ad1a898f1f 100644 --- a/src/tests/testCoss.cc +++ b/src/tests/testCoss.cc @@ -11,7 +11,7 @@ #include "HttpHeader.h" #include "HttpReply.h" #include "StoreFileSystem.h" -#include "SquidTime.h" +#include "testStoreSupport.h" #define TESTDIR "testCoss__testCossSearch" @@ -172,12 +172,10 @@ testCoss::testCossSearch() /* ok, ready to use */ Store::Root().init(); - /* ensure rebuilding finishes - run a mini event loop */ - while (store_dirs_rebuilding > 1) { - getCurrentTime(); - EventScheduler::GetInstance()->checkEvents(); - EventDispatcher::GetInstance()->dispatch(); - } + /* rebuild is a scheduled event */ + StockEventLoop loop; + + loop.run(); /* nothing to rebuild */ CPPUNIT_ASSERT(store_dirs_rebuilding == 1); diff --git a/src/tests/testEvent.cc b/src/tests/testEvent.cc index b00be3301b..9b30c2376d 100644 --- a/src/tests/testEvent.cc +++ b/src/tests/testEvent.cc @@ -62,8 +62,10 @@ testEvent::testDispatch() EventDispatcher dispatcher; CalledEvent event; dispatcher.add(new ev_entry("test event", CalledEvent::Handler, &event, 0, 0, false)); - dispatcher.dispatch(); - dispatcher.dispatch(); + /* return true when an event is dispatched */ + CPPUNIT_ASSERT_EQUAL(true, dispatcher.dispatch()); + /* return false when none were dispatched */ + CPPUNIT_ASSERT_EQUAL(false, dispatcher.dispatch()); CPPUNIT_ASSERT_EQUAL(1, event.calls); } @@ -79,7 +81,7 @@ testEvent::testCancel() scheduler.schedule("test event", CalledEvent::Handler, &event, 0, 0, false); scheduler.schedule("test event2", CalledEvent::Handler, &event_to_cancel, 0, 0, false); scheduler.cancel(CalledEvent::Handler, &event_to_cancel); - scheduler.checkEvents(); + scheduler.checkEvents(0); dispatcher.dispatch(); CPPUNIT_ASSERT_EQUAL(1, event.calls); CPPUNIT_ASSERT_EQUAL(0, event_to_cancel.calls); @@ -97,7 +99,7 @@ testEvent::testDump() CapturingStoreEntry * anEntry = new CapturingStoreEntry(); scheduler.schedule("last event", CalledEvent::Handler, &event, 0, 0, false); /* schedule and dispatch to set the last run event */ - scheduler.checkEvents(); + scheduler.checkEvents(0); dispatcher.dispatch(); scheduler.schedule("test event", CalledEvent::Handler, &event, 0, 0, false); scheduler.schedule("test event2", CalledEvent::Handler, &event2, 0, 0, false); @@ -133,14 +135,14 @@ testEvent::testCheckEvents() EventDispatcher dispatcher; EventScheduler scheduler(&dispatcher); CalledEvent event; - CPPUNIT_ASSERT_EQUAL(10, scheduler.checkEvents()); + CPPUNIT_ASSERT_EQUAL(10, scheduler.checkEvents(0)); /* event running now gets sent to the dispatcher and the delay is set to 10ms */ scheduler.schedule("test event", CalledEvent::Handler, &event, 0, 0, false); - CPPUNIT_ASSERT_EQUAL(10, scheduler.checkEvents()); + CPPUNIT_ASSERT_EQUAL(10, scheduler.checkEvents(0)); dispatcher.dispatch(); /* event running later results in a delay of the time till it runs */ scheduler.schedule("test event", CalledEvent::Handler, &event, 2, 0, false); - CPPUNIT_ASSERT_EQUAL(2000, scheduler.checkEvents()); + CPPUNIT_ASSERT_EQUAL(2000, scheduler.checkEvents(0)); dispatcher.dispatch(); CPPUNIT_ASSERT_EQUAL(1, event.calls); } diff --git a/src/tests/testEventLoop.cc b/src/tests/testEventLoop.cc index 2a28bfbf2b..753b5bdd3a 100644 --- a/src/tests/testEventLoop.cc +++ b/src/tests/testEventLoop.cc @@ -1,10 +1,12 @@ #include "squid.h" #include +#include "AsyncEngine.h" #include "CompletionDispatcher.h" #include "Mem.h" #include "testEventLoop.h" #include "EventLoop.h" +#include "event.h" CPPUNIT_TEST_SUITE_REGISTRATION( testEventLoop ); @@ -43,8 +45,8 @@ testEventLoop::testCreate() * Running the loop once is useful for integration with other loops, such as * migrating to it in incrementally. * - * This test works by having a customer dispatcher which records how many times its - * called. + * This test works by having a custom dispatcher and engine which record how + * many times they are called. */ class RecordDispatcher : public CompletionDispatcher @@ -59,27 +61,57 @@ public: { ++calls; } + + bool dispatch() + { + ++calls; + /* claim we dispatched calls to be useful for the testStopOnIdle test. + */ + return true; + } }; +class RecordingEngine : public AsyncEngine +{ + +public: + int calls; + int lasttimeout; + int return_timeout; + RecordingEngine(int return_timeout=0): calls(0), lasttimeout(0), + return_timeout(return_timeout) + {} + + virtual int checkEvents(int timeout) + { + ++calls; + lasttimeout = timeout; + return return_timeout; + } + }; + void testEventLoop::testRunOnce() { EventLoop theLoop; RecordDispatcher dispatcher; theLoop.registerDispatcher(&dispatcher); + RecordingEngine engine; + theLoop.registerEngine(&engine); theLoop.runOnce(); CPPUNIT_ASSERT_EQUAL(1, dispatcher.calls); + CPPUNIT_ASSERT_EQUAL(1, engine.calls); } /* - * completion dispatchers registered with the event loop are invoked by the event - * loop. + * completion dispatchers registered with the event loop are invoked by the + * event loop. * - * This test works by having a customer dispatcher which shuts the loop down once its - * been invoked twice. + * This test works by having a customer dispatcher which shuts the loop down + * once its been invoked twice. * - * It also tests that loop.run() and loop.stop() work, because if they dont work, - * this test will either hang, or fail. + * It also tests that loop.run() and loop.stop() work, because if they dont + * work, this test will either hang, or fail. */ class ShutdownDispatcher : public CompletionDispatcher @@ -96,6 +128,14 @@ public: if (++calls == 2) theLoop.stop(); } + + bool dispatch() + { + if (++calls == 2) + theLoop.stop(); + + return true; + } }; void @@ -105,5 +145,118 @@ testEventLoop::testRegisterDispatcher() ShutdownDispatcher testDispatcher(theLoop); theLoop.registerDispatcher(&testDispatcher); theLoop.run(); + /* we should get two calls because the test dispatched returns true from + * dispatch(), and calls stop on the second call. + */ CPPUNIT_ASSERT_EQUAL(2, testDispatcher.calls); } + +/* test that a registered async engine is invoked on each loop run + * we do this with an intstrumented async engine. + */ +void +testEventLoop::testRegisterEngine() +{ + EventLoop theLoop; + ShutdownDispatcher testDispatcher(theLoop); + theLoop.registerDispatcher(&testDispatcher); + RecordingEngine testEngine; + theLoop.registerEngine(&testEngine); + theLoop.run(); + CPPUNIT_ASSERT_EQUAL(2, testEngine.calls); +} + +/* each AsyncEngine needs to be given a timeout. We want one engine in each + * loop to be given the timeout value - and the rest to have a timeout of 0. + * The last registered engine should be given this timeout, which will mean + * that we dont block in the loop until the last engine. This will allow for + * dynamic introduction and removal of engines, as long as the last engine + * is one which can do a os call rather than busy waiting. + * + * So - we want the timeout hints returned from the earlier engines to be + * tracked, and the lowest non-negative value given to the last engine. + */ +void +testEventLoop::testEngineTimeout() +{ + EventLoop theLoop; + RecordingEngine engineOne(5); + RecordingEngine engineTwo; + theLoop.registerEngine(&engineOne); + theLoop.registerEngine(&engineTwo); + theLoop.runOnce(); + CPPUNIT_ASSERT_EQUAL(0, engineOne.lasttimeout); + CPPUNIT_ASSERT_EQUAL(5, engineTwo.lasttimeout); +} + +/* An event loop with all idle engines, and nothing dispatched in a run should + * automatically quit. The runOnce call should return True when the loop is + * entirely idle to make it easy for people running the loop by hand. + */ +void +testEventLoop::testStopOnIdle() +{ + EventLoop theLoop; + /* trivial case - no dispatchers or engines, should quit immediately */ + CPPUNIT_ASSERT_EQUAL(true, theLoop.runOnce()); + theLoop.run(); + /* add a dispatcher with nothing to dispatch - use an EventDispatcher as its + * sufficient and handy + */ + EventDispatcher dispatcher; + theLoop.registerDispatcher(&dispatcher); + CPPUNIT_ASSERT_EQUAL(true, theLoop.runOnce()); + theLoop.run(); + /* add an engine which is idle. + */ + RecordingEngine engine(AsyncEngine::EVENT_IDLE); + theLoop.registerEngine(&engine); + CPPUNIT_ASSERT_EQUAL(true, theLoop.runOnce()); + CPPUNIT_ASSERT_EQUAL(1, engine.calls); + theLoop.run(); + CPPUNIT_ASSERT_EQUAL(2, engine.calls); + /* add an engine which is suffering errors. This should result in 10 + * loops until the loop stops - because thats the error retry amount + */ + RecordingEngine failing_engine(AsyncEngine::EVENT_ERROR); + theLoop.registerEngine(&failing_engine); + CPPUNIT_ASSERT_EQUAL(false, theLoop.runOnce()); + CPPUNIT_ASSERT_EQUAL(1, failing_engine.calls); + theLoop.run(); + /* run resets the error count ... */ + CPPUNIT_ASSERT_EQUAL(11, failing_engine.calls); +} + +/* An event loop has a time service which is like an async engine but never + * generates events and there can only be one such service. + */ + +class StubTime : public TimeEngine +{ + +public: + StubTime::StubTime() : calls(0) {} + + int calls; + void tick() + { + ++calls; + } +}; + +void +testEventLoop::testSetTimeService() +{ + EventLoop theLoop; + StubTime myTime; + /* the loop will not error without a time service */ + theLoop.runOnce(); + /* we can set the time service */ + theLoop.setTimeService(&myTime); + /* it invokes our tick() call */ + theLoop.runOnce(); + CPPUNIT_ASSERT_EQUAL(1, myTime.calls); + /* it invokes our tick() call again */ + theLoop.runOnce(); + CPPUNIT_ASSERT_EQUAL(2, myTime.calls); +} diff --git a/src/tests/testEventLoop.h b/src/tests/testEventLoop.h index 6aa0f1d193..f3df5058ee 100644 --- a/src/tests/testEventLoop.h +++ b/src/tests/testEventLoop.h @@ -14,14 +14,26 @@ class testEventLoop : public CPPUNIT_NS::TestFixture CPPUNIT_TEST( testCreate ); CPPUNIT_TEST( testRunOnce ); CPPUNIT_TEST( testRegisterDispatcher ); + CPPUNIT_TEST( testRegisterEngine ); + CPPUNIT_TEST( testEngineTimeout ); + CPPUNIT_TEST( testSetTimeService ); + CPPUNIT_TEST( testStopOnIdle ); CPPUNIT_TEST_SUITE_END(); public: protected: void testCreate(); + void testEngineTimeout(); void testRunOnce(); void testRegisterDispatcher(); + void testRegisterEngine(); + void testSetTimeService(); + void testStopOnIdle(); + /* TODO: + * test that engine which errors a couple of times, then returns 0, then + * errors 10 times in a row triggers a fail on the 10th time around + */ }; #endif diff --git a/src/tests/testHeader_AsyncEngine.cc b/src/tests/testHeader_AsyncEngine.cc new file mode 100644 index 0000000000..39b7820af4 --- /dev/null +++ b/src/tests/testHeader_AsyncEngine.cc @@ -0,0 +1,4 @@ +/* This test tests that the header below can be processed on its own with + * no other #includes. Dont add any! + */ +#include "AsyncEngine.h" diff --git a/src/tests/testHeader_SquidTime.cc b/src/tests/testHeader_SquidTime.cc new file mode 100644 index 0000000000..d16c38fa12 --- /dev/null +++ b/src/tests/testHeader_SquidTime.cc @@ -0,0 +1,4 @@ +/* This test tests that the header below can be processed on its own with + * no other #includes. Dont add any! + */ +#include "SquidTime.h" diff --git a/src/tests/testNull.cc b/src/tests/testNull.cc index cbd932e5de..2441824f0f 100644 --- a/src/tests/testNull.cc +++ b/src/tests/testNull.cc @@ -11,7 +11,7 @@ #include "HttpHeader.h" #include "HttpReply.h" #include "StoreFileSystem.h" -#include "SquidTime.h" +#include "testStoreSupport.h" #define TESTDIR "testNull__testNullSearch" @@ -140,13 +140,9 @@ testNull::testNullSearch() /* ok, ready to use */ Store::Root().init(); - /* ensure rebuilding finishes - run a mini event loop */ - - while (store_dirs_rebuilding > 1) { - getCurrentTime(); - EventScheduler::GetInstance()->checkEvents(); - EventDispatcher::GetInstance()->dispatch(); - } + /* rebuild is a scheduled event */ + StockEventLoop loop; + loop.run(); /* nothing to rebuild */ CPPUNIT_ASSERT(store_dirs_rebuilding == 1); diff --git a/src/tests/testStoreSupport.cc b/src/tests/testStoreSupport.cc new file mode 100644 index 0000000000..a51350cfe6 --- /dev/null +++ b/src/tests/testStoreSupport.cc @@ -0,0 +1,47 @@ + +/* + * DEBUG: + * AUTHOR: Robert Collins + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 "testStoreSupport.h" +#include "event.h" +#include "EventLoop.h" +#include "SquidTime.h" + +/* construct a stock loop with event dispatching, a time service that advances + * 1 second a tick + */ +StockEventLoop::StockEventLoop() : default_time_engine(TimeEngine()) +{ + registerDispatcher(EventDispatcher::GetInstance()); + registerEngine(EventScheduler::GetInstance()); + setTimeService(&default_time_engine); +} diff --git a/src/tests/testStoreSupport.h b/src/tests/testStoreSupport.h new file mode 100644 index 0000000000..13c0c374e3 --- /dev/null +++ b/src/tests/testStoreSupport.h @@ -0,0 +1,51 @@ + +/* + * DEBUG: + * AUTHOR: Robert Collins + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + */ + +#ifndef SQUID_TESTSTORESUPPORT_H +#define SQUID_TESTSTORESUPPORT_H + +#include "EventLoop.h" + +/* construct a stock loop with event dispatching, a time service that advances + * 1 second a tick + */ + +class StockEventLoop : public EventLoop +{ + +public: + StockEventLoop(); + TimeEngine default_time_engine; +}; + +#endif /* SQUID_TESTSTORESUPPORT_H */ diff --git a/src/tests/testUfs.cc b/src/tests/testUfs.cc index 3fd15da110..08f5a2c678 100644 --- a/src/tests/testUfs.cc +++ b/src/tests/testUfs.cc @@ -9,7 +9,7 @@ #include "Mem.h" #include "HttpHeader.h" #include "HttpReply.h" -#include "SquidTime.h" +#include "testStoreSupport.h" #define TESTDIR "testUfs__testUfsSearch" @@ -103,15 +103,13 @@ testUfs::testUfsSearch() /* ok, ready to create */ aStore->create(); - /* ok, ready to use - init store & hash too */ + /* ok, ready to use - inits store & hash too */ Store::Root().init(); - /* ensure rebuilding finishes - run a mini event loop */ - while (store_dirs_rebuilding > 1) { - getCurrentTime(); - EventScheduler::GetInstance()->checkEvents(); - EventDispatcher::GetInstance()->dispatch(); - } + /* rebuild is a scheduled event */ + StockEventLoop loop; + + loop.run(); /* nothing to rebuild */ CPPUNIT_ASSERT(store_dirs_rebuilding == 1); diff --git a/src/time.cc b/src/time.cc index 3198517655..75101b5de0 100644 --- a/src/time.cc +++ b/src/time.cc @@ -1,6 +1,6 @@ /* - * $Id: time.cc,v 1.2 2006/05/08 23:38:33 robertc Exp $ + * $Id: time.cc,v 1.3 2006/08/12 01:43:11 robertc Exp $ * * DEBUG: section 21 Time Functions * AUTHOR: Harvest Derived @@ -51,3 +51,12 @@ getCurrentTime(void) (double) current_time.tv_usec / 1000000.0; return squid_curtime = current_time.tv_sec; } + +TimeEngine::~TimeEngine() +{} + +void +TimeEngine::tick() +{ + getCurrentTime(); +} diff --git a/test-suite/test_tools.cc b/test-suite/test_tools.cc index 815452e4f5..1e26f60f7c 100644 --- a/test-suite/test_tools.cc +++ b/test-suite/test_tools.cc @@ -1,6 +1,6 @@ /* - * $Id: test_tools.cc,v 1.7 2005/01/03 16:08:27 robertc Exp $ + * $Id: test_tools.cc,v 1.8 2006/08/12 01:43:12 robertc Exp $ * * AUTHOR: Robert Collins * @@ -107,13 +107,20 @@ va_dcl static void _db_print_stderr(const char *format, va_list args) { /* FIXME? */ - // if (opt_debug_stderr < Debug::level) - if (1 < Debug::level) + // if (opt_debug_stderr < Debug::level) + + if (1 < Debug::level) return; vfprintf(stderr, format, args); } +void +fatal_dump(const char *message) { + debug (0,0) ("Fatal: %s",message); + exit (1); +} + void fatal(const char *message) { debug (0,0) ("Fatal: %s",message); @@ -131,8 +138,7 @@ fatalvf(const char *fmt, va_list args) { /* printf-style interface for fatal */ #if STDC_HEADERS void -fatalf(const char *fmt,...) -{ +fatalf(const char *fmt,...) { va_list args; va_start(args, fmt); #else @@ -156,16 +162,14 @@ debug_trap(const char *message) { } std::ostream & -Debug::getDebugOut() -{ +Debug::getDebugOut() { assert (CurrentDebug == NULL); CurrentDebug = new std::ostringstream(); return *CurrentDebug; } void -Debug::finishDebug() -{ +Debug::finishDebug() { _db_print("%s\n", CurrentDebug->str().c_str()); delete CurrentDebug; CurrentDebug = NULL; @@ -176,8 +180,7 @@ std::ostringstream *Debug::CurrentDebug (NULL); MemImplementingAllocator *dlink_node_pool = NULL; dlink_node * -dlinkNodeNew() -{ +dlinkNodeNew() { if (dlink_node_pool == NULL) dlink_node_pool = MemPools::GetInstance().create("Dlink list nodes", sizeof(dlink_node)); @@ -187,8 +190,7 @@ dlinkNodeNew() /* the node needs to be unlinked FIRST */ void -dlinkNodeDelete(dlink_node * m) -{ +dlinkNodeDelete(dlink_node * m) { if (m == NULL) return; @@ -196,8 +198,7 @@ dlinkNodeDelete(dlink_node * m) } void -dlinkAdd(void *data, dlink_node * m, dlink_list * list) -{ +dlinkAdd(void *data, dlink_node * m, dlink_list * list) { m->data = data; m->prev = NULL; m->next = list->head; @@ -212,8 +213,7 @@ dlinkAdd(void *data, dlink_node * m, dlink_list * list) } void -dlinkAddAfter(void *data, dlink_node * m, dlink_node * n, dlink_list * list) -{ +dlinkAddAfter(void *data, dlink_node * m, dlink_node * n, dlink_list * list) { m->data = data; m->prev = n; m->next = n->next; @@ -229,8 +229,7 @@ dlinkAddAfter(void *data, dlink_node * m, dlink_node * n, dlink_list * list) } void -dlinkAddTail(void *data, dlink_node * m, dlink_list * list) -{ +dlinkAddTail(void *data, dlink_node * m, dlink_list * list) { m->data = data; m->next = NULL; m->prev = list->tail; @@ -245,8 +244,7 @@ dlinkAddTail(void *data, dlink_node * m, dlink_list * list) } void -dlinkDelete(dlink_node * m, dlink_list * list) -{ +dlinkDelete(dlink_node * m, dlink_list * list) { if (m->next) m->next->prev = m->prev; @@ -268,5 +266,4 @@ ctx_enter(const char *descr) { } void -ctx_exit(Ctx ctx) { -} +ctx_exit(Ctx ctx) {}