#ifndef __PROCESS_EXECUTOR_HPP__ #define __PROCESS_EXECUTOR_HPP__ #include #include #include #include #include namespace process { // Underlying "process" which handles invoking actual callbacks // created through an Executor. class ExecutorProcess : public Process { private: friend class Executor; ExecutorProcess() : ProcessBase(ID::generate("__executor__")) {} virtual ~ExecutorProcess() {} // Not copyable, not assignable. ExecutorProcess(const ExecutorProcess&); ExecutorProcess& operator = (const ExecutorProcess&); // No arg invoke. void invoke(const std::tr1::function& f) { f(); } // Args invoke. #define TEMPLATE(Z, N, DATA) \ template \ void CAT(invoke, N)( \ const std::tr1::function& f, \ ENUM_BINARY_PARAMS(N, A, a)) \ { \ f(ENUM_PARAMS(N, a)); \ } REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9. #undef TEMPLATE }; // Provides an abstraction that can take a standard function object // and convert it to a 'Deferred'. Each converted function object will // get invoked serially with respect to one another. class Executor { public: Executor() { spawn(process); } ~Executor() { terminate(process); wait(process); } void stop() { terminate(process); // TODO(benh): Note that this doesn't wait because that could // cause a deadlock ... thus, the semantics here are that no more // dispatches will occur after this function returns but one may // be occuring concurrently. } // We can't easily use 'std::tr1::_Placeholder' when doing macro // expansion via ENUM_BINARY_PARAMS because compilers don't like it // when you try and concatenate '<' 'N' '>'. Thus, we typedef them. private: #define TEMPLATE(Z, N, DATA) \ typedef std::tr1::_Placeholder _ ## N; REPEAT(10, TEMPLATE, _) #undef TEMPLATE public: // We provide wrappers for all standard function objects. Deferred defer( const std::tr1::function& f) { return Deferred( std::tr1::bind( &Executor::dispatcher, process.self(), f)); } #define TEMPLATE(Z, N, DATA) \ template \ Deferred defer( \ const std::tr1::function& f) \ { \ return Deferred( \ std::tr1::bind( \ &Executor::CAT(dispatcher, N), \ process.self(), f, \ ENUM_BINARY_PARAMS(N, _, () INTERCEPT))); \ } REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9. #undef TEMPLATE // Unfortunately, it is currently difficult to "forward" type // information from one result to another, so we must explicilty // define wrappers for all std::tr1::bind results. First we start // with the non-member std::tr1::bind results. Deferred defer( const std::tr1::_Bind& b) { return defer(std::tr1::function(b)); } #define TEMPLATE(Z, N, DATA) \ template \ Deferred defer( \ const std::tr1::_Bind< \ void(*(ENUM_PARAMS(N, _))) \ (ENUM_PARAMS(N, A))>& b) \ { \ return defer(std::tr1::function(b)); \ } REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9. #undef TEMPLATE // Now the member std::tr1::bind results: // 1. Non-const member (function), non-const pointer (receiver). // 2. Const member, non-const pointer. // 3. Const member, const pointer. // 4. Non-const member, non-const reference. // 5. Const member, non-const reference. // 6. Const member, const reference. // 7. Non-const member, value. // 8. Const member, value. #define TEMPLATE(Z, N, DATA) \ template \ Deferred defer( \ const std::tr1::_Bind \ (T* ENUM_TRAILING_PARAMS(N, _))>& b) \ { \ return defer(std::tr1::function(b)); \ } \ \ template \ Deferred defer( \ const std::tr1::_Bind \ (T* ENUM_TRAILING_PARAMS(N, _))>& b) \ { \ return defer(std::tr1::function(b)); \ } \ \ template \ Deferred defer( \ const std::tr1::_Bind \ (const T* ENUM_TRAILING_PARAMS(N, _))>& b) \ { \ return defer(std::tr1::function(b)); \ } \ \ template \ Deferred defer( \ const std::tr1::_Bind \ (std::tr1::reference_wrapper ENUM_TRAILING_PARAMS(N, _))>& b) \ { \ return defer(std::tr1::function(b)); \ } \ \ template \ Deferred defer( \ const std::tr1::_Bind \ (std::tr1::reference_wrapper ENUM_TRAILING_PARAMS(N, _))>& b) \ { \ return defer(std::tr1::function(b)); \ } \ \ template \ Deferred defer( \ const std::tr1::_Bind \ (std::tr1::reference_wrapper ENUM_TRAILING_PARAMS(N, _))>& b) \ { \ return defer(std::tr1::function(b)); \ } \ \ template \ Deferred defer( \ const std::tr1::_Bind \ (T ENUM_TRAILING_PARAMS(N, _))>& b) \ { \ return defer(std::tr1::function(b)); \ } \ \ template \ Deferred defer( \ const std::tr1::_Bind \ (T ENUM_TRAILING_PARAMS(N, _))>& b) \ { \ return defer(std::tr1::function(b)); \ } REPEAT(11, TEMPLATE, _) // No args and args A0 -> A9. #undef TEMPLATE private: // Not copyable, not assignable. Executor(const Executor&); Executor& operator = (const Executor&); static void dispatcher( const PID& pid, const std::tr1::function& f) { // TODO(benh): Why not just use internal::dispatch? dispatch(pid, &ExecutorProcess::invoke, f); } #define TEMPLATE(Z, N, DATA) \ template \ static void CAT(dispatcher, N)( \ const PID& pid, \ const std::tr1::function& f, \ ENUM_BINARY_PARAMS(N, A, a)) \ { \ dispatch( \ pid, \ &ExecutorProcess::CAT(invoke, N), \ f, ENUM_PARAMS(N, a)); \ } REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9. #undef TEMPLATE ExecutorProcess process; }; // Per thread executor pointer. The extra level of indirection from // _executor_ to __executor__ is used in order to take advantage of // the ThreadLocal operators without needing the extra dereference as // well as lazily construct the actual executor. extern ThreadLocal* _executor_; #define __executor__ \ (*_executor_ == NULL ? *_executor_ = new Executor() : *_executor_) } // namespace process { #endif // __PROCESS_EXECUTOR_HPP__