add doc write lock and make imgui use it

This commit is contained in:
Green Sky
2025-09-02 11:36:05 +02:00
parent 396a03c229
commit 9cab041af4
4 changed files with 80 additions and 8 deletions

View File

@@ -37,3 +37,20 @@ CRDTNotes::Doc* CRDTNotes::addDoc(const CRDTAgent& self_agent, const DocID& id)
return &doc; return &doc;
} }
void CRDTNotes::writeLockRelease(const DocID& id) {
assert(_doc_write_locks.count(id) > 0);
_doc_write_locks.erase(id);
}
bool CRDTNotes::isWriteLocked(const DocID& id) const {
return _doc_write_locks.count(id);
}
std::optional<CRDTNotes::DocWriteLock> CRDTNotes::writeLockAquire(const DocID& id) {
if (_doc_write_locks.count(id)) {
return std::nullopt; // replace with exception instead?
}
_doc_write_locks.emplace(id);
return DocWriteLock{*this, id};
}

View File

@@ -6,6 +6,8 @@
#include <cstdint> #include <cstdint>
#include <functional> #include <functional>
#include <unordered_map> #include <unordered_map>
#include <unordered_set>
#include <optional>
using ID32 = std::array<uint8_t, 32>; using ID32 = std::array<uint8_t, 32>;
@@ -37,9 +39,23 @@ class CRDTNotes {
uint64_t seq{0}; uint64_t seq{0};
}; };
// RAII lock wrapper
struct DocWriteLock {
CRDTNotes* notes;
DocID id;
// ctr assumes lock
DocWriteLock(CRDTNotes& notes, const DocID& id) : notes(&notes), id(id) {}
DocWriteLock(const DocWriteLock&) = delete;
DocWriteLock(DocWriteLock&& other) : notes(other.notes), id(other.id) { other.notes = nullptr; }
~DocWriteLock(void) { if (notes) { notes->writeLockRelease(id); } }
bool operator==(const DocWriteLock& other) const { return id == other.id; }
};
private: private:
// TODO: add metadata to docs // TODO: add metadata to docs
std::unordered_map<DocID, Doc> _docs; std::unordered_map<DocID, Doc> _docs;
std::unordered_set<DocID> _doc_write_locks;
public: public:
// config? // config?
@@ -52,5 +68,16 @@ class CRDTNotes {
Doc* getDoc(const DocID& id); Doc* getDoc(const DocID& id);
Doc* addDoc(const CRDTAgent& self_agent, const DocID& doc); Doc* addDoc(const CRDTAgent& self_agent, const DocID& doc);
void writeLockRelease(const DocID& id);
bool isWriteLocked(const DocID& id) const;
std::optional<DocWriteLock> writeLockAquire(const DocID& id);
};
template<>
struct std::hash<CRDTNotes::DocWriteLock> {
std::uint64_t operator()(const CRDTNotes::DocWriteLock& s) const noexcept {
return std::hash<ID32>{}(s.id);
}
}; };

View File

@@ -48,6 +48,12 @@ namespace detail {
} // detail } // detail
std::unordered_set<CRDTNotes::DocWriteLock>::iterator CRDTNotesImGui::findLock(const CRDTNotes::DocID& doc_id) {
auto it = _held_locks.begin();
for (; it != _held_locks.end() && it->id != doc_id; it++) {}
return it;
}
CRDTNotesImGui::CRDTNotesImGui(CRDTNotes& notes, CRDTNotesSync& notes_sync, ContactStore4I& cs) : _notes(notes), _notes_sync(notes_sync), _cs(cs) { CRDTNotesImGui::CRDTNotesImGui(CRDTNotes& notes, CRDTNotesSync& notes_sync, ContactStore4I& cs) : _notes(notes), _notes_sync(notes_sync), _cs(cs) {
} }
@@ -142,17 +148,36 @@ bool CRDTNotesImGui::renderDoc(const CRDTNotes::DocID& doc_id) {
return false; return false;
} }
auto lock_it = findLock(doc_id);
bool self_held = lock_it != _held_locks.end();
const bool foreign_held = !self_held && _notes.isWriteLocked(doc_id);
auto text = doc->getText(); auto text = doc->getText();
if (renderDocText(text)) { ImGui::InputTextMultiline(
"##doc",
&text,
{-1,-1},
ImGuiInputTextFlags_AllowTabInput |
(foreign_held ? ImGuiInputTextFlags_ReadOnly : ImGuiInputTextFlags_None) |
ImGuiInputTextFlags_CallbackAlways
//cb,
//&text
);
if (!foreign_held && !self_held && (ImGui::IsItemActive() || ImGui::IsItemEdited())) {
// TODO: check
_held_locks.emplace(_notes.writeLockAquire(doc_id).value());
self_held = true;
std::cout << "!!!! imgui lock aquired\n";
} else if (!foreign_held && self_held && !(ImGui::IsItemActive() || ImGui::IsItemEdited())) {
// release lock
_held_locks.erase(lock_it);
std::cout << "!!!! imgui lock released\n";
}
if (self_held && ImGui::IsItemEdited()) {
_notes_sync.merge(doc_id, text); _notes_sync.merge(doc_id, text);
return true; return true;
} }
return false; return false;
} }
bool CRDTNotesImGui::renderDocText(std::string& text) const {
// TODO: replace with text editor (zep) or visualize stuff??
return ImGui::InputTextMultiline("##doc", &text, {-1,-1}, ImGuiInputTextFlags_AllowTabInput);
}

View File

@@ -4,6 +4,7 @@
#include <solanaceae/contact/fwd.hpp> #include <solanaceae/contact/fwd.hpp>
#include <set> #include <set>
#include <unordered_set>
class CRDTNotesImGui { class CRDTNotesImGui {
CRDTNotes& _notes; CRDTNotes& _notes;
@@ -13,6 +14,9 @@ class CRDTNotesImGui {
bool _show_global_list {true}; bool _show_global_list {true};
std::set<CRDTNotes::DocID> _open_docs; std::set<CRDTNotes::DocID> _open_docs;
std::unordered_set<CRDTNotes::DocWriteLock> _held_locks;
std::unordered_set<CRDTNotes::DocWriteLock>::iterator findLock(const CRDTNotes::DocID& doc_id);
public: public:
CRDTNotesImGui(CRDTNotes& notes, CRDTNotesSync& notes_sync, ContactStore4I& cs); CRDTNotesImGui(CRDTNotes& notes, CRDTNotesSync& notes_sync, ContactStore4I& cs);
@@ -22,6 +26,5 @@ class CRDTNotesImGui {
bool renderContactListContactSmall(const Contact4 c, const bool selected) const; bool renderContactListContactSmall(const Contact4 c, const bool selected) const;
bool renderDoc(const CRDTNotes::DocID& doc_id); bool renderDoc(const CRDTNotes::DocID& doc_id);
bool renderDocText(std::string& text) const;
}; };