| 82 | /// so an ISR cannot observe a function from one registration and a context |
| 83 | /// from another. |
| 84 | class PollNeededCallbackSlot { |
| 85 | private: |
| 86 | struct Snapshot { |
| 87 | explicit Snapshot(PollNeededCallback cb) FL_NOEXCEPT |
| 88 | : callback(cb), next(nullptr) {} |
| 89 | |
| 90 | PollNeededCallback callback; |
| 91 | Snapshot* next; |
| 92 | }; |
| 93 | |
| 94 | public: |
| 95 | PollNeededCallbackSlot() FL_NOEXCEPT |
| 96 | : mSnapshot(nullptr), mRetired(nullptr) {} |
| 97 | |
| 98 | ~PollNeededCallbackSlot() FL_NOEXCEPT { |
| 99 | Snapshot* active = |
| 100 | mSnapshot.exchange(nullptr, fl::memory_order_acq_rel); |
| 101 | destroySnapshots(active); |
| 102 | destroySnapshots(mRetired); |
| 103 | mRetired = nullptr; |
| 104 | } |
| 105 | |
| 106 | void set(PollNeededCallback callback) FL_NOEXCEPT { |
| 107 | if (callback.callback == nullptr) { |
| 108 | clear(); |
| 109 | return; |
| 110 | } |
| 111 | Snapshot* snapshot = new Snapshot(callback); // ok bare allocation |
| 112 | retire(mSnapshot.exchange(snapshot, fl::memory_order_acq_rel)); |
| 113 | } |
| 114 | |
| 115 | void clear() FL_NOEXCEPT { |
| 116 | retire(mSnapshot.exchange(nullptr, fl::memory_order_acq_rel)); |
| 117 | } |
| 118 | |
| 119 | void invoke() const FL_NOEXCEPT { |
| 120 | Snapshot* snapshot = mSnapshot.load(fl::memory_order_acquire); |
| 121 | if (snapshot == nullptr) { |
| 122 | return; |
| 123 | } |
| 124 | snapshot->callback.invoke(); |
| 125 | } |
| 126 | |
| 127 | private: |
| 128 | void retire(Snapshot* snapshot) FL_NOEXCEPT { |
| 129 | if (snapshot == nullptr) { |
| 130 | return; |
| 131 | } |
| 132 | // An ISR may already have loaded this pointer, so reclaim only when |
| 133 | // the slot is destroyed and driver teardown has quiesced callbacks. |
| 134 | snapshot->next = mRetired; |
| 135 | mRetired = snapshot; |
| 136 | } |
| 137 | |
| 138 | static void destroySnapshots(Snapshot* snapshot) FL_NOEXCEPT { |
| 139 | while (snapshot != nullptr) { |
| 140 | Snapshot* next = snapshot->next; |
| 141 | delete snapshot; // ok bare allocation |