Flow 1.0.2
Flow project: Public API.
|
Flow module containing miscellaneous general-use facilities that don't fit into any other Flow module. More...
Namespaces | |
namespace | this_thread |
Short-hand for standard this-thread namespace. | |
Classes | |
class | Basic_blob |
A hand-optimized and API-tweaked replacement for vector<uint8_t> , i.e., buffer of bytes inside an allocated area of equal or larger size; also optionally supports limited garbage-collected memory pool functionality and SHM-friendly custom-allocator support. More... | |
class | Basic_string_view |
Essentially alias for a C++17-conforming string-view class template, which is a very lightweight std::string -like representation of a character sequence already in memory. More... | |
class | Blob_with_log_context |
Basic_blob that works in regular heap (and is itself placed in heap or stack) and memorizes a log::Logger, enabling easier logging albeit with a small perf trade-off. More... | |
class | Container_traits |
Properties of various container types. More... | |
class | Container_traits< boost::unordered_set< T > > |
Traits of boost::unordered_set . More... | |
class | Container_traits< std::map< K, V > > |
Traits of std::map . More... | |
class | Container_traits< std::set< T > > |
Traits of std::set . More... | |
class | Container_traits< util::Linked_hash_map< K, V > > |
Traits of flow::util::Linked_hash_map. More... | |
class | Container_traits< util::Linked_hash_set< T, Hash, Pred > > |
Traits of flow::util::Linked_hash_set. More... | |
class | Linked_hash_map |
An object of this class is a map that combines the lookup speed of a boost::unordered_map<> and ordering and iterator stability capabilities of an std::list<> . More... | |
class | Linked_hash_set |
An object of this class is a set that combines the lookup speed of an unordered_set<> and ordering and iterator stability capabilities of an std::list<> . More... | |
class | Null_interface |
An empty interface, consisting of nothing but a default virtual destructor, intended as a boiler-plate-reducing base for any other (presumably virtual -method-having) class that would otherwise require a default virtual destructor. More... | |
class | Rnd_gen_uniform_range |
Simple, non-thread-safe uniform-range random number generator. More... | |
class | Rnd_gen_uniform_range_base |
Base class for Rnd_gen_uniform_range and Rnd_gen_uniform_range_mt for various aliases and similar, so template arguments need not be involved. More... | |
class | Rnd_gen_uniform_range_mt |
Identical to Rnd_gen_uniform_range but safe for concurrent RNG given a single object. More... | |
class | Scoped_setter |
A simple RAII-pattern class template that, at construction, sets the specified location in memory to a specified value, memorizing the previous contents; and at destruction restores the value. More... | |
class | Shared_ptr_alias_holder |
Convenience class template that endows the given subclass T with nested aliases Ptr and Const_ptr aliased to shared_ptr<T> and shared_ptr<const T> respectively. More... | |
class | String_ostream |
Similar to ostringstream but allows fast read-only access directly into the std::string being written; and some limited write access to that string. More... | |
class | Unique_id_holder |
Each object of this class stores (at construction) and returns (on demand) a numeric ID unique from all other objects of the same class ever constructed, across all time from program start to end. More... | |
Typedefs | |
using | Blob_sans_log_context = Basic_blob<> |
Short-hand for a Basic_blob that allocates/deallocates in regular heap and is itself assumed to be stored in heap or on stack; sharing feature compile-time-disabled (with perf boost as a result). More... | |
using | Sharing_blob_sans_log_context = Basic_blob< std::allocator< uint8_t >, true > |
Identical to Blob_sans_log_context but with sharing feature compile-time-enabled. More... | |
using | Blob = Blob_with_log_context<> |
A concrete Blob_with_log_context that compile-time-disables Basic_blob::share() and the sharing API derived from it. More... | |
using | Sharing_blob = Blob_with_log_context< true > |
A concrete Blob_with_log_context that compile-time-enables Basic_blob::share() and the sharing API derived from it. More... | |
using | Scheduled_task_handle = boost::shared_ptr< Scheduled_task_handle_state > |
Black-box type that represents a handle to a scheduled task as scheduled by schedule_task_at() or schedule_task_from_now() or similar, which can be (optionally) used to control the scheduled task after it has been thus scheduled. More... | |
using | Scheduled_task_const_handle = boost::shared_ptr< const Scheduled_task_handle_state > |
Equivalent to Scheduled_task_handle but refers to immutable version of a task. More... | |
using | Scheduled_task = Function< void(bool short_fire)> |
Short-hand for tasks that can be scheduled/fired by schedule_task_from_now() and similar. More... | |
using | String_view = Basic_string_view< char > |
Commonly used char -based Basic_string_view. See its doc header. | |
using | Thread = boost::thread |
Short-hand for standard thread class. More... | |
using | Thread_id = Thread::id |
Short-hand for an OS-provided ID of a util::Thread. | |
using | Task_engine = boost::asio::io_service |
Short-hand for boost.asio event service, the central class of boost.asio. More... | |
using | Strand = Task_engine::strand |
Short-hand for boost.asio strand, an ancillary class that works with Task_engine for advanced task scheduling. | |
using | Timer = boost::asio::basic_waitable_timer< Fine_clock > |
boost.asio timer. More... | |
using | Auto_cleanup = boost::shared_ptr< void > |
Helper type for setup_auto_cleanup(). | |
using | Udp_endpoint = boost::asio::ip::udp::endpoint |
Short-hand for the UDP endpoint (IP/port) type. | |
using | Ip_address_v4 = boost::asio::ip::address_v4 |
Short-hand for the IPv4 address type. | |
using | Ip_address_v6 = boost::asio::ip::address_v6 |
Short-hand for the IPv6 address type. | |
using | Mutex_non_recursive = boost::mutex |
Short-hand for non-reentrant, exclusive mutex. ("Reentrant" = one can lock an already-locked-in-that-thread mutex.) | |
using | Mutex_recursive = boost::recursive_mutex |
Short-hand for reentrant, exclusive mutex. | |
using | Mutex_shared_non_recursive = boost::shared_mutex |
Short-hand for non-reentrant, shared-or-exclusive mutex. More... | |
using | Mutex_noop_shared_non_recursive = boost::null_mutex |
Short-hand for a mutex type equivalent to util::Mutex_shared_non_recursive – except that the lock/unlock mutex ops all do nothing. More... | |
template<typename Mutex > | |
using | Lock_guard = boost::unique_lock< Mutex > |
Short-hand for advanced-capability RAII lock guard for any mutex, ensuring exclusive ownership of that mutex. More... | |
template<typename Shared_mutex > | |
using | Shared_lock_guard = boost::shared_lock< Shared_mutex > |
Short-hand for shared mode advanced-capability RAII lock guard, particuarly for Mutex_shared_non_recursive mutexes. More... | |
using | Lock_guard_non_recursive = boost::unique_lock< Mutex_non_recursive > |
(Deprecated given C++1x) Short-hand for advanced-capability RAII lock guard for Mutex_non_recursive mutexes. More... | |
using | Lock_guard_recursive = boost::unique_lock< Mutex_recursive > |
(Deprecated given C++1x) Short-hand for advanced-capability RAII lock guard for Mutex_recursive mutexes. More... | |
using | Lock_guard_shared_non_recursive_sh = boost::shared_lock< Mutex_shared_non_recursive > |
(Deprecated given C++1x) Short-hand for shared mode advanced-capability RAII lock guard for Mutex_shared_non_recursive mutexes. More... | |
using | Lock_guard_shared_non_recursive_ex = boost::unique_lock< Mutex_shared_non_recursive > |
(Deprecated given C++1x) Short-hand for exclusive mode advanced-capability RAII lock guard for Mutex_shared_non_recursive mutexes. More... | |
using | Lock_guard_noop_shared_non_recursive_sh = boost::shared_lock< Mutex_noop_shared_non_recursive > |
(Deprecated given C++1x) Equivalent to Lock_guard_shared_non_recursive_sh but applied to Mutex_noop_shared_non_recursive. More... | |
using | Lock_guard_noop_shared_non_recursive_ex = boost::unique_lock< Mutex_noop_shared_non_recursive > |
(Deprecated given C++1x) Equivalent to Lock_guard_shared_non_recursive_ex but applied to Mutex_noop_shared_non_recursive. More... | |
Functions | |
template<typename Allocator , bool S_SHARING_ALLOWED> | |
void | swap (Basic_blob< Allocator, S_SHARING_ALLOWED > &blob1, Basic_blob< Allocator, S_SHARING_ALLOWED > &blob2, log::Logger *logger_ptr=0) |
Equivalent to blob1.swap(blob2) . More... | |
template<typename Allocator , bool S_SHARING_ALLOWED> | |
bool | blobs_sharing (const Basic_blob< Allocator, S_SHARING_ALLOWED > &blob1, const Basic_blob< Allocator, S_SHARING_ALLOWED > &blob2) |
Returns true if and only if both given objects are not zero() == true , and they either co-own a common underlying buffer, or are the same object. More... | |
template<bool S_SHARING_ALLOWED> | |
void | swap (Blob_with_log_context< S_SHARING_ALLOWED > &blob1, Blob_with_log_context< S_SHARING_ALLOWED > &blob2) |
On top of the similar Basic_blob related function, logs using the stored log context of blob1 . More... | |
template<typename Key , typename Mapped , typename Hash , typename Pred > | |
void | swap (Linked_hash_map< Key, Mapped, Hash, Pred > &val1, Linked_hash_map< Key, Mapped, Hash, Pred > &val2) |
Equivalent to val1.swap(val2) . More... | |
template<typename Key , typename Hash , typename Pred > | |
void | swap (Linked_hash_set< Key, Hash, Pred > &val1, Linked_hash_set< Key, Hash, Pred > &val2) |
Equivalent to val1.swap(val2) . More... | |
bool | scheduled_task_cancel (log::Logger *logger_ptr, Scheduled_task_handle task) |
Attempts to prevent the execution of a previously scheduled (by schedule_task_from_now() or similar) task. More... | |
bool | scheduled_task_short_fire (log::Logger *logger_ptr, Scheduled_task_handle task) |
Attempts to reschedule a previously scheduled (by schedule_task_from_now() or similar) task to fire immediately. More... | |
Fine_duration | scheduled_task_fires_from_now_or_canceled (log::Logger *logger_ptr, Scheduled_task_const_handle task) |
Returns how long remains until a previously scheduled (by schedule_task_from_now() or similar) task fires; or negative time if that point is in the past; or special value if the task has been canceled. More... | |
bool | scheduled_task_fired (log::Logger *logger_ptr, Scheduled_task_const_handle task) |
Returns whether a previously scheduled (by schedule_task_from_now() or similar) task has already fired. More... | |
bool | scheduled_task_canceled (log::Logger *logger_ptr, Scheduled_task_const_handle task) |
Returns whether a previously scheduled (by schedule_task_from_now() or similar) task has been canceled. More... | |
template<typename Scheduled_task_handler > | |
Scheduled_task_handle | schedule_task_from_now (log::Logger *logger_ptr, const Fine_duration &from_now, bool single_threaded, Task_engine *task_engine, Scheduled_task_handler &&task_body_moved) |
Schedule the given function to execute in a certain amount of time: A handy wrapper around Timer (asio's timer facility). More... | |
template<typename Scheduled_task_handler > | |
Scheduled_task_handle | schedule_task_at (log::Logger *logger_ptr, const Fine_time_pt &at, bool single_threaded, Task_engine *task_engine, Scheduled_task_handler &&task_body_moved) |
Identical to schedule_task_from_now() except the time is specified in absolute terms. More... | |
boost::chrono::microseconds | time_since_posix_epoch () |
Get the current POSIX (Unix) time as a duration from the Epoch time point. More... | |
void | beautify_chrono_ostream (std::ostream *os) |
Sets certain chrono -related formatting on the given ostream that results in a consistent, desirable output of duration s and certain types of time_point s. More... | |
size_t | deep_size (const std::string &val) |
Estimate of memory footprint of the given value, including memory allocated on its behalf – but excluding its shallow sizeof ! – in bytes. More... | |
template<typename Time_unit , typename N_items > | |
double | to_mbit_per_sec (N_items items_per_time, size_t bits_per_item=8) |
Utility that converts a bandwidth in arbitrary units in both numerator and denominator to the same bandwidth in megabits per second. More... | |
template<typename Integer > | |
Integer | ceil_div (Integer dividend, Integer divisor) |
Returns the result of the given non-negative integer divided by a positive integer, rounded up to the nearest integer. More... | |
template<typename T > | |
bool | in_closed_range (T const &min_val, T const &val, T const &max_val) |
Returns true if and only if the given value is within the given range, inclusive. More... | |
template<typename T > | |
bool | in_open_closed_range (T const &min_val, T const &val, T const &max_val) |
Returns true if and only if the given value is within the given range, given as a (low, high] pair. More... | |
template<typename T > | |
bool | in_closed_open_range (T const &min_val, T const &val, T const &max_val) |
Returns true if and only if the given value is within the given range, given as a [low, high) pair. More... | |
template<typename T > | |
bool | in_open_open_range (T const &min_val, T const &val, T const &max_val) |
Returns true if and only if the given value is within the given range, given as a (low, high) pair. More... | |
template<typename Container > | |
bool | key_exists (const Container &container, const typename Container::key_type &key) |
Returns true if and only if the given key is present at least once in the given associative container. More... | |
template<typename Cleanup_func > | |
Auto_cleanup | setup_auto_cleanup (const Cleanup_func &func) |
Provides a way to execute arbitrary (cleanup) code at the exit of the current block. More... | |
template<typename Minuend , typename Subtrahend > | |
bool | subtract_with_floor (Minuend *minuend, const Subtrahend &subtrahend, const Minuend &floor=0) |
Performs *minuend -= subtrahend , subject to a floor of floor . More... | |
template<typename From , typename To > | |
size_t | size_unit_convert (From num_froms) |
Answers the question what's the smallest integer number of To s sufficient to verbatim store the given number of From s?, where From and To are POD types. More... | |
template<typename T1 , typename ... T_rest> | |
void | feed_args_to_ostream (std::ostream *os, T1 const &ostream_arg1, T_rest const &... remaining_ostream_args) |
"Induction step" version of variadic function template that simply outputs arguments 2+ via << to the given ostream , in the order given. More... | |
template<typename T > | |
void | feed_args_to_ostream (std::ostream *os, T const &only_ostream_arg) |
"Induction base" for a variadic function template, this simply outputs given item to given ostream via << . More... | |
template<typename ... T> | |
void | ostream_op_to_string (std::string *target_str, T const &... ostream_args) |
Writes to the specified string, as if the given arguments were each passed, via << in sequence, to an ostringstream , and then the result were appended to the aforementioned string variable. More... | |
template<typename ... T> | |
std::string | ostream_op_string (T const &... ostream_args) |
Equivalent to ostream_op_to_string() but returns a new string by value instead of writing to the caller's string . More... | |
template<typename Map , typename Sequence > | |
void | sequence_to_inverted_lookup_map (Sequence const &src_seq, Map *target_map, const Function< typename Map::mapped_type(size_t)> &idx_to_map_val_func) |
Similar to the 2-arg overload of sequence_to_inverted_lookup_map() but with the ability to store a value based on the index into the input sequence instead of that index itself. More... | |
template<typename Map , typename Sequence > | |
void | sequence_to_inverted_lookup_map (Sequence const &src_seq, Map *target_map) |
Given a generic sequence (integer -> object) generates a generic map (object -> integer) providing inverse lookup. More... | |
template<typename Const_buffer_sequence > | |
std::ostream & | buffers_to_ostream (std::ostream &os, const Const_buffer_sequence &data, const std::string &indentation, size_t bytes_per_line=0) |
Writes a multi- or single-line string representation of the provided binary data to an output stream, complete with a printable and hex versions of each byte. More... | |
template<typename Const_buffer_sequence > | |
std::string | buffers_dump_string (const Const_buffer_sequence &data, const std::string &indentation, size_t bytes_per_line=0) |
Identical to buffers_to_ostream() but returns an std::string instead of writing to a given ostream . More... | |
template<typename Enum > | |
Enum | istream_to_enum (std::istream *is_ptr, Enum enum_default, Enum enum_sentinel, bool accept_num_encoding=true, bool case_sensitive=false, Enum enum_lowest=Enum(0)) |
Deserializes an enum class value from a standard input stream. More... | |
Flow module containing miscellaneous general-use facilities that don't fit into any other Flow module.
Each symbol therein is typically used by at least 1 other Flow module; but all public symbols (except ones under a detail/ subdirectory) are intended for use by Flow user as well.
using flow::util::Blob = typedef Blob_with_log_context<> |
A concrete Blob_with_log_context that compile-time-disables Basic_blob::share() and the sharing API derived from it.
It is likely the user will refer to Blob (or Sharing_blob) rather than Blob_with_log_context.
using flow::util::Blob_sans_log_context = typedef Basic_blob<> |
Short-hand for a Basic_blob that allocates/deallocates in regular heap and is itself assumed to be stored in heap or on stack; sharing feature compile-time-disabled (with perf boost as a result).
using flow::util::Lock_guard = typedef boost::unique_lock<Mutex> |
Short-hand for advanced-capability RAII lock guard for any mutex, ensuring exclusive ownership of that mutex.
Note the advanced API available for the underlying type: it is possible to relinquish ownership without unlocking, gain ownership of a locked mutex; and so on.
Mutex | A non-recursive or recursive mutex type. Recommend one of: Mutex_non_recursive, Mutex_recursive, Mutex_shared_non_recursive, Mutex_noop_shared_non_recursive. |
using flow::util::Lock_guard_non_recursive = typedef boost::unique_lock<Mutex_non_recursive> |
(Deprecated given C++1x) Short-hand for advanced-capability RAII lock guard for Mutex_non_recursive mutexes.
Lock_guard<Mutex_non_recursive>
possible. Remove it and all (outside) uses eventually. using flow::util::Lock_guard_noop_shared_non_recursive_ex = typedef boost::unique_lock<Mutex_noop_shared_non_recursive> |
(Deprecated given C++1x) Equivalent to Lock_guard_shared_non_recursive_ex but applied to Mutex_noop_shared_non_recursive.
Lock_guard<Mutex_noop_shared_non_recursive>
possible. Remove it and all (outside) uses eventually. using flow::util::Lock_guard_noop_shared_non_recursive_sh = typedef boost::shared_lock<Mutex_noop_shared_non_recursive> |
(Deprecated given C++1x) Equivalent to Lock_guard_shared_non_recursive_sh but applied to Mutex_noop_shared_non_recursive.
Shared_lock_guard<Mutex_noop_shared_non_recursive>
possible. Remove it and all (outside) uses eventually. using flow::util::Lock_guard_recursive = typedef boost::unique_lock<Mutex_recursive> |
(Deprecated given C++1x) Short-hand for advanced-capability RAII lock guard for Mutex_recursive mutexes.
Lock_guard<Mutex_recursive>
possible. Remove it and all (outside) uses eventually. using flow::util::Lock_guard_shared_non_recursive_ex = typedef boost::unique_lock<Mutex_shared_non_recursive> |
(Deprecated given C++1x) Short-hand for exclusive mode advanced-capability RAII lock guard for Mutex_shared_non_recursive mutexes.
Lock_guard<Mutex_shared_non_recursive>
possible. Remove it and all (outside) uses eventually. using flow::util::Lock_guard_shared_non_recursive_sh = typedef boost::shared_lock<Mutex_shared_non_recursive> |
(Deprecated given C++1x) Short-hand for shared mode advanced-capability RAII lock guard for Mutex_shared_non_recursive mutexes.
Shared_lock_guard<Mutex_shared_non_recursive>
possible. Remove it and all (outside) uses eventually. using flow::util::Mutex_noop_shared_non_recursive = typedef boost::null_mutex |
Short-hand for a mutex type equivalent to util::Mutex_shared_non_recursive – except that the lock/unlock mutex ops all do nothing.
One can parameterize templates accordingly; so that an algorithm can be generically written to work in both single- and multi-threaded contexts without branching into 2 code paths, yet avoid the unnecessary actual locking in the former case.
using flow::util::Mutex_shared_non_recursive = typedef boost::shared_mutex |
Short-hand for non-reentrant, shared-or-exclusive mutex.
When locking one of these, choose one of: Lock_guard<Mutex_shared_non_recursive>
, Shared_lock_guard<Mutex_shared_non_recursive>
. The level of locking acquired (shared vs. exclusive) depends on which you chose and is thus highly significant.
boost::shared_mutex
to std::shared_mutex
, as the former has a long-standing unresolved ticket about its impl being slow and/or outdated (as of Boost-1.80). However see also the note on util::Mutex_shared_non_recursive that explains why it might be best to avoid this type of mutex altogether in the first place (in most cases).Experts suggest fairly strongly that one should be very wary about using a shared mutex, over a simple non-recursive exclusive mutex, in almost any practical application. The reason there's a trade-off at all is that a Mutex_non_recursive is extremely fast to lock and unlock; the entire perf cost is in waiting for another thread to unlock first; so without contention it's almost free. In contrast apparently any real impl of Mutex_shared_non_recursive is much slower to lock/unlock. The trade-off is against allowing reads to proceed in parallel; but experts say this is "worth it" only when these read critical sections are lengthy and very frequently invoked. So, for example, if the lock/unlock is around an unordered_map
lookup with a quick hashing function, it would be quite difficult to induce enough lock contention to make a shared mutex better than an exclusive one. (Note this is assuming even no perf issue with boost::shared_mutex
specifically.)
using flow::util::Scheduled_task = typedef Function<void (bool short_fire)> |
Short-hand for tasks that can be scheduled/fired by schedule_task_from_now() and similar.
short_fire
shall be set to false
if the task executed based on its scheduled time; true
if it is fired early due to scheduled_task_short_fire().
using flow::util::Scheduled_task_const_handle = typedef boost::shared_ptr<const Scheduled_task_handle_state> |
Equivalent to Scheduled_task_handle
but refers to immutable version of a task.
One can safely construct a Scheduled_task_const_handle
directly from Scheduled_task_handle
(but not vice versa; nor would that compile without some kind of frowned-upon const_cast
or equivalent).
using flow::util::Scheduled_task_handle = typedef boost::shared_ptr<Scheduled_task_handle_state> |
Black-box type that represents a handle to a scheduled task as scheduled by schedule_task_at() or schedule_task_from_now() or similar, which can be (optionally) used to control the scheduled task after it has been thus scheduled.
Special value Scheduled_task_handle()
represents an invalid task and can be used as a sentinel, as with a null pointer.
Values of this type are to be passed around by value, not reference. They are light-weight. Officially, passing it by reference results in undefined behavior. Unofficially, it will probably work (just haven't thought super-hard about it to make sure) but will bring no performance benefit.
using flow::util::Shared_lock_guard = typedef boost::shared_lock<Shared_mutex> |
Short-hand for shared mode advanced-capability RAII lock guard, particuarly for Mutex_shared_non_recursive mutexes.
Shared_mutex | Typically Mutex_shared_non_recursive, but any mutex type with that both-exclusive-and-shared API will work. |
using flow::util::Sharing_blob = typedef Blob_with_log_context<true> |
A concrete Blob_with_log_context that compile-time-enables Basic_blob::share() and the sharing API derived from it.
It is likely the user will refer to Sharing_blob (or Blob) rather than Blob_with_log_context.
using flow::util::Sharing_blob_sans_log_context = typedef Basic_blob<std::allocator<uint8_t>, true> |
Identical to Blob_sans_log_context but with sharing feature compile-time-enabled.
The latter fact implies a small perf hit; see details in Basic_blob doc header.
using flow::util::Task_engine = typedef boost::asio::io_service |
Short-hand for boost.asio event service, the central class of boost.asio.
The reasons for the rename – as opposed to simply calling it Io_service
(historically it was indeed so named) – are as follows. To start, a later version of Boost (to which we eventually moved) realigned boost.asio's API to match the feedback boost.asio received when submitted as a candidate for inclusion into the official STL (sometime after C++17); in this reorg they renamed it from io_service
to io_context
. That's not the actual reason but more of a symptom of similar reasoning to the following:
io_service
actually doesn't; it can (and typically is) used to take over at least one thread, but that's not a part of its "essence," so to speak. I've noticed that when boost.asio neophytes see "service" they intuitively draw incorrect assumptions about what it does, and then one must repeatedly dash those assumptions.io_service
is all about working with various input/output (especially networking). While it's true that roughly 50% of the utility of boost.asio is its portable sockets/networking API, the other 50% is about its ability to flexibly execute user tasks in various threads; and, indeed, io_service
itself is more about the latter 50% rather than the former. Its two most basic and arguably oft-used features are simply io_service::post()
(which executes the given task ASAP); and the basic_waitable_timer
set of classes (which execute task at some specified point in the future). Note these have nothing to do with networking or I/O of any kind. The I/O comes from the "I/O object" classes/etc., such as tcp::socket
, but those are not io_service
; they merely work together with it to execute the user-specified success/failure handlers.So, neither "I/O" nor "service" is accurate. To fix both, then, we used this rename reasoning:
post()
– what is it handling?)io_service
does provide immense algorithmic value.io_service::run()
ning then to the extent there are any queues there might be multiple.Task_qs
– of which there could be multiple (plural) – was clumsy. (An early proof-of-concept used Task_qs
; people were not fans.) using flow::util::Thread = typedef boost::thread |
Short-hand for standard thread class.
We use/encourage use of boost.thread threads (and other boost.thread facilities) over std.thread counterparts – simply because it tends to be more full-featured. However, reminder: boost.thread IS (API-wise at least) std.thread, plus more (advanced?) stuff. Generally, boost.thread-using code can be converted to std.thread with little pain, unless one of the "advanced?" features is used.
using flow::util::Timer = typedef boost::asio::basic_waitable_timer<Fine_clock> |
boost.asio timer.
Can schedule a function to get called within a set amount of time or at a certain specific time relative to some Epoch.
Timer). The notes below still apply however. Please read them even if you won't use
Timer` directly.Important: While one can schedule events using very precise units using Fine_duration, the timer may not actually have that resolution. That is, I may schedule something to happen in 1 millisecond and then measure the time passed, using a high-res clock like Fine_clock, and discover that the wait was actually 15 milliseconds. This is not a problem for most timers. (In Flow itself, the most advanced use of timers by far is in flow::net_flow. Timers used in flow::net_flow – delayed ACK timer, Drop Timer, simulated latency timer – typically are for n x 50
milliseconds time periods or coarser.) However that may not be the case for all timers. (In particular, flow::net_flow packet pacing may use finer time periods.)
net_flow
user. I leave them in this public doc header for practical purposes, as examples needed to discuss some Inside-Baseball topics involved.So I looked into what boost.asio provides. deadline_timer
uses the system clock (universal_time()
) as the time reference, while basic_waitable_timer<Fine_clock>
uses the high-resolution clock (see Fine_clock). I tested both (using wait()
s of various lengths and using Fine_clock to measure duration) and observed the following resolutions on certain OS (not listing the hardware):
glibc
is too old to provide the timerfd
API, therefore Boost falls back to using epoll_wait()
directly; on a newer Linux this may get better results.)These results were observed for BOTH deadline_timer
and basic_waitable_timer<Fine_clock>
in Boost 1.50. Thus, there was no resolution advantage to using the latter – only an interface advantage. Conclusion: we'd be well-advised not to rely on anything much smaller than 20 milliseconds when scheduling timer events. One technique might be, given any time T < 20 ms, to assume T = 0 (i.e., execute immediately). This may or may not be appropriate depending on the situation.
However, using basic_waitable_timer<Fine_clock>
may get an additional nice property: Fine_clock always goes forward and cannot be affected by NTP or user changes. deadline_timer
may or may not be affected by such tomfoolery (untested, but the potential is clearly there). Therefore we do the best we can by using the Fine_clock-based timer.
cancel()
ing it does not always cause the handler to fire with operation_aborted
error code. Sometimes it was already "just
about" to fire at cancel()
time, so the handler will run with no error code regardless of the cancel()
. Personally I have never once found this behavior useful, and there is much code currently written to work around this "feature." Furthermore, even a "successful" cancel still runs the handler but with operation_aborted
; 99.999% of the time one would want to just not run it at all. Finally, in many cases, even having to create a timer object (and then the multiple steps required to actually schedule a thing) feels like more boiler-plate than should be necessary. To avoid all of these usability issues, see schedule_task_from_now() (and similar), which is a facility that wraps Timer
for a boiler-plate-free experience sufficient in the majority of practical situations. Timer
is to be used directly when that simpler facility is insufficient.b_w_t<Fine_clock>
happens to equal boost::asio::high_resolution_timer
. We chose to alias in terms of Fine_clock merely to ensure that Fine_duration is one-to-one compatible with it, as it is defined in terms of Fine_clock also. void flow::util::beautify_chrono_ostream | ( | std::ostream * | os | ) |
Sets certain chrono
-related formatting on the given ostream
that results in a consistent, desirable output of duration
s and certain types of time_point
s.
As of this writing this includes enabling short unit format (e.g., "ms" instead of "milliseconds") and avoiding Unicode characters (the Greek letter for micro becomes a similar-looking "u" instead).
os | The stream to affect. |
bool blobs_sharing | ( | const Basic_blob< Allocator, S_SHARING_ALLOWED > & | blob1, |
const Basic_blob< Allocator, S_SHARING_ALLOWED > & | blob2 | ||
) |
Returns true
if and only if both given objects are not zero() == true
, and they either co-own a common underlying buffer, or are the same object.
Note: by the nature of Basic_blob::share(), a true
returned value is orthogonal to whether Basic_blob::start() and Basic_blob::size() values are respectively equal; true
may be returned even if their [begin()
, end()
) ranges don't overlap at all – as long as the allocated buffer is co-owned by the 2 Basic_blob
s.
If &blob1 != &blob2
, true
indicates blob1
was obtained from blob2
via a chain of Basic_blob::share() (or wrapper thereof) calls, or vice versa.
blob1 | Object. |
blob2 | Object. |
blob1
and blob2
both operate on the same underlying buffer. std::string flow::util::buffers_dump_string | ( | const Const_buffer_sequence & | data, |
const std::string & | indentation, | ||
size_t | bytes_per_line = 0 |
||
) |
Identical to buffers_to_ostream() but returns an std::string
instead of writing to a given ostream
.
Const_buffer_sequence | See buffers_to_ostream(). |
data | See buffers_to_ostream(). |
indentation | See buffers_to_ostream(). |
bytes_per_line | See buffers_to_ostream(). |
std::ostream & flow::util::buffers_to_ostream | ( | std::ostream & | os, |
const Const_buffer_sequence & | data, | ||
const std::string & | indentation, | ||
size_t | bytes_per_line = 0 |
||
) |
Writes a multi- or single-line string representation of the provided binary data to an output stream, complete with a printable and hex versions of each byte.
bytes_per_line
to special value -1
. In this mode, the output consists of indentation
, followed by a pretty-printed hex dump of every byte in data
(no matter how many), followed by a pretty-printed ASCII-printable (or '.' if not so printable) character for each byte; that's it. No newline is added, and indentation
is included only the one time. It is recommended to therefore print "in-line" inside log messages (etc.) without surrounding newlines, etc., when using this mode.bytes_per_line
to a positive value; or 0 which auto-selects a decent default positive value. Then, each line represents up to that many bytes, which are pretty-printed similarly to single-line mode, in order of appearance in data
(with the last such line representing the last few bytes, formatting nicely including accounting for potentially fewer than the desired # of bytes per line). Every line starts with indentation
and ends with a newline – including the last line. Therefore, it is recommended to precede this call with an output of a newline and to avoid doing so after that call (unless blank line is desired).Example with a single contiguous memory area, multi-line mode:
See also buffers_dump_string() which returns a string and can thus be more easily used directly inside FLOW_LOG_DATA() and similar log macros.
This thing is slow... it's not trying to be fast and can't be all that fast anyway. As usual, though, if used in FLOW_LOG_DATA() (etc.) its slowness will only come into play if the log filter passes which (esp. for log::Severity::S_DATA) it usually won't.
That said buffers_dump_string() is even slower, because it'll use an intermediate ostream
independent of whatever ostream
you may or may not be placing the resulting string. However it's easier to use with FLOW_LOG_...()
, and since in that case perf is typically not an issue, it makes sense to use the easier thing typically.
However, if you do want to avoid that extra copy and need to also use buffers_to_ostream() directly inside FLOW_LOG_...()
then the following technique isn't too wordy and works:
The reason it returns os
and takes a reference-to-mutable instead of the customary (for this project's style, to indicate modification potential at call sites) pointer-to-mutable is in order to be bind()
able in such a way as to make an ostream
manipulator. In the example above we use a lambda instead of bind()
however.
Const_buffer_sequence | Type that models the boost.asio ConstBufferSequence concept (see Boost docs) which represents 1 or more scattered buffers in memory (only 1 buffer in a sequence is common). In particular boost::asio::const_buffer works when dumping a single buffer. |
os | The output stream to which to write. |
data | The data to write, given as an immutable sequence of buffers each of which is essentially a pointer and length. (Reminder: it is trivial to make such an object from a single buffer as well; for example given array<uint8_t, 256> data (an array of 256 bytes), you can just pass boost::asio::buffer(data) , as the latter returns an object whose type satisfies requirements of Const_buffer_sequence despite the original memory area being nothing more than a single byte array.) |
indentation | The indentation to use at the start of every line of output. |
bytes_per_line | If 0, act as-if a certain default positive value were passed; and then: If -1 , single-line mode is invoked. If a positive value, multi-line mode is invoked, with all lines but the last one consisting of a dump of that many contiguous bytes of data . (Yes, multi-line mode is still in force, even if there are only enough bytes in data for one line of output anyway.) |
os
. Integer flow::util::ceil_div | ( | Integer | dividend, |
Integer | divisor | ||
) |
Returns the result of the given non-negative integer divided by a positive integer, rounded up to the nearest integer.
Internally, it avoids floating-point math for performance.
Integer | A signed or unsigned integral type. |
dividend | Dividend; non-negative or assertion trips. |
divisor | Divisor; positive or assertion trips. |
(dividend / divisor)
. size_t flow::util::deep_size | ( | const std::string & | val | ) |
Estimate of memory footprint of the given value, including memory allocated on its behalf – but excluding its shallow sizeof
! – in bytes.
val | Value. |
void flow::util::feed_args_to_ostream | ( | std::ostream * | os, |
T const & | only_ostream_arg | ||
) |
"Induction base" for a variadic function template, this simply outputs given item to given ostream
via <<
.
T | See each of ...T in ostream_op_to_string(). |
os | Pointer to stream to which to sequentially send arguments for output. |
only_ostream_arg | See each of ostream_args in ostream_op_to_string(). |
void flow::util::feed_args_to_ostream | ( | std::ostream * | os, |
T1 const & | ostream_arg1, | ||
T_rest const &... | remaining_ostream_args | ||
) |
"Induction step" version of variadic function template that simply outputs arguments 2+ via <<
to the given ostream
, in the order given.
...T_rest | See ...T in ostream_op_to_string(). |
remaining_ostream_args | See ostream_args in ostream_op_to_string(). |
T1 | Same as each of ...T_rest . |
ostream_arg1 | Same as each of remaining_ostream_args . |
os | Pointer to stream to which to sequentially send arguments for output. |
bool flow::util::in_closed_open_range | ( | T const & | min_val, |
T const & | val, | ||
T const & | max_val | ||
) |
Returns true
if and only if the given value is within the given range, given as a [low, high) pair.
min_val | Lower part of the range. |
val | Value to check. |
max_val | Higher part of the range. Must be greater than min_val , or behavior is undefined. |
T | A type for which the operation x < y is defined and makes sense. Examples: double , char , unsigned int , Fine_duration. |
true
if and only if val
is in [min_val
, max_val
), i.e., min_val
<= val
< max_val
. bool flow::util::in_closed_range | ( | T const & | min_val, |
T const & | val, | ||
T const & | max_val | ||
) |
Returns true
if and only if the given value is within the given range, inclusive.
min_val | Lower part of the range. |
val | Value to check. |
max_val | Higher part of the range. Must be greater than or equal to min_val , or behavior is undefined. |
T | A type for which the operation x < y is defined and makes sense. Examples: double , char , unsigned int , Fine_duration. |
true
if and only if val
is in [min_val
, max_val
]. bool flow::util::in_open_closed_range | ( | T const & | min_val, |
T const & | val, | ||
T const & | max_val | ||
) |
Returns true
if and only if the given value is within the given range, given as a (low, high] pair.
min_val | Lower part of the range. |
val | Value to check. |
max_val | Higher part of the range. Must be greater than min_val , or behavior is undefined. |
T | A type for which the operation x < y is defined and makes sense. Examples: double , char , unsigned int , Fine_duration. |
true
if and only if val
is in (min_val
, max_val
], i.e., min_val
< val
<= max_val
. bool flow::util::in_open_open_range | ( | T const & | min_val, |
T const & | val, | ||
T const & | max_val | ||
) |
Returns true
if and only if the given value is within the given range, given as a (low, high) pair.
min_val | Lower part of the range. |
val | Value to check. |
max_val | Higher part of the range. Must be at least 2 greater than min_val , or behavior is undefined. |
T | A type for which the operation x < y is defined and makes sense. Examples: double , char , unsigned int , Fine_duration. |
true
if and only if val
is in (min_val
, max_val
), i.e., min_val
< val
< max_val
. Enum flow::util::istream_to_enum | ( | std::istream * | is_ptr, |
Enum | enum_default, | ||
Enum | enum_sentinel, | ||
bool | accept_num_encoding = true , |
||
bool | case_sensitive = false , |
||
Enum | enum_lowest = Enum(0) |
||
) |
Deserializes an enum class
value from a standard input stream.
Reads up to but not including the next non-alphanumeric-or-underscore character; the resulting string is then mapped to an Enum
. If none is recognized, enum_default
is the result. The recognized values are:
Enum
. (Can be disabled optionally.)Enum
, as determined by operator<<(ostream&)
– which must exist (or this will not compile). Informally we recommend the encoding to be the non-S_-prefix part of the actual Enum
member; e.g., "WARNING"
for log::Sev::S_WARNING. If the scanned token does not map to any of these, or if end-of-input is encountered immediately (empty token), then enum_default
is returned.Error semantics: There are no invalid values or exceptions thrown; enum_default
returned is the worst case. Do note *is_ptr
may not be good() == true
after return.
Tip: It is convenient to implement operator>>(istream&)
in terms of istream_to_enum(). With both >>
and <<
available, serialization/deserialization of the enum class
will work; this enables a few key things to work, including parsing from config file/command line via and conversion from string
via lexical_cast
.
Informal convention suggestion: S_END_SENTINEL
should be the sentinel member of Enum
. E.g., see log::Sev.
Enum | An enum class which must satisfy the following requirements or else risk undefined behavior (if it compiles): An element, enum_lowest , has a non-negative integer value. Subsequent elements are strictly monotonically increasing with increment 1, up to and including enum_sentinel . (Elements outside [enum_lowest , enum_sentinel ] may exist, as long as their numeric values don't conflict with those in-range, but informally we recommend against this.) ostream << Enum exists and works without throwing for all values in range [enum_lowest , enum_sentinel ). Each << -serialized string must be distinct from the others. Each << -serialized string must start with a non-digit and must consist only of alphanumerics and underscores. Exception: digit-leading is allowed if and only if !accept_num_encoding , though informally we recommend against it as a convention. |
is_ptr | Stream from which to deserialize. |
enum_default | Value to return if the token does not match either the numeric encoding (if enabled) or the << encoding. enum_sentinel is a sensible (but not the only sensible) choice. |
enum_sentinel | Enum value such that all valid deserializable values have numeric conversions strictly lower than it. |
accept_num_encoding | If true , a numeric value is accepted as an encoding; otherwise it is not (and will yield enum_default like any other non-matching token). |
case_sensitive | If true , then the token must exactly equal an ostream<< encoding of a non-sentinel Enum ; otherwise it may equal it modulo different case. |
enum_lowest | The lowest Enum value. Its integer value is very often 0, sometimes 1. Behavior undefined if it is negative. |
bool flow::util::key_exists | ( | const Container & | container, |
const typename Container::key_type & | key | ||
) |
Returns true
if and only if the given key is present at least once in the given associative container.
Container | Associative container type (boost::unordered_map , std::set , etc.). In particular must have the members find() , end() , and key_type . |
container | Container to search. |
key | Key to find. |
std::string flow::util::ostream_op_string | ( | T const &... | ostream_args | ) |
Equivalent to ostream_op_to_string() but returns a new string
by value instead of writing to the caller's string
.
This is useful at least in constructor initializers, where it is not possible to first declare a stack variable.
With the C++11-y use of move semantics in STL it should be no slower than using ostream_op_to_string()
– meaning, it is no slower, period, as this library now requires C++11.
...T | See ostream_op_to_string(). |
ostream_args | See ostream_op_to_string(). |
std::string
. void flow::util::ostream_op_to_string | ( | std::string * | target_str, |
T const &... | ostream_args | ||
) |
Writes to the specified string, as if the given arguments were each passed, via <<
in sequence, to an ostringstream
, and then the result were appended to the aforementioned string variable.
Tip: It works nicely, 99% as nicely as simply <<
ing an ostream
; but certain language subtleties mean you may have to fully qualify some template instances among ostream_args
. Do so if you receive a "deduced incomplete pack" (clang) or similar error, as I have seen when using, e.g., chrono::symbol_format
formatter (which the compile error forced me to qualify as: symbol_format<char, ostream::traits_type>
).
...T | Each type T is such that os << t , with types T const & t and ostream& os , builds and writes t to os , returning lvalue os . Usually in practice this means the existence of ostream& operator<<(ostream&, T const &) or ostream& operator<<(ostream&, T) overload, the latter usually for basic types T . See also tip above, if compiler is unable to deduce a given T (even when it would deduce it in os << t ). |
target_str | Pointer to the string to which to append. |
ostream_args | One or more arguments, such that each argument arg is suitable for os << arg , where os is an ostream . |
Scheduled_task_handle flow::util::schedule_task_at | ( | log::Logger * | logger_ptr, |
const Fine_time_pt & | at, | ||
bool | single_threaded, | ||
Task_engine * | task_engine, | ||
Scheduled_task_handler && | task_body_moved | ||
) |
Identical to schedule_task_from_now() except the time is specified in absolute terms.
The current implementation is such that there is no performance benefit to using schedule_task_from_now(at - Fine_clock::now(), ...)
over schedule_task_at(at, ...)
. Therefore, if it is convenient for caller's code reuse to do the former, there is no perf downside to it, so feel free.
logger_ptr | See schedule_task_from_now(). |
at | Fire at this absolute time. If this is in the past, it will fire ASAP. |
single_threaded | See schedule_task_from_now(). |
task_engine | See schedule_task_from_now(). |
task_body_moved | See schedule_task_from_now(). |
Scheduled_task_handler | See schedule_task_from_now(). |
Scheduled_task_handle flow::util::schedule_task_from_now | ( | log::Logger * | logger_ptr, |
const Fine_duration & | from_now, | ||
bool | single_threaded, | ||
Task_engine * | task_engine, | ||
Scheduled_task_handler && | task_body_moved | ||
) |
Schedule the given function to execute in a certain amount of time: A handy wrapper around Timer (asio's timer facility).
Compared to using Timer, this has far simplified semantics at the cost of certain less-used features of Timer. Recommend using this facility when sufficient; otherwise use Timer directly. The trade-offs are explained below, but first:
Conceptually this is similar to JavaScript's ubiquitous (albeit single-threaded) setTimeout()
feature. The given function shall execute as if post()
ed onto the given Task_engine
; unless successfully canceled by scheduled_task_cancel(X)
, where X is the (optionally used and entirely ignorable) returned handle. Barring unrelated crashes/etc. there are exactly three mutually exclusive outcomes of this function executing:
bool short_fire
arg to it set to false
.short_fire == true
. To trigger this, use scheduled_task_short_fire() before the scheduled time.All related functions are thread-safe w/r/t a given returned Scheduled_task_handle
, if single_threaded == false
. In addition, for extra performance (internally, by omitting certain locking), set single_threaded = true
only if you can guarantee the following:
*task_engine
is run()
ning in no more than one thread throughout all work with the returned Scheduled_task_handle
including the present function.Scheduled_task_handle
are also – if at all – called from that one thread.The minimum is step 1: Call schedule_task_at() or schedule_task_from_now(). Optionally, if one saves the return value X from either, one can do step 2: scheduled_task_short_fire(X)
or scheduled_task_cancel(X)
, which will succeed (return true
) if called sufficiently early. There is no step 3; any subsequent calls on X will fail (return false
).
scheduled_task_*()
function on it.)Error_code
passed in to your task function.Timer::cancel()
means it will still execute but with operation_aborted
code.)Timer::cancel()
can result in ~3 behaviors: it already executed, so it does nothing; it has not yet executed but was JUST about to execute, so it will still execute with non-operation_aborted
code; it has not yet executed, so now it will execute but with operation_aborted
. With this facility, it's simpler: it either succeeds (so acts per previous bullet – does not execute); or it fails, hence it will have executed (and there is no Error_code
to decipher).cancel()
actually means short-firing (firing ASAP) but with a special Error_code
. One can also change the expiration time to the past to short-fire in a different way. With this facility, cancellation means it won't run; and scheduled_task_short_fire() means it will fire early; that's it.Timer
for performance?': It might not be fine if and only if timer firings and/or cancellations occur many times a second in a performance-sensitive environment. E.g., if it's something fired and/or canceled every second repeatedly, it's fine; it it's packet pacing that must sensitively fire near the resolution limit of the native Timer facility, it might not be fine, and it's safer to use Timer repeatedly.Timer has certain informational accessors (like one that returns the scheduled firing time) that we lack. (Again, we could provide this also – but why?)
Timer
s can be internally maintained to implement this. This may or may not be worth the complexity, but if the API can remain identically simple while cleanly eliminating the one perf-related reason to choose Timer over this simpler facility, then that is a clean win from the API user's point of view. By comparison, other possible improvements mentioned complicate the API which makes them less attractive.from_now
values). class
. The picture is slightly muddled because we DO provide some "methods" – so why not make it a class
after all, just a simpler one than Timer? Answer: I believe this keeps the API simple: Step 1: Schedule it. Step 2 (optional): Short-fire or cancel it. There are no corner cases introduced as might have been via increased potential statefulness inherent with a class. But see the following to-do.Serial_task_timer
). I believe it internally uses integer "task ID" to distinguish between scheduled tasks issued in some chronological order, so that boost.asio firing a task after it has been canceled/pre-fired can be easily detected.logger_ptr | Logging, if any – including in the background – will be done via this logger. |
from_now | Fire ASAP once this time period passes (0 to fire ASAP). A negative value has the same effect as 0. |
single_threaded | Set to a true value if and only if, basically, *task_engine is single-threaded, and you promise not to call anything on the returned Scheduled_task_handle except from that same thread. More formally, see above. |
task_engine | The Task_engine onto which the given task may be post() ed (or equivalent). |
task_body_moved | The task to execute within *task_engine unless successfully canceled. See template param doc below also regarding Strand and other executor binding. |
Scheduled_task_handler | Completion handler with signature compatible with void (bool short_fired) . This allows for standard boost.asio semantics, including associating with an executor such as a boost::asio::strand . In particular you may pass in: bind_executor(S, F) , where F(bool short_fire) is the handler, and S is a Strand (or other executor). Binding to a Strand will ensure the fired or short-fired body will not execute concurrently with any other handler also bound to it. |
bool flow::util::scheduled_task_cancel | ( | log::Logger * | logger_ptr, |
Scheduled_task_handle | task | ||
) |
Attempts to prevent the execution of a previously scheduled (by schedule_task_from_now() or similar) task.
For semantics, in the context of the entire facility, see schedule_task_from_now() doc header.
logger_ptr | See scheduled_task_short_fire(). |
task | See scheduled_task_short_fire(). |
true
if the task has not executed and will NEVER have executed, AND no other scheduled_task_cancel() with the same arg has succeeded before this. false
if it has or will soon have executed because the present call occurred too late to stop it. Namely, it may have fired or short-fired already. bool flow::util::scheduled_task_canceled | ( | log::Logger * | logger_ptr, |
Scheduled_task_const_handle | task | ||
) |
Returns whether a previously scheduled (by schedule_task_from_now() or similar) task has been canceled.
Note that this cannot be true
while scheduled_task_fired() is false
and vice versa (but see thread safety note below).
Thread safety notes in the scheduled_task_fired() doc header apply equally here.
logger_ptr | See scheduled_task_fired(). |
task | See scheduled_task_fired(). |
bool flow::util::scheduled_task_fired | ( | log::Logger * | logger_ptr, |
Scheduled_task_const_handle | task | ||
) |
Returns whether a previously scheduled (by schedule_task_from_now() or similar) task has already fired.
Note that this cannot be true
while scheduled_task_canceled() is false
and vice versa (but see thread safety note below).
Also note that, while thread-safe, if !single_threaded
in the original scheduling call, then the value returned might be different even if checked immediately after this function exits, in the same thread. However, if single_threaded
(and one indeed properly uses task
from one thread only) then it is guaranteed this value is consistent/correct synchronously in the caller's thread, until code in that thread actively changes it (e.g., by canceling task).
logger_ptr | See scheduled_task_fires_from_now_or_canceled(). |
task | See scheduled_task_fires_from_now_or_canceled(). |
Fine_duration flow::util::scheduled_task_fires_from_now_or_canceled | ( | log::Logger * | logger_ptr, |
Scheduled_task_const_handle | task | ||
) |
Returns how long remains until a previously scheduled (by schedule_task_from_now() or similar) task fires; or negative time if that point is in the past; or special value if the task has been canceled.
This is based solely on what was specified when scheduling it; it may be different from when it will actually fire or has fired. However, a special value (see below) is returned, if the task has been canceled (scheduled_task_cancel()).
logger_ptr | Logging, if any, will be done synchronously via this logger. |
task | (Copy of) the handle returned by a previous schedule_task_*() call. |
Fine_duration::max()
to indicate the task has been canceled. Note a non-max()
(probably negative) duration will be returned even if it has already fired. bool flow::util::scheduled_task_short_fire | ( | log::Logger * | logger_ptr, |
Scheduled_task_handle | task | ||
) |
Attempts to reschedule a previously scheduled (by schedule_task_from_now() or similar) task to fire immediately.
For semantics, in the context of the entire facility, see schedule_task_from_now() doc header.
logger_ptr | See schedule_task_from_now(). |
task | (Copy of) the handle returned by a previous schedule_task_*() call. |
true
if the task will indeed have executed soon with short_fire == true
. false
if it has or will soon have executed with short_file == false
; or if it has already been successfully canceled via scheduled_task_cancel(). void flow::util::sequence_to_inverted_lookup_map | ( | Sequence const & | src_seq, |
Map * | target_map | ||
) |
Given a generic sequence (integer -> object) generates a generic map (object -> integer) providing inverse lookup.
See the 3-arg overload if you want to provide a more complex lookup function to store something else based on each index.
A naive way of implementing lookups otherwise would be a linear search for the object; using this will use RAM to avoid the slow searches.
Sequence | Sequence such as std::vector<T> or std::array<T> . Informally, T should be something light-weight and hence usable as a key type for a map type (Map ). |
Map | Map that maps T from Sequence to size_t . Example: std::map<T, size_t> . |
src_seq | Input sequence. |
target_map | Output map. Note it will not be pre-cleared; informally, this means one can shove 2+ lookup maps into one. If null behavior undefined (assertion may trip). |
void flow::util::sequence_to_inverted_lookup_map | ( | Sequence const & | src_seq, |
Map * | target_map, | ||
const Function< typename Map::mapped_type(size_t)> & | idx_to_map_val_func | ||
) |
Similar to the 2-arg overload of sequence_to_inverted_lookup_map() but with the ability to store a value based on the index into the input sequence instead of that index itself.
See the 2-arg overload.
Sequence | Sequence such as std::vector<T> or std::array<T> . Informally, T should be something light-weight and hence usable as a key type for a map type (Map ). |
Map | Map that maps T from Sequence to another type X . Example: unordered_map<T, X> , where an X can be computed from a size_t index. |
src_seq | See 2-arg overload. |
target_map | See 2-arg overload. |
idx_to_map_val_func | Given an index idx into src_seq , (*target_map)[] shall contain idx_to_map_val_func(idx) . Use this arg to instead perform a second lookup before storing a value in *target_map . Use the 2-arg overload if you'd like to store the index itself. |
Auto_cleanup flow::util::setup_auto_cleanup | ( | const Cleanup_func & | func | ) |
Provides a way to execute arbitrary (cleanup) code at the exit of the current block.
Simply save the returned object into a local variable that will go out of scope when your code block exits. Example:
Cleanup_func | Any type such that given an instance Cleanup_func f , the expression f() is valid. |
func | func() will be called when cleanup is needed. |
func()
to be called. size_t flow::util::size_unit_convert | ( | From | num_froms | ) |
Answers the question what's the smallest integer number of To
s sufficient to verbatim store the given number of From
s?, where From
and To
are POD types.
For example, one needs 1 uint64_t
to store 1, 2, 3, or 4 uint16_t
s, hence size_unit_convert<uint16_t, uint64_t>(1 or 2 or 3 or 4) == 1
. Similarly, 5 or 6 or 7 or 8 -> 2. It works in the opposite direction, too; if we are storing uint64_t
s in multiples of uint16_t
, then 1 -> 4, 2 -> 8, 3 -> 12, etc.
To be clear, when From
bit width is smaller than To
bit width, some of the bits will be padding and presumably unused. For example, raw data buffers of arbitrary bytes are often arranged in multi-byte "words."
From | The POD type of the values that must be encoded in From s. |
To | The POD type of the array that would store the From s. |
num_froms | How many From s does one want to encode in an array of To s? |
To
s are sufficient to encode num_from
From
s verbatim? bool flow::util::subtract_with_floor | ( | Minuend * | minuend, |
const Subtrahend & | subtrahend, | ||
const Minuend & | floor = 0 |
||
) |
Performs *minuend -= subtrahend
, subject to a floor of floor
.
Avoids underflow/overflow to the extent it's reasonably possible, but no more. The return value indicates whether the floor was hit; this allows one to chain high-performance subtractions like this:
Minuend | Numeric type. |
Subtrahend | Numeric type, such that given Subtrahend s , Minuend(s) is something reasonable for all s involved. |
minuend | *minuend is set to either (*minuend - subtrahend) or floor , whichever is higher. |
subtrahend | Ditto. |
floor | Ditto. Negatives are OK. Typically it's best to keep the magnitude of this small. |
true
if *minuend == floor
at function exit; false
if *minuend > floor
. void swap | ( | Basic_blob< Allocator, S_SHARING_ALLOWED > & | blob1, |
Basic_blob< Allocator, S_SHARING_ALLOWED > & | blob2, | ||
log::Logger * | logger_ptr = 0 |
||
) |
Equivalent to blob1.swap(blob2)
.
blob1 | Object. |
blob2 | Object. |
logger_ptr | The Logger implementation to use in this routine (synchronously) only. Null allowed. |
void swap | ( | Blob_with_log_context< S_SHARING_ALLOWED > & | blob1, |
Blob_with_log_context< S_SHARING_ALLOWED > & | blob2 | ||
) |
On top of the similar Basic_blob related function, logs using the stored log context of blob1
.
blob1 | See super-class related API. |
blob2 | See super-class related API. |
void swap | ( | Linked_hash_map< Key, Mapped, Hash, Pred > & | val1, |
Linked_hash_map< Key, Mapped, Hash, Pred > & | val2 | ||
) |
Equivalent to val1.swap(val2)
.
val1 | Object. |
val2 | Object. |
void swap | ( | Linked_hash_set< Key, Hash, Pred > & | val1, |
Linked_hash_set< Key, Hash, Pred > & | val2 | ||
) |
Equivalent to val1.swap(val2)
.
val1 | Object. |
val2 | Object. |
boost::chrono::microseconds flow::util::time_since_posix_epoch | ( | ) |
Get the current POSIX (Unix) time as a duration from the Epoch time point.
This is the amount of time – according to the user-settable system clock time – to have passed since the POSIX (Unix) Epoch – January 1st, 1970, 00:00:00 UTC – not counting leap seconds to have been inserted or deleted between then and now.
The boost::chrono
duration type is chosen so as to support the entire supported resolution of the OS-exposed system clock (but probably no more than that).
ceil<seconds>()
in the desired human-friendly form, splicing in the left-over microseconds where desired (boost.locale lacks formatters for sub-second-resolution time points). However see below for a typically-superior alternative.Using this for time stamp output is no longer needed or convenient, as a much nicer way presents itself when combined with boost.chrono I/O-v2 time_point
-outputting ostream<<
overload. Just grab time_point
boost::chrono::system_clock::now()
(or another system_clock
-originated value); its default-formatted ostream<<
output will include date, time with microsecond+ precision, and time-zone specifier (one can choose UTC or local time).
However, as of this writing, it is not possible to directly obtain just the microsecond+ part of this, in isolation, according boost.chrono docs. (One could hack it by taking a substring.) Quote: "Unfortunately there are no formatting/parsing sequences which indicate fractional seconds." From: https://www.boost.org/doc/libs/1_76_0/doc/html/chrono/users_guide.html#chrono.users_guide.tutorial.i_o.system_clock_time_point_io)
double flow::util::to_mbit_per_sec | ( | N_items | items_per_time, |
size_t | bits_per_item = 8 |
||
) |
Utility that converts a bandwidth in arbitrary units in both numerator and denominator to the same bandwidth in megabits per second.
The input bandwidth is given in "items" per Time_unit(1)
; where Time_unit
is an arbitrary boost.chrono duration
type that must be explicitly provided as input; and an "item" is defined as bits_per_item
bits. Useful at least for logging. It's probably easiest to understand by example; see below; rather than by parsing that description I just wrote.
To be clear (as C++ syntax is not super-expressive in this case) – the template parameter Time_unit
is an explicit input to the function template, essentially instructing it as to in what units items_per_time
is. Thus all uses of this function should look similar to:
Time_unit | boost::chrono::duration<Rep, Period> for some specific Rep and Period . See boost::chrono::duration documentation. Example types: boost::chrono::milliseconds ; boost::chrono::seconds ; see example use code above. |
N_items | Some (not necessarily integral) numeric type. Strictly speaking, any type convertible to double works. |
items_per_time | The value, in items per Time_unit(1) (where there are bits_per_item bits in 1 item) to convert to megabits per second. Note this need not be an integer. |
bits_per_item | Number of bits in an item, where items_per_time is given as a number of items. |