blob: e0266f925f2639d6797ba916fe35cceef7b34e23 [file] [log] [blame]
John Park33858a32018-09-28 23:05:48 -07001#ifndef AOS_TRANSACTION_H_
2#define AOS_TRANSACTION_H_
Brian Silverman3d37a5f2014-09-05 18:42:20 -04003
4#include <stdint.h>
5
6#include <array>
7
John Park33858a32018-09-28 23:05:48 -07008#include "aos/util/compiler_memory_barrier.h"
9#include "aos/logging/logging.h"
Brian Silverman3d37a5f2014-09-05 18:42:20 -040010
11namespace aos {
12namespace transaction {
13
14// Manages a LIFO stack of Work objects. Designed to help implement transactions
15// by providing a safe way to undo things etc.
16//
17// number_works Work objects are created statically and then Create is called on
18// each as it is added to the stack. When the work should do whatever it does,
19// DoWork() will be called. The work objects get no notification when they are
20// dropped off of the stack.
21//
22// Work::DoWork() must be idempotent because it may get called multiple times if
23// CompleteWork() is interrupted part of the way through.
24//
25// This class handles compiler memory barriers etc to make sure only fully
26// created works are ever invoked, and each work will be fully created by the
27// time AddWork returns. This does not mean it's safe for multiple threads to
28// interact with an instance of this class at the same time.
29template <class Work, int number_works>
30class WorkStack {
31 public:
32 // Calls DoWork() on all the works that have been added and then removes them
33 // all from the stack.
34 void CompleteWork() {
35 int current = stack_index_;
36 while (current > 0) {
37 stack_.at(--current).DoWork();
38 }
39 aos_compiler_memory_barrier();
40 stack_index_ = 0;
41 aos_compiler_memory_barrier();
42 }
43
44 // Drops all works that have been added.
45 void DropWork() {
46 stack_index_ = 0;
47 aos_compiler_memory_barrier();
48 }
49
50 // Returns true if we have any works to complete right now.
51 bool HasWork() const { return stack_index_ != 0; }
52
53 // Forwards all of its arguments to Work::Create, which it calls on the next
54 // work to be added.
55 template <class... A>
56 void AddWork(A &&... a) {
57 if (stack_index_ >= number_works) {
Austin Schuhf257f3c2019-10-27 21:00:43 -070058 AOS_LOG(FATAL, "too many works\n");
Brian Silverman3d37a5f2014-09-05 18:42:20 -040059 }
60 stack_.at(stack_index_).Create(::std::forward<A>(a)...);
61 aos_compiler_memory_barrier();
62 ++stack_index_;
63 aos_compiler_memory_barrier();
64 }
65
66 private:
67 // The next index into stack_ for a new work to be added.
68 int stack_index_ = 0;
69 ::std::array<Work, number_works> stack_;
70};
71
72// When invoked, sets *pointer to the value it had when this work was Created.
73template <class T>
74class RestoreValueWork {
75 public:
76 void Create(T *pointer) {
77 pointer_ = pointer;
78 value_ = *pointer;
79 }
80 void DoWork() {
81 *pointer_ = value_;
82 }
83
84 private:
85 T *pointer_;
86 T value_;
87};
88
89// Handles the casting necessary to restore any kind of pointer.
90class RestorePointerWork : public RestoreValueWork<void *> {
91 public:
92 template <class T>
93 void Create(T **pointer) {
94 static_assert(sizeof(T *) == sizeof(void *),
95 "that's a weird pointer");
96 RestoreValueWork<void *>::Create(reinterpret_cast<void **>(pointer));
97 }
98};
99
100} // namespace transaction
101} // namespace aos
102
John Park33858a32018-09-28 23:05:48 -0700103#endif // AOS_TRANSACTION_H_