<commit_before>#include <boost/program_options.hpp>
#include <boost/optional.hpp>
#include <iostream>
#include <boost/thread.hpp>
#include <silicium/http/receive_request.hpp>
#include <silicium/asio/tcp_acceptor.hpp>
#include <silicium/asio/writing_observable.hpp>
#include <silicium/observable/spawn_coroutine.hpp>
#include <silicium/observable/spawn_observable.hpp>
#include <silicium/sink/iterator_sink.hpp>
#include <silicium/http/generate_response.hpp>
#include <silicium/observable/total_consumer.hpp>
#include <silicium/boost_threading.hpp>
#include <silicium/run_process.hpp>
#include <functional>
#include "server/find_cmake.hpp"
#include "server/find_gcc.hpp"
#include "server/find_executable.hpp"
#include "server/cmake.hpp"

namespace
{
	template <class AsyncWriteStream, class YieldContext, class Status, class StatusText>
	void quick_final_response(AsyncWriteStream &client, YieldContext &&yield, Status &&status, StatusText &&status_text, std::string const &content)
	{
		std::vector<char> response;
		{
			auto response_writer = Si::make_container_sink(response);
			Si::http::generate_status_line(response_writer, "HTTP/1.0", std::forward<Status>(status), std::forward<StatusText>(status_text));
			Si::http::generate_header(response_writer, "Content-Length", boost::lexical_cast<Si::noexcept_string>(content.size()));
			Si::append(response_writer, "\r\n");
			Si::append(response_writer, content);
		}

		//you can handle the error if you want
		boost::system::error_code error = Si::asio::write(client, Si::make_memory_range(response), yield);

		//ignore shutdown failures, they do not matter here
		client.shutdown(boost::asio::ip::tcp::socket::shutdown_both, error);
	}

	struct notification
	{
	};

	struct notification_server
	{
		typedef notification element_type;

		notification_server(boost::asio::io_service &io, boost::asio::ip::tcp::endpoint endpoint, Si::noexcept_string secret)
			: m_server(
				Si::erase_unique(
					Si::transform(
						Si::asio::make_tcp_acceptor(boost::asio::ip::tcp::acceptor(io, endpoint)),
						[this](Si::asio::tcp_acceptor_result maybe_client) -> Si::nothing
						{
							auto client = maybe_client.get();
							Si::spawn_coroutine([this, client](Si::spawn_context yield)
							{
								serve_client(*client, yield);
							});
							return{};
						}
					)
				)
			)
			, m_is_running(false)
			, m_secret(std::move(secret))
		{
		}

		void async_get_one(Si::ptr_observer<Si::observer<element_type>> observer)
		{
			if (!m_is_running)
			{
				m_server.start();
				m_is_running = true;
			}
			return Si::visit<void>(
				m_observer_or_notification,
				[this, observer](Si::observer<element_type> * &my_observer)
				{
					assert(!my_observer);
					my_observer = observer.get();
				},
				[this, observer](notification)
				{
					m_observer_or_notification = static_cast<Si::observer<element_type> *>(nullptr);
					observer.got_element(notification());
				}
			);
		}

	private:

		Si::total_consumer<Si::unique_observable<Si::nothing>> m_server;
		bool m_is_running;
		Si::noexcept_string m_secret;
		Si::fast_variant<Si::observer<element_type> *, notification> m_observer_or_notification;

		template <class YieldContext>
		void serve_client(boost::asio::ip::tcp::socket &client, YieldContext &&yield)
		{
			Si::error_or<boost::optional<Si::http::request>> maybe_request = Si::http::receive_request(client, yield);
			if (maybe_request.is_error())
			{
				std::cerr << client.remote_endpoint().address() << ": " << maybe_request.error() << '\n';
				return;
			}

			if (!maybe_request.get())
			{
				return;
			}

			Si::http::request const &request = *maybe_request.get();
			if (std::string::npos == request.path.find(m_secret))
			{
				quick_final_response(client, yield, "403", "Forbidden", "the path does not contain the correct secret");
				return;
			}

			Si::visit<void>(
				m_observer_or_notification,
				[](Si::observer<element_type> * &observer)
				{
					Si::exchange(observer, nullptr)->got_element(notification());
				},
				[](notification const &)
				{
				}
			);

			quick_final_response(client, yield, "200", "OK", "the server has been successfully notified");
		}
	};

	struct options
	{
		std::string repository;
		boost::uint16_t port;
		Si::noexcept_string secret;
	};

	boost::optional<options> parse_options(int argc, char **argv)
	{
		options result;
		result.port = 8080;

		boost::program_options::options_description desc("Allowed options");
		desc.add_options()
			("help", "produce help message")
			("repository,r", boost::program_options::value(&result.repository), "the URI for git cloning the code")
			("port,p", boost::program_options::value(&result.port), "port to listen on for POSTed push notifications")
			("secret,s", boost::program_options::value(&result.secret), "a string that needs to be in the query for the notification to be accepted")
		;

		boost::program_options::positional_options_description positional;
		positional.add("repository", 1);
		positional.add("port", 1);
		positional.add("secret", 1);
		boost::program_options::variables_map vm;
		try
		{
			boost::program_options::store(boost::program_options::command_line_parser(argc, argv).options(desc).positional(positional).run(), vm);
		}
		catch (boost::program_options::error const &ex)
		{
			std::cerr
				<< ex.what() << '\n'
				<< desc << "\n";
			return boost::none;
		}

		boost::program_options::notify(vm);

		if (vm.count("help"))
		{
		    std::cerr << desc << "\n";
			return boost::none;
		}

		if (result.repository.empty())
		{
			std::cerr << "Missing option value --repository\n";
			std::cerr << desc << "\n";
			return boost::none;
		}

		return std::move(result);
	}

	enum class build_result
	{
		success,
		failure,
		missing_dependency
	};

	Si::error_or<boost::optional<boost::filesystem::path>> find_git()
	{
		return buildserver::find_executable_unix("git", {});
	}

	void git_clone(std::string const &repository, boost::filesystem::path const &destination, boost::filesystem::path const &git_exe)
	{
		Si::process_parameters parameters;
		parameters.executable = git_exe;
		parameters.current_path = destination.parent_path();
		parameters.arguments = {"clone", repository, destination.string()};
		if (Si::run_process(parameters) != 0)
		{
			throw std::runtime_error("git-clone failed");
		}
	}

	build_result build(std::string const &repository, boost::filesystem::path const &workspace)
	{
		boost::optional<boost::filesystem::path> maybe_git = find_git().get();
		if (!maybe_git)
		{
			return build_result::missing_dependency;
		}

		boost::optional<boost::filesystem::path> maybe_cmake = buildserver::find_cmake().get();
		if (!maybe_cmake)
		{
			return build_result::missing_dependency;
		}

		boost::filesystem::path const source = workspace / "source.git";
		git_clone(repository, source, *maybe_git);

		boost::filesystem::path const build = workspace / "build";
		boost::filesystem::create_directories(build);

		buildserver::cmake_exe cmake(*maybe_cmake);
		boost::system::error_code error = cmake.generate(source, build, {});
		if (error)
		{
			boost::throw_exception(boost::system::system_error(error));
		}

		error = cmake.build(build, boost::thread::hardware_concurrency());
		if (error)
		{
			boost::throw_exception(boost::system::system_error(error));
		}

		return build_result::success;
	}
}

namespace Si
{
	template <class Element, class ThreadingAPI>
	struct thread_observable2
	{
		typedef Element element_type;

		explicit thread_observable2(std::function<element_type ()> action)
			: m_action(std::move(action))
		{
		}

		template <class Observer>
		void async_get_one(Observer &&observer)
		{
			assert(m_action);
			auto action = std::move(m_action);
			m_worker = ThreadingAPI::launch_async([
				observer
#if SILICIUM_COMPILER_HAS_EXTENDED_CAPTURE
					= std::forward<Observer>(observer)
#endif
				,
				action
#if SILICIUM_COMPILER_HAS_EXTENDED_CAPTURE
					= std::move(action)
#endif
				]() mutable
			{
				std::forward<Observer>(observer).got_element(action());
			});
		}

	private:

		std::function<element_type ()> m_action;
		typename ThreadingAPI::template future<void>::type m_worker;
	};

	template <class ThreadingAPI, class Action>
	auto make_thread_observable2(Action &&action)
	{
		return thread_observable2<decltype(action()), ThreadingAPI>(std::forward<Action>(action));
	}

	template <class Next>
	struct posting_observable : private observer<typename Next::element_type>
	{
		typedef typename Next::element_type element_type;

		explicit posting_observable(boost::asio::io_service &io, Next next)
			: m_io(&io)
			, m_observer(nullptr)
			, m_next(std::move(next))
		{
		}

		template <class Observer>
		void async_get_one(Observer &&observer_)
		{
			m_observer = observer_.get();
			m_next.async_get_one(extend(std::forward<Observer>(observer_), observe_by_ref(static_cast<observer<element_type> &>(*this))));
		}

	private:

		boost::asio::io_service *m_io;
		observer<element_type> *m_observer;
		Next m_next;

		virtual void got_element(element_type value) SILICIUM_OVERRIDE
		{
			auto observer_ = Si::exchange(m_observer, nullptr);
			m_io->post([observer_, value = std::move(value)]() mutable
			{
				observer_->got_element(std::move(value));
			});
		}

		virtual void ended() SILICIUM_OVERRIDE
		{
			auto observer_ = Si::exchange(m_observer, nullptr);
			m_io->post([observer_]() mutable
			{
				observer_->ended();
			});
		}
	};

	template <class Next>
	auto make_posting_observable(boost::asio::io_service &io, Next &&next)
	{
		return posting_observable<typename std::decay<Next>::type>(io, std::forward<Next>(next));
	}
}

int main(int argc, char **argv)
{
	auto parsed_options = parse_options(argc, argv);
	if (!parsed_options)
	{
		return 1;
	}

	//TODO: make the workspace configurable
	boost::filesystem::path const &workspace = boost::filesystem::current_path();

	boost::asio::io_service io;
	notification_server notifications(io, boost::asio::ip::tcp::endpoint(boost::asio::ip::address_v4::any(), parsed_options->port), parsed_options->secret);
	auto all_done = Si::make_total_consumer(
		Si::transform(
			Si::ref(notifications),
			[&](boost::optional<notification> element)
			{
				assert(element);
				std::cerr << "Received a notification\n";
				try
				{
					Si::spawn_observable(
						Si::transform(
							Si::make_posting_observable(
								io,
								Si::make_thread_observable2<Si::boost_threading>([&]()
								{
									boost::filesystem::remove_all(workspace);
									boost::filesystem::create_directories(workspace);
									return build(parsed_options->repository, workspace);
								})
							),
							[](build_result result)
							{
								switch (result)
								{
								case build_result::success:
									std::cerr << "Build success\n";
									break;

								case build_result::failure:
									std::cerr << "Build failure\n";
									break;

								case build_result::missing_dependency:
									std::cerr << "Build dependency missing\n";
									break;
								}
								return Si::nothing();
							}
						)
					);
				}
				catch (std::exception const &ex)
				{
					std::cerr << "Exception: " <<  ex.what() << '\n';
				}
				return Si::nothing();
			}
		)
	);
	all_done.start();
	io.run();
}
<commit_msg>thread_observable2 calls ended() when finished<commit_after>#include <boost/program_options.hpp>
#include <boost/optional.hpp>
#include <iostream>
#include <boost/thread.hpp>
#include <silicium/http/receive_request.hpp>
#include <silicium/asio/tcp_acceptor.hpp>
#include <silicium/asio/writing_observable.hpp>
#include <silicium/observable/spawn_coroutine.hpp>
#include <silicium/observable/spawn_observable.hpp>
#include <silicium/sink/iterator_sink.hpp>
#include <silicium/http/generate_response.hpp>
#include <silicium/observable/total_consumer.hpp>
#include <silicium/boost_threading.hpp>
#include <silicium/run_process.hpp>
#include <functional>
#include "server/find_cmake.hpp"
#include "server/find_gcc.hpp"
#include "server/find_executable.hpp"
#include "server/cmake.hpp"

namespace
{
	template <class AsyncWriteStream, class YieldContext, class Status, class StatusText>
	void quick_final_response(AsyncWriteStream &client, YieldContext &&yield, Status &&status, StatusText &&status_text, std::string const &content)
	{
		std::vector<char> response;
		{
			auto response_writer = Si::make_container_sink(response);
			Si::http::generate_status_line(response_writer, "HTTP/1.0", std::forward<Status>(status), std::forward<StatusText>(status_text));
			Si::http::generate_header(response_writer, "Content-Length", boost::lexical_cast<Si::noexcept_string>(content.size()));
			Si::append(response_writer, "\r\n");
			Si::append(response_writer, content);
		}

		//you can handle the error if you want
		boost::system::error_code error = Si::asio::write(client, Si::make_memory_range(response), yield);

		//ignore shutdown failures, they do not matter here
		client.shutdown(boost::asio::ip::tcp::socket::shutdown_both, error);
	}

	struct notification
	{
	};

	template <class Observer>
	struct notification_server
	{
		typedef notification element_type;

		notification_server(boost::asio::io_service &io, boost::asio::ip::tcp::endpoint endpoint, Si::noexcept_string secret)
			: m_server(
				Si::erase_unique(
					Si::transform(
						Si::asio::make_tcp_acceptor(boost::asio::ip::tcp::acceptor(io, endpoint)),
						[this](Si::asio::tcp_acceptor_result maybe_client) -> Si::nothing
						{
							auto client = maybe_client.get();
							Si::spawn_coroutine([this, client](Si::spawn_context yield)
							{
								serve_client(*client, yield);
							});
							return{};
						}
					)
				)
			)
			, m_is_running(false)
			, m_secret(std::move(secret))
		{
		}

		void async_get_one(Observer observer)
		{
			if (!m_is_running)
			{
				m_server.start();
				m_is_running = true;
			}
			return Si::visit<void>(
				m_observer_or_notification,
				[this, &observer](Observer &my_observer) mutable
				{
					assert(!my_observer.get());
					my_observer = std::move(observer);
				},
				[this, &observer](notification)
				{
					m_observer_or_notification = Observer();
					std::move(observer).got_element(notification());
				}
			);
		}

	private:

		Si::total_consumer<Si::unique_observable<Si::nothing>> m_server;
		bool m_is_running;
		Si::noexcept_string m_secret;
		Si::fast_variant<Observer, notification> m_observer_or_notification;

		template <class YieldContext>
		void serve_client(boost::asio::ip::tcp::socket &client, YieldContext &&yield)
		{
			Si::error_or<boost::optional<Si::http::request>> maybe_request = Si::http::receive_request(client, yield);
			if (maybe_request.is_error())
			{
				std::cerr << client.remote_endpoint().address() << ": " << maybe_request.error() << '\n';
				return;
			}

			if (!maybe_request.get())
			{
				return;
			}

			Si::http::request const &request = *maybe_request.get();
			if (std::string::npos == request.path.find(m_secret))
			{
				quick_final_response(client, yield, "403", "Forbidden", "the path does not contain the correct secret");
				return;
			}

			Si::visit<void>(
				m_observer_or_notification,
				[](Observer &observer)
				{
					Si::exchange(observer, Observer()).got_element(notification());
				},
				[](notification const &)
				{
				}
			);

			quick_final_response(client, yield, "200", "OK", "the server has been successfully notified");
		}
	};

	struct options
	{
		std::string repository;
		boost::uint16_t port;
		Si::noexcept_string secret;
	};

	boost::optional<options> parse_options(int argc, char **argv)
	{
		options result;
		result.port = 8080;

		boost::program_options::options_description desc("Allowed options");
		desc.add_options()
			("help", "produce help message")
			("repository,r", boost::program_options::value(&result.repository), "the URI for git cloning the code")
			("port,p", boost::program_options::value(&result.port), "port to listen on for POSTed push notifications")
			("secret,s", boost::program_options::value(&result.secret), "a string that needs to be in the query for the notification to be accepted")
		;

		boost::program_options::positional_options_description positional;
		positional.add("repository", 1);
		positional.add("port", 1);
		positional.add("secret", 1);
		boost::program_options::variables_map vm;
		try
		{
			boost::program_options::store(boost::program_options::command_line_parser(argc, argv).options(desc).positional(positional).run(), vm);
		}
		catch (boost::program_options::error const &ex)
		{
			std::cerr
				<< ex.what() << '\n'
				<< desc << "\n";
			return boost::none;
		}

		boost::program_options::notify(vm);

		if (vm.count("help"))
		{
		    std::cerr << desc << "\n";
			return boost::none;
		}

		if (result.repository.empty())
		{
			std::cerr << "Missing option value --repository\n";
			std::cerr << desc << "\n";
			return boost::none;
		}

		return std::move(result);
	}

	enum class build_result
	{
		success,
		failure,
		missing_dependency
	};

	Si::error_or<boost::optional<boost::filesystem::path>> find_git()
	{
		return buildserver::find_executable_unix("git", {});
	}

	void git_clone(std::string const &repository, boost::filesystem::path const &destination, boost::filesystem::path const &git_exe)
	{
		Si::process_parameters parameters;
		parameters.executable = git_exe;
		parameters.current_path = destination.parent_path();
		parameters.arguments = {"clone", repository, destination.string()};
		if (Si::run_process(parameters) != 0)
		{
			throw std::runtime_error("git-clone failed");
		}
	}

	build_result build(std::string const &repository, boost::filesystem::path const &workspace)
	{
		boost::optional<boost::filesystem::path> maybe_git = find_git().get();
		if (!maybe_git)
		{
			return build_result::missing_dependency;
		}

		boost::optional<boost::filesystem::path> maybe_cmake = buildserver::find_cmake().get();
		if (!maybe_cmake)
		{
			return build_result::missing_dependency;
		}

		boost::filesystem::path const source = workspace / "source.git";
		git_clone(repository, source, *maybe_git);

		boost::filesystem::path const build = workspace / "build";
		boost::filesystem::create_directories(build);

		buildserver::cmake_exe cmake(*maybe_cmake);
		boost::system::error_code error = cmake.generate(source, build, {});
		if (error)
		{
			boost::throw_exception(boost::system::system_error(error));
		}

		error = cmake.build(build, boost::thread::hardware_concurrency());
		if (error)
		{
			boost::throw_exception(boost::system::system_error(error));
		}

		return build_result::success;
	}
}

namespace Si
{
	template <class Element, class ThreadingAPI>
	struct thread_observable2
	{
		typedef Element element_type;

		explicit thread_observable2(std::function<element_type ()> action)
			: m_action(std::move(action))
			, m_has_finished(false)
		{
		}

		template <class Observer>
		void async_get_one(Observer &&observer)
		{
			if (m_has_finished)
			{
				return std::forward<Observer>(observer).ended();
			}
			assert(m_action);
			auto action = std::move(m_action);
			m_worker = ThreadingAPI::launch_async([
				this,
				observer
#if SILICIUM_COMPILER_HAS_EXTENDED_CAPTURE
					= std::forward<Observer>(observer)
#endif
				,
				action
#if SILICIUM_COMPILER_HAS_EXTENDED_CAPTURE
					= std::move(action)
#endif
				]() mutable
			{
				m_has_finished = true;
				std::forward<Observer>(observer).got_element(action());
			});
		}

	private:

		std::function<element_type ()> m_action;
		typename ThreadingAPI::template future<void>::type m_worker;
		bool m_has_finished;
	};

	template <class ThreadingAPI, class Action>
	auto make_thread_observable2(Action &&action)
	{
		return thread_observable2<decltype(action()), ThreadingAPI>(std::forward<Action>(action));
	}

	template <class Next>
	struct posting_observable : private observer<typename Next::element_type>
	{
		typedef typename Next::element_type element_type;

		explicit posting_observable(boost::asio::io_service &io, Next next)
			: m_io(&io)
			, m_observer(nullptr)
			, m_next(std::move(next))
		{
		}

		template <class Observer>
		void async_get_one(Observer &&observer_)
		{
			m_observer = observer_.get();
			m_next.async_get_one(extend(std::forward<Observer>(observer_), observe_by_ref(static_cast<observer<element_type> &>(*this))));
		}

	private:

		boost::asio::io_service *m_io;
		observer<element_type> *m_observer;
		Next m_next;

		virtual void got_element(element_type value) SILICIUM_OVERRIDE
		{
			auto observer_ = Si::exchange(m_observer, nullptr);
			m_io->post([observer_, value = std::move(value)]() mutable
			{
				observer_->got_element(std::move(value));
			});
		}

		virtual void ended() SILICIUM_OVERRIDE
		{
			auto observer_ = Si::exchange(m_observer, nullptr);
			m_io->post([observer_]() mutable
			{
				observer_->ended();
			});
		}
	};

	template <class Next>
	auto make_posting_observable(boost::asio::io_service &io, Next &&next)
	{
		return posting_observable<typename std::decay<Next>::type>(io, std::forward<Next>(next));
	}
}

int main(int argc, char **argv)
{
	auto parsed_options = parse_options(argc, argv);
	if (!parsed_options)
	{
		return 1;
	}

	//TODO: make the workspace configurable
	boost::filesystem::path const &workspace = boost::filesystem::current_path();

	boost::asio::io_service io;
	notification_server<Si::ptr_observer<Si::observer<notification>>> notifications(io, boost::asio::ip::tcp::endpoint(boost::asio::ip::address_v4::any(), parsed_options->port), parsed_options->secret);
	auto all_done = Si::make_total_consumer(
		Si::transform(
			Si::ref(notifications),
			[&](boost::optional<notification> element)
			{
				assert(element);
				std::cerr << "Received a notification\n";
				try
				{
					Si::spawn_observable(
						Si::transform(
							Si::make_posting_observable(
								io,
								Si::make_thread_observable2<Si::boost_threading>([&]()
								{
									boost::filesystem::remove_all(workspace);
									boost::filesystem::create_directories(workspace);
									return build(parsed_options->repository, workspace);
								})
							),
							[](build_result result)
							{
								switch (result)
								{
								case build_result::success:
									std::cerr << "Build success\n";
									break;

								case build_result::failure:
									std::cerr << "Build failure\n";
									break;

								case build_result::missing_dependency:
									std::cerr << "Build dependency missing\n";
									break;
								}
								return Si::nothing();
							}
						)
					);
				}
				catch (std::exception const &ex)
				{
					std::cerr << "Exception: " <<  ex.what() << '\n';
				}
				return Si::nothing();
			}
		)
	);
	all_done.start();
	io.run();
}
<|endoftext|>


<commit_before>// Frame.cpp

#include "Frame.h"
#include "Application.h"
#include "Puzzle.h"
#include "Canvas.h"
#include <wx/menu.h>
#include <wx/aboutdlg.h>
#include <wx/msgdlg.h>
#include <wx/sizer.h>

Frame::Frame( void ) : wxFrame( 0, wxID_ANY, "Symmetry Group Madness" ), timer( this, ID_Timer )
{
	wxMenu* gameMenu = new wxMenu();
	wxMenuItem* newGameMenuItem = new wxMenuItem( gameMenu, ID_NewGame, "New Game", "Start a new game at level 1." );
	wxMenuItem* saveGameMenuItem = new wxMenuItem( gameMenu, ID_SaveGame, "Save Game", "Save your current game to disk." );
	wxMenuItem* loadGameMenuItem = new wxMenuItem( gameMenu, ID_LoadGame, "Load Game", "Load a previously saved game from disk." );
	wxMenuItem* exitMenuItem = new wxMenuItem( gameMenu, ID_Exit, "Exit", "Exit this program." );
	gameMenu->Append( newGameMenuItem );
	gameMenu->AppendSeparator();
	gameMenu->Append( saveGameMenuItem );
	gameMenu->Append( loadGameMenuItem );
	gameMenu->AppendSeparator();
	gameMenu->Append( exitMenuItem );

	wxMenu* helpMenu = new wxMenu();
	wxMenuItem* solveMenuItem = new wxMenuItem( helpMenu, ID_Solve, "Solve", "Let the computer attempt to find a solution to the puzzle." );
	wxMenuItem* aboutMenuItem = new wxMenuItem( helpMenu, ID_About, "About", "Show the about-box." );
	helpMenu->Append( solveMenuItem );
	helpMenu->AppendSeparator();
	helpMenu->Append( aboutMenuItem );

	wxMenuBar* menuBar = new wxMenuBar();
	menuBar->Append( gameMenu, "Game" );
	menuBar->Append( helpMenu, "Help" );
	SetMenuBar( menuBar );

	wxStatusBar* statusBar = new wxStatusBar( this );
	SetStatusBar( statusBar );

	Bind( wxEVT_MENU, &Frame::OnNewGame, this, ID_NewGame );
	Bind( wxEVT_MENU, &Frame::OnSaveGame, this, ID_SaveGame );
	Bind( wxEVT_MENU, &Frame::OnLoadGame, this, ID_LoadGame );
	Bind( wxEVT_MENU, &Frame::OnSolve, this, ID_Solve );
	Bind( wxEVT_MENU, &Frame::OnExit, this, ID_Exit );
	Bind( wxEVT_MENU, &Frame::OnAbout, this, ID_About );
	Bind( wxEVT_UPDATE_UI, &Frame::OnUpdateMenuItemUI, this, ID_NewGame );
	Bind( wxEVT_UPDATE_UI, &Frame::OnUpdateMenuItemUI, this, ID_SaveGame );
	Bind( wxEVT_UPDATE_UI, &Frame::OnUpdateMenuItemUI, this, ID_LoadGame );
	Bind( wxEVT_UPDATE_UI, &Frame::OnUpdateMenuItemUI, this, ID_Solve );
	Bind( wxEVT_TIMER, &Frame::OnTimer, this, ID_Timer );

	canvas = new Canvas( this );

	wxBoxSizer* boxSizer = new wxBoxSizer( wxVERTICAL );
	boxSizer->Add( canvas, 1, wxGROW );
	SetSizer( boxSizer );

	timer.Start(1);
}

/*virtual*/ Frame::~Frame( void )
{
}

void Frame::OnExit( wxCommandEvent& event )
{
	if( wxGetApp().SetPuzzle( nullptr ) )
		Close( true );
}

void Frame::OnAbout( wxCommandEvent& event )
{
	wxAboutDialogInfo aboutDialogInfo;

	aboutDialogInfo.SetName( "Symmetry Group Madness" );
	aboutDialogInfo.SetVersion( "1.0" );
	aboutDialogInfo.SetDescription( "This program is free software and distributed under the MIT license." );
	aboutDialogInfo.SetCopyright( "Copyright (C) 2016 Spencer T. Parkin <spencertparkin@gmail.com>" );
	//aboutDialogInfo.SetWebSite( "http://spencerparkin.github.io/SymmetryGroupMadness" );

	wxAboutBox( aboutDialogInfo );
}

void Frame::OnTimer( wxTimerEvent& event )
{
	canvas->Refresh();
}

void Frame::OnSolve( wxCommandEvent& event )
{
	Puzzle* puzzle = wxGetApp().GetPuzzle();
	if( puzzle )
	{
		timer.Stop();

		if( puzzle->EnqueueSolution() )
		{
			int solutionSize = ( int )puzzle->autoRotationQueue.size();
			if( wxYES != wxMessageBox( wxString::Format( "A solution was found with %d moves.  Run solution?", solutionSize ), "Solution found!", wxICON_EXCLAMATION | wxYES_NO, this ) )
				puzzle->autoRotationQueue.clear();
		}
		else
		{
			wxMessageBox( "Failed to find a solution.  I suck.", "Solution not found.", wxICON_ERROR, this );
		}

		timer.Start(1);
	}
}

void Frame::OnNewGame( wxCommandEvent& event )
{
	if( wxGetApp().SetPuzzle( nullptr ) )
	{
		Puzzle* puzzle = new Puzzle();
		puzzle->SetupLevel(1);
		wxGetApp().SetPuzzle( puzzle );
		canvas->Refresh();
	}
}

void Frame::OnSaveGame( wxCommandEvent& event )
{
	Puzzle* puzzle = wxGetApp().GetPuzzle();
	if( puzzle )
		puzzle->Save();
}

void Frame::OnLoadGame( wxCommandEvent& event )
{
	wxGetApp().SetPuzzle( nullptr );

	Puzzle* puzzle = new Puzzle();
	if( !puzzle->Load() )
		delete puzzle;
	else
		wxGetApp().SetPuzzle( puzzle );
	canvas->Refresh();
}

void Frame::OnUpdateMenuItemUI( wxUpdateUIEvent& event )
{
	switch( event.GetId() )
	{
		case ID_NewGame:
		{
			event.Enable( true );
			break;
		}
		case ID_SaveGame:
		{
			Puzzle* puzzle = wxGetApp().GetPuzzle();
			event.Enable( ( puzzle && puzzle->modified ) ? true : false );
			break;
		}
		case ID_LoadGame:
		{
			event.Enable( true );
			break;
		}
		case ID_Solve:
		{
			Puzzle* puzzle = wxGetApp().GetPuzzle();
			event.Enable( puzzle && !puzzle->GetPermutation().IsIdentity() );
			break;
		}
	}
}

// Frame.cpp
<commit_msg>unfortunate bug<commit_after>// Frame.cpp

#include "Frame.h"
#include "Application.h"
#include "Puzzle.h"
#include "Canvas.h"
#include <wx/menu.h>
#include <wx/aboutdlg.h>
#include <wx/msgdlg.h>
#include <wx/sizer.h>

// BUG: The "percent solved" thing as not only a bit dumb, but, due probably to round-off error,
//      it is also not always accurate enough to detect when the puzzle is actually solved.  If every
//      puzzle had its permutation group encoded, then we could use that as a possibly fool-proof way
//      to know when the puzzle is in the solved state.

Frame::Frame( void ) : wxFrame( 0, wxID_ANY, "Symmetry Group Madness" ), timer( this, ID_Timer )
{
	wxMenu* gameMenu = new wxMenu();
	wxMenuItem* newGameMenuItem = new wxMenuItem( gameMenu, ID_NewGame, "New Game", "Start a new game at level 1." );
	wxMenuItem* saveGameMenuItem = new wxMenuItem( gameMenu, ID_SaveGame, "Save Game", "Save your current game to disk." );
	wxMenuItem* loadGameMenuItem = new wxMenuItem( gameMenu, ID_LoadGame, "Load Game", "Load a previously saved game from disk." );
	wxMenuItem* exitMenuItem = new wxMenuItem( gameMenu, ID_Exit, "Exit", "Exit this program." );
	gameMenu->Append( newGameMenuItem );
	gameMenu->AppendSeparator();
	gameMenu->Append( saveGameMenuItem );
	gameMenu->Append( loadGameMenuItem );
	gameMenu->AppendSeparator();
	gameMenu->Append( exitMenuItem );

	wxMenu* helpMenu = new wxMenu();
	wxMenuItem* solveMenuItem = new wxMenuItem( helpMenu, ID_Solve, "Solve", "Let the computer attempt to find a solution to the puzzle." );
	wxMenuItem* aboutMenuItem = new wxMenuItem( helpMenu, ID_About, "About", "Show the about-box." );
	helpMenu->Append( solveMenuItem );
	helpMenu->AppendSeparator();
	helpMenu->Append( aboutMenuItem );

	wxMenuBar* menuBar = new wxMenuBar();
	menuBar->Append( gameMenu, "Game" );
	menuBar->Append( helpMenu, "Help" );
	SetMenuBar( menuBar );

	wxStatusBar* statusBar = new wxStatusBar( this );
	SetStatusBar( statusBar );

	Bind( wxEVT_MENU, &Frame::OnNewGame, this, ID_NewGame );
	Bind( wxEVT_MENU, &Frame::OnSaveGame, this, ID_SaveGame );
	Bind( wxEVT_MENU, &Frame::OnLoadGame, this, ID_LoadGame );
	Bind( wxEVT_MENU, &Frame::OnSolve, this, ID_Solve );
	Bind( wxEVT_MENU, &Frame::OnExit, this, ID_Exit );
	Bind( wxEVT_MENU, &Frame::OnAbout, this, ID_About );
	Bind( wxEVT_UPDATE_UI, &Frame::OnUpdateMenuItemUI, this, ID_NewGame );
	Bind( wxEVT_UPDATE_UI, &Frame::OnUpdateMenuItemUI, this, ID_SaveGame );
	Bind( wxEVT_UPDATE_UI, &Frame::OnUpdateMenuItemUI, this, ID_LoadGame );
	Bind( wxEVT_UPDATE_UI, &Frame::OnUpdateMenuItemUI, this, ID_Solve );
	Bind( wxEVT_TIMER, &Frame::OnTimer, this, ID_Timer );

	canvas = new Canvas( this );

	wxBoxSizer* boxSizer = new wxBoxSizer( wxVERTICAL );
	boxSizer->Add( canvas, 1, wxGROW );
	SetSizer( boxSizer );

	timer.Start(1);
}

/*virtual*/ Frame::~Frame( void )
{
}

void Frame::OnExit( wxCommandEvent& event )
{
	if( wxGetApp().SetPuzzle( nullptr ) )
		Close( true );
}

void Frame::OnAbout( wxCommandEvent& event )
{
	wxAboutDialogInfo aboutDialogInfo;

	aboutDialogInfo.SetName( "Symmetry Group Madness" );
	aboutDialogInfo.SetVersion( "1.0" );
	aboutDialogInfo.SetDescription( "This program is free software and distributed under the MIT license." );
	aboutDialogInfo.SetCopyright( "Copyright (C) 2016 Spencer T. Parkin <spencertparkin@gmail.com>" );
	//aboutDialogInfo.SetWebSite( "http://spencerparkin.github.io/SymmetryGroupMadness" );

	wxAboutBox( aboutDialogInfo );
}

void Frame::OnTimer( wxTimerEvent& event )
{
	canvas->Refresh();
}

void Frame::OnSolve( wxCommandEvent& event )
{
	Puzzle* puzzle = wxGetApp().GetPuzzle();
	if( puzzle )
	{
		timer.Stop();

		if( puzzle->EnqueueSolution() )
		{
			int solutionSize = ( int )puzzle->autoRotationQueue.size();
			if( wxYES != wxMessageBox( wxString::Format( "A solution was found with %d moves.  Run solution?", solutionSize ), "Solution found!", wxICON_EXCLAMATION | wxYES_NO, this ) )
				puzzle->autoRotationQueue.clear();
		}
		else
		{
			wxMessageBox( "Failed to find a solution.  I suck.", "Solution not found.", wxICON_ERROR, this );
		}

		timer.Start(1);
	}
}

void Frame::OnNewGame( wxCommandEvent& event )
{
	if( wxGetApp().SetPuzzle( nullptr ) )
	{
		Puzzle* puzzle = new Puzzle();
		puzzle->SetupLevel(1);
		wxGetApp().SetPuzzle( puzzle );
		canvas->Refresh();
	}
}

void Frame::OnSaveGame( wxCommandEvent& event )
{
	Puzzle* puzzle = wxGetApp().GetPuzzle();
	if( puzzle )
		puzzle->Save();
}

void Frame::OnLoadGame( wxCommandEvent& event )
{
	wxGetApp().SetPuzzle( nullptr );

	Puzzle* puzzle = new Puzzle();
	if( !puzzle->Load() )
		delete puzzle;
	else
		wxGetApp().SetPuzzle( puzzle );
	canvas->Refresh();
}

void Frame::OnUpdateMenuItemUI( wxUpdateUIEvent& event )
{
	switch( event.GetId() )
	{
		case ID_NewGame:
		{
			event.Enable( true );
			break;
		}
		case ID_SaveGame:
		{
			Puzzle* puzzle = wxGetApp().GetPuzzle();
			event.Enable( ( puzzle && puzzle->modified ) ? true : false );
			break;
		}
		case ID_LoadGame:
		{
			event.Enable( true );
			break;
		}
		case ID_Solve:
		{
			Puzzle* puzzle = wxGetApp().GetPuzzle();
			event.Enable( puzzle && !puzzle->GetPermutation().IsIdentity() );
			break;
		}
	}
}

// Frame.cpp
<|endoftext|>


<commit_before>//===-- buffer_queue_test.cc ----------------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is a part of XRay, a function call tracing system.
//
//===----------------------------------------------------------------------===//
#include "xray_buffer_queue.h"
#include "gtest/gtest.h"

#include <future>
#include <system_error>
#include <unistd.h>

namespace __xray {

static constexpr size_t kSize = 4096;

TEST(BufferQueueTest, API) {
  bool Success = false;
  BufferQueue Buffers(kSize, 1, Success);
  ASSERT_TRUE(Success);
}

TEST(BufferQueueTest, GetAndRelease) {
  bool Success = false;
  BufferQueue Buffers(kSize, 1, Success);
  ASSERT_TRUE(Success);
  BufferQueue::Buffer Buf;
  ASSERT_EQ(Buffers.getBuffer(Buf), std::error_code());
  ASSERT_NE(nullptr, Buf.Buffer);
  ASSERT_EQ(Buffers.releaseBuffer(Buf), std::error_code());
  ASSERT_EQ(nullptr, Buf.Buffer);
}

TEST(BufferQueueTest, GetUntilFailed) {
  bool Success = false;
  BufferQueue Buffers(kSize, 1, Success);
  ASSERT_TRUE(Success);
  BufferQueue::Buffer Buf0;
  EXPECT_EQ(Buffers.getBuffer(Buf0), std::error_code());
  BufferQueue::Buffer Buf1;
  EXPECT_EQ(std::errc::not_enough_memory, Buffers.getBuffer(Buf1));
  EXPECT_EQ(Buffers.releaseBuffer(Buf0), std::error_code());
}

TEST(BufferQueueTest, ReleaseUnknown) {
  bool Success = false;
  BufferQueue Buffers(kSize, 1, Success);
  ASSERT_TRUE(Success);
  BufferQueue::Buffer Buf;
  Buf.Buffer = reinterpret_cast<void *>(0xdeadbeef);
  Buf.Size = kSize;
  EXPECT_EQ(std::errc::argument_out_of_domain, Buffers.releaseBuffer(Buf));
}

TEST(BufferQueueTest, ErrorsWhenFinalising) {
  bool Success = false;
  BufferQueue Buffers(kSize, 2, Success);
  ASSERT_TRUE(Success);
  BufferQueue::Buffer Buf;
  ASSERT_EQ(Buffers.getBuffer(Buf), std::error_code());
  ASSERT_NE(nullptr, Buf.Buffer);
  ASSERT_EQ(Buffers.finalize(), std::error_code());
  BufferQueue::Buffer OtherBuf;
  ASSERT_EQ(std::errc::state_not_recoverable, Buffers.getBuffer(OtherBuf));
  ASSERT_EQ(std::errc::state_not_recoverable, Buffers.finalize());
  ASSERT_EQ(Buffers.releaseBuffer(Buf), std::error_code());
}

TEST(BufferQueueTest, MultiThreaded) {
  bool Success = false;
  BufferQueue Buffers(kSize, 100, Success);
  ASSERT_TRUE(Success);
  auto F = [&] {
    BufferQueue::Buffer B;
    while (!Buffers.getBuffer(B)) {
      Buffers.releaseBuffer(B);
    }
  };
  auto T0 = std::async(std::launch::async, F);
  auto T1 = std::async(std::launch::async, F);
  auto T2 = std::async(std::launch::async, [&] {
    while (!Buffers.finalize())
      ;
  });
  F();
}

TEST(BufferQueueTest, Apply) {
  bool Success = false;
  BufferQueue Buffers(kSize, 10, Success);
  ASSERT_TRUE(Success);
  auto Count = 0;
  BufferQueue::Buffer B;
  for (int I = 0; I < 10; ++I) {
    ASSERT_FALSE(Buffers.getBuffer(B));
    ASSERT_FALSE(Buffers.releaseBuffer(B));
  }
  Buffers.apply([&](const BufferQueue::Buffer &B) { ++Count; });
  ASSERT_EQ(Count, 10);
}

} // namespace __xray
<commit_msg>[XRay] Fix gtest error code comparison. NFC.<commit_after>//===-- buffer_queue_test.cc ----------------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is a part of XRay, a function call tracing system.
//
//===----------------------------------------------------------------------===//
#include "xray_buffer_queue.h"
#include "gtest/gtest.h"

#include <future>
#include <system_error>
#include <unistd.h>

namespace __xray {

static constexpr size_t kSize = 4096;

TEST(BufferQueueTest, API) {
  bool Success = false;
  BufferQueue Buffers(kSize, 1, Success);
  ASSERT_TRUE(Success);
}

TEST(BufferQueueTest, GetAndRelease) {
  bool Success = false;
  BufferQueue Buffers(kSize, 1, Success);
  ASSERT_TRUE(Success);
  BufferQueue::Buffer Buf;
  ASSERT_EQ(Buffers.getBuffer(Buf), std::error_code());
  ASSERT_NE(nullptr, Buf.Buffer);
  ASSERT_EQ(Buffers.releaseBuffer(Buf), std::error_code());
  ASSERT_EQ(nullptr, Buf.Buffer);
}

TEST(BufferQueueTest, GetUntilFailed) {
  bool Success = false;
  BufferQueue Buffers(kSize, 1, Success);
  ASSERT_TRUE(Success);
  BufferQueue::Buffer Buf0;
  EXPECT_EQ(Buffers.getBuffer(Buf0), std::error_code());
  BufferQueue::Buffer Buf1;
  EXPECT_EQ(std::make_error_code(std::errc::not_enough_memory),
            Buffers.getBuffer(Buf1));
  EXPECT_EQ(Buffers.releaseBuffer(Buf0), std::error_code());
}

TEST(BufferQueueTest, ReleaseUnknown) {
  bool Success = false;
  BufferQueue Buffers(kSize, 1, Success);
  ASSERT_TRUE(Success);
  BufferQueue::Buffer Buf;
  Buf.Buffer = reinterpret_cast<void *>(0xdeadbeef);
  Buf.Size = kSize;
  EXPECT_EQ(std::make_error_code(std::errc::argument_out_of_domain),
            Buffers.releaseBuffer(Buf));
}

TEST(BufferQueueTest, ErrorsWhenFinalising) {
  bool Success = false;
  BufferQueue Buffers(kSize, 2, Success);
  ASSERT_TRUE(Success);
  BufferQueue::Buffer Buf;
  ASSERT_EQ(Buffers.getBuffer(Buf), std::error_code());
  ASSERT_NE(nullptr, Buf.Buffer);
  ASSERT_EQ(Buffers.finalize(), std::error_code());
  BufferQueue::Buffer OtherBuf;
  ASSERT_EQ(std::make_error_code(std::errc::state_not_recoverable),
            Buffers.getBuffer(OtherBuf));
  ASSERT_EQ(std::make_error_code(std::errc::state_not_recoverable),
            Buffers.finalize());
  ASSERT_EQ(Buffers.releaseBuffer(Buf), std::error_code());
}

TEST(BufferQueueTest, MultiThreaded) {
  bool Success = false;
  BufferQueue Buffers(kSize, 100, Success);
  ASSERT_TRUE(Success);
  auto F = [&] {
    BufferQueue::Buffer B;
    while (!Buffers.getBuffer(B)) {
      Buffers.releaseBuffer(B);
    }
  };
  auto T0 = std::async(std::launch::async, F);
  auto T1 = std::async(std::launch::async, F);
  auto T2 = std::async(std::launch::async, [&] {
    while (!Buffers.finalize())
      ;
  });
  F();
}

TEST(BufferQueueTest, Apply) {
  bool Success = false;
  BufferQueue Buffers(kSize, 10, Success);
  ASSERT_TRUE(Success);
  auto Count = 0;
  BufferQueue::Buffer B;
  for (int I = 0; I < 10; ++I) {
    ASSERT_FALSE(Buffers.getBuffer(B));
    ASSERT_FALSE(Buffers.releaseBuffer(B));
  }
  Buffers.apply([&](const BufferQueue::Buffer &B) { ++Count; });
  ASSERT_EQ(Count, 10);
}

} // namespace __xray
<|endoftext|>


<commit_before>// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include "routablefactories60.h"
#include "routingpolicyfactories.h"
#include "routablerepository.h"
#include "routingpolicyrepository.h"
#include "replymerger.h"
#include <vespa/document/util/stringutil.h>
#include <vespa/documentapi/documentapi.h>
#include <vespa/vespalib/util/exceptions.h>
#include <vespa/messagebus/error.h>
#include <sstream>
#include <cassert>

#include <vespa/log/log.h>
LOG_SETUP(".documentprotocol");

using document::DocumentTypeRepo;

namespace documentapi {

const mbus::string DocumentProtocol::NAME = "document";

DocumentProtocol::DocumentProtocol(std::shared_ptr<const DocumentTypeRepo> repo, const string &configId) :
    _routingPolicyRepository(std::make_unique<RoutingPolicyRepository>()),
    _routableRepository(std::make_unique<RoutableRepository>()),
    _repo(std::move(repo))
{
    // Prepare config string for routing policy factories.
    string cfg = (configId.empty() ? "client" : configId);

    // When adding factories to this list, please KEEP THEM ORDERED alphabetically like they are now.
    putRoutingPolicyFactory("AND", std::make_shared<RoutingPolicyFactories::AndPolicyFactory>());
    putRoutingPolicyFactory("Content", std::make_shared<RoutingPolicyFactories::ContentPolicyFactory>());
    putRoutingPolicyFactory("Storage", std::make_shared<RoutingPolicyFactories::ContentPolicyFactory>()); // TODO Vespa 8: remove
    putRoutingPolicyFactory("DocumentRouteSelector", std::make_shared<RoutingPolicyFactories::DocumentRouteSelectorPolicyFactory>(*_repo, cfg));
    putRoutingPolicyFactory("Extern", std::make_shared<RoutingPolicyFactories::ExternPolicyFactory>());
    putRoutingPolicyFactory("LoadBalancer", std::make_shared<RoutingPolicyFactories::LoadBalancerPolicyFactory>());
    putRoutingPolicyFactory("LocalService", std::make_shared<RoutingPolicyFactories::LocalServicePolicyFactory>());
    putRoutingPolicyFactory("MessageType", std::make_shared<RoutingPolicyFactories::MessageTypePolicyFactory>());
    putRoutingPolicyFactory("RoundRobin", std::make_shared<RoutingPolicyFactories::RoundRobinPolicyFactory>());
    putRoutingPolicyFactory("SubsetService", std::make_shared<RoutingPolicyFactories::SubsetServicePolicyFactory>());

    // Prepare version specifications to use when adding routable factories.
    vespalib::VersionSpecification version6(6, 221);

    std::vector<vespalib::VersionSpecification> from6  = { version6 };

    // Add 6.x serialization
    putRoutableFactory(MESSAGE_CREATEVISITOR, std::make_shared<RoutableFactories60::CreateVisitorMessageFactory>(), from6);
    putRoutableFactory(MESSAGE_DESTROYVISITOR, std::make_shared<RoutableFactories60::DestroyVisitorMessageFactory>(), from6);
    putRoutableFactory(MESSAGE_DOCUMENTLIST, std::make_shared<RoutableFactories60::DocumentListMessageFactory>(*_repo), from6);
    putRoutableFactory(MESSAGE_DOCUMENTSUMMARY, std::make_shared<RoutableFactories60::DocumentSummaryMessageFactory>(), from6);
    putRoutableFactory(MESSAGE_EMPTYBUCKETS, std::make_shared<RoutableFactories60::EmptyBucketsMessageFactory>(), from6);
    putRoutableFactory(MESSAGE_GETBUCKETLIST, std::make_shared<RoutableFactories60::GetBucketListMessageFactory>(), from6);
    putRoutableFactory(MESSAGE_GETBUCKETSTATE, std::make_shared<RoutableFactories60::GetBucketStateMessageFactory>(), from6);
    putRoutableFactory(MESSAGE_GETDOCUMENT, std::make_shared<RoutableFactories60::GetDocumentMessageFactory>(), from6);
    putRoutableFactory(MESSAGE_MAPVISITOR, std::make_shared<RoutableFactories60::MapVisitorMessageFactory>(), from6);
    putRoutableFactory(MESSAGE_PUTDOCUMENT, std::make_shared<RoutableFactories60::PutDocumentMessageFactory>(*_repo), from6);
    putRoutableFactory(MESSAGE_QUERYRESULT, std::make_shared<RoutableFactories60::QueryResultMessageFactory>(), from6);
    putRoutableFactory(MESSAGE_REMOVEDOCUMENT, std::make_shared<RoutableFactories60::RemoveDocumentMessageFactory>(), from6);
    putRoutableFactory(MESSAGE_REMOVELOCATION, std::make_shared<RoutableFactories60::RemoveLocationMessageFactory>(*_repo), from6);
    putRoutableFactory(MESSAGE_SEARCHRESULT, std::make_shared<RoutableFactories60::SearchResultMessageFactory>(), from6);
    putRoutableFactory(MESSAGE_STATBUCKET, std::make_shared<RoutableFactories60::StatBucketMessageFactory>(), from6);
    putRoutableFactory(MESSAGE_UPDATEDOCUMENT, std::make_shared<RoutableFactories60::UpdateDocumentMessageFactory>(*_repo), from6);
    putRoutableFactory(MESSAGE_VISITORINFO, std::make_shared<RoutableFactories60::VisitorInfoMessageFactory>(), from6);
    putRoutableFactory(REPLY_CREATEVISITOR, std::make_shared<RoutableFactories60::CreateVisitorReplyFactory>(), from6);
    putRoutableFactory(REPLY_DESTROYVISITOR, std::make_shared<RoutableFactories60::DestroyVisitorReplyFactory>(), from6);
    putRoutableFactory(REPLY_DOCUMENTIGNORED, std::make_shared<RoutableFactories60::DocumentIgnoredReplyFactory>(), from6);
    putRoutableFactory(REPLY_DOCUMENTLIST, std::make_shared<RoutableFactories60::DocumentListReplyFactory>(), from6);
    putRoutableFactory(REPLY_DOCUMENTSUMMARY, std::make_shared<RoutableFactories60::DocumentSummaryReplyFactory>(), from6);
    putRoutableFactory(REPLY_EMPTYBUCKETS, std::make_shared<RoutableFactories60::EmptyBucketsReplyFactory>(), from6);
    putRoutableFactory(REPLY_GETBUCKETLIST, std::make_shared<RoutableFactories60::GetBucketListReplyFactory>(), from6);
    putRoutableFactory(REPLY_GETBUCKETSTATE, std::make_shared<RoutableFactories60::GetBucketStateReplyFactory>(), from6);
    putRoutableFactory(REPLY_GETDOCUMENT, std::make_shared<RoutableFactories60::GetDocumentReplyFactory>(*_repo), from6);
    putRoutableFactory(REPLY_MAPVISITOR, std::make_shared<RoutableFactories60::MapVisitorReplyFactory>(), from6);
    putRoutableFactory(REPLY_PUTDOCUMENT, std::make_shared<RoutableFactories60::PutDocumentReplyFactory>(), from6);
    putRoutableFactory(REPLY_QUERYRESULT, std::make_shared<RoutableFactories60::QueryResultReplyFactory>(), from6);
    putRoutableFactory(REPLY_REMOVEDOCUMENT, std::make_shared<RoutableFactories60::RemoveDocumentReplyFactory>(), from6);
    putRoutableFactory(REPLY_REMOVELOCATION, std::make_shared<RoutableFactories60::RemoveLocationReplyFactory>(), from6);
    putRoutableFactory(REPLY_SEARCHRESULT, std::make_shared<RoutableFactories60::SearchResultReplyFactory>(), from6);
    putRoutableFactory(REPLY_STATBUCKET, std::make_shared<RoutableFactories60::StatBucketReplyFactory>(), from6);
    putRoutableFactory(REPLY_UPDATEDOCUMENT, std::make_shared<RoutableFactories60::UpdateDocumentReplyFactory>(), from6);
    putRoutableFactory(REPLY_VISITORINFO, std::make_shared<RoutableFactories60::VisitorInfoReplyFactory>(), from6);
    putRoutableFactory(REPLY_WRONGDISTRIBUTION, std::make_shared<RoutableFactories60::WrongDistributionReplyFactory>(), from6);
}

DocumentProtocol::~DocumentProtocol() = default;

mbus::IRoutingPolicy::UP
DocumentProtocol::createPolicy(const mbus::string &name, const mbus::string &param) const
{
    return _routingPolicyRepository->createPolicy(name, param);
}

DocumentProtocol &
DocumentProtocol::putRoutingPolicyFactory(const string &name, IRoutingPolicyFactory::SP factory)
{
    _routingPolicyRepository->putFactory(name, std::move(factory));
    return *this;
}

mbus::Blob
DocumentProtocol::encode(const vespalib::Version &version, const mbus::Routable &routable) const
{
    mbus::Blob blob(_routableRepository->encode(version, routable));
        // When valgrind reports errors of uninitialized data being written to
        // the network, it is useful to be able to see the serialized data to
        // try to identify what bits are uninitialized.
    if (LOG_WOULD_LOG(spam)) {
        std::ostringstream message;
        document::StringUtil::printAsHex(
                message, blob.data(), blob.size());
        LOG(spam, "Encoded message of protocol %s type %u using version %s serialization:\n%s",
            routable.getProtocol().c_str(), routable.getType(),
            version.toString().c_str(), message.str().c_str());
    }
    return blob;
}

mbus::Routable::UP
DocumentProtocol::decode(const vespalib::Version &version, mbus::BlobRef data) const
{
    try {
        return _routableRepository->decode(version, data);
    } catch (vespalib::Exception &e) {
        LOG(warning, "%s", e.getMessage().c_str());
        return mbus::Routable::UP();
    }
}

uint32_t
DocumentProtocol::getRoutableTypes(const vespalib::Version &version, std::vector<uint32_t> &out) const
{
    return _routableRepository->getRoutableTypes(version, out);
}

DocumentProtocol &
DocumentProtocol::putRoutableFactory(uint32_t type, IRoutableFactory::SP factory,
                                     const vespalib::VersionSpecification &version)
{
    _routableRepository->putFactory(version, type, std::move(factory));
    return *this;
}

DocumentProtocol &
DocumentProtocol::putRoutableFactory(uint32_t type, IRoutableFactory::SP factory,
                                     const std::vector<vespalib::VersionSpecification> &versions)
{
    for (const auto & version : versions) {
        putRoutableFactory(type, factory, version);
    }
    return *this;
}

string
DocumentProtocol::getErrorName(uint32_t errorCode) {
    switch (errorCode) {
        case ERROR_MESSAGE_IGNORED:               return "MESSAGE_IGNORED";
        case ERROR_POLICY_FAILURE:                return "POLICY_FAILURE";
        case ERROR_DOCUMENT_NOT_FOUND:            return "DOCUMENT_NOT_FOUND";
        case ERROR_EXISTS:                        return "EXISTS";
        case ERROR_BUCKET_NOT_FOUND:              return "BUCKET_NOT_FOUND";
        case ERROR_BUCKET_DELETED:                return "BUCKET_DELETED";
        case ERROR_NOT_IMPLEMENTED:               return "NOT_IMPLEMENTED";
        case ERROR_ILLEGAL_PARAMETERS:            return "ILLEGAL_PARAMETERS";
        case ERROR_IGNORED:                       return "IGNORED";
        case ERROR_UNKNOWN_COMMAND:               return "UNKNOWN_COMMAND";
        case ERROR_UNPARSEABLE:                   return "UNPARSEABLE";
        case ERROR_NO_SPACE:                      return "NO_SPACE";
        case ERROR_INTERNAL_FAILURE:              return "INTERNAL_FAILURE";
        case ERROR_PROCESSING_FAILURE:            return "PROCESSING_FAILURE";
        case ERROR_TIMESTAMP_EXIST:               return "TIMESTAMP_EXIST";
        case ERROR_STALE_TIMESTAMP:               return "STALE_TIMESTAMP";
        case ERROR_NODE_NOT_READY:                return "NODE_NOT_READY";
        case ERROR_WRONG_DISTRIBUTION:            return "WRONG_DISTRIBUTION";
        case ERROR_REJECTED:                      return "REJECTED";
        case ERROR_ABORTED:                       return "ABORTED";
        case ERROR_BUSY:                          return "BUSY";
        case ERROR_NOT_CONNECTED:                 return "NOT_CONNECTED";
        case ERROR_DISK_FAILURE:                  return "DISK_FAILURE";
        case ERROR_IO_FAILURE:                    return "IO_FAILURE";
        case ERROR_SUSPENDED:                     return "SUSPENDED";
        case ERROR_TEST_AND_SET_CONDITION_FAILED: return "TEST_AND_SET_CONDITION_FAILED";
    }
    return mbus::ErrorCode::getName(errorCode);
}

void
DocumentProtocol::merge(mbus::RoutingContext &ctx)
{
    std::set<uint32_t> mask;
    merge(ctx, mask);
}

void
DocumentProtocol::merge(mbus::RoutingContext& ctx,
                        const std::set<uint32_t>& mask)
{
    ReplyMerger rm;
    uint32_t idx = 0;
    for (mbus::RoutingNodeIterator it = ctx.getChildIterator();
         it.isValid(); it.next(), ++idx)
    {
        if (mask.find(idx) != mask.end()) {
            continue;
        }
        rm.merge(idx, it.getReplyRef());
    }
    assert(idx != 0);
    ReplyMerger::Result res(rm.mergedReply());
    if (res.isSuccessful()) {
        const uint32_t okIdx = res.getSuccessfulReplyIndex();
        ctx.setReply(ctx.getChildIterator().skip(okIdx).removeReply());
    } else {
        assert(res.hasGeneratedReply());
        ctx.setReply(res.releaseGeneratedReply());
    }
}

bool
DocumentProtocol::hasOnlyErrorsOfType(const mbus::Reply &reply, uint32_t errCode)
{
    for (uint32_t i = 0; i < reply.getNumErrors(); ++i) {
        if (reply.getError(i).getCode() != errCode) {
            return false;
        }
    }
    return true;
}

}
<commit_msg>remove 'Storage' protocol<commit_after>// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include "routablefactories60.h"
#include "routingpolicyfactories.h"
#include "routablerepository.h"
#include "routingpolicyrepository.h"
#include "replymerger.h"
#include <vespa/document/util/stringutil.h>
#include <vespa/documentapi/documentapi.h>
#include <vespa/vespalib/util/exceptions.h>
#include <vespa/messagebus/error.h>
#include <sstream>
#include <cassert>

#include <vespa/log/log.h>
LOG_SETUP(".documentprotocol");

using document::DocumentTypeRepo;

namespace documentapi {

const mbus::string DocumentProtocol::NAME = "document";

DocumentProtocol::DocumentProtocol(std::shared_ptr<const DocumentTypeRepo> repo, const string &configId) :
    _routingPolicyRepository(std::make_unique<RoutingPolicyRepository>()),
    _routableRepository(std::make_unique<RoutableRepository>()),
    _repo(std::move(repo))
{
    // Prepare config string for routing policy factories.
    string cfg = (configId.empty() ? "client" : configId);

    // When adding factories to this list, please KEEP THEM ORDERED alphabetically like they are now.
    putRoutingPolicyFactory("AND", std::make_shared<RoutingPolicyFactories::AndPolicyFactory>());
    putRoutingPolicyFactory("Content", std::make_shared<RoutingPolicyFactories::ContentPolicyFactory>());
    putRoutingPolicyFactory("DocumentRouteSelector", std::make_shared<RoutingPolicyFactories::DocumentRouteSelectorPolicyFactory>(*_repo, cfg));
    putRoutingPolicyFactory("Extern", std::make_shared<RoutingPolicyFactories::ExternPolicyFactory>());
    putRoutingPolicyFactory("LoadBalancer", std::make_shared<RoutingPolicyFactories::LoadBalancerPolicyFactory>());
    putRoutingPolicyFactory("LocalService", std::make_shared<RoutingPolicyFactories::LocalServicePolicyFactory>());
    putRoutingPolicyFactory("MessageType", std::make_shared<RoutingPolicyFactories::MessageTypePolicyFactory>());
    putRoutingPolicyFactory("RoundRobin", std::make_shared<RoutingPolicyFactories::RoundRobinPolicyFactory>());
    putRoutingPolicyFactory("SubsetService", std::make_shared<RoutingPolicyFactories::SubsetServicePolicyFactory>());

    // Prepare version specifications to use when adding routable factories.
    vespalib::VersionSpecification version6(6, 221);

    std::vector<vespalib::VersionSpecification> from6  = { version6 };

    // Add 6.x serialization
    putRoutableFactory(MESSAGE_CREATEVISITOR, std::make_shared<RoutableFactories60::CreateVisitorMessageFactory>(), from6);
    putRoutableFactory(MESSAGE_DESTROYVISITOR, std::make_shared<RoutableFactories60::DestroyVisitorMessageFactory>(), from6);
    putRoutableFactory(MESSAGE_DOCUMENTLIST, std::make_shared<RoutableFactories60::DocumentListMessageFactory>(*_repo), from6);
    putRoutableFactory(MESSAGE_DOCUMENTSUMMARY, std::make_shared<RoutableFactories60::DocumentSummaryMessageFactory>(), from6);
    putRoutableFactory(MESSAGE_EMPTYBUCKETS, std::make_shared<RoutableFactories60::EmptyBucketsMessageFactory>(), from6);
    putRoutableFactory(MESSAGE_GETBUCKETLIST, std::make_shared<RoutableFactories60::GetBucketListMessageFactory>(), from6);
    putRoutableFactory(MESSAGE_GETBUCKETSTATE, std::make_shared<RoutableFactories60::GetBucketStateMessageFactory>(), from6);
    putRoutableFactory(MESSAGE_GETDOCUMENT, std::make_shared<RoutableFactories60::GetDocumentMessageFactory>(), from6);
    putRoutableFactory(MESSAGE_MAPVISITOR, std::make_shared<RoutableFactories60::MapVisitorMessageFactory>(), from6);
    putRoutableFactory(MESSAGE_PUTDOCUMENT, std::make_shared<RoutableFactories60::PutDocumentMessageFactory>(*_repo), from6);
    putRoutableFactory(MESSAGE_QUERYRESULT, std::make_shared<RoutableFactories60::QueryResultMessageFactory>(), from6);
    putRoutableFactory(MESSAGE_REMOVEDOCUMENT, std::make_shared<RoutableFactories60::RemoveDocumentMessageFactory>(), from6);
    putRoutableFactory(MESSAGE_REMOVELOCATION, std::make_shared<RoutableFactories60::RemoveLocationMessageFactory>(*_repo), from6);
    putRoutableFactory(MESSAGE_SEARCHRESULT, std::make_shared<RoutableFactories60::SearchResultMessageFactory>(), from6);
    putRoutableFactory(MESSAGE_STATBUCKET, std::make_shared<RoutableFactories60::StatBucketMessageFactory>(), from6);
    putRoutableFactory(MESSAGE_UPDATEDOCUMENT, std::make_shared<RoutableFactories60::UpdateDocumentMessageFactory>(*_repo), from6);
    putRoutableFactory(MESSAGE_VISITORINFO, std::make_shared<RoutableFactories60::VisitorInfoMessageFactory>(), from6);
    putRoutableFactory(REPLY_CREATEVISITOR, std::make_shared<RoutableFactories60::CreateVisitorReplyFactory>(), from6);
    putRoutableFactory(REPLY_DESTROYVISITOR, std::make_shared<RoutableFactories60::DestroyVisitorReplyFactory>(), from6);
    putRoutableFactory(REPLY_DOCUMENTIGNORED, std::make_shared<RoutableFactories60::DocumentIgnoredReplyFactory>(), from6);
    putRoutableFactory(REPLY_DOCUMENTLIST, std::make_shared<RoutableFactories60::DocumentListReplyFactory>(), from6);
    putRoutableFactory(REPLY_DOCUMENTSUMMARY, std::make_shared<RoutableFactories60::DocumentSummaryReplyFactory>(), from6);
    putRoutableFactory(REPLY_EMPTYBUCKETS, std::make_shared<RoutableFactories60::EmptyBucketsReplyFactory>(), from6);
    putRoutableFactory(REPLY_GETBUCKETLIST, std::make_shared<RoutableFactories60::GetBucketListReplyFactory>(), from6);
    putRoutableFactory(REPLY_GETBUCKETSTATE, std::make_shared<RoutableFactories60::GetBucketStateReplyFactory>(), from6);
    putRoutableFactory(REPLY_GETDOCUMENT, std::make_shared<RoutableFactories60::GetDocumentReplyFactory>(*_repo), from6);
    putRoutableFactory(REPLY_MAPVISITOR, std::make_shared<RoutableFactories60::MapVisitorReplyFactory>(), from6);
    putRoutableFactory(REPLY_PUTDOCUMENT, std::make_shared<RoutableFactories60::PutDocumentReplyFactory>(), from6);
    putRoutableFactory(REPLY_QUERYRESULT, std::make_shared<RoutableFactories60::QueryResultReplyFactory>(), from6);
    putRoutableFactory(REPLY_REMOVEDOCUMENT, std::make_shared<RoutableFactories60::RemoveDocumentReplyFactory>(), from6);
    putRoutableFactory(REPLY_REMOVELOCATION, std::make_shared<RoutableFactories60::RemoveLocationReplyFactory>(), from6);
    putRoutableFactory(REPLY_SEARCHRESULT, std::make_shared<RoutableFactories60::SearchResultReplyFactory>(), from6);
    putRoutableFactory(REPLY_STATBUCKET, std::make_shared<RoutableFactories60::StatBucketReplyFactory>(), from6);
    putRoutableFactory(REPLY_UPDATEDOCUMENT, std::make_shared<RoutableFactories60::UpdateDocumentReplyFactory>(), from6);
    putRoutableFactory(REPLY_VISITORINFO, std::make_shared<RoutableFactories60::VisitorInfoReplyFactory>(), from6);
    putRoutableFactory(REPLY_WRONGDISTRIBUTION, std::make_shared<RoutableFactories60::WrongDistributionReplyFactory>(), from6);
}

DocumentProtocol::~DocumentProtocol() = default;

mbus::IRoutingPolicy::UP
DocumentProtocol::createPolicy(const mbus::string &name, const mbus::string &param) const
{
    return _routingPolicyRepository->createPolicy(name, param);
}

DocumentProtocol &
DocumentProtocol::putRoutingPolicyFactory(const string &name, IRoutingPolicyFactory::SP factory)
{
    _routingPolicyRepository->putFactory(name, std::move(factory));
    return *this;
}

mbus::Blob
DocumentProtocol::encode(const vespalib::Version &version, const mbus::Routable &routable) const
{
    mbus::Blob blob(_routableRepository->encode(version, routable));
        // When valgrind reports errors of uninitialized data being written to
        // the network, it is useful to be able to see the serialized data to
        // try to identify what bits are uninitialized.
    if (LOG_WOULD_LOG(spam)) {
        std::ostringstream message;
        document::StringUtil::printAsHex(
                message, blob.data(), blob.size());
        LOG(spam, "Encoded message of protocol %s type %u using version %s serialization:\n%s",
            routable.getProtocol().c_str(), routable.getType(),
            version.toString().c_str(), message.str().c_str());
    }
    return blob;
}

mbus::Routable::UP
DocumentProtocol::decode(const vespalib::Version &version, mbus::BlobRef data) const
{
    try {
        return _routableRepository->decode(version, data);
    } catch (vespalib::Exception &e) {
        LOG(warning, "%s", e.getMessage().c_str());
        return mbus::Routable::UP();
    }
}

uint32_t
DocumentProtocol::getRoutableTypes(const vespalib::Version &version, std::vector<uint32_t> &out) const
{
    return _routableRepository->getRoutableTypes(version, out);
}

DocumentProtocol &
DocumentProtocol::putRoutableFactory(uint32_t type, IRoutableFactory::SP factory,
                                     const vespalib::VersionSpecification &version)
{
    _routableRepository->putFactory(version, type, std::move(factory));
    return *this;
}

DocumentProtocol &
DocumentProtocol::putRoutableFactory(uint32_t type, IRoutableFactory::SP factory,
                                     const std::vector<vespalib::VersionSpecification> &versions)
{
    for (const auto & version : versions) {
        putRoutableFactory(type, factory, version);
    }
    return *this;
}

string
DocumentProtocol::getErrorName(uint32_t errorCode) {
    switch (errorCode) {
        case ERROR_MESSAGE_IGNORED:               return "MESSAGE_IGNORED";
        case ERROR_POLICY_FAILURE:                return "POLICY_FAILURE";
        case ERROR_DOCUMENT_NOT_FOUND:            return "DOCUMENT_NOT_FOUND";
        case ERROR_EXISTS:                        return "EXISTS";
        case ERROR_BUCKET_NOT_FOUND:              return "BUCKET_NOT_FOUND";
        case ERROR_BUCKET_DELETED:                return "BUCKET_DELETED";
        case ERROR_NOT_IMPLEMENTED:               return "NOT_IMPLEMENTED";
        case ERROR_ILLEGAL_PARAMETERS:            return "ILLEGAL_PARAMETERS";
        case ERROR_IGNORED:                       return "IGNORED";
        case ERROR_UNKNOWN_COMMAND:               return "UNKNOWN_COMMAND";
        case ERROR_UNPARSEABLE:                   return "UNPARSEABLE";
        case ERROR_NO_SPACE:                      return "NO_SPACE";
        case ERROR_INTERNAL_FAILURE:              return "INTERNAL_FAILURE";
        case ERROR_PROCESSING_FAILURE:            return "PROCESSING_FAILURE";
        case ERROR_TIMESTAMP_EXIST:               return "TIMESTAMP_EXIST";
        case ERROR_STALE_TIMESTAMP:               return "STALE_TIMESTAMP";
        case ERROR_NODE_NOT_READY:                return "NODE_NOT_READY";
        case ERROR_WRONG_DISTRIBUTION:            return "WRONG_DISTRIBUTION";
        case ERROR_REJECTED:                      return "REJECTED";
        case ERROR_ABORTED:                       return "ABORTED";
        case ERROR_BUSY:                          return "BUSY";
        case ERROR_NOT_CONNECTED:                 return "NOT_CONNECTED";
        case ERROR_DISK_FAILURE:                  return "DISK_FAILURE";
        case ERROR_IO_FAILURE:                    return "IO_FAILURE";
        case ERROR_SUSPENDED:                     return "SUSPENDED";
        case ERROR_TEST_AND_SET_CONDITION_FAILED: return "TEST_AND_SET_CONDITION_FAILED";
    }
    return mbus::ErrorCode::getName(errorCode);
}

void
DocumentProtocol::merge(mbus::RoutingContext &ctx)
{
    std::set<uint32_t> mask;
    merge(ctx, mask);
}

void
DocumentProtocol::merge(mbus::RoutingContext& ctx,
                        const std::set<uint32_t>& mask)
{
    ReplyMerger rm;
    uint32_t idx = 0;
    for (mbus::RoutingNodeIterator it = ctx.getChildIterator();
         it.isValid(); it.next(), ++idx)
    {
        if (mask.find(idx) != mask.end()) {
            continue;
        }
        rm.merge(idx, it.getReplyRef());
    }
    assert(idx != 0);
    ReplyMerger::Result res(rm.mergedReply());
    if (res.isSuccessful()) {
        const uint32_t okIdx = res.getSuccessfulReplyIndex();
        ctx.setReply(ctx.getChildIterator().skip(okIdx).removeReply());
    } else {
        assert(res.hasGeneratedReply());
        ctx.setReply(res.releaseGeneratedReply());
    }
}

bool
DocumentProtocol::hasOnlyErrorsOfType(const mbus::Reply &reply, uint32_t errCode)
{
    for (uint32_t i = 0; i < reply.getNumErrors(); ++i) {
        if (reply.getError(i).getCode() != errCode) {
            return false;
        }
    }
    return true;
}

}
<|endoftext|>


<commit_before>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright (C) 2011 - 2017                                                  *
 * Dominik Charousset <dominik.charousset (at) haw-hamburg.de>                *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#ifndef CAF_INTRUSIVE_LIFO_INBOX_HPP
#define CAF_INTRUSIVE_LIFO_INBOX_HPP

#include <atomic>
#include <condition_variable>
#include <mutex>

#include "caf/config.hpp"

#include "caf/intrusive/inbox_result.hpp"

namespace caf {
namespace intrusive {

/// An intrusive, thread-safe LIFO queue implementation for a single reader
/// with any number of writers.
template <class Policy>
class lifo_inbox {
public:
  using policy_type = Policy;

  using value_type = typename policy_type::mapped_type;

  using pointer = value_type*;

  using node_type = typename value_type::node_type;

  using node_pointer = node_type*;

  using unique_pointer = typename policy_type::unique_pointer;

  using deleter_type = typename unique_pointer::deleter_type;

  /// Tries to enqueue a new element to the inbox.
  /// @threadsafe
  inbox_result push_front(pointer new_element) noexcept {
    CAF_ASSERT(new_element != nullptr);
    pointer e = stack_.load();
    auto eof = stack_closed_tag();
    auto blk = reader_blocked_tag();
    while (e != eof) {
      // A tag is never part of a non-empty list.
      new_element->next = e != blk ? e : nullptr;
      if (stack_.compare_exchange_strong(e, new_element))
        return  e == reader_blocked_tag() ? inbox_result::unblocked_reader
                                            : inbox_result::success;
      // Continue with new value of `e`.
    }
    // The queue has been closed, drop messages.
    deleter_type d;
    d(new_element);
    return inbox_result::queue_closed;
  }

  /// Tries to enqueue a new element to the inbox.
  /// @threadsafe
  inbox_result push_front(unique_pointer x) noexcept {
    return push_front(x.release());
  }

  /// Tries to enqueue a new element to the mailbox.
  /// @threadsafe
  template <class... Ts>
  inbox_result emplace_front(Ts&&... xs) {
    return push_front(new value_type(std::forward<Ts>(xs)...));
  }

  /// Queries whether this queue is empty.
  /// @pre `!closed() && !blocked()`
  bool empty() const noexcept {
    CAF_ASSERT(!closed());
    CAF_ASSERT(!blocked());
    return stack_.load() == stack_empty_tag();
  }

  /// Queries whether this has been closed.
  bool closed() const noexcept {
    return stack_.load() == stack_closed_tag();
  }

  /// Queries whether this has been marked as blocked, i.e.,
  /// the owner of the list is waiting for new data.
  bool blocked() const noexcept {
    return stack_.load() == reader_blocked_tag();
  }

  /// Tries to set this queue from `empty` to `blocked`.
  bool try_block() noexcept {
    auto e = stack_empty_tag();
    return stack_.compare_exchange_strong(e, reader_blocked_tag());
  }

  /// Tries to set this queue from `blocked` to `empty`.
  bool try_unblock() noexcept {
    auto e = reader_blocked_tag();
    return stack_.compare_exchange_strong(e, stack_empty_tag());
  }

  /// Sets the head to `new_head` and returns the previous head if the queue
  /// was not empty.
  pointer take_head(pointer new_head) noexcept {
    // This member function should only be used to transition to closed or
    // empty.
    CAF_ASSERT(new_head == stack_closed_tag()
               || new_head == stack_empty_tag());
    // Must not be called on a closed queue.
    pointer e = stack_.load();
    CAF_ASSERT(e != stack_closed_tag());
    // We don't assert these conditions again since only the owner is allowed
    // to call this member function, i.e., there's never a race on `take_head`.
    while (e != new_head) {
      if (stack_.compare_exchange_weak(e, new_head)) {
        CAF_ASSERT(e != stack_closed_tag());
        if (is_empty_or_blocked_tag(e)) {
          // Sanity check: going from empty/blocked to closed.
          CAF_ASSERT(new_head == stack_closed_tag());
          return nullptr;
        }
        return e;
      }
      // Next iteration.
    }
    return nullptr;
  }

  /// Sets the head to `stack_empty_tag()` and returns the previous head if
  /// the queue was not empty.
  pointer take_head() noexcept {
    return take_head(stack_empty_tag());
  }

  /// Closes this queue and deletes all remaining elements.
  /// @warning Call only from the reader (owner).
  void close() noexcept {
    deleter_type d;
    static_assert(noexcept(d(std::declval<pointer>())),
                  "deleter is not noexcept");
    close(d);
  }

  /// Closes this queue and applies `f` to each pointer. The function object
  /// `f` must take ownership of the passed pointer.
  /// @warning Call only from the reader (owner).
  template <class F>
  void close(F& f) noexcept(noexcept(f(std::declval<pointer>()))) {
    node_pointer ptr = take_head(stack_closed_tag());
    while (ptr != nullptr) {
      auto next = ptr->next;
      f(promote(ptr));
      ptr = next;
    }
  }

  lifo_inbox() noexcept {
    stack_ = stack_empty_tag();
  }

  ~lifo_inbox() noexcept {
    if (!closed())
      close();
  }

  // -- synchronized access ---------------------------------------------------

  template <class Mutex, class CondVar>
  bool synchronized_push_front(Mutex& mtx, CondVar& cv, pointer ptr) {
    switch (push_front(ptr)) {
      default:
        // enqueued message to a running actor's mailbox
        return true;
      case inbox_result::unblocked_reader: {
        std::unique_lock<Mutex> guard(mtx);
        cv.notify_one();
        return true;
      }
      case inbox_result::queue_closed:
        // actor no longer alive
        return false;
    }
  }

  template <class Mutex, class CondVar>
  bool synchronized_push_front(Mutex& mtx, CondVar& cv, unique_pointer ptr) {
    return synchronized_push_front(mtx, cv, ptr.relase());
  }

  template <class Mutex, class CondVar, class... Ts>
  bool synchronized_emplace_front(Mutex& mtx, CondVar& cv, Ts&&... xs) {
    return synchronized_push_front(mtx, cv,
                                   new value_type(std::forward<Ts>(xs)...));
  }

  template <class Mutex, class CondVar>
  void synchronized_await(Mutex& mtx, CondVar& cv) {
    CAF_ASSERT(!closed());
    if (try_block()) {
      std::unique_lock<Mutex> guard(mtx);
      while (blocked())
        cv.wait(guard);
    }
  }

  template <class Mutex, class CondVar, class TimePoint>
  bool synchronized_await(Mutex& mtx, CondVar& cv, const TimePoint& timeout) {
    CAF_ASSERT(!closed());
    if (try_block()) {
      std::unique_lock<Mutex> guard(mtx);
      while (blocked()) {
        if (cv.wait_until(guard, timeout) == std::cv_status::timeout) {
          // if we're unable to set the queue from blocked to empty,
          // than there's a new element in the list
          return !try_unblock();
        }
      }
    }
    return true;
  }

private:
  static constexpr pointer stack_empty_tag() {
    // We are *never* going to dereference the returned pointer. It is only
    // used as indicator wheter this queue is empty or not.
    return static_cast<pointer>(nullptr);
  }

  pointer stack_closed_tag() const noexcept {
    // We are *never* going to dereference the returned pointer. It is only
    // used as indicator wheter this queue is closed or not.
    return reinterpret_cast<pointer>(reinterpret_cast<intptr_t>(this) + 1);
  }

  pointer reader_blocked_tag() const noexcept {
    // We are *never* going to dereference the returned pointer. It is only
    // used as indicator wheter the owner of the queue is currently waiting for
    // new messages.
    return reinterpret_cast<pointer>(const_cast<lifo_inbox*>(this));
  }

  bool is_empty_or_blocked_tag(pointer x) const noexcept {
    return x == stack_empty_tag() || x == reader_blocked_tag();
  }

  // -- member variables ------------------------------------------------------

  std::atomic<pointer> stack_;
};

} // namespace intrusive
} // namespace caf

#endif // CAF_INTRUSIVE_LIFO_INBOX_HPP
<commit_msg>Improve assertions in LIFO inbox<commit_after>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright (C) 2011 - 2017                                                  *
 * Dominik Charousset <dominik.charousset (at) haw-hamburg.de>                *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#ifndef CAF_INTRUSIVE_LIFO_INBOX_HPP
#define CAF_INTRUSIVE_LIFO_INBOX_HPP

#include <atomic>
#include <condition_variable>
#include <mutex>

#include "caf/config.hpp"

#include "caf/intrusive/inbox_result.hpp"

namespace caf {
namespace intrusive {

/// An intrusive, thread-safe LIFO queue implementation for a single reader
/// with any number of writers.
template <class Policy>
class lifo_inbox {
public:
  using policy_type = Policy;

  using value_type = typename policy_type::mapped_type;

  using pointer = value_type*;

  using node_type = typename value_type::node_type;

  using node_pointer = node_type*;

  using unique_pointer = typename policy_type::unique_pointer;

  using deleter_type = typename unique_pointer::deleter_type;

  /// Tries to enqueue a new element to the inbox.
  /// @threadsafe
  inbox_result push_front(pointer new_element) noexcept {
    CAF_ASSERT(new_element != nullptr);
    pointer e = stack_.load();
    auto eof = stack_closed_tag();
    auto blk = reader_blocked_tag();
    while (e != eof) {
      // A tag is never part of a non-empty list.
      new_element->next = e != blk ? e : nullptr;
      if (stack_.compare_exchange_strong(e, new_element))
        return  e == reader_blocked_tag() ? inbox_result::unblocked_reader
                                            : inbox_result::success;
      // Continue with new value of `e`.
    }
    // The queue has been closed, drop messages.
    deleter_type d;
    d(new_element);
    return inbox_result::queue_closed;
  }

  /// Tries to enqueue a new element to the inbox.
  /// @threadsafe
  inbox_result push_front(unique_pointer x) noexcept {
    return push_front(x.release());
  }

  /// Tries to enqueue a new element to the mailbox.
  /// @threadsafe
  template <class... Ts>
  inbox_result emplace_front(Ts&&... xs) {
    return push_front(new value_type(std::forward<Ts>(xs)...));
  }

  /// Queries whether this queue is empty.
  /// @pre `!closed() && !blocked()`
  bool empty() const noexcept {
    CAF_ASSERT(!closed());
    CAF_ASSERT(!blocked());
    return stack_.load() == stack_empty_tag();
  }

  /// Queries whether this has been closed.
  bool closed() const noexcept {
    return stack_.load() == stack_closed_tag();
  }

  /// Queries whether this has been marked as blocked, i.e.,
  /// the owner of the list is waiting for new data.
  bool blocked() const noexcept {
    return stack_.load() == reader_blocked_tag();
  }

  /// Tries to set this queue from `empty` to `blocked`.
  bool try_block() noexcept {
    auto e = stack_empty_tag();
    return stack_.compare_exchange_strong(e, reader_blocked_tag());
  }

  /// Tries to set this queue from `blocked` to `empty`.
  bool try_unblock() noexcept {
    auto e = reader_blocked_tag();
    return stack_.compare_exchange_strong(e, stack_empty_tag());
  }

  /// Sets the head to `new_head` and returns the previous head if the queue
  /// was not empty.
  pointer take_head(pointer new_head) noexcept {
    // This member function should only be used to transition to closed or
    // empty.
    CAF_ASSERT(new_head == stack_closed_tag()
               || new_head == stack_empty_tag());
    pointer e = stack_.load();
    // Must not be called on a closed queue.
    CAF_ASSERT(e != stack_closed_tag());
    // Must not be called on a blocked queue unless for setting it to closed,
    // because that would mean an actor accesses its mailbox after blocking its
    // mailbox but before receiving anything.
    CAF_ASSERT(e != reader_blocked_tag() || new_head == stack_closed_tag());
    // We don't assert these conditions again since only the owner is allowed
    // to call this member function, i.e., there's never a race on `take_head`.
    while (e != new_head) {
      if (stack_.compare_exchange_weak(e, new_head)) {
        CAF_ASSERT(e != stack_closed_tag());
        if (is_empty_or_blocked_tag(e)) {
          // Sanity check: going from empty/blocked to closed.
          CAF_ASSERT(new_head == stack_closed_tag());
          return nullptr;
        }
        return e;
      }
      // Next iteration.
    }
    return nullptr;
  }

  /// Sets the head to `stack_empty_tag()` and returns the previous head if
  /// the queue was not empty.
  pointer take_head() noexcept {
    return take_head(stack_empty_tag());
  }

  /// Closes this queue and deletes all remaining elements.
  /// @warning Call only from the reader (owner).
  void close() noexcept {
    deleter_type d;
    static_assert(noexcept(d(std::declval<pointer>())),
                  "deleter is not noexcept");
    close(d);
  }

  /// Closes this queue and applies `f` to each pointer. The function object
  /// `f` must take ownership of the passed pointer.
  /// @warning Call only from the reader (owner).
  template <class F>
  void close(F& f) noexcept(noexcept(f(std::declval<pointer>()))) {
    node_pointer ptr = take_head(stack_closed_tag());
    while (ptr != nullptr) {
      auto next = ptr->next;
      f(promote(ptr));
      ptr = next;
    }
  }

  lifo_inbox() noexcept {
    stack_ = stack_empty_tag();
  }

  ~lifo_inbox() noexcept {
    if (!closed())
      close();
  }

  // -- synchronized access ---------------------------------------------------

  template <class Mutex, class CondVar>
  bool synchronized_push_front(Mutex& mtx, CondVar& cv, pointer ptr) {
    switch (push_front(ptr)) {
      default:
        // enqueued message to a running actor's mailbox
        return true;
      case inbox_result::unblocked_reader: {
        std::unique_lock<Mutex> guard(mtx);
        cv.notify_one();
        return true;
      }
      case inbox_result::queue_closed:
        // actor no longer alive
        return false;
    }
  }

  template <class Mutex, class CondVar>
  bool synchronized_push_front(Mutex& mtx, CondVar& cv, unique_pointer ptr) {
    return synchronized_push_front(mtx, cv, ptr.relase());
  }

  template <class Mutex, class CondVar, class... Ts>
  bool synchronized_emplace_front(Mutex& mtx, CondVar& cv, Ts&&... xs) {
    return synchronized_push_front(mtx, cv,
                                   new value_type(std::forward<Ts>(xs)...));
  }

  template <class Mutex, class CondVar>
  void synchronized_await(Mutex& mtx, CondVar& cv) {
    CAF_ASSERT(!closed());
    if (try_block()) {
      std::unique_lock<Mutex> guard(mtx);
      while (blocked())
        cv.wait(guard);
    }
  }

  template <class Mutex, class CondVar, class TimePoint>
  bool synchronized_await(Mutex& mtx, CondVar& cv, const TimePoint& timeout) {
    CAF_ASSERT(!closed());
    if (try_block()) {
      std::unique_lock<Mutex> guard(mtx);
      while (blocked()) {
        if (cv.wait_until(guard, timeout) == std::cv_status::timeout) {
          // if we're unable to set the queue from blocked to empty,
          // than there's a new element in the list
          return !try_unblock();
        }
      }
    }
    return true;
  }

private:
  static constexpr pointer stack_empty_tag() {
    // We are *never* going to dereference the returned pointer. It is only
    // used as indicator wheter this queue is empty or not.
    return static_cast<pointer>(nullptr);
  }

  pointer stack_closed_tag() const noexcept {
    // We are *never* going to dereference the returned pointer. It is only
    // used as indicator wheter this queue is closed or not.
    return reinterpret_cast<pointer>(reinterpret_cast<intptr_t>(this) + 1);
  }

  pointer reader_blocked_tag() const noexcept {
    // We are *never* going to dereference the returned pointer. It is only
    // used as indicator wheter the owner of the queue is currently waiting for
    // new messages.
    return reinterpret_cast<pointer>(const_cast<lifo_inbox*>(this));
  }

  bool is_empty_or_blocked_tag(pointer x) const noexcept {
    return x == stack_empty_tag() || x == reader_blocked_tag();
  }

  // -- member variables ------------------------------------------------------

  std::atomic<pointer> stack_;
};

} // namespace intrusive
} // namespace caf

#endif // CAF_INTRUSIVE_LIFO_INBOX_HPP
<|endoftext|>