--------------------- PatchSet 6241 Date: 2007/12/05 04:43:09 Author: rousskov Branch: async-calls Tag: (none) Log: JobDialer now implements the call/handleException/end logic. Moved canBeCalled logic from AsyncJob::callStart into its own method, now used by job dialers. inCall now stores in-progress AsyncCall and not just its name. This helps with canceling calls in canBeCalled() and debugging. Replaced "[asyncX]" job ID reporting with [jobX]. Removed unused code. Members: src/ICAP/AsyncJob.cc:1.3.4.9->1.3.4.10 src/ICAP/AsyncJob.h:1.3.14.7->1.3.14.8 Index: squid3/src/ICAP/AsyncJob.cc =================================================================== RCS file: /cvsroot/squid-sf//squid3/src/ICAP/AsyncJob.cc,v retrieving revision 1.3.4.9 retrieving revision 1.3.4.10 diff -u -r1.3.4.9 -r1.3.4.10 --- squid3/src/ICAP/AsyncJob.cc 29 Nov 2007 18:28:49 -0000 1.3.4.9 +++ squid3/src/ICAP/AsyncJob.cc 5 Dec 2007 04:43:09 -0000 1.3.4.10 @@ -51,9 +51,11 @@ // there is no call wrapper waiting for our return, so we fake it debugs(93, 5, typeName << " will delete this, reason: " << stopReason); - inCall = "FAKE-deleteThis"; - + AsyncCall *fakeCall = asyncCall(93,4, "FAKE-deleteThis", + MemFun(this, &AsyncJob::deleteThis, aReason)); + inCall = fakeCall; callEnd(); + delete fakeCall; } void AsyncJob::mustStop(const char *aReason) @@ -88,33 +90,35 @@ return true; // so that it is safe for kids to use } -bool AsyncJob::callStart(const char *method) +bool AsyncJob::canBeCalled(AsyncCall *call) const { - // we must be called asynchronously and hence, the caller must lock us - Must(cbdataReferenceValid(toCbdata())); - - debugs(93, 5, typeName << "::" << method << " called" << status()); - if (inCall) { - // this may happen when we have bugs or when arguably buggy - // comm interface calls us while we are closing the connection - debugs(93, 5, HERE << typeName << "::" << inCall << - " is in progress; " << typeName << "::" << method << - " cancels reentry."); - return false; + // This may happen when we have bugs or some module is not calling + // us asynchronously (comm used to do that). + debugs(93, 5, HERE << *inCall << " is in progress; " << + *call << " canot reenter the job."); + return call->cancel("reentrant job call"); } - inCall = method; return true; } -void AsyncJob::callException(const TextException &e) +void AsyncJob::callStart(AsyncCall *call) { // we must be called asynchronously and hence, the caller must lock us Must(cbdataReferenceValid(toCbdata())); - debugs(93, 3, typeName << "::" << inCall << " caught an exception: " << - e.message << ' ' << status()); + Must(!inCall); // see AsyncJob::canBeCalled + + inCall = call; + debugs(inCall->debugSection, inCall->debugLevel, + typeName << " status in:" << status()); +} + +void AsyncJob::callException(const TextException &e) +{ + // we must be called asynchronously and hence, the caller must lock us + Must(cbdataReferenceValid(toCbdata())); mustStop("exception"); } @@ -122,11 +126,9 @@ void AsyncJob::callEnd() { if (done()) { - debugs(93, 5, typeName << "::" << inCall << " ends job " << - status()); + debugs(93, 5, *inCall << " ends job" << status()); - const char *inCallSaved = inCall; - const char *typeNameSaved = typeName; + AsyncCall *inCallSaved = inCall; void *thisSaved = this; swanSong(); @@ -134,12 +136,12 @@ delete this; // this is the only place where the object is deleted // careful: this object does not exist any more - debugs(93, 6, HERE << typeNameSaved << "::" << inCallSaved << - " ended " << thisSaved); + debugs(93, 6, HERE << *inCallSaved << " ended " << thisSaved); return; } - debugs(93, 6, typeName << "::" << inCall << " ended" << status()); + debugs(inCall->debugSection, inCall->debugLevel, + typeName << " status out:" << status()); inCall = NULL; } @@ -154,8 +156,59 @@ buf.Printf("Stopped, reason:"); buf.Printf(stopReason); } - buf.Printf(" async%d]", id); + buf.Printf(" job%d]", id); buf.terminate(); return buf.content(); } + + +/* JobDialer */ + +JobDialer::JobDialer(AsyncJob *aJob): job(aJob), lock(NULL) +{ + lock = cbdataReference(job->toCbdata()); +} + +JobDialer::JobDialer(const JobDialer &d): CallDialer(d), + job(d.job), lock(d.lock) +{ + assert(!call); // otherwise, two dialers will share the same call + cbdataReference(lock); +} + +JobDialer::~JobDialer(){ + // TODO: assert(!call) and set dialer.call to NULL in AsyncCall kids + // TODO: move this->call manipulation logic from AsyncCall kids to call? + cbdataReferenceDone(lock); +} + + +bool +JobDialer::canDial() +{ + assert(call); + if (!cbdataReferenceValid(lock)) + return call->cancel("job is gone"); + + return job->canBeCalled(call); +} + +void +JobDialer::dial() +{ + assert(cbdataReferenceValid(lock)); // canDial() checks for this + + job->callStart(call); + + try { + doDial(); + } + catch (const TextException &e) { + debugs(call->debugSection, 3, + HERE << call->name << " threw exception: " << e.message); + job->callException(e); + } + + job->callEnd(); // may delete job +} Index: squid3/src/ICAP/AsyncJob.h =================================================================== RCS file: /cvsroot/squid-sf//squid3/src/ICAP/AsyncJob.h,v retrieving revision 1.3.14.7 retrieving revision 1.3.14.8 diff -u -r1.3.14.7 -r1.3.14.8 --- squid3/src/ICAP/AsyncJob.h 17 Nov 2007 18:01:54 -0000 1.3.14.7 +++ squid3/src/ICAP/AsyncJob.h 5 Dec 2007 04:43:10 -0000 1.3.14.8 @@ -1,39 +1,16 @@ +// TODO: move src/ICAP/AsyncJob.* to src/ + + /* - * $Id: AsyncJob.h,v 1.3.14.7 2007/11/17 18:01:54 chtsanti 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. - * + * $Id: AsyncJob.h,v 1.3.14.8 2007/12/05 04:43:10 rousskov Exp $ */ #ifndef SQUID_ASYNC_JOB_H #define SQUID_ASYNC_JOB_H +#include "AsyncCall.h" + /* * AsyncJob is an API and a base for a class that implements a stand-alone * "job", "task", or "logical processing thread" which receives asynchronous @@ -82,14 +59,15 @@ public: // asynchronous call maintenance - bool callStart(const char *methodName); + bool canBeCalled(AsyncCall *call) const; + void callStart(AsyncCall *call); virtual void callException(const TextException &e); virtual void callEnd(); protected: const char *stopReason; // reason for forcing done() to be true const char *typeName; // kid (leaf) class name, for debugging - const char *inCall; // name of the asynchronous call being executed, if any + AsyncCall *inCall; // the asynchronous call being handled, if any const unsigned int id; private: @@ -97,27 +75,54 @@ }; -// call guards for all "asynchronous" note*() methods -// TODO: Move to core. +/* + * This is a base class for all job call dialers. It does all the job + * dialing logic (debugging, handling exceptions, etc.) except for calling + * the job method. The latter is not possible without templates and we + * want to keep this class simple and template-free. Thus, we add a dial() + * virtual method that the JobCallT template below will implement for us, + * calling the job. + */ +class JobDialer: public CallDialer +{ +public: + JobDialer(AsyncJob *aJob); + JobDialer(const JobDialer &d); + virtual ~JobDialer(); + + virtual bool canDial(); + void dial(); + + AsyncJob *job; + void *lock; // job's cbdata + +protected: + virtual void doDial() = 0; // actually calls the job method + +private: + // not implemented and should not be needed + JobDialer &operator =(const JobDialer &); +}; + +#include "AsyncJobCalls.h" + +template +bool +CallJob(int debugSection, int debugLevel, const char *fileName, int fileLine, + const char *callName, const Dialer &dialer) +{ + AsyncCall *call = asyncCall(debugSection, debugLevel, callName, dialer); + return ScheduleCall(fileName, fileLine, call); +} + -// asynchronous call entry: -// - open the try clause; -// - call callStart(). -#define AsyncCallEnter(method) \ - try { \ - if (!callStart(#method)) \ - return; - -// asynchronous call exit: -// - close the try clause; -// - catch exceptions; -// - let callEnd() handle transaction termination conditions -#define AsyncCallExit() \ - } \ - catch (const TextException &e) { \ - callException(e); \ - } \ - callEnd(); +#define CallJobHere(debugSection, debugLevel, job, method) \ + CallJob((debugSection), (debugLevel), __FILE__, __LINE__, #method, \ + MemFun((job), &method)) + +#define CallJobHere1(debugSection, debugLevel, job, method, arg1) \ + CallJob((debugSection), (debugLevel), __FILE__, __LINE__, #method, \ + MemFun((job), &method, (arg1))) #endif /* SQUID_ASYNC_JOB_H */