summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMaxime Coste <mawww@kakoune.org>2025-06-03 09:26:15 +1000
committerMaxime Coste <mawww@kakoune.org>2025-06-03 09:26:15 +1000
commitc7a7c35963133c0d57dc2abe341dac6c7763b19c (patch)
treee63dea1056c1bab318e8aacfa6e33f3b3cf3a6f4 /src
parentccb3090361103368ed57d628b5922e18a49fc604 (diff)
Prevent deletion of buffers while creating new windows
`kak -n -E 'hook global WinCreate .* %{ delete-buffer }'` was crashing because we would delete the buffer during window construction, which would not be able to delete the window as it was not fully constructed and registered yet. This led to a window referencing a deleted buffer. Fixing this by deleting the window later on failed because we can enter an infinite loop where we constantly create a new *scratch* buffer, then a window to display it, which deletes that buffer. Make it an error to try to delete a buffer while a new window is being setup by adding a Locked flag to buffers and checking that in BufferManager::delete_buffer Fixes #5311
Diffstat (limited to 'src')
-rw-r--r--src/buffer.hh1
-rw-r--r--src/buffer_manager.cc3
-rw-r--r--src/client.cc3
-rw-r--r--src/client_manager.cc3
4 files changed, 10 insertions, 0 deletions
diff --git a/src/buffer.hh b/src/buffer.hh
index 3a885ec8..2bcd8cc3 100644
--- a/src/buffer.hh
+++ b/src/buffer.hh
@@ -121,6 +121,7 @@ public:
Debug = 1 << 5,
ReadOnly = 1 << 6,
NoBufSetOption = 1 << 7,
+ Locked = 1 << 8,
};
friend constexpr bool with_bit_ops(Meta::Type<Flags>) { return true; }
friend class BufferIterator;
diff --git a/src/buffer_manager.cc b/src/buffer_manager.cc
index c7d8f0aa..ea86beb9 100644
--- a/src/buffer_manager.cc
+++ b/src/buffer_manager.cc
@@ -47,6 +47,9 @@ Buffer* BufferManager::create_buffer(String name, Buffer::Flags flags, BufferLin
void BufferManager::delete_buffer(Buffer& buffer)
{
+ if (buffer.flags() & Buffer::Flags::Locked)
+ throw runtime_error{"Trying to delete a locked buffer"};
+
auto it = find_if(m_buffers, [&](auto& p) { return p.get() == &buffer; });
if (it == m_buffers.end()) // we might be trying to recursively delete this buffer
return;
diff --git a/src/client.cc b/src/client.cc
index da4ed9f0..2cb1c973 100644
--- a/src/client.cc
+++ b/src/client.cc
@@ -194,6 +194,9 @@ void Client::change_buffer(Buffer& buffer, Optional<FunctionRef<void()>> set_sel
if (m_buffer_reload_dialog_opened)
close_buffer_reload_dialog();
+ buffer.flags() |= Buffer::Flags::Locked;
+ OnScopeEnd unlock{[&] { buffer.flags() &= ~Buffer::Flags::Locked; }};
+
auto& client_manager = ClientManager::instance();
WindowAndSelections ws = client_manager.get_free_window(buffer);
diff --git a/src/client_manager.cc b/src/client_manager.cc
index 3c8a7b6e..2e7d76e7 100644
--- a/src/client_manager.cc
+++ b/src/client_manager.cc
@@ -55,6 +55,9 @@ Client* ClientManager::create_client(std::unique_ptr<UserInterface>&& ui, int pi
if (buffer == nullptr)
buffer = &BufferManager::instance().get_first_buffer();
+ buffer->flags() |= Buffer::Flags::Locked;
+ OnScopeEnd unlock{[&] { buffer->flags() &= ~Buffer::Flags::Locked; }};
+
WindowAndSelections ws = get_free_window(*buffer);
Client* client = new Client{std::move(ui), std::move(ws.window),
std::move(ws.selections), pid,