Line data Source code
1 : #include "ThreadedBLEMIDISender.hpp"
2 :
3 : #include <AH/Containers/CRTP.hpp>
4 :
5 : BEGIN_CS_NAMESPACE
6 :
7 : template <class Derived>
8 29 : ThreadedBLEMIDISender<Derived>::~ThreadedBLEMIDISender() {
9 29 : lock_t lck(shared.mtx);
10 : // Tell the sender that this is the last packet
11 29 : shared.stop = true;
12 : // Tell the sender to not to wait for the timeout
13 29 : shared.flush = true;
14 29 : lck.unlock();
15 29 : cv.notify_one();
16 : // Wait for it to be sent, and join the thread when done
17 29 : if (send_thread.joinable())
18 28 : send_thread.join();
19 58 : }
20 :
21 : template <class Derived>
22 28 : void ThreadedBLEMIDISender<Derived>::begin() {
23 28 : send_thread = std::thread([this] {
24 : // As long as you didn't get the stop signal, wait for data to send
25 50 : while (handleSendEvents())
26 : ; // loop
27 : });
28 28 : }
29 :
30 : template <class Derived>
31 40 : auto ThreadedBLEMIDISender<Derived>::acquirePacket() -> ProtectedBuilder {
32 120 : return {&shared.packet, lock_t {shared.mtx}};
33 80 : }
34 :
35 : template <class Derived>
36 21 : void ThreadedBLEMIDISender<Derived>::releasePacketAndNotify(
37 : ProtectedBuilder &lck) {
38 21 : lck.lck.unlock();
39 21 : cv.notify_one();
40 21 : }
41 :
42 : template <class Derived>
43 21 : void ThreadedBLEMIDISender<Derived>::sendNow(ProtectedBuilder &lck) {
44 21 : assert(lck.lck.owns_lock());
45 : // No need to send empty packets
46 21 : if (shared.packet.empty())
47 0 : return;
48 :
49 : // Tell the background sender thread to send the packet now
50 21 : shared.flush = true;
51 21 : lck.lck.unlock();
52 21 : cv.notify_one();
53 :
54 : // Wait for flush to complete (when the sender clears the flush flag)
55 21 : lck.lck.lock();
56 63 : cv.wait(lck.lck, [this] { return !shared.flush; });
57 : }
58 :
59 : template <class Derived>
60 7 : void ThreadedBLEMIDISender<Derived>::updateMTU(uint16_t mtu) {
61 7 : uint16_t force_min_mtu_c = force_min_mtu;
62 7 : if (force_min_mtu_c == 0)
63 0 : min_mtu = mtu;
64 : else
65 7 : min_mtu = std::min(force_min_mtu_c, mtu);
66 : DEBUGFN(NAMEDVALUE(min_mtu));
67 7 : auto lck = acquirePacket();
68 7 : if (lck.packet->getSize() == 0)
69 7 : lck.packet->setCapacity(min_mtu - 3);
70 14 : }
71 :
72 : template <class Derived>
73 7 : void ThreadedBLEMIDISender<Derived>::forceMinMTU(uint16_t mtu) {
74 7 : force_min_mtu = mtu;
75 7 : updateMTU(min_mtu);
76 7 : }
77 :
78 : template <class Derived>
79 3 : void ThreadedBLEMIDISender<Derived>::setTimeout(
80 : std::chrono::milliseconds timeout) {
81 3 : lock_t lck(shared.mtx);
82 3 : shared.timeout = timeout;
83 6 : }
84 :
85 : template <class Derived>
86 50 : bool ThreadedBLEMIDISender<Derived>::handleSendEvents() {
87 50 : lock_t lck(shared.mtx);
88 :
89 : // Wait for a packet to be started (or for a stop signal)
90 149 : cv.wait(lck, [this] { return !shared.packet.empty() || shared.stop; });
91 : // Wait for flush signal or timeout.
92 50 : auto timeout = shared.timeout;
93 110 : cv.wait_for(lck, timeout, [this] { return shared.flush; });
94 :
95 : // Stop this thread
96 50 : if (shared.stop)
97 28 : return false;
98 : // Note: do not send anything in this case, because we might be in the base
99 : // class destructor, and the subclass implementing the sendData function
100 : // might already be destroyed.
101 :
102 : // Send the packet over BLE, empty the buffer, and update the buffer
103 : // size based on the MTU of the connected clients.
104 22 : BLEDataView data {shared.packet.getBuffer(), shared.packet.getSize()};
105 22 : if (data.length > 0)
106 22 : CRTP(Derived).sendData(data);
107 22 : shared.packet.reset();
108 22 : shared.packet.setCapacity(min_mtu - 3);
109 : // Note: the MTU may have been reduced asynchronously, in which case the
110 : // sending of the data may fail, or it may be truncated. However, since
111 : // updating the MTU while a transmission is already going on is rare, we
112 : // don't handle this case, as it would require parsing and re-encoding the
113 : // buffer into two or more packets.
114 :
115 : // Notify the main thread that the flush was done.
116 22 : if (shared.flush) {
117 21 : shared.flush = false;
118 21 : lck.unlock();
119 21 : cv.notify_one();
120 : }
121 22 : return true;
122 50 : }
123 :
124 : END_CS_NAMESPACE
|