blob: f0a268428fd29e177740207b9f03c646622a58e4 [file] [log] [blame]
Austin Schuh20b2b082019-09-11 20:42:56 -07001#ifndef AOS_IPC_LIB_QUEUE_RACER_H_
2#define AOS_IPC_LIB_QUEUE_RACER_H_
3
Tyler Chatowbf0609c2021-07-31 16:13:27 -07004#include <cstring>
Austin Schuh20b2b082019-09-11 20:42:56 -07005
6#include "aos/ipc_lib/lockless_queue.h"
7
8namespace aos {
9namespace ipc_lib {
10
11struct ThreadState;
12
Eric Schmiedebergef44b8a2022-02-28 17:30:38 -070013struct QueueRacerConfiguration {
14 // Number of threads that send messages
15 const int num_threads;
16 // Number of messages sent by each thread
17 const uint64_t num_messages;
18 // Allows QueueRacer to check for multiple returns from calling Send()
19 const std::vector<LocklessQueueSender::Result> expected_send_results = {
20 LocklessQueueSender::Result::GOOD};
21 // Channel Storage Duration for queue used by QueueRacer
22 const monotonic_clock::duration channel_storage_duration =
23 std::chrono::nanoseconds(1);
24 // Set to true if all writes and reads are expected to be successful
25 // This allows QueueRacer to be used for checking failure scenarios
26 const bool check_writes_and_reads;
27};
28
Austin Schuh20b2b082019-09-11 20:42:56 -070029// Class to test the queue by spinning up a bunch of writing threads and racing
30// them together to all write at once.
31class QueueRacer {
32 public:
Brian Silvermanfc0d2e82020-08-12 19:58:35 -070033 QueueRacer(LocklessQueue queue, int num_threads, uint64_t num_messages);
Eric Schmiedebergef44b8a2022-02-28 17:30:38 -070034 QueueRacer(LocklessQueue queue, const QueueRacerConfiguration &config);
Austin Schuh20b2b082019-09-11 20:42:56 -070035
36 // Runs an iteration of the race.
37 //
38 // This spins up num_threads, each of which sends num_messages. These must
39 // both be able to fit in the queue without wrapping.
40 //
41 // Then, this reads back all the messages and confirms that all were received
42 // in order, and none were missed.
43 //
44 // If race_reads is set, start reading (and retry if data isn't ready yet)
45 // while writes are still happening.
46 //
47 // If wrap_writes is nonzero, write enough to overwrite old data. This
48 // necesitates a loser check at the end.
49 //
50 // If both are set, run an even looser test.
Austin Schuh82ea7382023-07-14 15:17:34 -070051 //
52 // set_should_read is used to determine if we should pass in a valid
53 // should_read function, and should_read_result is the return result of that
54 // function.
55 void RunIteration(bool race_reads, int write_wrap_count, bool set_should_read,
56 bool should_read_result);
Austin Schuh20b2b082019-09-11 20:42:56 -070057
58 size_t CurrentIndex() {
Brian Silvermanfc0d2e82020-08-12 19:58:35 -070059 return LocklessQueueReader(queue_).LatestIndex().index();
Austin Schuh20b2b082019-09-11 20:42:56 -070060 }
61
62 private:
63 // Wipes the queue memory out so we get a clean start.
Brian Silvermanfc0d2e82020-08-12 19:58:35 -070064 void Reset() {
Austin Schuhfaec51a2023-09-08 17:43:32 -070065 memset(reinterpret_cast<void *>(queue_.memory()), 0,
66 LocklessQueueMemorySize(queue_.config()));
Brian Silvermanfc0d2e82020-08-12 19:58:35 -070067 }
Austin Schuh20b2b082019-09-11 20:42:56 -070068
69 // This is a separate method so that when all the ASSERT_* methods, we still
70 // clean up all the threads. Otherwise we get an assert on the way out of
71 // RunIteration instead of getting all the way back to gtest.
72 void CheckReads(bool race_reads, int write_wrap_count,
Austin Schuh82ea7382023-07-14 15:17:34 -070073 ::std::vector<ThreadState> *threads, bool set_should_read,
74 bool should_read_result);
Austin Schuh20b2b082019-09-11 20:42:56 -070075
Brian Silvermanfc0d2e82020-08-12 19:58:35 -070076 LocklessQueue queue_;
Austin Schuh20b2b082019-09-11 20:42:56 -070077 const uint64_t num_threads_;
78 const uint64_t num_messages_;
Eric Schmiedebergef44b8a2022-02-28 17:30:38 -070079 const monotonic_clock::duration channel_storage_duration_;
80 // Allows QueueRacer to check for multiple returns from calling Send()
81 const std::vector<LocklessQueueSender::Result> expected_send_results_;
82 const bool check_writes_and_reads_;
Austin Schuh20b2b082019-09-11 20:42:56 -070083 // The overall number of writes executed will always be between the two of
84 // these. We can't atomically count writes, so we have to bound them.
85 //
86 // Number of writes about to be started.
87 ::std::atomic<uint64_t> started_writes_;
88 // Number of writes completed.
89 ::std::atomic<uint64_t> finished_writes_;
Austin Schuh82ea7382023-07-14 15:17:34 -070090
91 std::function<bool(uint32_t, monotonic_clock::time_point,
92 realtime_clock::time_point, monotonic_clock::time_point,
93 realtime_clock::time_point, uint32_t, UUID, size_t)>
94 should_read_ = [](uint32_t, monotonic_clock::time_point,
95 realtime_clock::time_point, monotonic_clock::time_point,
96 realtime_clock::time_point, uint32_t, UUID,
97 size_t) { return true; };
Austin Schuh20b2b082019-09-11 20:42:56 -070098};
99
100} // namespace ipc_lib
101} // namespace aos
102
103#endif // AOS_IPC_LIB_QUEUE_RACER_H_