boostorg/fiber

use it in qt

bizehao opened this issue · 0 comments

This is the scheduling I implemented in QT, but it has issues and sometimes it won't be scheduled. Occasionally, I am confused and can't find where the problem lies

using namespace std::chrono_literals;
namespace {
	uint64_t milliseconds_until(
		const std::chrono::steady_clock::time_point& abs_time)
	{
		using std::chrono::duration_cast;
		auto now = std::chrono::steady_clock::now();
		if (now >= abs_time) return 0;
		return duration_cast<std::chrono::milliseconds>(abs_time - now).count() + 1;
	}
	bool is_max_time(const std::chrono::steady_clock::time_point& abs_time)
	{
		return abs_time == std::chrono::steady_clock::time_point::max();
	}
}

class scheduler : public boost::fibers::algo::algorithm
{
public:
	scheduler() {
		QTimer::singleShot(0, [this]() mutable {
			QEventLoop eventLoop;
			while (true) {
				if (has_ready_fibers()) {
					//eventLoop.processEvents(QEventLoop::ApplicationExec | QEventLoop::EventLoopExec);
					while (eventLoop.processEvents(QEventLoop::AllEvents));
					std::unique_lock< boost::fibers::mutex > lk(mtx_);
					cnd_.wait(lk);
				}
				else {
					eventLoop.processEvents(QEventLoop::WaitForMoreEvents);
				}
			}
			});
	};

	~scheduler() override {};

	void awakened(boost::fibers::context* ctx) noexcept override {
		BOOST_ASSERT(nullptr != ctx);
		BOOST_ASSERT(!ctx->ready_is_linked());
		ctx->ready_link(rqueue_); /*< fiber, enqueue on ready queue >*/
		if (!ctx->is_context(boost::fibers::type::dispatcher_context)) {
			++counter_;
		}
	}

	boost::fibers::context* pick_next() noexcept override {
		boost::fibers::context* ctx = nullptr;
		if (!rqueue_.empty()) {
			ctx = &rqueue_.front();
			rqueue_.pop_front();
			BOOST_ASSERT(nullptr != ctx);
			BOOST_ASSERT(boost::fibers::context::active() != ctx);
			if (!ctx->is_context(boost::fibers::type::dispatcher_context)) {
				--counter_;
			}
		}
		return ctx;
	}

	bool has_ready_fibers() const noexcept override {
		return 0 < counter_;
	}

	void suspend_until(std::chrono::steady_clock::time_point const& abs_time) noexcept override {
		if (!is_max_time(abs_time)) {
			QTimer::singleShot(milliseconds_until(abs_time), []() {
				boost::this_fiber::yield();
				});
		}
		cnd_.notify_one();
	}

	void notify() noexcept override {
		QTimer::singleShot(0, []() {
			boost::this_fiber::yield();
			});
	}

	// Non-copyable and non-movable
	scheduler(const scheduler&) = delete;
	scheduler& operator=(const scheduler&) = delete;
	scheduler(scheduler&&) = delete;
	scheduler& operator=(scheduler&&) = delete;

private:
	boost::fibers::mutex                            mtx_;
	boost::fibers::condition_variable               cnd_;
	std::size_t                                     counter_{ 0 };
	boost::fibers::scheduler::ready_queue_type      rqueue_;
};

//test code
boost::fibers::use_scheduling_algorithm<scheduler>();

boost::fibers::fiber ff{ []() {
		for (int i = 0; i < 10; i++) {
			boost::this_fiber::sleep_for(5s);
			qDebug() << "i: " << i;
		}
		} };

ff.detach();