--- /dev/null
+
+/*
+ * $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"
--- /dev/null
+
+/*
+ * $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 */
/*
- * $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/
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 */
/*
- * $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
*
*/
-#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
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
/*
- * $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/
#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
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<CompletionDispatcher *> dispatcher_vector;
dispatcher_vector dispatchers;
+ typedef Vector<AsyncEngine *> engine_vector;
+ engine_vector engines;
+ TimeEngine * timeService;
};
#
# 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:
#
ACLChecklist.h \
$(squid_ACLSOURCES) \
asn.cc \
+ AsyncEngine.cc \
+ AsyncEngine.h \
authenticate.cc \
authenticate.h \
cache_cf.cc \
## 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 \
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 \
$(DELAY_POOL_SOURCE) \
CacheDigest.cc \
ConfigParser.cc \
+ EventLoop.cc \
+ event.cc \
HttpMsg.cc \
store_dir.cc \
store.cc \
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 \
refresh.cc \
tests/stub_store_client.cc \
tests/stub_tools.cc \
+ tests/testStoreSupport.cc \
+ tests/testStoreSupport.h \
time.cc \
URLScheme.cc \
wordlist.cc
/*
- * $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
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 */
/*
- * $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
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;
+ };
+}
#define __COMM_H__
#include "squid.h"
+#include "AsyncEngine.h"
+#include "CompletionDispatcher.h"
#include "StoreIOBuffer.h"
#include "Array.h"
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
/*
- * $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
*
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;
/*
- * $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
queue.push_back(event);
}
-void
+bool
EventDispatcher::dispatch()
{
+ bool result = queue.size() != 0;
+
for (Vector<ev_entry *>::iterator i = queue.begin(); i != queue.end(); ++i) {
ev_entry * event = *i;
EVH *callback;
}
queue.clean();
+ return result;
}
EventDispatcher *
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;
/*
- * $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/
#include "squid.h"
#include "Array.h"
+#include "AsyncEngine.h"
#include "CompletionDispatcher.h"
/* forward decls */
void add
(const char *name, EVH * func, void *arg, double when, int, bool cbdata=true);
- void dispatch();
+ bool dispatch();
static EventDispatcher *GetInstance();
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 */
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:
/*
- * $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
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);
#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)
}
EventLoop &loop;
+ bool events_dispatched;
};
-void
+bool
SignalDispatcher::dispatch()
{
if (do_reconfigure) {
#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
/* 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;
}
static void
-SquidShutdown(void *unused)
+SquidShutdown()
{
#if USE_WIN32_SERVICE
WIN32_svcstatusupdate(SERVICE_STOP_PENDING, 10000);
#include "HttpHeader.h"
#include "HttpReply.h"
#include "StoreFileSystem.h"
-#include "SquidTime.h"
+#include "testStoreSupport.h"
#define TESTDIR "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);
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);
}
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);
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);
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);
}
#include "squid.h"
#include <cppunit/TestAssert.h>
+#include "AsyncEngine.h"
#include "CompletionDispatcher.h"
#include "Mem.h"
#include "testEventLoop.h"
#include "EventLoop.h"
+#include "event.h"
CPPUNIT_TEST_SUITE_REGISTRATION( testEventLoop );
* 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
{
++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
if (++calls == 2)
theLoop.stop();
}
+
+ bool dispatch()
+ {
+ if (++calls == 2)
+ theLoop.stop();
+
+ return true;
+ }
};
void
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);
+}
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
--- /dev/null
+/* This test tests that the header below can be processed on its own with
+ * no other #includes. Dont add any!
+ */
+#include "AsyncEngine.h"
--- /dev/null
+/* This test tests that the header below can be processed on its own with
+ * no other #includes. Dont add any!
+ */
+#include "SquidTime.h"
#include "HttpHeader.h"
#include "HttpReply.h"
#include "StoreFileSystem.h"
-#include "SquidTime.h"
+#include "testStoreSupport.h"
#define TESTDIR "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);
--- /dev/null
+
+/*
+ * 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);
+}
--- /dev/null
+
+/*
+ * 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 */
#include "Mem.h"
#include "HttpHeader.h"
#include "HttpReply.h"
-#include "SquidTime.h"
+#include "testStoreSupport.h"
#define TESTDIR "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);
/*
- * $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
(double) current_time.tv_usec / 1000000.0;
return squid_curtime = current_time.tv_sec;
}
+
+TimeEngine::~TimeEngine()
+{}
+
+void
+TimeEngine::tick()
+{
+ getCurrentTime();
+}
/*
- * $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
*
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);
/* 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
}
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;
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));
/* the node needs to be unlinked FIRST */
void
-dlinkNodeDelete(dlink_node * m)
-{
+dlinkNodeDelete(dlink_node * m) {
if (m == NULL)
return;
}
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;
}
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;
}
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;
}
void
-dlinkDelete(dlink_node * m, dlink_list * list)
-{
+dlinkDelete(dlink_node * m, dlink_list * list) {
if (m->next)
m->next->prev = m->prev;
}
void
-ctx_exit(Ctx ctx) {
-}
+ctx_exit(Ctx ctx) {}