[core] Introduce Scheduler::GetSequenced() API

The newly introduced `Scheduler::GetSequenced()` returns
sequenced schedulers from the cache limited to 10 instances,
preventing from spawning too many threads.
This commit is contained in:
Mikhail Pozdnyakov
2019-11-27 23:41:30 +02:00
parent 316f1d0ad1
commit b5794730b5
3 changed files with 83 additions and 0 deletions

View File

@@ -69,8 +69,21 @@ public:
// Get the scheduler for asynchronous tasks. This method
// will lazily initialize a shared worker pool when ran
// from the first time.
// The scheduled tasks might run in parallel on different
// threads.
// TODO : Rename to GetPool()
static std::shared_ptr<Scheduler> GetBackground();
// Get the *sequenced* scheduler for asynchronous tasks.
// Unlike the method above, the returned scheduler
// (once stored) represents a single thread, thus each
// newly scheduled task is guarantied to run after the
// previously scheduled one.
//
// Sequenced scheduler can be used for running tasks
// on the same thread-unsafe object.
static std::shared_ptr<Scheduler> GetSequenced();
protected:
template <typename TaskFn, typename ReplyFn>
void scheduleAndReplyValue(const TaskFn& task,

View File

@@ -41,4 +41,31 @@ std::shared_ptr<Scheduler> Scheduler::GetBackground() {
return scheduler;
}
// static
std::shared_ptr<Scheduler> Scheduler::GetSequenced() {
const std::size_t kSchedulersCount = 10;
static std::vector<std::weak_ptr<Scheduler>> weaks(kSchedulersCount);
static std::mutex mtx;
static std::size_t lastUsedIndex = 0u;
std::lock_guard<std::mutex> lock(mtx);
if (++lastUsedIndex == kSchedulersCount) lastUsedIndex = 0u;
std::shared_ptr<Scheduler> result;
for (std::size_t i = 0; i < kSchedulersCount; ++i) {
auto& weak = weaks[i];
if (auto scheduler = weak.lock()) {
if (lastUsedIndex == i) result = scheduler;
continue;
}
result = std::make_shared<SequencedScheduler>();
weak = result;
lastUsedIndex = i;
break;
}
return result;
}
} //namespace mbgl

View File

@@ -169,4 +169,47 @@ TEST(AsyncTask, scheduleAndReplyValue) {
auto sheduler = Scheduler::GetBackground();
sheduler->scheduleAndReplyValue(runInBackground, onResult);
loop.run();
}
TEST(AsyncTask, SequencedScheduler) {
RunLoop loop;
std::thread::id caller_id = std::this_thread::get_id();
std::thread::id bg_id;
int count = 0;
auto first = [caller_id, &bg_id, &count]() {
EXPECT_EQ(0, count);
bg_id = std::this_thread::get_id();
EXPECT_NE(caller_id, bg_id);
count++;
};
auto second = [&bg_id, &count]() {
EXPECT_EQ(1, count);
EXPECT_EQ(bg_id, std::this_thread::get_id());
count++;
};
auto third = [&bg_id, &count, &loop]() {
EXPECT_EQ(2, count);
EXPECT_EQ(bg_id, std::this_thread::get_id());
loop.stop();
};
auto sheduler = Scheduler::GetSequenced();
sheduler->schedule(first);
sheduler->schedule(second);
sheduler->schedule(third);
loop.run();
}
TEST(AsyncTask, MultipleSequencedSchedulers) {
std::vector<std::shared_ptr<Scheduler>> shedulers;
for (int i = 0; i < 10; ++i) {
auto scheduler = Scheduler::GetSequenced();
EXPECT_TRUE(std::none_of(
shedulers.begin(), shedulers.end(), [&scheduler](const auto &item) { return item == scheduler; }));
shedulers.emplace_back(std::move(scheduler));
}
EXPECT_EQ(shedulers.front(), Scheduler::GetSequenced());
}