Budget the sample buffer in samples per signal, under a hard memory limit
--buffer-samples (k/M/G) replaces --buffer-mb: the request is rounded down to a power of two for every signal and the shared time ring. A separate memory limit (--memory-limit-mb, a Preferences row, default 8 GiB) halves the plan until the real ring bytes fit and says so on stderr and in the status line. The per-source key buffer_mb becomes buffer_samples; an old buffer_mb is ignored. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
616cde5b13
commit
034c82f7d7
20 files changed
+516
-134
No files matched your search
+29
-1
@@ -4,6 +4,7 @@
|
||||
#include "ui_prefs.hpp"
|
||||
|
||||
#include "event_store.hpp" // `default_events_dir()`, so the row shows the path the cache uses
|
||||
#include "status_text.hpp" // `format_bytes()`
|
||||
#include "ui_app.hpp" // the application's half of the contract, at the bottom of this file
|
||||
|
||||
#include <adwaita.h>
|
||||
@@ -36,6 +37,7 @@ Settings read_rows(const PrefsForm& f) {
|
||||
s.event_cache_dir = gtk_editable_get_text(GTK_EDITABLE(f.dir_row));
|
||||
s.event_cache_max = static_cast<int>(adw_spin_row_get_value(f.keep_row));
|
||||
s.relative_time = adw_switch_row_get_active(f.relative_row) != FALSE;
|
||||
s.memory_limit_mb = static_cast<int>(adw_spin_row_get_value(f.memory_row));
|
||||
s.theme = theme_name(adw_combo_row_get_selected(f.theme_row));
|
||||
// The row shows the *effective* directory, so the default is written out in full rather than
|
||||
// described. Leaving it at that default is not a choice to pin it: the file keeps "unset", and the
|
||||
@@ -63,6 +65,7 @@ void fill_rows(PrefsForm& f, const Settings& s) {
|
||||
(s.event_cache_dir.empty() ? default_events_dir() : s.event_cache_dir).c_str());
|
||||
adw_spin_row_set_value(f.keep_row, s.event_cache_max < 0 ? 0.0 : s.event_cache_max);
|
||||
adw_switch_row_set_active(f.relative_row, s.relative_time ? TRUE : FALSE);
|
||||
adw_spin_row_set_value(f.memory_row, s.memory_limit_mb);
|
||||
adw_combo_row_set_selected(f.theme_row, s.theme == "light" ? 1 : (s.theme == "dark" ? 2 : 0));
|
||||
f.filling = false;
|
||||
}
|
||||
@@ -141,6 +144,7 @@ void on_closed(AdwDialog*, gpointer user) {
|
||||
f.dir_row = nullptr;
|
||||
f.keep_row = nullptr;
|
||||
f.relative_row = nullptr;
|
||||
f.memory_row = nullptr;
|
||||
f.theme_row = nullptr;
|
||||
f.browse = nullptr;
|
||||
f.parent = nullptr;
|
||||
@@ -155,12 +159,14 @@ void resolve_rows(PrefsForm& f) {
|
||||
f.keep_row = ADW_SPIN_ROW(gtk_builder_get_object(f.builder, "keep_row"));
|
||||
f.relative_row = ADW_SWITCH_ROW(gtk_builder_get_object(f.builder, "relative_row"));
|
||||
f.theme_row = ADW_COMBO_ROW(gtk_builder_get_object(f.builder, "theme_row"));
|
||||
f.memory_row = ADW_SPIN_ROW(gtk_builder_get_object(f.builder, "memory_row"));
|
||||
if (f.dir_row == nullptr || f.keep_row == nullptr || f.relative_row == nullptr ||
|
||||
f.theme_row == nullptr) {
|
||||
f.theme_row == nullptr || f.memory_row == nullptr) {
|
||||
f.dir_row = nullptr;
|
||||
f.keep_row = nullptr;
|
||||
f.relative_row = nullptr;
|
||||
f.theme_row = nullptr;
|
||||
f.memory_row = nullptr;
|
||||
g_clear_object(&f.builder);
|
||||
}
|
||||
}
|
||||
@@ -214,6 +220,7 @@ void prefs_open(PrefsForm& form, GtkWidget* parent, const Settings& current,
|
||||
g_signal_connect(form.dir_row, "changed", G_CALLBACK(on_entry_changed), &form);
|
||||
g_signal_connect(form.keep_row, "notify::value", G_CALLBACK(on_row_notify), &form);
|
||||
g_signal_connect(form.relative_row, "notify::active", G_CALLBACK(on_row_notify), &form);
|
||||
g_signal_connect(form.memory_row, "notify::value", G_CALLBACK(on_row_notify), &form);
|
||||
g_signal_connect(form.theme_row, "notify::selected", G_CALLBACK(on_row_notify), &form);
|
||||
if (GObject* reset = gtk_builder_get_object(form.builder, "reset_row"); reset != nullptr) {
|
||||
g_signal_connect(reset, "activated", G_CALLBACK(on_reset), &form);
|
||||
@@ -239,9 +246,29 @@ void prefs_probe_change(PrefsForm& form) {
|
||||
gtk_editable_set_text(GTK_EDITABLE(form.dir_row), "/tmp/udpscope-prefs-events");
|
||||
adw_spin_row_set_value(form.keep_row, 7.0);
|
||||
adw_switch_row_set_active(form.relative_row, TRUE);
|
||||
adw_spin_row_set_value(form.memory_row, 4096.0);
|
||||
adw_combo_row_set_selected(form.theme_row, 2);
|
||||
}
|
||||
|
||||
/// A changed memory limit reaches the running model. Only when it differs from the model's own (every
|
||||
/// preference arrives through the same callback, and a theme change is not a re-plan), and without
|
||||
/// re-planning while a file or a replayed event is what the buffer holds -- clearing those would
|
||||
/// throw away what the user opened, so the limit is stored and the next plan honours it.
|
||||
static void apply_memory_limit(App& app, int mb) {
|
||||
if (app.model == nullptr || mb <= 0) return;
|
||||
const std::uint64_t bytes = static_cast<std::uint64_t>(mb) * 1024u * 1024u;
|
||||
if (bytes == app.model->memory_limit_bytes()) return;
|
||||
const bool holds_a_view = !app.waveform_name.empty() || app.replaying;
|
||||
const bool rebuilt = app.model->set_memory_limit_bytes(bytes, !holds_a_view);
|
||||
std::fprintf(stderr, "[udpscope] memory limit now %s (rebuilt=%d%s)\n",
|
||||
format_bytes(bytes).c_str(), rebuilt ? 1 : 0,
|
||||
holds_a_view ? ", applies to the next plan" : "");
|
||||
std::fflush(stderr);
|
||||
if (rebuilt) {
|
||||
set_notice(app, "memory limit " + format_bytes(bytes) + ": buffer re-planned, history cleared", 5);
|
||||
}
|
||||
}
|
||||
|
||||
/// The application's entry point: **Preferences…** in the overflow menu. Kept here rather than in
|
||||
/// `ui.cpp` with the rest of the chrome, because it is the application's half of *this* form's
|
||||
/// contract -- what a preference changing means to a running scope -- and reading it beside the form is
|
||||
@@ -249,6 +276,7 @@ void prefs_probe_change(PrefsForm& form) {
|
||||
void open_preferences(App& app) {
|
||||
prefs_open(app.prefs, app.window, app.settings, [&app](const Settings& s, bool written) {
|
||||
app.settings = s;
|
||||
apply_memory_limit(app, s.memory_limit_mb);
|
||||
// Only a failure is announced. A change arrives once per keystroke in the directory row, and a
|
||||
// status line that flashed on each one would be the app shouting about its own bookkeeping.
|
||||
if (!written) set_notice(app, "could not write the preferences file", 4);
|
||||
|
||||
Reference in new issue
Block a user