Skip to content

Commit c0d24c6

Browse files
akyrtzibenlangmuir
authored andcommitted
[CAS/ActionCache] Add asynchronous versions for the ActionCache APIs
(cherry picked from commit ab601d8)
1 parent a36aad5 commit c0d24c6

File tree

16 files changed

+347
-34
lines changed

16 files changed

+347
-34
lines changed

clang/lib/Frontend/CompileJobCacheResult.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ Error CompileJobCacheResult::forEachLoadedOutput(
4141
size_t Count = getNumOutputs();
4242
for (size_t I = 0; I < Count; ++I) {
4343
ObjectRef Ref = getOutputObject(I);
44-
FutureOutputs.push_back(getCAS().getProxyAsync(Ref));
44+
FutureOutputs.push_back(getCAS().getProxyFuture(Ref));
4545
}
4646

4747
// Make sure all the outputs have materialized.

clang/lib/Frontend/CompilerInstance.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2469,7 +2469,7 @@ static bool addCachedModuleFileToInMemoryCache(
24692469
llvm::report_fatal_error("missing main output");
24702470
// FIXME: We wait to materialize each module file before proceeding, which
24712471
// introduces latency for a network CAS. Instead we should collect all the
2472-
// module keys and materialize them concurrently using \c getProxyAsync, for
2472+
// module keys and materialize them concurrently using \c getProxyFuture, for
24732473
// better network utilization.
24742474
auto OutputProxy = CAS.getProxy(Output->Object);
24752475
if (!OutputProxy) {

llvm/include/llvm-c/CAS/PluginAPI_functions.h

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,6 @@ LLCAS_PUBLIC llcas_lookup_result_t llcas_cas_load_object(
189189
* Whether the call is asynchronous or not depends on the implementation.
190190
*
191191
* \param ctx_cb pointer to pass to the callback function.
192-
*
193192
*/
194193
LLCAS_PUBLIC void llcas_cas_load_object_async(llcas_cas_t, llcas_objectid_t,
195194
void *ctx_cb,
@@ -257,6 +256,18 @@ LLCAS_PUBLIC llcas_lookup_result_t llcas_actioncache_get_for_digest(
257256
llcas_cas_t, llcas_digest_t key, llcas_objectid_t *p_value, bool globally,
258257
char **error);
259258

259+
/**
260+
* Like \c llcas_actioncache_get_for_digest but result is provided to a callback
261+
* function. Whether the call is asynchronous or not depends on the
262+
* implementation.
263+
*
264+
* \param ctx_cb pointer to pass to the callback function.
265+
*/
266+
LLCAS_PUBLIC void
267+
llcas_actioncache_get_for_digest_async(llcas_cas_t, llcas_digest_t key,
268+
bool globally, void *ctx_cb,
269+
llcas_actioncache_get_cb);
270+
260271
/**
261272
* Associates a \c llcas_objectid_t \p value with a \p key. It is invalid to set
262273
* a different \p value to the same \p key.
@@ -274,6 +285,18 @@ LLCAS_PUBLIC bool llcas_actioncache_put_for_digest(llcas_cas_t,
274285
llcas_objectid_t value,
275286
bool globally, char **error);
276287

288+
/**
289+
* Like \c llcas_actioncache_put_for_digest but result is provided to a callback
290+
* function. Whether the call is asynchronous or not depends on the
291+
* implementation.
292+
*
293+
* \param ctx_cb pointer to pass to the callback function.
294+
*/
295+
LLCAS_PUBLIC void
296+
llcas_actioncache_put_for_digest_async(llcas_cas_t, llcas_digest_t key,
297+
llcas_objectid_t value, bool globally,
298+
void *ctx_cb, llcas_actioncache_put_cb);
299+
277300
LLVM_C_EXTERN_C_END
278301

279302
#endif /* LLVM_C_CAS_PLUGINAPI_FUNCTIONS_H */

llvm/include/llvm-c/CAS/PluginAPI_types.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,4 +94,25 @@ typedef enum {
9494
typedef void (*llcas_cas_load_object_cb)(void *ctx, llcas_lookup_result_t,
9595
llcas_loaded_object_t, char *error);
9696

97+
/**
98+
* Callback for \c llcas_actioncache_get_for_digest_async.
99+
*
100+
* \param ctx pointer passed through from the
101+
* \c llcas_actioncache_get_for_digest_async call.
102+
* \param error message if an error occurred. If set, the memory it points to
103+
* needs to be released via \c llcas_string_dispose.
104+
*/
105+
typedef void (*llcas_actioncache_get_cb)(void *ctx, llcas_lookup_result_t,
106+
llcas_objectid_t, char *error);
107+
108+
/**
109+
* Callback for \c llcas_actioncache_put_for_digest_async.
110+
*
111+
* \param ctx pointer passed through from the
112+
* \c llcas_actioncache_put_for_digest_async call.
113+
* \param error message if an error occurred. If set, the memory it points to
114+
* needs to be released via \c llcas_string_dispose.
115+
*/
116+
typedef void (*llcas_actioncache_put_cb)(void *ctx, bool failed, char *error);
117+
97118
#endif /* LLVM_C_CAS_PLUGINAPI_TYPES_H */

llvm/include/llvm/CAS/ActionCache.h

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@
99
#ifndef LLVM_CAS_CASACTIONCACHE_H
1010
#define LLVM_CAS_CASACTIONCACHE_H
1111

12+
#include "llvm/ADT/FunctionExtras.h"
1213
#include "llvm/ADT/Optional.h"
1314
#include "llvm/ADT/StringRef.h"
1415
#include "llvm/CAS/CASID.h"
1516
#include "llvm/CAS/CASReference.h"
1617
#include "llvm/Support/Error.h"
18+
#include <future>
1719

1820
namespace llvm::cas {
1921

@@ -40,6 +42,20 @@ class CacheKey {
4042
std::string Key;
4143
};
4244

45+
using AsyncCASIDValue = AsyncValue<CASID>;
46+
47+
/// This is used to workaround the issue of MSVC needing default-constructible
48+
/// types for \c std::promise/future.
49+
struct AsyncErrorValue {
50+
Error take() { return std::move(Value); }
51+
52+
AsyncErrorValue() : Value(Error::success()) {}
53+
AsyncErrorValue(Error &&E) : Value(std::move(E)) {}
54+
55+
private:
56+
Error Value;
57+
};
58+
4359
/// A cache from a key describing an action to the result of doing it.
4460
///
4561
/// Actions are expected to be pure (collision is an error).
@@ -57,6 +73,18 @@ class ActionCache {
5773
return getImpl(arrayRefFromStringRef(ActionKey.getKey()), Globally);
5874
}
5975

76+
/// Asynchronous version of \c get.
77+
std::future<AsyncCASIDValue> getFuture(const CacheKey &ActionKey,
78+
bool Globally = false) const;
79+
80+
/// Asynchronous version of \c get.
81+
void getAsync(
82+
const CacheKey &ActionKey, bool Globally,
83+
unique_function<void(Expected<std::optional<CASID>>)> Callback) const {
84+
return getImplAsync(arrayRefFromStringRef(ActionKey.getKey()), Globally,
85+
std::move(Callback));
86+
}
87+
6088
/// Cache \p Result for the \p ActionKey computation.
6189
///
6290
/// \param Globally if true it is a hint to the underlying implementation that
@@ -70,13 +98,35 @@ class ActionCache {
7098
return putImpl(arrayRefFromStringRef(ActionKey.getKey()), Result, Globally);
7199
}
72100

101+
/// Asynchronous version of \c put.
102+
std::future<AsyncErrorValue> putFuture(const CacheKey &ActionKey,
103+
const CASID &Result,
104+
bool Globally = false);
105+
106+
/// Asynchronous version of \c put.
107+
void putAsync(const CacheKey &ActionKey, const CASID &Result, bool Globally,
108+
unique_function<void(Error)> Callback) {
109+
assert(Result.getContext().getHashSchemaIdentifier() ==
110+
getContext().getHashSchemaIdentifier() &&
111+
"Hash schema mismatch");
112+
return putImplAsync(arrayRefFromStringRef(ActionKey.getKey()), Result,
113+
Globally, std::move(Callback));
114+
}
115+
73116
virtual ~ActionCache() = default;
74117

75118
protected:
76119
virtual Expected<Optional<CASID>> getImpl(ArrayRef<uint8_t> ResolvedKey,
77120
bool Globally) const = 0;
121+
virtual void getImplAsync(
122+
ArrayRef<uint8_t> ResolvedKey, bool Globally,
123+
unique_function<void(Expected<std::optional<CASID>>)> Callback) const;
124+
78125
virtual Error putImpl(ArrayRef<uint8_t> ResolvedKey, const CASID &Result,
79126
bool Globally) = 0;
127+
virtual void putImplAsync(ArrayRef<uint8_t> ResolvedKey, const CASID &Result,
128+
bool Globally,
129+
unique_function<void(Error)> Callback);
80130

81131
ActionCache(const CASContext &Context) : Context(Context) {}
82132

llvm/include/llvm/CAS/CASID.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "llvm/ADT/SmallString.h"
1515
#include "llvm/ADT/StringExtras.h"
1616
#include "llvm/ADT/StringRef.h"
17+
#include "llvm/Support/Error.h"
1718

1819
namespace llvm {
1920

@@ -121,6 +122,21 @@ class CASID {
121122
SmallString<32> Hash;
122123
};
123124

125+
/// This is used to workaround the issue of MSVC needing default-constructible
126+
/// types for \c std::promise/future.
127+
template <typename T> struct AsyncValue {
128+
Expected<std::optional<T>> take() { return std::move(Value); }
129+
130+
AsyncValue() : Value(std::nullopt) {}
131+
AsyncValue(Error &&E) : Value(std::move(E)) {}
132+
AsyncValue(T &&V) : Value(std::move(V)) {}
133+
AsyncValue(std::nullopt_t) : Value(std::nullopt) {}
134+
AsyncValue(Expected<std::optional<T>> &&Obj) : Value(std::move(Obj)) {}
135+
136+
private:
137+
Expected<std::optional<T>> Value;
138+
};
139+
124140
} // namespace cas
125141

126142
template <> struct DenseMapInfo<cas::CASID> {

llvm/include/llvm/CAS/ObjectStore.h

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,11 @@ template <typename T> class unique_function;
2626

2727
namespace cas {
2828

29-
struct AsyncProxyValue;
3029
class ObjectStore;
3130
class ObjectProxy;
3231

32+
using AsyncProxyValue = AsyncValue<ObjectProxy>;
33+
3334
/// Content-addressable storage for objects.
3435
///
3536
/// Conceptually, objects are stored in a "unique set".
@@ -251,7 +252,12 @@ class ObjectStore {
251252
Expected<std::optional<ObjectProxy>> getProxyIfExists(ObjectRef Ref);
252253

253254
/// Asynchronous version of \c getProxyIfExists.
254-
std::future<AsyncProxyValue> getProxyAsync(ObjectRef Ref);
255+
std::future<AsyncProxyValue> getProxyFuture(ObjectRef Ref);
256+
257+
/// Asynchronous version of \c getProxyIfExists using a callback.
258+
void getProxyAsync(
259+
ObjectRef Ref,
260+
unique_function<void(Expected<std::optional<ObjectProxy>>)> Callback);
255261

256262
/// Read the data from \p Data into \p OS.
257263
uint64_t readData(ObjectHandle Node, raw_ostream &OS, uint64_t Offset = 0,
@@ -347,20 +353,6 @@ class ObjectProxy {
347353
ObjectHandle H;
348354
};
349355

350-
/// This is used to workaround the issue of MSVC needing default-constructible
351-
/// types for \c std::promise/future.
352-
struct AsyncProxyValue {
353-
Expected<std::optional<ObjectProxy>> take() { return std::move(Value); }
354-
355-
AsyncProxyValue() : Value(std::nullopt) {}
356-
AsyncProxyValue(Error &&E) : Value(std::move(E)) {}
357-
AsyncProxyValue(ObjectProxy V) : Value(std::move(V)) {}
358-
AsyncProxyValue(std::nullopt_t) : Value(std::nullopt) {}
359-
360-
private:
361-
Expected<std::optional<ObjectProxy>> Value;
362-
};
363-
364356
std::unique_ptr<ObjectStore> createInMemoryCAS();
365357

366358
/// \returns true if \c LLVM_ENABLE_ONDISK_CAS configuration was enabled.

llvm/lib/CAS/ActionCache.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,46 @@ CacheKey::CacheKey(const ObjectProxy &Proxy)
2020
: CacheKey(Proxy.getCAS(), Proxy.getRef()) {}
2121
CacheKey::CacheKey(const ObjectStore &CAS, const ObjectRef &Ref)
2222
: Key(toStringRef(CAS.getID(Ref).getHash())) {}
23+
24+
std::future<AsyncCASIDValue> ActionCache::getFuture(const CacheKey &ActionKey,
25+
bool Globally) const {
26+
std::promise<AsyncCASIDValue> Promise;
27+
auto Future = Promise.get_future();
28+
getAsync(ActionKey, Globally,
29+
[Promise =
30+
std::move(Promise)](Expected<std::optional<CASID>> ID) mutable {
31+
Promise.set_value(std::move(ID));
32+
});
33+
return Future;
34+
}
35+
36+
std::future<AsyncErrorValue> ActionCache::putFuture(const CacheKey &ActionKey,
37+
const CASID &Result,
38+
bool Globally) {
39+
std::promise<AsyncErrorValue> Promise;
40+
auto Future = Promise.get_future();
41+
putAsync(ActionKey, Result, Globally,
42+
[Promise = std::move(Promise)](Error E) mutable {
43+
Promise.set_value(std::move(E));
44+
});
45+
return Future;
46+
}
47+
48+
void ActionCache::getImplAsync(
49+
ArrayRef<uint8_t> ResolvedKey, bool Globally,
50+
unique_function<void(Expected<std::optional<CASID>>)> Callback) const {
51+
// The default implementation is synchronous.
52+
Optional<CASID> Val;
53+
if (Error E = getImpl(ResolvedKey, Globally).moveInto(Val))
54+
return Callback(std::move(E));
55+
if (Val)
56+
return Callback(std::move(*Val));
57+
return Callback(std::nullopt);
58+
}
59+
60+
void ActionCache::putImplAsync(ArrayRef<uint8_t> ResolvedKey,
61+
const CASID &Result, bool Globally,
62+
unique_function<void(Error)> Callback) {
63+
// The default implementation is synchronous.
64+
return Callback(putImpl(ResolvedKey, Result, Globally));
65+
}

llvm/lib/CAS/ObjectStore.cpp

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -117,23 +117,33 @@ ObjectStore::getProxyIfExists(ObjectRef Ref) {
117117
return ObjectProxy::load(*this, Ref, *H);
118118
}
119119

120-
std::future<AsyncProxyValue> ObjectStore::getProxyAsync(ObjectRef Ref) {
120+
std::future<AsyncProxyValue> ObjectStore::getProxyFuture(ObjectRef Ref) {
121121
std::promise<AsyncProxyValue> Promise;
122122
auto Future = Promise.get_future();
123+
getProxyAsync(Ref, [Promise = std::move(Promise)](
124+
Expected<std::optional<ObjectProxy>> Obj) mutable {
125+
Promise.set_value(std::move(Obj));
126+
});
127+
return Future;
128+
}
129+
130+
void ObjectStore::getProxyAsync(
131+
ObjectRef Ref,
132+
unique_function<void(Expected<std::optional<ObjectProxy>>)> Callback) {
123133
// FIXME: there is potential for use-after-free for the 'this' pointer.
124134
// Either we should always allocate shared pointers for \c ObjectStore objects
125135
// and pass \c shared_from_this() or expect that the caller will not release
126136
// the \c ObjectStore before the callback returns.
127-
loadIfExistsAsync(Ref, [this, Ref, Promise = std::move(Promise)](
128-
Expected<std::optional<ObjectHandle>> H) mutable {
129-
if (!H)
130-
Promise.set_value(H.takeError());
131-
else if (!*H)
132-
Promise.set_value(std::nullopt);
133-
else
134-
Promise.set_value(ObjectProxy::load(*this, Ref, **H));
135-
});
136-
return Future;
137+
return loadIfExistsAsync(
138+
Ref, [this, Ref, Callback = std::move(Callback)](
139+
Expected<std::optional<ObjectHandle>> H) mutable {
140+
if (!H)
141+
Callback(H.takeError());
142+
else if (!*H)
143+
Callback(std::nullopt);
144+
else
145+
Callback(ObjectProxy::load(*this, Ref, **H));
146+
});
137147
}
138148

139149
Error ObjectStore::createUnknownObjectError(const CASID &ID) {

llvm/lib/CAS/PluginAPI.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,18 @@ struct llcas_functions_t {
8080
bool globally,
8181
char **error);
8282

83+
void (*actioncache_get_for_digest_async)(llcas_cas_t, llcas_digest_t key,
84+
bool globally, void *ctx_cb,
85+
llcas_actioncache_get_cb);
86+
8387
bool (*actioncache_put_for_digest)(llcas_cas_t, llcas_digest_t key,
8488
llcas_objectid_t value, bool globally,
8589
char **error);
90+
91+
void (*actioncache_put_for_digest_async)(llcas_cas_t, llcas_digest_t key,
92+
llcas_objectid_t value,
93+
bool globally, void *ctx_cb,
94+
llcas_actioncache_put_cb);
8695
};
8796

8897
#endif // LLVM_LIB_CAS_PLUGINAPI_H

0 commit comments

Comments
 (0)