Refactor Scheduler.

This commit is contained in:
ykiko
2024-11-28 16:06:15 +08:00
parent 939dbc3dc5
commit c240c7e7bc
6 changed files with 278 additions and 178 deletions

View File

@@ -282,7 +282,7 @@ struct promise_type : result<T> {
}
};
template <typename T>
template <typename T = void>
class promise {
public:
using promise_type = async::promise_type<T>;
@@ -309,5 +309,27 @@ private:
coroutine_handle h;
};
/// Suspend current coroutine and invoke the callback with its handle.
/// Note the callback invoked before the coroutine is suspended. So it is
///
template <typename Callback>
auto suspend(Callback&& callback) {
struct suspend_awaiter {
Callback callback;
bool await_ready() noexcept {
return false;
}
void await_suspend(std::coroutine_handle<> handle) noexcept {
callback(handle);
}
void await_resume() noexcept {}
};
return suspend_awaiter{std::forward<Callback>(callback)};
}
} // namespace clice::async

121
include/Server/Scheduler.h Normal file
View File

@@ -0,0 +1,121 @@
#pragma once
#include <deque>
#include "Async.h"
#include "llvm/ADT/StringMap.h"
namespace clice {
class Compiler;
/// Information of building precompiled header.
struct PCH {
/// The path of this PCH.
std::string path;
/// The source file path.
std::string sourcePath;
/// The header part of source file used to build this PCH.
std::string preamble;
/// The arguments used to build this PCH.
std::string arguments;
/// All files involved in building this PCH(excluding the source file).
std::vector<std::string> deps;
/// FIXME: use asyncronous file system API.
bool needUpdate(llvm::StringRef sourceContent) {
/// Check whether the header part changed.
if(sourceContent.substr(0, preamble.size()) != preamble) {
return true;
}
/// Check timestamp of all files involved in building this PCH.
fs::file_status build;
if(auto error = fs::status(path, build)) {
llvm::errs() << "Error: " << error.message() << "\n";
std::terminate();
}
/// TODO: check whether deps changed through comparing timestamps.
return false;
}
void apply(Compiler& compiler) const;
};
/// Information of building precompiled module.
struct PCM {};
struct File;
struct Task {
/// Whether this task is a build task.
bool isBuild = false;
/// The coroutine handle of this task.
std::coroutine_handle<> waiting;
};
struct File {
bool isIdle = true;
/// The compiler instance of this file.
std::unique_ptr<Compiler> compiler;
std::deque<Task> waitings;
};
class Scheduler {
public:
async::promise<void> updatePCH(llvm::StringRef path,
llvm::StringRef content,
llvm::ArrayRef<const char*> args);
async::promise<void> updatePCM() {
co_return;
}
async::promise<void> buildAST(llvm::StringRef path, llvm::StringRef content);
async::promise<void> add(llvm::StringRef path, llvm::StringRef content);
async::promise<void> update(llvm::StringRef path, llvm::StringRef content);
async::promise<void> save(llvm::StringRef path);
async::promise<void> close(llvm::StringRef path);
/// Schedule a task for a file. If the file is building, the task will be
/// appended to the task list of the file and wait for the building to finish.
/// Otherwise, the task will be executed immediately.
template <typename Task>
auto schedule(llvm::StringRef path, Task&& task)
-> async::promise<decltype(task(std::declval<Compiler&>()))> {
auto& file = files[path];
if(!file.isIdle) {
co_await async::suspend([&](auto handle) {
file.waitings.push_back({.isBuild = false, .waiting = handle});
});
}
file.isIdle = false;
auto& compiler = *file.compiler;
auto result = co_await async::schedule_task([&task, &compiler] { return task(compiler); });
if(!file.waitings.empty()) {
auto task = std::move(file.waitings.front());
async::schedule(task.waiting);
file.waitings.pop_front();
}
file.isIdle = true;
co_return result;
}
private:
llvm::StringMap<PCH> pchs;
llvm::StringMap<File> files;
};
} // namespace clice

View File

@@ -3,6 +3,8 @@
#include "Async.h"
#include "Config.h"
#include "Logger.h"
#include "Scheduler.h"
#include "Basic/URI.h"
#include "Basic/Document.h"
#include "Compiler/Compiler.h"
#include "Support/JSON.h"
@@ -151,9 +153,6 @@ private:
});
}
llvm::StringMap<onRequest> requests;
llvm::StringMap<onNotification> notifications;
private:
/// ============================================================================
/// Lifestyle Message
@@ -245,81 +244,9 @@ private:
const proto::DocumentRangeFormattingParams& params);
private:
/// Information of building precompiled header.
struct PCH {
/// The path of this PCH.
std::string path;
/// The source file path.
std::string sourcePath;
/// The header part of source file used to build this PCH.
std::string preamble;
/// The arguments used to build this PCH.
std::string arguments;
/// All files involved in building this PCH(excluding the source file).
std::vector<std::string> deps;
/// FIXME: use asyncronous file system API.
bool needUpdate(llvm::StringRef sourceContent) {
/// Check whether the header part changed.
if(sourceContent.substr(0, preamble.size()) != preamble) {
return true;
}
/// Check timestamp of all files involved in building this PCH.
fs::file_status build;
if(auto error = fs::status(path, build)) {
llvm::errs() << "Error: " << error.message() << "\n";
std::terminate();
}
/// TODO: check whether deps changed through comparing timestamps.
return false;
}
};
/// Information of building precompiled module.
struct PCM {};
async::promise<void> updatePCH(llvm::StringRef filepath,
llvm::StringRef content,
llvm::ArrayRef<const char*> args);
async::promise<void> updatePCM() {
co_return;
}
async::promise<void> buildAST(llvm::StringRef filepath, llvm::StringRef content);
struct TranslationUnit {
enum class State {
Building,
Ready,
};
enum class TaskKind {
Build,
Consume,
};
struct Task {
TaskKind kind;
llvm::unique_function<async::promise<void>(Compiler&)> request;
};
State state;
std::unique_ptr<Compiler> compiler;
std::vector<Task> tasks;
};
/// Schedule a task for a file. If the file is building, the task will be
/// appended to the task list of the file and wait for the building to finish.
/// Otherwise, the task will be executed immediately.
async::promise<void> schedule(llvm::StringRef path,
llvm::unique_function<async::promise<void>(Compiler&)> callback);
private:
llvm::StringMap<PCH> pchs;
llvm::StringMap<TranslationUnit> units;
Scheduler scheduler;
llvm::StringMap<onRequest> requests;
llvm::StringMap<onNotification> notifications;
};
} // namespace clice