mirror of
https://github.com/meshtastic/firmware.git
synced 2025-10-27 15:02:41 +00:00
Eliminating foot-gun and placing Phone NodeInfo into a mutex
This commit is contained in:
parent
1314e5d105
commit
e4f5b8c696
@ -71,8 +71,12 @@ void PhoneAPI::handleStartConfig()
|
|||||||
LOG_DEBUG("Got %d files in manifest", filesManifest.size());
|
LOG_DEBUG("Got %d files in manifest", filesManifest.size());
|
||||||
|
|
||||||
LOG_INFO("Start API client config");
|
LOG_INFO("Start API client config");
|
||||||
nodeInfoForPhone.num = 0; // Don't keep returning old nodeinfos
|
// Protect against concurrent BLE callbacks: they run in NimBLE's FreeRTOS task and also touch nodeInfoQueue.
|
||||||
nodeInfoQueue.clear();
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(nodeInfoMutex);
|
||||||
|
nodeInfoForPhone = {};
|
||||||
|
nodeInfoQueue.clear();
|
||||||
|
}
|
||||||
resetReadIndex();
|
resetReadIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,8 +98,12 @@ void PhoneAPI::close()
|
|||||||
onConnectionChanged(false);
|
onConnectionChanged(false);
|
||||||
fromRadioScratch = {};
|
fromRadioScratch = {};
|
||||||
toRadioScratch = {};
|
toRadioScratch = {};
|
||||||
nodeInfoForPhone = {};
|
// Clear cached node info under lock because NimBLE callbacks can still be draining it.
|
||||||
nodeInfoQueue.clear();
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(nodeInfoMutex);
|
||||||
|
nodeInfoForPhone = {};
|
||||||
|
nodeInfoQueue.clear();
|
||||||
|
}
|
||||||
packetForPhone = NULL;
|
packetForPhone = NULL;
|
||||||
filesManifest.clear();
|
filesManifest.clear();
|
||||||
fromRadioNum = 0;
|
fromRadioNum = 0;
|
||||||
@ -241,13 +249,20 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
|||||||
LOG_DEBUG("Send My NodeInfo");
|
LOG_DEBUG("Send My NodeInfo");
|
||||||
auto us = nodeDB->readNextMeshNode(readIndex);
|
auto us = nodeDB->readNextMeshNode(readIndex);
|
||||||
if (us) {
|
if (us) {
|
||||||
nodeInfoForPhone = TypeConversions::ConvertToNodeInfo(us);
|
auto info = TypeConversions::ConvertToNodeInfo(us);
|
||||||
nodeInfoForPhone.has_hops_away = false;
|
info.has_hops_away = false;
|
||||||
nodeInfoForPhone.is_favorite = true;
|
info.is_favorite = true;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(nodeInfoMutex);
|
||||||
|
nodeInfoForPhone = info;
|
||||||
|
}
|
||||||
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_node_info_tag;
|
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_node_info_tag;
|
||||||
fromRadioScratch.node_info = nodeInfoForPhone;
|
fromRadioScratch.node_info = info;
|
||||||
// Should allow us to resume sending NodeInfo in STATE_SEND_OTHER_NODEINFOS
|
// Should allow us to resume sending NodeInfo in STATE_SEND_OTHER_NODEINFOS
|
||||||
nodeInfoForPhone.num = 0;
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(nodeInfoMutex);
|
||||||
|
nodeInfoForPhone.num = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (config_nonce == SPECIAL_NONCE_ONLY_NODES) {
|
if (config_nonce == SPECIAL_NONCE_ONLY_NODES) {
|
||||||
// If client only wants node info, jump directly to sending nodes
|
// If client only wants node info, jump directly to sending nodes
|
||||||
@ -434,23 +449,30 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
|||||||
|
|
||||||
case STATE_SEND_OTHER_NODEINFOS: {
|
case STATE_SEND_OTHER_NODEINFOS: {
|
||||||
LOG_DEBUG("Send known nodes");
|
LOG_DEBUG("Send known nodes");
|
||||||
if (nodeInfoForPhone.num == 0 && !nodeInfoQueue.empty()) {
|
meshtastic_NodeInfo infoToSend = {};
|
||||||
// Serve the next cached node without re-reading from the DB iterator.
|
{
|
||||||
nodeInfoForPhone = nodeInfoQueue.front();
|
std::lock_guard<std::mutex> guard(nodeInfoMutex);
|
||||||
nodeInfoQueue.pop_front();
|
if (nodeInfoForPhone.num == 0 && !nodeInfoQueue.empty()) {
|
||||||
|
// Serve the next cached node without re-reading from the DB iterator.
|
||||||
|
nodeInfoForPhone = nodeInfoQueue.front();
|
||||||
|
nodeInfoQueue.pop_front();
|
||||||
|
}
|
||||||
|
infoToSend = nodeInfoForPhone;
|
||||||
|
if (infoToSend.num != 0)
|
||||||
|
nodeInfoForPhone = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nodeInfoForPhone.num != 0) {
|
if (infoToSend.num != 0) {
|
||||||
// Just in case we stored a different user.id in the past, but should never happen going forward
|
// Just in case we stored a different user.id in the past, but should never happen going forward
|
||||||
sprintf(nodeInfoForPhone.user.id, "!%08x", nodeInfoForPhone.num);
|
sprintf(infoToSend.user.id, "!%08x", infoToSend.num);
|
||||||
LOG_DEBUG("nodeinfo: num=0x%x, lastseen=%u, id=%s, name=%s", nodeInfoForPhone.num, nodeInfoForPhone.last_heard,
|
LOG_DEBUG("nodeinfo: num=0x%x, lastseen=%u, id=%s, name=%s", infoToSend.num, infoToSend.last_heard,
|
||||||
nodeInfoForPhone.user.id, nodeInfoForPhone.user.long_name);
|
infoToSend.user.id, infoToSend.user.long_name);
|
||||||
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_node_info_tag;
|
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_node_info_tag;
|
||||||
fromRadioScratch.node_info = nodeInfoForPhone;
|
fromRadioScratch.node_info = infoToSend;
|
||||||
nodeInfoForPhone = {};
|
|
||||||
prefetchNodeInfos();
|
prefetchNodeInfos();
|
||||||
} else {
|
} else {
|
||||||
LOG_DEBUG("Done sending nodeinfo");
|
LOG_DEBUG("Done sending nodeinfo");
|
||||||
|
std::lock_guard<std::mutex> guard(nodeInfoMutex);
|
||||||
nodeInfoQueue.clear();
|
nodeInfoQueue.clear();
|
||||||
state = STATE_SEND_FILEMANIFEST;
|
state = STATE_SEND_FILEMANIFEST;
|
||||||
// Go ahead and send that ID right now
|
// Go ahead and send that ID right now
|
||||||
@ -559,20 +581,23 @@ void PhoneAPI::prefetchNodeInfos()
|
|||||||
{
|
{
|
||||||
bool added = false;
|
bool added = false;
|
||||||
// Keep the queue topped up so BLE reads stay responsive even if DB fetches take a moment.
|
// Keep the queue topped up so BLE reads stay responsive even if DB fetches take a moment.
|
||||||
while (nodeInfoQueue.size() < kNodePrefetchDepth) {
|
{
|
||||||
auto nextNode = nodeDB->readNextMeshNode(readIndex);
|
std::lock_guard<std::mutex> guard(nodeInfoMutex);
|
||||||
if (!nextNode)
|
while (nodeInfoQueue.size() < kNodePrefetchDepth) {
|
||||||
break;
|
auto nextNode = nodeDB->readNextMeshNode(readIndex);
|
||||||
|
if (!nextNode)
|
||||||
|
break;
|
||||||
|
|
||||||
auto info = TypeConversions::ConvertToNodeInfo(nextNode);
|
auto info = TypeConversions::ConvertToNodeInfo(nextNode);
|
||||||
bool isUs = info.num == nodeDB->getNodeNum();
|
bool isUs = info.num == nodeDB->getNodeNum();
|
||||||
info.hops_away = isUs ? 0 : info.hops_away;
|
info.hops_away = isUs ? 0 : info.hops_away;
|
||||||
info.last_heard = isUs ? getValidTime(RTCQualityFromNet) : info.last_heard;
|
info.last_heard = isUs ? getValidTime(RTCQualityFromNet) : info.last_heard;
|
||||||
info.snr = isUs ? 0 : info.snr;
|
info.snr = isUs ? 0 : info.snr;
|
||||||
info.via_mqtt = isUs ? false : info.via_mqtt;
|
info.via_mqtt = isUs ? false : info.via_mqtt;
|
||||||
info.is_favorite = info.is_favorite || isUs;
|
info.is_favorite = info.is_favorite || isUs;
|
||||||
nodeInfoQueue.push_back(info);
|
nodeInfoQueue.push_back(info);
|
||||||
added = true;
|
added = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (added)
|
if (added)
|
||||||
@ -614,10 +639,17 @@ bool PhoneAPI::available()
|
|||||||
case STATE_SEND_COMPLETE_ID:
|
case STATE_SEND_COMPLETE_ID:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case STATE_SEND_OTHER_NODEINFOS:
|
case STATE_SEND_OTHER_NODEINFOS: {
|
||||||
if (nodeInfoQueue.empty())
|
std::lock_guard<std::mutex> guard(nodeInfoMutex);
|
||||||
prefetchNodeInfos();
|
if (nodeInfoQueue.empty()) {
|
||||||
|
// Drop the lock before prefetching; prefetchNodeInfos() will re-acquire it.
|
||||||
|
goto PREFETCH_NODEINFO;
|
||||||
|
}
|
||||||
|
}
|
||||||
return true; // Always say we have something, because we might need to advance our state machine
|
return true; // Always say we have something, because we might need to advance our state machine
|
||||||
|
PREFETCH_NODEINFO:
|
||||||
|
prefetchNodeInfos();
|
||||||
|
return true;
|
||||||
case STATE_SEND_PACKETS: {
|
case STATE_SEND_PACKETS: {
|
||||||
if (!queueStatusPacketForPhone)
|
if (!queueStatusPacketForPhone)
|
||||||
queueStatusPacketForPhone = service->getQueueStatusForPhone();
|
queueStatusPacketForPhone = service->getQueueStatusForPhone();
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
#include "meshtastic/portnums.pb.h"
|
#include "meshtastic/portnums.pb.h"
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -84,6 +85,8 @@ class PhoneAPI
|
|||||||
std::deque<meshtastic_NodeInfo> nodeInfoQueue;
|
std::deque<meshtastic_NodeInfo> nodeInfoQueue;
|
||||||
// Tunable size of the node info cache so we can keep BLE reads non-blocking.
|
// Tunable size of the node info cache so we can keep BLE reads non-blocking.
|
||||||
static constexpr size_t kNodePrefetchDepth = 4;
|
static constexpr size_t kNodePrefetchDepth = 4;
|
||||||
|
// Protect nodeInfoForPhone + nodeInfoQueue because NimBLE callbacks run in a separate FreeRTOS task.
|
||||||
|
std::mutex nodeInfoMutex;
|
||||||
|
|
||||||
meshtastic_ToRadio toRadioScratch = {
|
meshtastic_ToRadio toRadioScratch = {
|
||||||
0}; // this is a static scratch object, any data must be copied elsewhere before returning
|
0}; // this is a static scratch object, any data must be copied elsewhere before returning
|
||||||
|
|||||||
@ -139,7 +139,7 @@ class NimbleBluetoothFromRadioCallback : public NimBLECharacteristicCallbacks
|
|||||||
{
|
{
|
||||||
bluetoothPhoneAPI->phoneWants = true;
|
bluetoothPhoneAPI->phoneWants = true;
|
||||||
bluetoothPhoneAPI->setIntervalFromNow(0);
|
bluetoothPhoneAPI->setIntervalFromNow(0);
|
||||||
std::lock_guard<std::mutex> guard(bluetoothPhoneAPI->nimble_mutex);
|
std::lock_guard<std::mutex> guard(bluetoothPhoneAPI->nimble_mutex); // BLE callbacks run in NimBLE task
|
||||||
|
|
||||||
if (!bluetoothPhoneAPI->hasChecked) {
|
if (!bluetoothPhoneAPI->hasChecked) {
|
||||||
// Fetch payload on demand; prefetch keeps this fast for the first read.
|
// Fetch payload on demand; prefetch keeps this fast for the first read.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user