Skip to content

Commit a643fa3

Browse files
committed
[fbuffers] Future file buffer implementation
A file buffer that can be fulfilled using string keys. The extract method waits until the file is provided.
1 parent f1f8ced commit a643fa3

File tree

2 files changed

+144
-0
lines changed

2 files changed

+144
-0
lines changed

src/llama-mmap.cpp

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,112 @@ template <> void llama_file_buffer<true>::write_u32(uint32_t val) const {
341341
template struct llama_file_buffer<false>;
342342
template struct llama_file_buffer<true>;
343343

344+
// llama_future_file_buffer implementation
345+
346+
namespace {
347+
std::string final_key(const std::string & promise_key, const std::string & context) {
348+
return promise_key + ":" + context;
349+
}
350+
351+
std::mutex promise_registry_mutex;
352+
353+
std::map<std::string, std::promise<std::unique_ptr<llama_file_buffer<false>>>> promise_registry_ro;
354+
std::map<std::string, std::promise<std::unique_ptr<llama_file_buffer<true>>>> promise_registry_rw;
355+
356+
template <bool Writable>
357+
std::map<std::string, std::promise<std::unique_ptr<llama_file_buffer<Writable>>>> & promise_registry() {
358+
if constexpr (Writable) {
359+
return promise_registry_rw;
360+
} else {
361+
return promise_registry_ro;
362+
}
363+
}
364+
365+
/// @brief Ensures a promise exists in the registry for the given key.
366+
/// If it doesn't exist, creates it. Returns an iterator to the promise.
367+
/// Thread-safe.
368+
template <bool Writable>
369+
typename std::map<std::string, std::promise<std::unique_ptr<llama_file_buffer<Writable>>>>::iterator
370+
ensure_promise_registry(const std::string & key) {
371+
std::lock_guard<std::mutex> lock(promise_registry_mutex);
372+
auto it = promise_registry<Writable>().find(key);
373+
if (it != promise_registry<Writable>().end()) {
374+
return it;
375+
}
376+
auto result =
377+
promise_registry<Writable>().emplace(key, std::promise<std::unique_ptr<llama_file_buffer<Writable>>>());
378+
LLAMA_LOG_CMAKE_DEBUG("%s: created future file buffer %p for %s\n", __func__, (void *) &(*it), key.c_str());
379+
return result.first;
380+
}
381+
} // namespace
382+
383+
template <bool Writable>
384+
llama_future_file_buffer<Writable>::llama_future_file_buffer(const std::string & promise_key,
385+
const std::string & context) :
386+
file_buffer_future(),
387+
file_buffer() {
388+
std::string key = final_key(promise_key, context);
389+
file_buffer_promise_iterator = ensure_promise_registry<Writable>(key);
390+
file_buffer_future = file_buffer_promise_iterator->second.get_future();
391+
}
392+
393+
template <bool Writable>
394+
llama_future_file_buffer<Writable>::llama_future_file_buffer(llama_future_file_buffer && other) noexcept :
395+
file_buffer_promise_iterator(std::move(other.file_buffer_promise_iterator)),
396+
file_buffer_future(std::move(other.file_buffer_future)),
397+
file_buffer(std::move(other.file_buffer)) {
398+
// Set the other object's iterator to end() to mark it as moved from
399+
// to avoid early erasure at destruction of the moved other object
400+
other.file_buffer_promise_iterator = promise_registry<Writable>().end();
401+
}
402+
403+
template <bool Writable>
404+
llama_future_file_buffer<Writable> & llama_future_file_buffer<Writable>::operator=(
405+
llama_future_file_buffer && other) noexcept {
406+
if (this != &other) {
407+
file_buffer_promise_iterator = std::move(other.file_buffer_promise_iterator);
408+
file_buffer_future = std::move(other.file_buffer_future);
409+
file_buffer = std::move(other.file_buffer);
410+
other.file_buffer_promise_iterator = promise_registry<Writable>().end();
411+
}
412+
return *this;
413+
}
414+
415+
template <bool Writable> llama_future_file_buffer<Writable>::~llama_future_file_buffer() {
416+
std::lock_guard<std::mutex> lock(promise_registry_mutex);
417+
if (file_buffer_promise_iterator != promise_registry<Writable>().end()) {
418+
promise_registry<Writable>().erase(file_buffer_promise_iterator);
419+
}
420+
}
421+
422+
template <bool Writable>
423+
bool llama_future_file_buffer<Writable>::fulfill_promise(const std::string & promise_key, const std::string & context,
424+
std::unique_ptr<llama_file_buffer<Writable>> && value) {
425+
std::string key = final_key(promise_key, context);
426+
auto it = ensure_promise_registry<Writable>(key);
427+
if (it != promise_registry<Writable>().end()) {
428+
LLAMA_LOG_CMAKE_DEBUG("fulfilling future file buffer %p for %s\n", (void *) &(*it), key.c_str());
429+
it->second.set_value(std::move(value));
430+
return true;
431+
}
432+
return false;
433+
}
434+
435+
template <bool Writable>
436+
std::unique_ptr<llama_file_buffer<Writable>> llama_future_file_buffer<Writable>::extract() const {
437+
if (file_buffer) {
438+
return std::move(file_buffer);
439+
}
440+
441+
auto future_result = file_buffer_future.get();
442+
file_buffer = std::move(future_result);
443+
return std::move(file_buffer);
444+
}
445+
446+
// Explicit instantiations for llama_future_file_buffer
447+
template struct llama_future_file_buffer<false>;
448+
template struct llama_future_file_buffer<true>;
449+
344450
// llama_mmap
345451

346452
struct llama_mmap::impl {

src/llama-mmap.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
#include <cstdint>
44
#include <memory>
55
#include <vector>
6+
#include "uint8-buff-stream.h"
7+
#include <future>
8+
#include <string>
9+
#include <map>
610

711
struct llama_file;
812
struct llama_mmap;
@@ -74,9 +78,43 @@ template <bool Writable> struct llama_file_buffer : public llama_file {
7478
std::unique_ptr<std::basic_streambuf<uint8_t>> streambuf;
7579
};
7680

81+
template <bool Writable> struct llama_future_file_buffer {
82+
/// @brief A file buffer object whose operations will block
83+
/// until the given promise key is set with a file buffer.
84+
/// @param promise_key The key to use for the promise (e.g. a file path).
85+
/// @param context The context to use for the promise, used to distinguish same promise key (e.g. for a same file opened twice).
86+
llama_future_file_buffer(const std::string & promise_key, const std::string & context);
87+
88+
// Delete copy constructor and copy assignment operator
89+
llama_future_file_buffer(const llama_future_file_buffer &) = delete;
90+
llama_future_file_buffer & operator=(const llama_future_file_buffer &) = delete;
91+
92+
llama_future_file_buffer(llama_future_file_buffer && other) noexcept;
93+
llama_future_file_buffer & operator=(llama_future_file_buffer && other) noexcept;
94+
95+
~llama_future_file_buffer();
96+
97+
/// @brief Sets the given key and context with a file buffer so that
98+
/// operations can resume/start.
99+
static bool fulfill_promise(const std::string & promise_key, const std::string & context,
100+
std::unique_ptr<llama_file_buffer<Writable>> && value);
101+
102+
/// @brief Waits for future buffer or obtains current if already
103+
/// fulfilled and moves the future contents outside the registry.
104+
std::unique_ptr<llama_file_buffer<Writable>> extract() const;
105+
106+
private:
107+
typename std::map<std::string, std::promise<std::unique_ptr<llama_file_buffer<Writable>>>>::iterator
108+
file_buffer_promise_iterator;
109+
mutable std::future<std::unique_ptr<llama_file_buffer<Writable>>> file_buffer_future;
110+
mutable std::unique_ptr<llama_file_buffer<Writable>> file_buffer;
111+
};
112+
77113
// Type aliases for convenience
78114
using llama_file_buffer_ro = llama_file_buffer<false>;
79115
using llama_file_buffer_rw = llama_file_buffer<true>;
116+
using llama_future_file_buffer_ro = llama_future_file_buffer<false>;
117+
using llama_future_file_buffer_rw = llama_future_file_buffer<true>;
80118

81119
struct llama_mmap {
82120
llama_mmap(const llama_mmap &) = delete;

0 commit comments

Comments
 (0)