Commit 394d641e authored by Dominik Charousset's avatar Dominik Charousset

refactored singleton handling

singletons use a `static create_singleton`, `initialize`,
`dispose`, and `destroy` interface to minimize code in `singleton_manager`
parent 9c00c1ae
......@@ -294,3 +294,4 @@ src/actor_addressing.cpp
cppa/weak_ptr_anchor.hpp
src/weak_ptr_anchor.cpp
cppa/actor_companion_mixin.hpp
cppa/detail/singleton_mixin.hpp
......@@ -42,9 +42,15 @@
#include "cppa/attachable.hpp"
#include "cppa/util/shared_spinlock.hpp"
#include "cppa/detail/singleton_mixin.hpp"
namespace cppa { namespace detail {
class actor_registry {
class singleton_manager;
class actor_registry : public singleton_mixin<actor_registry> {
friend class singleton_mixin<actor_registry>;
public:
......@@ -55,8 +61,6 @@ class actor_registry {
*/
typedef std::pair<actor_ptr, std::uint32_t> value_type;
actor_registry();
/**
* @brief Returns the {nullptr, invalid_exit_reason}.
*/
......@@ -98,6 +102,8 @@ class actor_registry {
mutable util::shared_spinlock m_instances_mtx;
entries m_entries;
actor_registry();
};
} } // namespace cppa::detail
......
......@@ -34,13 +34,15 @@
#include <map>
#include <string>
#include "cppa/detail/singleton_mixin.hpp"
namespace cppa { namespace detail {
class decorated_names_map {
class decorated_names_map : public singleton_mixin<decorated_names_map> {
public:
friend class singleton_mixin<decorated_names_map>;
decorated_names_map();
public:
// returns either a decorated version of @p demangled_name or
// @p demangled_name itself
......@@ -48,6 +50,8 @@ class decorated_names_map {
private:
decorated_names_map();
std::map<std::string, std::string> m_map;
};
......
......@@ -32,16 +32,21 @@
#define CPPA_EMPTY_TUPLE_HPP
#include "cppa/detail/abstract_tuple.hpp"
#include "cppa/detail/singleton_mixin.hpp"
namespace cppa { namespace detail {
class empty_tuple : public abstract_tuple {
class empty_tuple : public singleton_mixin<empty_tuple,abstract_tuple> {
friend class singleton_manager;
friend class singleton_mixin<empty_tuple,abstract_tuple>;
typedef singleton_mixin<empty_tuple,abstract_tuple> super;
public:
using abstract_tuple::const_iterator;
empty_tuple();
size_t size() const;
void* mutable_at(size_t);
abstract_tuple* copy() const;
......@@ -50,6 +55,13 @@ class empty_tuple : public abstract_tuple {
const uniform_type_info* type_at(size_t) const;
const std::type_info* type_token() const;
private:
empty_tuple();
inline void initialize() /*override*/ { ref(); }
inline void destroy() /*override*/ { deref(); }
};
} } // namespace cppa::detail
......
......@@ -38,13 +38,15 @@
#include "cppa/group.hpp"
#include "cppa/util/shared_spinlock.hpp"
#include "cppa/detail/singleton_mixin.hpp"
namespace cppa { namespace detail {
class group_manager {
class group_manager : public singleton_mixin<group_manager> {
public:
friend class singleton_mixin<group_manager>;
group_manager();
public:
intrusive_ptr<group> get(const std::string& module_name,
const std::string& group_identifier);
......@@ -62,6 +64,8 @@ class group_manager {
modules_map m_mmap;
std::mutex m_mmap_mtx;
group_manager();
};
} } // namespace cppa::detail
......
......@@ -54,9 +54,9 @@ namespace cppa { namespace detail {
class logging {
public:
friend class singleton_manager;
virtual ~logging();
public:
virtual void log(const char* level,
const char* class_name,
......@@ -65,14 +65,8 @@ class logging {
int line_num,
const std::string& msg ) = 0;
virtual void start() = 0;
virtual void stop() = 0;
static logging* instance();
static logging* create_singleton();
class trace_helper {
public:
......@@ -102,6 +96,18 @@ class logging {
};
protected:
virtual ~logging();
static logging* create_singleton();
virtual void initialize() = 0;
virtual void destroy() = 0;
inline void dispose() { delete this; }
};
} } // namespace cppa::detail
......
......@@ -70,7 +70,6 @@ class singleton_manager {
static actor_registry* get_actor_registry();
// created on-the-fly on a successfull call to set_scheduler()
static network::middleman* get_middleman();
static uniform_type_info_map* get_uniform_type_info_map();
......@@ -83,16 +82,42 @@ class singleton_manager {
private:
/**
* @brief Type @p T has to provide: <tt>static T* create_singleton()</tt>,
* <tt>void initialize()</tt>, <tt>void destroy()</tt>,
* and <tt>dispose()</tt>.
* The constructor of T shall be lightweigt, since more than one object
* might get constructed initially.
* <tt>dispose()</tt> is called on objects with failed CAS operation.
* <tt>initialize()</tt> is called on objects with succeeded CAS operation.
* <tt>destroy()</tt> is called during shutdown on initialized objects.
*
* Both <tt>dispose</tt> and <tt>destroy</tt> must delete the object
* eventually.
*/
template<typename T>
static void stop_and_kill(std::atomic<T*>& ptr) {
static T* lazy_get(std::atomic<T*>& ptr) {
T* result = ptr.load();
while (result == nullptr) {
auto tmp = T::create_singleton();
if (ptr.compare_exchange_weak(result, tmp)) {
tmp->initialize();
result = tmp;
}
else tmp->dispose();
}
return result;
}
template<typename T>
static void destroy(std::atomic<T*>& ptr) {
for (;;) {
auto p = ptr.load();
if (p == nullptr) {
return;
}
else if (ptr.compare_exchange_weak(p, nullptr)) {
p->stop();
delete p;
p->destroy();
ptr = nullptr;
return;
}
......
/******************************************************************************\
* ___ __ *
* /\_ \ __/\ \ *
* \//\ \ /\_\ \ \____ ___ _____ _____ __ *
* \ \ \ \/\ \ \ '__`\ /'___\/\ '__`\/\ '__`\ /'__`\ *
* \_\ \_\ \ \ \ \L\ \/\ \__/\ \ \L\ \ \ \L\ \/\ \L\.\_ *
* /\____\\ \_\ \_,__/\ \____\\ \ ,__/\ \ ,__/\ \__/.\_\ *
* \/____/ \/_/\/___/ \/____/ \ \ \/ \ \ \/ \/__/\/_/ *
* \ \_\ \ \_\ *
* \/_/ \/_/ *
* *
* Copyright (C) 2011, 2012 *
* Dominik Charousset <dominik.charousset@haw-hamburg.de> *
* *
* This file is part of libcppa. *
* libcppa is free software: you can redistribute it and/or modify it under *
* the terms of the GNU Lesser General Public License as published by the *
* Free Software Foundation, either version 3 of the License *
* or (at your option) any later version. *
* *
* libcppa 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 Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with libcppa. If not, see <http://www.gnu.org/licenses/>. *
\******************************************************************************/
#ifndef CPPA_SINGLETON_MIXIN_HPP
#define CPPA_SINGLETON_MIXIN_HPP
#include <utility>
namespace cppa { namespace detail {
class singleton_manager;
// a mixin for simple singleton classes
template<class Derived, class Base = void>
class singleton_mixin : public Base {
friend class singleton_manager;
inline static Derived* create_singleton() { return new Derived; }
inline void dispose() { delete this; }
inline void destroy() { delete this; }
inline void initialize() { }
protected:
template<typename... Args>
singleton_mixin(Args&&... args) : Base(std::forward<Args>(args)...) { }
virtual ~singleton_mixin() { }
};
template<class Derived>
class singleton_mixin<Derived, void> {
friend class singleton_manager;
inline static Derived* create_singleton() { return new Derived; }
inline void dispose() { delete this; }
inline void destroy() { delete this; }
inline void initialize() { }
protected:
virtual ~singleton_mixin() { }
};
} } // namespace cppa::detail
#endif // CPPA_SINGLETON_MIXIN_HPP
......@@ -49,9 +49,13 @@ class thread_pool_scheduler : public scheduler {
struct worker;
void start() /*override*/;
thread_pool_scheduler();
void stop() /*override*/;
thread_pool_scheduler(size_t num_worker_threads);
void initialize() /*override*/;
void destroy() /*override*/;
void enqueue(scheduled_actor* what) /*override*/;
......@@ -74,12 +78,13 @@ class thread_pool_scheduler : public scheduler {
//typedef util::single_reader_queue<abstract_scheduled_actor> job_queue;
typedef util::producer_consumer_list<scheduled_actor> job_queue;
size_t m_num_threads;
job_queue m_queue;
scheduled_actor_dummy m_dummy;
std::thread m_supervisor;
static void worker_loop(worker*);
static void supervisor_loop(job_queue*, scheduled_actor*);
static void supervisor_loop(job_queue*, scheduled_actor*, size_t);
actor_ptr spawn_impl(scheduled_actor_ptr what);
......
......@@ -35,6 +35,7 @@
#include <string>
#include <utility> // std::pair
#include "cppa/detail/singleton_mixin.hpp"
#include "cppa/detail/default_uniform_type_info_impl.hpp"
namespace cppa { class uniform_type_info; }
......@@ -44,9 +45,10 @@ namespace cppa { namespace detail {
class uniform_type_info_map_helper;
// note: this class is implemented in uniform_type_info.cpp
class uniform_type_info_map {
class uniform_type_info_map : public singleton_mixin<uniform_type_info_map> {
friend class uniform_type_info_map_helper;
friend class singleton_mixin<uniform_type_info_map>;
public:
......@@ -54,10 +56,6 @@ class uniform_type_info_map {
typedef std::map<std::string, uniform_type_info*> uti_map_type;
typedef std::map<int, std::pair<set_type, set_type> > int_map_type;
uniform_type_info_map();
~uniform_type_info_map();
inline const int_map_type& int_names() const {
return m_ints;
}
......@@ -82,6 +80,10 @@ class uniform_type_info_map {
// maps sizeof(-integer_type-) to { signed-names-set, unsigned-names-set }
int_map_type m_ints;
uniform_type_info_map();
~uniform_type_info_map();
};
} } // namespace cppa::detail
......
......@@ -73,12 +73,13 @@ class middleman {
protected:
virtual void stop() = 0;
virtual void start() = 0;
virtual void destroy() = 0;
virtual void initialize() = 0;
private:
static middleman* create_singleton();
inline void dispose() { delete this; }
};
......
......@@ -59,14 +59,15 @@ namespace detail {
// forwards self_type as actor_ptr, otherwise equal to std::forward
template<typename T>
struct spawn_fwd_ {
static inline T&& _(T&& arg) { return std::move(arg); }
static inline T& _(T& arg) { return arg; }
static inline const T& _(const T& arg) { return arg; }
static inline T&& _(T&& arg) { return std::move(arg); }
static inline T& _(T& arg) { return arg; }
static inline const T& _(const T& arg) { return arg; }
};
template<>
struct spawn_fwd_<self_type> {
static inline actor_ptr _(const self_type& s) { return s.get(); }
static inline actor_ptr _(const self_type& s) { return s.get(); }
};
class singleton_manager;
} // namespace detail
/**
......@@ -78,23 +79,32 @@ class scheduler {
channel* delayed_send_helper();
friend class detail::singleton_manager;
protected:
scheduler();
public:
virtual ~scheduler();
/**
* @warning Always call scheduler::start on overriding.
* @warning Always call scheduler::initialize on overriding.
*/
virtual void start();
virtual void initialize();
/**
* @warning Always call scheduler::stop on overriding.
* @warning Always call scheduler::destroy on overriding.
*/
virtual void stop();
virtual void destroy();
private:
static scheduler* create_singleton();
inline void dispose() { delete this; }
public:
virtual void enqueue(scheduled_actor*) = 0;
......@@ -225,8 +235,13 @@ class scheduler {
void set_scheduler(scheduler* sched);
/**
* @brief Gets the actual used scheduler implementation.
* @returns The active scheduler (usually default constructed).
* @brief Sets a thread pool scheduler with @p num_threads worker threads.
* @throws std::runtime_error if there's already a scheduler defined.
*/
void set_default_scheduler(size_t num_threads);
/**
* @brief Returns the currently running scheduler.
*/
scheduler* get_scheduler();
......
......@@ -33,8 +33,7 @@
namespace cppa { namespace detail {
empty_tuple::empty_tuple() : abstract_tuple(tuple_impl_info::statically_typed) {
}
empty_tuple::empty_tuple() : super(tuple_impl_info::statically_typed) { }
size_t empty_tuple::size() const {
return 0;
......
......@@ -67,16 +67,17 @@ class logging_impl : public logging {
public:
void start() {
void initialize() {
m_thread = thread([this] { (*this)(); });
log("TRACE ", "logging", "run", __FILE__, 31337, "ENTRY");
}
void stop() {
void destroy() {
log("TRACE ", "logging", "run", __FILE__, 31337, "EXIT");
// an empty string means: shut down
m_queue.push_back(new log_event{0, ""});
m_thread.join();
delete this;
}
void operator()() {
......
......@@ -439,7 +439,7 @@ class middleman_impl : public abstract_middleman {
protected:
void start() {
void initialize() {
int pipefds[2];
if (pipe(pipefds) != 0) { CPPA_CRITICAL("cannot create pipe"); }
m_pipe_read = pipefds[0];
......@@ -449,7 +449,7 @@ class middleman_impl : public abstract_middleman {
m_thread = thread([this] { middleman_loop(this); });
}
void stop() {
void destroy() {
run_later([this] {
CPPA_LOG_TRACE("lambda from middleman_impl::stop");
this->m_done = true;
......@@ -458,6 +458,7 @@ class middleman_impl : public abstract_middleman {
m_thread.join();
close(m_pipe_read);
close(m_pipe_write);
delete this;
}
private:
......
......@@ -220,12 +220,13 @@ void scheduler_helper::time_emitter(scheduler_helper::ptr_type m_self) {
scheduler::scheduler() : m_helper(new scheduler_helper) {
}
void scheduler::start() {
void scheduler::initialize() {
m_helper->start();
}
void scheduler::stop() {
void scheduler::destroy() {
m_helper->stop();
delete this;
}
scheduler::~scheduler() {
......@@ -254,19 +255,16 @@ void set_scheduler(scheduler* sched) {
}
}
void set_default_scheduler(size_t num_threads) {
set_scheduler(new detail::thread_pool_scheduler(num_threads));
}
scheduler* get_scheduler() {
scheduler* result = detail::singleton_manager::get_scheduler();
if (result == nullptr) {
result = new detail::thread_pool_scheduler;
try {
set_scheduler(result);
}
catch (std::runtime_error&) {
delete result;
return detail::singleton_manager::get_scheduler();
}
}
return result;
}
scheduler* scheduler::create_singleton() {
return new detail::thread_pool_scheduler;
}
} // namespace cppa
......@@ -49,16 +49,11 @@
#include "cppa/detail/thread_pool_scheduler.hpp"
#include "cppa/detail/uniform_type_info_map.hpp"
using std::cout;
using std::endl;
namespace cppa { void shutdown() { detail::singleton_manager::shutdown(); } }
namespace {
using namespace cppa;
using namespace cppa::detail;
//volatile uniform_type_info_map* s_uniform_type_info_map = 0;
namespace cppa { namespace detail {
namespace {
std::atomic<uniform_type_info_map*> s_uniform_type_info_map;
std::atomic<decorated_names_map*> s_decorated_names_map;
......@@ -69,55 +64,27 @@ std::atomic<empty_tuple*> s_empty_tuple;
std::atomic<scheduler*> s_scheduler;
std::atomic<logging*> s_logger;
template<typename T>
T* lazy_get(std::atomic<T*>& ptr) {
T* result = ptr.load(std::memory_order_seq_cst);
if (result == nullptr) {
auto tmp = new T;
if (ptr.compare_exchange_strong(result, tmp, std::memory_order_seq_cst)) {
return tmp;
}
else {
delete tmp;
return lazy_get(ptr);
}
}
return result;
}
} // namespace <anonymous>
namespace cppa { void shutdown() { singleton_manager::shutdown(); } }
namespace cppa { namespace detail {
void singleton_manager::shutdown() {
CPPA_LOGF_TRACE("");
CPPA_LOGF_DEBUG("prepare to shutdown");
if (self.unchecked() != nullptr) {
try { self.unchecked()->quit(exit_reason::normal); }
catch (actor_exited&) { }
}
auto rptr = s_actor_registry.load();
if (rptr) {
rptr->await_running_count_equal(0);
}
stop_and_kill(s_scheduler);
stop_and_kill(s_middleman);
if (rptr) rptr->await_running_count_equal(0);
destroy(s_scheduler);
destroy(s_middleman);
std::atomic_thread_fence(std::memory_order_seq_cst);
// it's safe now to delete all other singletons now
delete s_actor_registry.load();
s_actor_registry = nullptr;
delete s_group_manager.load();
s_group_manager = nullptr;
auto et = s_empty_tuple.load();
if (et) et->deref();
s_empty_tuple = nullptr;
delete s_uniform_type_info_map.load();
s_uniform_type_info_map = nullptr;
delete s_decorated_names_map.load();
s_decorated_names_map = nullptr;
// last but not least: kill logger
stop_and_kill(s_logger);
destroy(s_actor_registry);
destroy(s_group_manager);
destroy(s_empty_tuple);
destroy(s_uniform_type_info_map);
destroy(s_decorated_names_map);
destroy(s_logger);
}
actor_registry* singleton_manager::get_actor_registry() {
......@@ -133,7 +100,7 @@ group_manager* singleton_manager::get_group_manager() {
}
scheduler* singleton_manager::get_scheduler() {
return s_scheduler.load();
return lazy_get(s_scheduler);
}
decorated_names_map* singleton_manager::get_decorated_names_map() {
......@@ -141,62 +108,27 @@ decorated_names_map* singleton_manager::get_decorated_names_map() {
}
logging* singleton_manager::get_logger() {
auto result = s_logger.load();
if (result == nullptr) {
auto tmp = logging::create_singleton();
if (s_logger.compare_exchange_weak(result, tmp) == false) {
delete tmp;
}
else {
result = tmp;
result->start();
}
}
return result;
return lazy_get(s_logger);
}
bool singleton_manager::set_scheduler(scheduler* ptr) {
scheduler* expected = nullptr;
if (s_scheduler.compare_exchange_weak(expected, ptr)) {
ptr->start();
network::middleman* mm = network::middleman::create_singleton();
network::middleman* mm_expected = nullptr;
if (s_middleman.compare_exchange_weak(mm_expected, mm)) {
mm->start();
}
else delete mm;
ptr->initialize();
return true;
}
else {
delete ptr;
ptr->dispose();
return false;
}
}
network::middleman* singleton_manager::get_middleman() {
auto result = s_middleman.load();
if (result == nullptr) {
scheduler* s = new thread_pool_scheduler;
// set_scheduler sets s_network_manager
set_scheduler(s);
return get_middleman();
}
return result;
return lazy_get(s_middleman);
}
empty_tuple* singleton_manager::get_empty_tuple() {
empty_tuple* result = s_empty_tuple.load();
if (result == nullptr) {
auto tmp = new empty_tuple;
if (s_empty_tuple.compare_exchange_weak(result, tmp) == false) {
delete tmp;
}
else {
result = tmp;
result->ref();
}
}
return result;
return lazy_get(s_empty_tuple);
}
} } // namespace cppa::detail
......@@ -160,11 +160,20 @@ void thread_pool_scheduler::worker_loop(thread_pool_scheduler::worker* w) {
(*w)();
}
thread_pool_scheduler::thread_pool_scheduler() {
m_num_threads = std::max<size_t>(std::thread::hardware_concurrency() * 2, 4);
}
thread_pool_scheduler::thread_pool_scheduler(size_t num_worker_threads) {
m_num_threads = num_worker_threads;
}
void thread_pool_scheduler::supervisor_loop(job_queue* jqueue,
scheduled_actor* dummy) {
scheduled_actor* dummy,
size_t num_threads) {
std::vector<std::unique_ptr<thread_pool_scheduler::worker> > workers;
size_t num_workers = std::max<size_t>(std::thread::hardware_concurrency() * 2, 4);
for (size_t i = 0; i < num_workers; ++i) {
for (size_t i = 0; i < num_threads; ++i) {
workers.emplace_back(new worker(jqueue, dummy));
workers.back()->start();
}
......@@ -174,13 +183,13 @@ void thread_pool_scheduler::supervisor_loop(job_queue* jqueue,
}
}
void thread_pool_scheduler::start() {
void thread_pool_scheduler::initialize() {
m_supervisor = std::thread(&thread_pool_scheduler::supervisor_loop,
&m_queue, &m_dummy);
super::start();
&m_queue, &m_dummy, m_num_threads);
super::initialize();
}
void thread_pool_scheduler::stop() {
void thread_pool_scheduler::destroy() {
m_queue.push_back(&m_dummy);
m_supervisor.join();
// make sure job queue is empty, because destructor of m_queue would
......@@ -195,7 +204,7 @@ void thread_pool_scheduler::stop() {
}
ptr = m_queue.try_pop();
}
super::stop();
super::destroy();
}
void thread_pool_scheduler::enqueue(scheduled_actor* what) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment