Skip to content

Commit 97d4829

Browse files
authored
Merge pull request #8934 from akyrtzi/akyrtzi/pr/6.0-cas-queries-cancellation
[6.0][clang][cas] Implement cancellation functionality for the asynchronous CAS query APIs
2 parents 3ccd82b + a2f4129 commit 97d4829

File tree

15 files changed

+500
-154
lines changed

15 files changed

+500
-154
lines changed

clang/test/CAS/libclang-replay-job.c

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
// RUN: clang-scan-deps -compilation-database %t/cdb.json \
88
// RUN: -format experimental-include-tree-full \
99
// RUN: -cas-path %t/cas -fcas-plugin-path %llvmshlibdir/libCASPluginTest%pluginext \
10-
// RUN: -fcas-plugin-option upstream-path=%t/cas-upstream -fcas-plugin-option no-logging \
10+
// RUN: -fcas-plugin-option no-logging \
1111
// RUN: > %t/deps.json
1212

1313
// RUN: %deps-to-rsp %t/deps.json --tu-index 0 > %t/cc1.rsp
@@ -24,21 +24,30 @@
2424
// RUN: -e "s/^.*hit for '//" \
2525
// RUN: -e "s/' .*$//" > %t/cache-key
2626

27+
// RUN: c-index-test core -upload-cached-job -cas-path %t/cas @%t/cache-key -test-cas-cancellation \
28+
// RUN: -fcas-plugin-path %llvmshlibdir/libCASPluginTest%pluginext \
29+
// RUN: -fcas-plugin-option upstream-path=%t/cas-upstream \
30+
// RUN: 2>&1 | FileCheck %s --check-prefix=UPLOAD-CANCEL
31+
// UPLOAD-CANCEL: actioncache_put_for_digest_async cancelled
32+
2733
// Delete the "local" cache and use the "upstream" one to re-materialize the outputs locally.
2834
// RUN: rm -rf %t/cas
2935

3036
// Re-run the scan to populate the include-tree in the cas
3137
// RUN: clang-scan-deps -compilation-database %t/cdb.json \
3238
// RUN: -format experimental-include-tree-full \
3339
// RUN: -cas-path %t/cas -fcas-plugin-path %llvmshlibdir/libCASPluginTest%pluginext \
34-
// RUN: -fcas-plugin-option upstream-path=%t/cas-upstream -fcas-plugin-option no-logging \
40+
// RUN: -fcas-plugin-option no-logging \
3541
// RUN: > %t/deps2.json
3642
// RUN: diff -u %t/deps.json %t/deps2.json
3743

3844

39-
// RUN: c-index-test core -materialize-cached-job -cas-path %t/cas @%t/cache-key \
45+
// RUN: c-index-test core -materialize-cached-job -cas-path %t/cas @%t/cache-key -test-cas-cancellation \
4046
// RUN: -fcas-plugin-path %llvmshlibdir/libCASPluginTest%pluginext \
41-
// RUN: -fcas-plugin-option upstream-path=%t/cas-upstream -fcas-plugin-option no-logging
47+
// RUN: -fcas-plugin-option upstream-path=%t/cas-upstream \
48+
// RUN: 2>&1 | FileCheck %s --check-prefix=MATERIALIZE-CANCEL
49+
// MATERIALIZE-CANCEL: actioncache_get_for_digest_async cancelled
50+
// MATERIALIZE-CANCEL: load_object_async cancelled
4251

4352
// RUN: c-index-test core -replay-cached-job -cas-path %t/cas @%t/cache-key \
4453
// RUN: -fcas-plugin-path %llvmshlibdir/libCASPluginTest%pluginext \

clang/tools/c-index-test/core_main.cpp

Lines changed: 171 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ enum class ActionType {
5858
AggregateAsJSON,
5959
ScanDeps,
6060
ScanDepsByModuleName,
61+
UploadCachedJob,
6162
MaterializeCachedJob,
6263
ReplayCachedJob,
6364
PruneCAS,
@@ -85,6 +86,8 @@ Action(cl::desc("Action:"), cl::init(ActionType::None),
8586
"Get file dependencies"),
8687
clEnumValN(ActionType::ScanDepsByModuleName, "scan-deps-by-mod-name",
8788
"Get file dependencies by module name alone"),
89+
clEnumValN(ActionType::UploadCachedJob, "upload-cached-job",
90+
"Upload cached compilation data to upstream CAS"),
8891
clEnumValN(ActionType::MaterializeCachedJob, "materialize-cached-job",
8992
"Materialize cached compilation data from upstream CAS"),
9093
clEnumValN(ActionType::ReplayCachedJob, "replay-cached-job",
@@ -154,6 +157,10 @@ static cl::list<std::string> CASPluginOpts("fcas-plugin-option",
154157
cl::desc("Plugin CAS Options"));
155158
static llvm::cl::opt<std::string>
156159
WorkingDir("working-dir", llvm::cl::desc("Path for working directory"));
160+
static cl::opt<bool> TestCASCancellation(
161+
"test-cas-cancellation",
162+
cl::desc(
163+
"perform extra CAS API invocation and cancel it for testing purposes"));
157164
}
158165
} // anonymous namespace
159166

@@ -865,41 +872,117 @@ static int scanDeps(ArrayRef<const char *> Args, std::string WorkingDirectory,
865872
return 1;
866873
}
867874

868-
static int materializeCachedJob(std::string CacheKey, CXCASDatabases DBs) {
869-
struct CompResult {
870-
CXCASCachedCompilation Comp = nullptr;
871-
CXError Err = nullptr;
875+
static int uploadCachedJob(std::string CacheKey, CXCASDatabases DBs) {
876+
CXError Err = nullptr;
877+
CXCASCachedCompilation CComp = clang_experimental_cas_getCachedCompilation(
878+
DBs, CacheKey.c_str(), /*Globally*/ false, &Err);
879+
auto CleanupCachedComp = llvm::make_scope_exit(
880+
[&] { clang_experimental_cas_CachedCompilation_dispose(CComp); });
881+
if (!CComp) {
882+
if (Err) {
883+
llvm::errs() << clang_Error_getDescription(Err) << "\n";
884+
clang_Error_dispose(Err);
885+
} else {
886+
llvm::errs() << "cache key was not found\n";
887+
}
888+
return 1;
889+
}
890+
891+
/// \returns true of an error occurred.
892+
auto invokeMakeGlobal = [&](bool Cancel) -> bool {
893+
CXCASCancellationToken CancelToken = nullptr;
894+
auto CleanupCancelTok = llvm::make_scope_exit([&] {
895+
if (Cancel)
896+
clang_experimental_cas_CancellationToken_dispose(CancelToken);
897+
});
898+
899+
std::promise<CXError> CallPromise;
900+
clang_experimental_cas_CachedCompilation_makeGlobal(
901+
CComp, &CallPromise,
902+
[](void *Ctx, CXError Err) {
903+
static_cast<std::promise<CXError> *>(Ctx)->set_value(Err);
904+
},
905+
Cancel ? &CancelToken : nullptr);
906+
if (Cancel) {
907+
clang_experimental_cas_CancellationToken_cancel(CancelToken);
908+
}
909+
CXError CallRes = CallPromise.get_future().get();
910+
if (CallRes) {
911+
llvm::errs() << clang_Error_getDescription(CallRes) << "\n";
912+
return true;
913+
}
914+
return false;
872915
};
873-
std::promise<CompResult> CompPromise;
874-
auto CompFuture = CompPromise.get_future();
875-
struct CompCall {
876-
std::promise<CompResult> Promise;
916+
917+
if (options::TestCASCancellation) {
918+
// Cancel an invocation for testing purposes.
919+
if (invokeMakeGlobal(/*Cancel=*/true))
920+
return 1;
921+
}
922+
if (invokeMakeGlobal(/*Cancel=*/false))
923+
return 1;
924+
925+
return 0;
926+
}
927+
928+
static int materializeCachedJob(std::string CacheKey, CXCASDatabases DBs) {
929+
/// \returns true of an error occurred.
930+
auto invokeGetCachedCompilation =
931+
[&](bool Cancel, CXCASCachedCompilation &OutComp) -> bool {
932+
OutComp = nullptr;
933+
CXCASCancellationToken CancelToken = nullptr;
934+
auto CleanupCancelTok = llvm::make_scope_exit([&] {
935+
if (Cancel)
936+
clang_experimental_cas_CancellationToken_dispose(CancelToken);
937+
});
938+
939+
struct CompResult {
940+
CXCASCachedCompilation Comp = nullptr;
941+
CXError Err = nullptr;
942+
};
943+
std::promise<CompResult> CompPromise;
944+
auto CompFuture = CompPromise.get_future();
945+
struct CompCall {
946+
std::promise<CompResult> Promise;
947+
};
948+
CompCall *CallCtx = new CompCall{std::move(CompPromise)};
949+
clang_experimental_cas_getCachedCompilation_async(
950+
DBs, CacheKey.c_str(), /*Globally*/ true, CallCtx,
951+
[](void *Ctx, CXCASCachedCompilation Comp, CXError Err) {
952+
std::unique_ptr<CompCall> CallCtx(static_cast<CompCall *>(Ctx));
953+
CallCtx->Promise.set_value(CompResult{Comp, Err});
954+
},
955+
Cancel ? &CancelToken : nullptr);
956+
if (Cancel) {
957+
clang_experimental_cas_CancellationToken_cancel(CancelToken);
958+
}
959+
CompResult Res = CompFuture.get();
960+
OutComp = Res.Comp;
961+
if (!OutComp && !Cancel) {
962+
if (Res.Err) {
963+
llvm::errs() << clang_Error_getDescription(Res.Err) << "\n";
964+
clang_Error_dispose(Res.Err);
965+
} else {
966+
llvm::errs() << "cache key was not found\n";
967+
}
968+
return true;
969+
}
970+
return false;
877971
};
878-
CompCall *CallCtx = new CompCall{std::move(CompPromise)};
879-
clang_experimental_cas_getCachedCompilation_async(
880-
DBs, CacheKey.c_str(), /*Globally*/ true, CallCtx,
881-
[](void *Ctx, CXCASCachedCompilation Comp, CXError Err) {
882-
std::unique_ptr<CompCall> CallCtx(static_cast<CompCall *>(Ctx));
883-
CallCtx->Promise.set_value(CompResult{Comp, Err});
884-
},
885-
/*cancelToken*/ nullptr);
886-
CompResult Res = CompFuture.get();
887-
CXCASCachedCompilation CComp = Res.Comp;
888972

973+
CXCASCachedCompilation CComp = nullptr;
889974
auto CleanupCachedComp = llvm::make_scope_exit([&] {
890975
if (CComp)
891976
clang_experimental_cas_CachedCompilation_dispose(CComp);
892-
if (Res.Err)
893-
clang_Error_dispose(Res.Err);
894977
});
895-
if (!CComp) {
896-
if (Res.Err) {
897-
llvm::errs() << clang_Error_getDescription(Res.Err) << "\n";
898-
} else {
899-
llvm::errs() << "cache key was not found\n";
900-
}
901-
return 1;
978+
979+
if (options::TestCASCancellation) {
980+
// Cancel an invocation for testing purposes.
981+
if (invokeGetCachedCompilation(/*Cancel=*/true, CComp))
982+
return 1;
902983
}
984+
if (invokeGetCachedCompilation(/*Cancel=*/false, CComp))
985+
return 1;
903986

904987
for (unsigned
905988
I = 0,
@@ -912,42 +995,63 @@ static int materializeCachedJob(std::string CacheKey, CXCASDatabases DBs) {
912995
auto CleanupOutputID =
913996
llvm::make_scope_exit([&] { clang_disposeString(OutputID); });
914997

915-
struct LoadResult {
916-
CXCASObject Obj = nullptr;
917-
CXError Err = nullptr;
918-
};
919-
std::promise<LoadResult> LoadPromise;
920-
auto LoadFuture = LoadPromise.get_future();
921-
struct LoadCall {
922-
std::promise<LoadResult> Promise;
923-
};
924-
LoadCall *CallCtx = new LoadCall{std::move(LoadPromise)};
925-
clang_experimental_cas_loadObjectByString_async(
926-
DBs, clang_getCString(OutputID), CallCtx,
927-
[](void *Ctx, CXCASObject Obj, CXError Err) {
928-
std::unique_ptr<LoadCall> CallCtx(static_cast<LoadCall *>(Ctx));
929-
CallCtx->Promise.set_value(LoadResult{Obj, Err});
930-
},
931-
/*cancelToken*/ nullptr);
998+
/// \returns true of an error occurred.
999+
auto invokeLoadObject = [&](bool Cancel, CXCASObject &OutObj) -> bool {
1000+
OutObj = nullptr;
1001+
CXCASCancellationToken CancelToken = nullptr;
1002+
auto CleanupCancelTok = llvm::make_scope_exit([&] {
1003+
if (Cancel)
1004+
clang_experimental_cas_CancellationToken_dispose(CancelToken);
1005+
});
9321006

933-
LoadResult Res = LoadFuture.get();
934-
CXCASObject CASObj = Res.Obj;
1007+
struct LoadResult {
1008+
CXCASObject Obj = nullptr;
1009+
CXError Err = nullptr;
1010+
};
1011+
std::promise<LoadResult> LoadPromise;
1012+
auto LoadFuture = LoadPromise.get_future();
1013+
struct LoadCall {
1014+
std::promise<LoadResult> Promise;
1015+
};
1016+
LoadCall *CallCtx = new LoadCall{std::move(LoadPromise)};
1017+
clang_experimental_cas_loadObjectByString_async(
1018+
DBs, clang_getCString(OutputID), CallCtx,
1019+
[](void *Ctx, CXCASObject Obj, CXError Err) {
1020+
std::unique_ptr<LoadCall> CallCtx(static_cast<LoadCall *>(Ctx));
1021+
CallCtx->Promise.set_value(LoadResult{Obj, Err});
1022+
},
1023+
Cancel ? &CancelToken : nullptr);
1024+
if (Cancel) {
1025+
clang_experimental_cas_CancellationToken_cancel(CancelToken);
1026+
}
1027+
LoadResult Res = LoadFuture.get();
1028+
OutObj = Res.Obj;
1029+
if (!OutObj && !Cancel) {
1030+
if (Res.Err) {
1031+
llvm::errs() << clang_Error_getDescription(Res.Err) << "\n";
1032+
clang_Error_dispose(Res.Err);
1033+
} else {
1034+
llvm::errs() << "cache key was not found\n";
1035+
}
1036+
return true;
1037+
}
1038+
return false;
1039+
};
9351040

1041+
CXCASObject CASObj = nullptr;
9361042
auto CleanupLoadObj = llvm::make_scope_exit([&] {
9371043
if (CASObj)
9381044
clang_experimental_cas_CASObject_dispose(CASObj);
939-
if (Res.Err)
940-
clang_Error_dispose(Res.Err);
9411045
});
9421046

943-
if (!CASObj) {
944-
if (Res.Err) {
945-
llvm::errs() << clang_Error_getDescription(Res.Err) << "\n";
946-
} else {
947-
llvm::errs() << "compilation output ID was not found\n";
948-
}
949-
return 1;
1047+
if (options::TestCASCancellation) {
1048+
// Cancel an invocation for testing purposes.
1049+
if (invokeLoadObject(/*Cancel=*/true, CASObj))
1050+
return 1;
9501051
}
1052+
if (invokeLoadObject(/*Cancel=*/false, CASObj))
1053+
return 1;
1054+
9511055
if (!clang_experimental_cas_CachedCompilation_isOutputMaterialized(CComp,
9521056
I))
9531057
report_fatal_error("output was not materialized?");
@@ -1393,6 +1497,18 @@ int indextest_core_main(int argc, const char **argv) {
13931497
options::OutputDir, DBs, options::ModuleName);
13941498
}
13951499

1500+
if (options::Action == ActionType::UploadCachedJob) {
1501+
if (options::InputFiles.empty()) {
1502+
errs() << "error: missing cache key\n";
1503+
return 1;
1504+
}
1505+
if (!DBs) {
1506+
errs() << "error: CAS was not configured\n";
1507+
return 1;
1508+
}
1509+
return uploadCachedJob(options::InputFiles[0], DBs);
1510+
}
1511+
13961512
if (options::Action == ActionType::MaterializeCachedJob) {
13971513
if (options::InputFiles.empty()) {
13981514
errs() << "error: missing cache key\n";

0 commit comments

Comments
 (0)