Commits

Martin Vejnár committed 3d2da73

Added task::cancellable and task::finishable.

  • Participants
  • Parent commits 2273fca

Comments (0)

Files changed (5)

libyb/async/cancellation_token.cpp

+#include "cancellation_token.hpp"
+#include "detail/cancellation_token_task.hpp"
+using namespace yb;
+
+cancellation_token::cancellation_token()
+	: m_core(0)
+{
+}
+
+cancellation_token::cancellation_token(detail::cancellation_token_core_base * core)
+	: m_core(core)
+{
+	assert(m_core);
+	m_core->addref();
+}
+
+cancellation_token::cancellation_token(cancellation_token const & o)
+	: m_core(o.m_core)
+{
+	if (m_core)
+		m_core->addref();
+}
+
+cancellation_token::~cancellation_token()
+{
+	if (m_core)
+		m_core->release();
+}
+
+cancellation_token & cancellation_token::operator=(cancellation_token const & o)
+{
+	if (o.m_core)
+		o.m_core->addref();
+	if (m_core)
+		m_core->release();
+	m_core = o.m_core;
+	return *this;
+}
+
+void cancellation_token::cancel(cancel_level cl)
+{
+	if (m_core)
+		m_core->cancel(cl);
+}

libyb/async/cancellation_token.hpp

+#ifndef LIBYB_ASYNC_CANCELLATION_TOKEN_HPP
+#define LIBYB_ASYNC_CANCELLATION_TOKEN_HPP
+
+#include "cancel_level.hpp"
+
+namespace yb {
+
+namespace detail {
+class cancellation_token_core_base;
+}
+
+class cancellation_token
+{
+public:
+	cancellation_token();
+	explicit cancellation_token(detail::cancellation_token_core_base * core);
+	cancellation_token(cancellation_token const & o);
+	~cancellation_token();
+	cancellation_token & operator=(cancellation_token const & o);
+	void cancel(cancel_level cl);
+
+private:
+	detail::cancellation_token_core_base * m_core;
+};
+
+} // namespace yb
+
+#endif // LIBYB_ASYNC_CANCELLATION_TOKEN_HPP

libyb/async/detail/cancellation_token_task.hpp

+#ifndef LIBYB_ASYNC_DETAIL_CANCELLATION_TOKEN_TASK_HPP
+#define LIBYB_ASYNC_DETAIL_CANCELLATION_TOKEN_TASK_HPP
+
+#include "../task_base.hpp"
+#include "../cancel_exception.hpp"
+#include "../../utils/noncopyable.hpp"
+
+namespace yb {
+namespace detail {
+
+class cancellation_token_core_base
+	: noncopyable
+{
+public:
+	cancellation_token_core_base()
+		: m_refcount(0)
+	{
+	}
+
+	virtual ~cancellation_token_core_base()
+	{
+	}
+
+	void addref()
+	{
+		++m_refcount;
+	}
+
+	void release()
+	{
+		if (--m_refcount == 0)
+			delete this;
+	}
+
+	virtual void cancel(cancel_level cl) = 0;
+
+private:
+	int m_refcount;
+};
+
+template <typename R>
+struct cancellation_token_core
+	: public cancellation_token_core_base
+{
+	task<R> m_task;
+
+	void cancel(cancel_level cl)
+	{
+		m_task.cancel(cl);
+	}
+};
+
+template <typename R>
+class cancellation_token_task
+	: public task_base<R>, noncopyable
+{
+public:
+	explicit cancellation_token_task(cancellation_token_core<R> * core)
+		: m_core(core)
+	{
+		assert(m_core);
+		m_core->addref();
+	}
+
+	~cancellation_token_task()
+	{
+		m_core->release();
+	}
+
+	void cancel(cancel_level cl) throw()
+	{
+		m_core->m_task.cancel(cl);
+	}
+
+	task_result<R> cancel_and_wait() throw()
+	{
+		return m_core->m_task.cancel_and_wait();
+	}
+
+	void prepare_wait(task_wait_preparation_context & ctx)
+	{
+		m_core->m_task.prepare_wait(ctx);
+	}
+
+	task<R> finish_wait(task_wait_finalization_context & ctx) throw()
+	{
+		m_core->m_task.finish_wait(ctx);
+		if (m_core->m_task.has_result())
+			return std::move(m_core->m_task);
+		return nulltask;
+	}
+
+private:
+	cancellation_token_core<R> * m_core;
+};
+
+template <>
+class cancellation_token_task<void>
+	: public task_base<void>, noncopyable
+{
+public:
+	cancellation_token_task(cancellation_token_core<void> * core, bool catch_cancel = false)
+		: m_core(core), m_catch_cancel(catch_cancel)
+	{
+		assert(m_core);
+		m_core->addref();
+	}
+
+	~cancellation_token_task()
+	{
+		m_core->release();
+	}
+
+	void cancel(cancel_level cl) throw()
+	{
+		m_core->m_task.cancel(cl);
+	}
+
+	task_result<void> cancel_and_wait() throw()
+	{
+		task_result<void> r = m_core->m_task.cancel_and_wait();
+		if (m_catch_cancel && r.has_exception())
+		{
+			try
+			{
+				r.rethrow();
+			}
+			catch (task_cancelled const &)
+			{
+				return task_result<void>();
+			}
+			catch (...)
+			{
+				return task_result<void>(std::current_exception());
+			}
+		}
+		return std::move(r);
+	}
+
+	void prepare_wait(task_wait_preparation_context & ctx)
+	{
+		m_core->m_task.prepare_wait(ctx);
+	}
+
+	task<void> finish_wait(task_wait_finalization_context & ctx) throw()
+	{
+		m_core->m_task.finish_wait(ctx);
+		if (m_core->m_task.has_result())
+		{
+			task_result<void> r = m_core->m_task.get_result();
+			if (m_catch_cancel && r.has_exception())
+			{
+				try
+				{
+					r.rethrow();
+				}
+				catch (task_cancelled const &)
+				{
+					return async::value();
+				}
+				catch (...)
+				{
+					return async::raise<void>();
+				}
+			}
+			return async::result(std::move(r));
+		}
+		return nulltask;
+	}
+
+private:
+	cancellation_token_core<void> * m_core;
+	bool m_catch_cancel;
+};
+
+} // namespace detail
+} // namespace yb
+
+#endif // LIBYB_ASYNC_DETAIL_CANCELLATION_TOKEN_TASK_HPP

libyb/async/detail/task_fwd.hpp

 #include "../task_result.hpp"
 #include "../cancel_level.hpp"
 #include "../../utils/noncopyable.hpp"
+#include "../cancellation_token.hpp"
 #include <memory> // unique_ptr
 #include <exception> // exception_ptr, exception
 
 
 	task<void> ignore_result();
 
+	task<R> cancellable(cancellation_token & ct);
+	task<R> finishable(cancellation_token & ct);
+
 private:
 	typedef task_base<R> * task_base_ptr;
 

libyb/async/detail/task_impl.hpp

 #include "sequential_composition_task.hpp"
 #include "loop_task.hpp"
 #include "cancel_level_upgrade_task.hpp"
+#include "cancellation_token_task.hpp"
 #include <type_traits>
 
 namespace yb {
 	return std::move(*this);
 }
 
+template <typename R>
+task<R> task<R>::cancellable(cancellation_token & ct)
+{
+	if (m_kind != k_task)
+		return std::move(*this);
+
+	detail::cancellation_token_core<R> * core = new detail::cancellation_token_core<R>();
+	cancellation_token new_ct(core);
+	task<R> core_task(new detail::cancellation_token_task<R>(core));
+
+	core->m_task = std::move(*this);
+	ct = new_ct;
+	return std::move(core_task);
+}
+
+template <>
+inline task<void> task<void>::finishable(cancellation_token & ct)
+{
+	if (m_kind != k_task)
+		return std::move(*this);
+
+	detail::cancellation_token_core<void> * core = new detail::cancellation_token_core<void>();
+	cancellation_token new_ct(core);
+	task<void> core_task(new detail::cancellation_token_task<void>(core, true));
+
+	core->m_task = std::move(*this);
+	ct = new_ct;
+	return std::move(core_task);
+}
+
 template <typename F>
 typename detail::task_protect_type<F>::type protect(F f)
 {