From 18ee38af90b527f617af7bf9bbcf2e8e7cb0df17 Mon Sep 17 00:00:00 2001 From: KJ Tsanaktsidis Date: Thu, 25 Nov 2021 17:56:19 +1100 Subject: Allow Raindrops objects to be backed by a file Currently, all memory used by Raindrops is mapped as MAP_ANONYMOUS. This means that although Raindrops counters can be shared between processes that have forked from each other, it is not possible to share the counter values with another, unrelated process. This patch adds support for backing the Raindrops mapping with a file descriptor obtained from an IO object. The #initialize API has been enhanced with two new keyword options: Raindrops.new(size, io: nil, zero: false) If an instance of IO is provided, then the underlying file descriptor for that IO will be used to back the memory mapping Raindrops creates. An unrelated process can then open the same file, and read the counters; either by mmap'ing the file itself (or using Raindrops to do so), or by making ordinary seek()/read() calls if performance is not a concern. Note that the provided IO object _must_ implement #truncate; this is used to set the size of the file to be right-sized for the memory mapping that is created. If the zero argument is passed as true, then the mapping will be zero'd by Raindrops as part of its initialization. If it's false, then the Raindrops counters existing in the file will be preserved. This allows counter values to be persisted (although note that Raindrops makes no attempt to msync the values, so they are not durable to e.g. system crashes). Counter values can easily be shared between processes in-memory only without touching the disk by passing in a File on a tmpfs as the io object. --- lib/raindrops.rb | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'lib') diff --git a/lib/raindrops.rb b/lib/raindrops.rb index ba273eb..dc61952 100644 --- a/lib/raindrops.rb +++ b/lib/raindrops.rb @@ -36,6 +36,30 @@ class Raindrops def total active + queued end + end unless defined? ListenStats + + # call-seq: + # Raindrops.new(size, io: nil) -> raindrops object + # + # Initializes a Raindrops object to hold +size+ counters. +size+ is + # only a hint and the actual number of counters the object has is + # dependent on the CPU model, number of cores, and page size of + # the machine. The actual size of the object will always be equal + # or greater than the specified +size+. + # If +io+ is provided, then the Raindrops memory will be backed by + # the specified file; otherwise, it will allocate anonymous memory. + # The IO object must respond to +truncate+, as this is used to set + # the size of the file. + # If +zero+ is provided, then the memory region is zeroed prior to + # returning. This is only meaningful if +io+ is also provided; in + # that case it controls whether any existing counter values in +io+ + # are retained (false) or whether it is entirely zeroed (true). + def initialize(size, io: nil, zero: false) + # This ruby wrapper exists to handle the keyword-argument handling, + # which is otherwise kind of awkward in C. We delegate the keyword + # arguments to the _actual_ initialize implementation as positional + # args. + initialize_cimpl(size, io, zero) end autoload :Linux, 'raindrops/linux' -- cgit v1.2.3-24-ge0c7