/* * Copyright (C) 1996-2023 The Squid Software Foundation and contributors * * Squid software is distributed under GPLv2+ license and includes * contributions from numerous individuals and organizations. * Please see the COPYING and CONTRIBUTORS files for details. */ #ifndef SQUID_SRC_BASE_ASYNCJOBCALLS_H #define SQUID_SRC_BASE_ASYNCJOBCALLS_H #include "base/AsyncJob.h" #include "base/CbcPointer.h" #include "debug/Messages.h" #include "debug/Stream.h" /** \ingroup AsyncJobAPI * 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 requires knowing the number and type of method * parameters. Thus, we add a dial() virtual method that the MemFunT templates * below implement for us, calling the job's method with the right params. */ template class JobDialer: public CallDialer { public: typedef Job DestClass; typedef CbcPointer JobPointer; JobDialer(const JobPointer &aJob); JobDialer(const JobDialer &d); virtual bool canDial(AsyncCall &call); void dial(AsyncCall &call); JobPointer job; protected: virtual void doDial() = 0; // actually calls the job method private: // not implemented and should not be needed JobDialer &operator =(const JobDialer &); }; /// schedule an async job call using a dialer; use CallJobHere macros instead template AsyncCall::Pointer CallJob(int debugSection, int debugLevel, const char *fileName, int fileLine, const char *callName, const Dialer &dialer) { AsyncCall::Pointer call = asyncCall(debugSection, debugLevel, callName, dialer); ScheduleCall(fileName, fileLine, call); return call; } #define CallJobHere(debugSection, debugLevel, job, Class, method) \ CallJob((debugSection), (debugLevel), __FILE__, __LINE__, \ (#Class "::" #method), \ JobMemFun((job), &Class::method)) #define CallJobHere1(debugSection, debugLevel, job, Class, method, arg1) \ CallJob((debugSection), (debugLevel), __FILE__, __LINE__, \ (#Class "::" #method), \ JobMemFun((job), &Class::method, (arg1))) /// Convenience macro to create a Dialer-based job callback #define JobCallback(dbgSection, dbgLevel, Dialer, job, method) \ asyncCall((dbgSection), (dbgLevel), #method, \ Dialer(CbcPointer(job), &method)) /* * *MemFunT are member function (i.e., class method) wrappers. They store * details of a method call in an object so that the call can be delayed * and executed asynchronously. Details may include the object pointer, * the handler method pointer, and parameters. To simplify, we require * all handlers to return void and not be constant. */ /* * We need one wrapper for every supported member function arity (i.e., * number of handler arguments). The first template parameter is the class * type of the handler. That class must be an AsyncJob child. */ // Arity names are from http://en.wikipedia.org/wiki/Arity template class NullaryMemFunT: public JobDialer { public: typedef void (Job::*Method)(); explicit NullaryMemFunT(const CbcPointer &aJob, Method aMethod): JobDialer(aJob), method(aMethod) {} void print(std::ostream &os) const override { os << "()"; } public: Method method; protected: void doDial() override { ((&(*this->job))->*method)(); } }; template class UnaryMemFunT: public JobDialer { public: typedef void (Job::*Method)(Argument1); explicit UnaryMemFunT(const CbcPointer &aJob, Method aMethod, const Data &anArg1): JobDialer(aJob), method(aMethod), arg1(anArg1) {} void print(std::ostream &os) const override { os << '(' << arg1 << ')'; } public: Method method; Data arg1; protected: void doDial() override { ((&(*this->job))->*method)(arg1); } }; // ... add more as needed // Now we add global templated functions that create the member function // wrappers above. These are for convenience: it is often easier to // call a templated function than to create a templated object. template NullaryMemFunT JobMemFun(const CbcPointer &job, typename NullaryMemFunT::Method method) { return NullaryMemFunT(job, method); } template UnaryMemFunT JobMemFun(const CbcPointer &job, typename UnaryMemFunT::Method method, Argument1 arg1) { return UnaryMemFunT(job, method, arg1); } // inlined methods template JobDialer::JobDialer(const JobPointer &aJob): job(aJob) { } template JobDialer::JobDialer(const JobDialer &d): CallDialer(d), job(d.job) { } template bool JobDialer::canDial(AsyncCall &call) { if (!job) return call.cancel("job gone"); return job->canBeCalled(call); } template void JobDialer::dial(AsyncCall &call) { job->callStart(call); try { doDial(); } catch (const std::exception &e) { debugs(call.debugSection, 3, call.name << " threw exception: " << e.what()); if (!job) { debugs(call.debugSection, Critical(70), "ERROR: Squid BUG: Job invalidated during " << call.name << " that threw exception: " << e.what()); return; // see also: bug 4981, commit e3b6f15, and XXX in Http::Stream class description } job->callException(e); } if (!job) { debugs(call.debugSection, Critical(71), "ERROR: Squid BUG: Job invalidated during " << call.name); return; } job->callEnd(); // may delete job } #endif /* SQUID_SRC_BASE_ASYNCJOBCALLS_H */