aboutsummaryrefslogtreecommitdiff
path: root/src/libutil
diff options
context:
space:
mode:
authoreldritch horrors <pennae@lix.systems>2024-08-30 19:01:30 +0200
committereldritch horrors <pennae@lix.systems>2024-08-30 19:01:30 +0200
commite0fd0ba211b38827627218424f92b7d0e059a626 (patch)
tree0b0b1cf1f32302f683d7ab1bcd1a7469fc6a3e3c /src/libutil
parentc2b90d235fb5dd721898d8d41d73a51607654890 (diff)
libstore: use notifications for stats counters
updating statistics *immediately* when any counter changes declutters things somewhat and makes useful status reports less dependent on the current worker main loop. using callbacks will make it easier to move the worker loop into kj entirely, using only promises for scheduling. Change-Id: I695dfa83111b1ec09b1a54cff268f3c1d7743ed6
Diffstat (limited to 'src/libutil')
-rw-r--r--src/libutil/meson.build1
-rw-r--r--src/libutil/notifying-counter.hh99
2 files changed, 100 insertions, 0 deletions
diff --git a/src/libutil/meson.build b/src/libutil/meson.build
index 6566f7f46..1ac31c7eb 100644
--- a/src/libutil/meson.build
+++ b/src/libutil/meson.build
@@ -95,6 +95,7 @@ libutil_headers = files(
'monitor-fd.hh',
'mount.hh',
'namespaces.hh',
+ 'notifying-counter.hh',
'pool.hh',
'position.hh',
'print-elided.hh',
diff --git a/src/libutil/notifying-counter.hh b/src/libutil/notifying-counter.hh
new file mode 100644
index 000000000..dc58aac91
--- /dev/null
+++ b/src/libutil/notifying-counter.hh
@@ -0,0 +1,99 @@
+#pragma once
+/// @file
+
+#include <cassert>
+#include <functional>
+#include <memory>
+
+namespace nix {
+
+template<std::integral T>
+class NotifyingCounter
+{
+private:
+ T counter;
+ std::function<void()> notify;
+
+public:
+ class Bump
+ {
+ friend class NotifyingCounter;
+
+ struct SubOnFree
+ {
+ T delta;
+
+ void operator()(NotifyingCounter * c) const
+ {
+ c->add(-delta);
+ }
+ };
+
+ // lightly misuse unique_ptr to get RAII types with destructor callbacks
+ std::unique_ptr<NotifyingCounter<T>, SubOnFree> at;
+
+ Bump(NotifyingCounter<T> & at, T delta) : at(&at, {delta}) {}
+
+ public:
+ Bump() = default;
+ Bump(decltype(nullptr)) {}
+
+ T delta() const
+ {
+ return at ? at.get_deleter().delta : 0;
+ }
+
+ void reset()
+ {
+ at.reset();
+ }
+ };
+
+ explicit NotifyingCounter(std::function<void()> notify, T initial = 0)
+ : counter(initial)
+ , notify(std::move(notify))
+ {
+ assert(this->notify);
+ }
+
+ // bumps hold pointers to this, so we should neither copy nor move.
+ NotifyingCounter(const NotifyingCounter &) = delete;
+ NotifyingCounter & operator=(const NotifyingCounter &) = delete;
+ NotifyingCounter(NotifyingCounter &&) = delete;
+ NotifyingCounter & operator=(NotifyingCounter &&) = delete;
+
+ T get() const
+ {
+ return counter;
+ }
+
+ operator T() const
+ {
+ return counter;
+ }
+
+ void add(T delta)
+ {
+ counter += delta;
+ notify();
+ }
+
+ NotifyingCounter & operator+=(T delta)
+ {
+ add(delta);
+ return *this;
+ }
+
+ NotifyingCounter & operator++(int)
+ {
+ return *this += 1;
+ }
+
+ Bump addTemporarily(T delta)
+ {
+ add(delta);
+ return Bump{*this, delta};
+ }
+};
+
+}