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 78 : send_thread = std::thread([this] { 24 : // As long as you didn't get the stop signal, wait for data to send 25 78 : while (handleSendEvents()) 26 : ; // loop 27 : }); 28 28 : } 29 : 30 : template <class Derived> 31 40 : auto ThreadedBLEMIDISender<Derived>::acquirePacket() -> ProtectedBuilder { 32 40 : return {&shared.packet, lock_t {shared.mtx}}; 33 : } 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 150 : 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