Flow 1.0.2
Flow project: Full implementation reference.
op.hpp
Go to the documentation of this file.
1/* Flow
2 * Copyright 2023 Akamai Technologies, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the
5 * "License"); you may not use this file except in
6 * compliance with the License. You may obtain a copy
7 * of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in
12 * writing, software distributed under the License is
13 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
14 * CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing
16 * permissions and limitations under the License. */
17
18/// @file
19#pragma once
20
22#include "flow/log/log.hpp"
23#include "flow/util/random.hpp"
24#include <vector>
25
26namespace flow::async
27{
28// Types.
29
30/* See `using Op =` in async_fwd.hpp for a key (in this context) doc header.
31 * @todo Maybe this stuff has grown big enough to deserve its own op_fwd.hpp? */
32
33/**
34 * Simple, immutable `vector`-like sequence of N opaque async::Op objects, usually corresponding to N worker threads,
35 * with typically used operations such as `[index]` and random `Op` selection.
36 *
37 * ### Thread safety ###
38 * The object is immutable after construction; therefore each `*this` is safe for concurrent Op_list method calls.
39 *
40 * @internal
41 *
42 * ### Design rationale ###
43 * This really only exists, at least originally, in service of Concurrent_task_loop::per_thread_ops() (which returns
44 * an Op_list nowadays). An alternative approach would have been to move these simple facilities directly into
45 * Concurrent_task_loop superclass. Historically that's how it started; there was a `vector<Op>` directly therein.
46 * It worked fine but (1) complicated the otherwise pure interface Concurrent_task_loop into part-interface,
47 * part-logic-store-for-subclasses; and (2) increased the required cooperation between subclass and superclass (which is
48 * cute in finished code but harder to build on/maintain). Hence, as experienced OO coders learn eventually,
49 * composition (where X is a member of Y) is more flexible and no more verbose than inheritance (where Y is a
50 * subclass of X); in both case the desired/achieved effect is to let Y leverage X's abilities. Hence Op_list is a
51 * separate little class to be included as a member by whatever subclass of Concurrent_task_loop needs it.
52 *
53 * It is also a good sign that Op_list stands alone as a clean, reusable concept. It may find other uses than
54 * in Concurrent_task_loop::per_thread_ops().
55 */
56class Op_list :
57 public log::Log_context
58{
59public:
60 // Types.
61
62 /**
63 * Short-hand for function that that takes the target `Op`-list index and returns new async::Op whose copy
64 * is suitable as the permanent `Op` at that index.
65 */
66 using Create_op_func = Function<Op (size_t target_op_idx)>;
67
68 // Constructors/destructor.
69
70 /**
71 * Populates the async::Op list, which is immutable after this returns. Caller may permanently
72 * store `*this` with a `const` qualifier.
73 *
74 * @param logger_ptr
75 * Logger to which to log subsequently.
76 * @param n_ops
77 * How many `Op`s will we permanently store? The index range will therefore be
78 * `[0, n_ops)` or `[0, n_ops - 1)`. Must be at least 1, or `assert()` trips.
79 * @param create_op_func
80 * Caller's factory function that will generate the actual `n_ops` async::Op objects to store.
81 * The caller shall populate each `Op` it returns with a (small) value of whatever arbitrary type it wants.
82 * `create_op_func()` shall be called, in order, `n_ops` times synchronously, with index argument
83 * ranging fully through `[0, n_ops)`. A copy of `Op create_op_func(idx)` is saved, and
84 * subsequently `(*this)[idx]` will return a reference to this permanently stored copy.
85 */
86 explicit Op_list(log::Logger* logger_ptr, size_t n_ops, const Create_op_func& create_op_func);
87
88 // Methods.
89
90 /**
91 * Returns the number of async::Op stored in this Op_list. The returned value is always the same.
92 * @return See above.
93 */
94 size_t size() const;
95
96 /**
97 * Returns reference to immutable async::Op at the given index in `[0, size())`.
98 * It is just like the `const` variety of `"vector<Op>::operator[]"`.
99 *
100 * @param idx
101 * Value in `[0, size())`. `assert()` trips if out of range.
102 * @return See above. Note the address stored in the returned *reference* is valid until destructor runs;
103 * hence it's not necessary (though cheap) to copy the `Op`.
104 */
105 const Op& operator[](size_t idx) const;
106
107 /**
108 * Returns `(*this)[R]`, where we randomly select R as if by random_idx() and communicate it to the caller via
109 * optional out-arg. Logs a TRACE message.
110 *
111 * @param chosen_idx
112 * If not null, then a post-condition is that `*chosen_idx` has been set to the index of the returned
113 * `Op`.
114 * @return See above. Note the address stored in the returned *reference* is valid until destructor runs;
115 * hence it's not necessary (though cheap) to copy the `Op`.
116 */
117 const Op& random_op(size_t* chosen_idx = 0) const;
118
119 /**
120 * Returns a randomly selected index from range [O, size()).
121 * @return See above.
122 */
123 size_t random_idx() const;
124
125 /**
126 * Returns reference to immutable internally stored sequence of async::Op in order consistent with other
127 * methods of this class.
128 * @return See above.
129 */
130 const std::vector<Op>& ops_sequence() const;
131
132private:
133 /**
134 * The payload of async::Op objects. This is effectively `const` (immutable) after construction. While `Op`
135 * copy is light-weight, the effective `const`ness we can nevertheless save a copy by always taking
136 * addresses like &m_ops[i] when possible.
137 */
138 std::vector<Op> m_ops;
139
140 /**
141 * Random number facility for generating random indices in `[0, m_ops.size())`. Thread-safe sans mutex.
142 *
143 * ### Implementation detail ###
144 * `m_rnd_op_idx()` is the only used operation, and it emits a pseudo-random number. This can be viewed as
145 * as merely accessing an outside resource, conceptually, hence such operations should probably be `const`, as
146 * mutexes are for example. So, as with a mutex, make it `mutable`.
147 */
149}; // class Op_list
150
151} // namespace flow::async
Simple, immutable vector-like sequence of N opaque async::Op objects, usually corresponding to N work...
Definition: op.hpp:58
const Op & random_op(size_t *chosen_idx=0) const
Returns (*this)[R], where we randomly select R as if by random_idx() and communicate it to the caller...
Definition: op.cpp:51
Op_list(log::Logger *logger_ptr, size_t n_ops, const Create_op_func &create_op_func)
Populates the async::Op list, which is immutable after this returns.
Definition: op.cpp:26
util::Rnd_gen_uniform_range_mt< size_t > m_rnd_op_idx
Random number facility for generating random indices in [0, m_ops.size()).
Definition: op.hpp:148
const std::vector< Op > & ops_sequence() const
Returns reference to immutable internally stored sequence of async::Op in order consistent with other...
Definition: op.cpp:70
const Op & operator[](size_t idx) const
Returns reference to immutable async::Op at the given index in [0, size()).
Definition: op.cpp:46
std::vector< Op > m_ops
The payload of async::Op objects.
Definition: op.hpp:138
size_t random_idx() const
Returns a randomly selected index from range [O, size()).
Definition: op.cpp:65
size_t size() const
Returns the number of async::Op stored in this Op_list.
Definition: op.cpp:41
Convenience class that simply stores a Logger and/or Component passed into a constructor; and returns...
Definition: log.hpp:1619
Interface that the user should implement, passing the implementing Logger into logging classes (Flow'...
Definition: log.hpp:1291
Identical to Rnd_gen_uniform_range but safe for concurrent RNG given a single object.
Definition: random.hpp:158
Flow module containing tools enabling multi-threaded event loops operating under the asynchronous-tas...
Definition: async_fwd.hpp:75
boost::any Op
An object of this opaque type represents a collection of 1 or more async::Task, past or future,...
Definition: async_fwd.hpp:153