Interest in an Active Object CRTP "decorator"?

Hi Guys, I've recently been working a lot with Active Object implementations. Basically it's an instance of a class that has asynchronous operations scheduled to be executed in its own thread of execution. I'm posting (in-line) a very simple implementation of a CRTP "active object enabler". Any interest in something like this to be included in Boost? --->8-- [NOTE: untested, licensed under the Boost Software License, Copyright 2008 Dean Michael Berris] template <class Derived> struct active { private: shared_ptr<io_service> queue; shared_ptr<io_service::work> sentinel; shared_ptr<thread> lifetime_thread; protected: active() : queue(new io_service()), sentinel(new io_service::work(*queue)), lifetime_thread(new thread(bind(&io_service::run, queue))) { static_cast<Derived*>->init(); }; ~active() { sentinel.reset(); lifetime_thread->join(); static_cast<Derived*>->destroy(); }; void post(function<void()> f) { queue->post(f); }; }; --->8-- Example usage would be something like this: --->8-- struct logger : active<logger> { void init() { }; // required void destroy() { }; // required void operator() (string const & message) { active<logger>::post(bind(&logger::write, this, string(message))); } void write(string message) { cout << message << endl; } }; // ... vector<string> messages; logger log_; for_each(messages.begin(), messages.end(), ref(log_)); --->8-- Hope this helps! -- Dean Michael C. Berris Software Engineer, Friendster, Inc.

----- Original Message ----- From: "Dean Michael Berris" <mikhailberis@gmail.com> To: <boost@lists.boost.org> Sent: Saturday, November 08, 2008 4:56 PM Subject: [boost] Interest in an Active Object CRTP "decorator"?
Any interest in something like this to be included in Boost?
--->8-- [NOTE: untested, licensed under the Boost Software License, Copyright 2008 Dean Michael Berris]
template <class Derived> struct active { private: shared_ptr<io_service> queue; shared_ptr<io_service::work> sentinel; shared_ptr<thread> lifetime_thread;
protected: active() : queue(new io_service()), sentinel(new io_service::work(*queue)), lifetime_thread(new thread(bind(&io_service::run, queue))) { static_cast<Derived*>->init(); };
~active() { sentinel.reset(); lifetime_thread->join(); static_cast<Derived*>->destroy(); };
void post(function<void()> f) { queue->post(f); };
};
--->8--
Example usage would be something like this:
--->8--
struct logger : active<logger> { void init() { }; // required void destroy() { }; // required void operator() (string const & message) { active<logger>::post(bind(&logger::write, this, string(message))); } void write(string message) { cout << message << endl; } };
Why the usual constructor is not enough -- init is called after active is constructed? IMO static_cast<Derived*>(this)->destroy(); is very dangerous. You are calling a function for an object that has already been destructed, so any access to Derived data is undefined. So ath the end whay do you need CRTP? Why do you need the usual constructor is not enough -- init is called after active is constructed? Best, Vicente

Hi Vicente! On Sun, Nov 9, 2008 at 1:06 AM, vicente.botet <vicente.botet@wanadoo.fr> wrote: [snip]
Why the usual constructor is not enough -- init is called after active is constructed?
Good question. Actually, you're right. I got confused about the order of construction/destruction.
IMO static_cast<Derived*>(this)->destroy(); is very dangerous. You are calling a function for an object that has already been destructed, so any access to Derived data is undefined.
Again, you're right.
So ath the end whay do you need CRTP? Why do you need the usual constructor is not enough -- init is called after active is constructed?
It looks like CRTP is no longer needed. :-) So it will look something like this: template <class ThreadingPolicy> struct active : ThreadingPolicy { private: shared_ptr<io_service> queue; shared_ptr<io_service::work> sentinel; typename ThreadingPolicy::thread_type lifetime_thread; protected: active() : ThreadingPolicy(), queue(new io_service()), sentinel(new io_service::work(*queue)) { ThreadingPolicy::init_threading(lifetime_thread, queue); } ~active() { sentinel.reset(); ThreadingPolicy::destroy_threading(lifetime_thread); } void post(function<void()> f) { queue->post(ThreadingPolicy::wrap(f)); } }; Now the active object is no longer constrained to a single thread by choosing the correct policy. struct single_thread { protected: typedef shared_ptr<thread> thread_type; void init_threading(thread_type t, shared_ptr<io_service> q) { t.reset(new thread( bind(&io_service::run, q) ) ); } void destroy_threading(thread_type t) { t->join(); } function<void()> wrap(function<void()> f) { return f; } }; struct thread_pool { // ... }; Thanks for the insights, any interest in seeing something like this in Boost? -- Dean Michael C. Berris Software Engineer, Friendster, Inc.

Thanks for the insights, any interest in seeing something like this in Boost?
Yes - at least I'm interessted in. io_service seams to be a queue for work-items - how is it used in the case of threadpool policy (threadpools usally have also an internal queue). Oliver -- "Feel free" - 5 GB Mailbox, 50 FreeSMS/Monat ... Jetzt GMX ProMail testen: http://www.gmx.net/de/go/promail

Hi Oliver! On Mon, Nov 10, 2008 at 3:49 PM, Oliver Kowalke <k-oli@gmx.de> wrote:
Thanks for the insights, any interest in seeing something like this in Boost?
Yes - at least I'm interessted in. io_service seams to be a queue for work-items - how is it used in the case of threadpool policy (threadpools usally have also an internal queue).
I have done this before (using asio::io_service run by a pool of threads), and something like this would do it: template <unsigned N> struct thread_pool { protected: typedef shared_ptr<thread_group> thread_type; void init_threading(thread_type t, shared_ptr<io_service> q) { t.reset(new thread_group()); for (unsigned i=0; i < N; ++i) t->create_thread(bind(&io_service::run, q)); } void destroy_threading(thread_type t) { t->join_all(); }; function<void()> wrap(function<void()> f) { return f; }; }; Of course, this assumes that functions 'wrapped' would do their own synchronization (or perhaps use a strand to make sure operations that do get scheduled will execute accordingly). HTH! -- Dean Michael C. Berris Software Engineer, Friendster, Inc.
participants (3)
-
Dean Michael Berris
-
Oliver Kowalke
-
vicente.botet