Skip to content

Commit d02df13

Browse files
[libSwiftScan][Caching] Add async download API
Add a new async download API from CASID. This helps build system better schedule the job/remove duplicated downloads, than using an opaque cached_output handle.
1 parent 66e4487 commit d02df13

File tree

3 files changed

+130
-83
lines changed

3 files changed

+130
-83
lines changed

include/swift-c/DependencyScan/DependencyScan.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,18 @@ SWIFTSCAN_PUBLIC void
610610
SWIFTSCAN_PUBLIC void swiftscan_cache_cancellation_token_dispose(
611611
swiftscan_cache_cancellation_token_t);
612612

613+
/// Async load CASObject using CASID. This is only to perform the download to
614+
/// local CAS where result is returned via callback. \c ctx is an opaque value
615+
/// that passed to the callback and \c swiftscan_cache_cancellation_token_t will
616+
/// return an token that can be used to cancel the async operation. The token
617+
/// needs to be freed by caller using
618+
/// `swiftscan_cache_cancellation_token_dispose`. If no token is needed, nullptr
619+
/// can be passed and no token will be returned.
620+
SWIFTSCAN_PUBLIC void swiftscan_cache_download_cas_object_async(
621+
swiftscan_cas_t, const char *id, void *ctx,
622+
void (*callback)(void *ctx, bool success, swiftscan_string_ref_t error),
623+
swiftscan_cache_cancellation_token_t *);
624+
613625
/// Create a swift cached compilation replay instance with its command-line
614626
/// invocation. Return nullptr when errors occurs and the error message is
615627
/// returned via \c error parameter and its caller needs to free the message

tools/libSwiftScan/SwiftCaching.cpp

Lines changed: 117 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,98 @@ bool swiftscan_cached_output_load(swiftscan_cached_output_t output,
467467
return success();
468468
}
469469

470+
namespace {
471+
/// Asynchronously visits the graph of the object node to ensure it's fully
472+
/// materialized.
473+
class AsyncObjectLoader
474+
: public std::enable_shared_from_this<AsyncObjectLoader> {
475+
void *Ctx;
476+
void (*Callback)(void *Ctx, bool, swiftscan_string_ref_t);
477+
llvm::cas::ObjectStore &CAS;
478+
479+
// The output handle to update after load if set.
480+
SwiftCachedOutputHandle *OutputHandle;
481+
482+
llvm::SmallDenseSet<llvm::cas::ObjectRef> ObjectsSeen;
483+
unsigned NumPending = 0;
484+
std::optional<llvm::cas::ObjectProxy> RootObj;
485+
std::atomic<bool> MissingNode{false};
486+
/// The first error that occurred.
487+
std::optional<llvm::Error> ErrOccurred;
488+
std::mutex Mutex;
489+
490+
public:
491+
AsyncObjectLoader(void *Ctx,
492+
void (*Callback)(void *Ctx, bool, swiftscan_string_ref_t),
493+
llvm::cas::ObjectStore &CAS,
494+
SwiftCachedOutputHandle *Handle = nullptr)
495+
: Ctx(Ctx), Callback(Callback), CAS(CAS), OutputHandle(Handle) {}
496+
497+
void visit(llvm::cas::ObjectRef Ref, bool IsRootNode) {
498+
{
499+
std::lock_guard<std::mutex> Guard(Mutex);
500+
if (!ObjectsSeen.insert(Ref).second)
501+
return;
502+
++NumPending;
503+
}
504+
auto This = shared_from_this();
505+
CAS.getProxyAsync(
506+
Ref, [This, IsRootNode](
507+
llvm::Expected<std::optional<llvm::cas::ObjectProxy>> Obj) {
508+
SWIFT_DEFER { This->finishedNode(); };
509+
if (!Obj) {
510+
This->encounteredError(Obj.takeError());
511+
return;
512+
}
513+
if (!*Obj) {
514+
This->MissingNode = true;
515+
return;
516+
}
517+
if (IsRootNode)
518+
This->RootObj = *Obj;
519+
if (auto Err = (*Obj)->forEachReference(
520+
[&This](llvm::cas::ObjectRef Sub) -> llvm::Error {
521+
This->visit(Sub, /*IsRootNode*/ false);
522+
return llvm::Error::success();
523+
})) {
524+
This->encounteredError(std::move(Err));
525+
return;
526+
}
527+
});
528+
}
529+
530+
void finishedNode() {
531+
{
532+
std::lock_guard<std::mutex> Guard(Mutex);
533+
assert(NumPending);
534+
--NumPending;
535+
if (NumPending != 0)
536+
return;
537+
}
538+
if (ErrOccurred) {
539+
std::string ErrMsg = toString(std::move(*ErrOccurred));
540+
return Callback(Ctx, false, createNonOwningString(ErrMsg));
541+
}
542+
if (MissingNode)
543+
return Callback(Ctx, false, swift::c_string_utils::create_null());
544+
545+
if (OutputHandle)
546+
OutputHandle->LoadedProxy = RootObj;
547+
return Callback(Ctx, true, swift::c_string_utils::create_null());
548+
}
549+
550+
/// Only keeps the first error that occurred.
551+
void encounteredError(llvm::Error &&E) {
552+
std::lock_guard<std::mutex> Guard(Mutex);
553+
if (ErrOccurred) {
554+
llvm::consumeError(std::move(E));
555+
return;
556+
}
557+
ErrOccurred = std::move(E);
558+
}
559+
};
560+
} // namespace
561+
470562
void swiftscan_cached_output_load_async(
471563
swiftscan_cached_output_t output, void *ctx,
472564
void (*callback)(void *ctx, bool success, swiftscan_string_ref_t error),
@@ -478,93 +570,35 @@ void swiftscan_cached_output_load_async(
478570
if (Out->LoadedProxy)
479571
return callback(ctx, true, swift::c_string_utils::create_null());
480572

481-
/// Asynchronously visits the graph of the object node to ensure it's fully
482-
/// materialized.
483-
class AsyncObjectLoader
484-
: public std::enable_shared_from_this<AsyncObjectLoader> {
485-
void *Ctx;
486-
void (*Callback)(void *Ctx, bool, swiftscan_string_ref_t);
487-
SwiftCachedOutputHandle &Handle;
488-
489-
llvm::SmallDenseSet<llvm::cas::ObjectRef> ObjectsSeen;
490-
unsigned NumPending = 0;
491-
std::optional<llvm::cas::ObjectProxy> RootObj;
492-
std::atomic<bool> MissingNode{false};
493-
/// The first error that occurred.
494-
std::optional<llvm::Error> ErrOccurred;
495-
std::mutex Mutex;
496-
497-
public:
498-
AsyncObjectLoader(void *Ctx,
499-
void (*Callback)(void *Ctx, bool, swiftscan_string_ref_t),
500-
SwiftCachedOutputHandle &Handle)
501-
: Ctx(Ctx), Callback(Callback), Handle(Handle) {}
502-
503-
void visit(llvm::cas::ObjectRef Ref, bool IsRootNode) {
504-
{
505-
std::lock_guard<std::mutex> Guard(Mutex);
506-
if (!ObjectsSeen.insert(Ref).second)
507-
return;
508-
++NumPending;
509-
}
510-
auto This = shared_from_this();
511-
Handle.CAS.getProxyAsync(
512-
Ref, [This, IsRootNode](
513-
llvm::Expected<std::optional<llvm::cas::ObjectProxy>> Obj) {
514-
SWIFT_DEFER { This->finishedNode(); };
515-
if (!Obj) {
516-
This->encounteredError(Obj.takeError());
517-
return;
518-
}
519-
if (!*Obj) {
520-
This->MissingNode = true;
521-
return;
522-
}
523-
if (IsRootNode)
524-
This->RootObj = *Obj;
525-
if (auto Err = (*Obj)->forEachReference(
526-
[&This](llvm::cas::ObjectRef Sub) -> llvm::Error {
527-
This->visit(Sub, /*IsRootNode*/ false);
528-
return llvm::Error::success();
529-
})) {
530-
This->encounteredError(std::move(Err));
531-
return;
532-
}
533-
});
534-
}
573+
auto Loader =
574+
std::make_shared<AsyncObjectLoader>(ctx, callback, Out->CAS, Out);
575+
Loader->visit(Out->Ref, /*IsRootNode*/ true);
576+
}
535577

536-
void finishedNode() {
537-
{
538-
std::lock_guard<std::mutex> Guard(Mutex);
539-
assert(NumPending);
540-
--NumPending;
541-
if (NumPending != 0)
542-
return;
543-
}
544-
if (ErrOccurred) {
545-
std::string ErrMsg = toString(std::move(*ErrOccurred));
546-
return Callback(Ctx, false, createNonOwningString(ErrMsg));
547-
}
548-
if (MissingNode)
549-
return Callback(Ctx, false, swift::c_string_utils::create_null());
550-
551-
Handle.LoadedProxy = RootObj;
552-
return Callback(Ctx, true, swift::c_string_utils::create_null());
553-
}
578+
void swiftscan_cache_download_cas_object_async(
579+
swiftscan_cas_t cas, const char *id, void *ctx,
580+
void (*callback)(void *ctx, bool success, swiftscan_string_ref_t error),
581+
swiftscan_cache_cancellation_token_t *token) {
582+
if (token)
583+
*token = nullptr;
554584

555-
/// Only keeps the first error that occurred.
556-
void encounteredError(llvm::Error &&E) {
557-
std::lock_guard<std::mutex> Guard(Mutex);
558-
if (ErrOccurred) {
559-
llvm::consumeError(std::move(E));
560-
return;
561-
}
562-
ErrOccurred = std::move(E);
563-
}
585+
auto failure = [ctx, callback](llvm::Error E) {
586+
std::string ErrMsg = toString(std::move(E));
587+
return callback(ctx, false, createNonOwningString(ErrMsg));
564588
};
565589

566-
auto Loader = std::make_shared<AsyncObjectLoader>(ctx, callback, *Out);
567-
Loader->visit(Out->Ref, /*IsRootNode*/ true);
590+
auto &CAS = unwrap(cas)->getCAS();
591+
auto ID = CAS.parseID(id);
592+
if (!ID)
593+
return failure(ID.takeError());
594+
595+
auto Ref = CAS.getReference(*ID);
596+
if (!Ref)
597+
return callback(ctx, false, swift::c_string_utils::create_null());
598+
599+
auto Loader =
600+
std::make_shared<AsyncObjectLoader>(ctx, callback, CAS);
601+
Loader->visit(*Ref, /*IsRootNode*/ true);
568602
}
569603

570604
bool swiftscan_cached_output_is_materialized(swiftscan_cached_output_t output) {

tools/libSwiftScan/libSwiftScan.exports

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ swiftscan_cached_output_is_materialized
9898
swiftscan_cached_output_get_casid
9999
swiftscan_cached_output_get_name
100100
swiftscan_cached_output_dispose
101+
swiftscan_cache_download_cas_object_async
101102
swiftscan_cache_replay_instance_create
102103
swiftscan_cache_replay_instance_dispose
103104
swiftscan_cache_action_cancel

0 commit comments

Comments
 (0)