Skip to content

Commit 0f08432

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 ac7eddf commit 0f08432

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
@@ -338,6 +338,112 @@ template <> void llama_file_buffer<true>::write_u32(uint32_t val) const {
338338
template struct llama_file_buffer<false>;
339339
template struct llama_file_buffer<true>;
340340

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

343449
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)