feat(ble): expose isInitialized and reclaim memory before radios start
The controller and the WiFi driver each need a large aggregate allocation, which a live app sitting on garbage can deny - that is why a failed connect often succeeded on retry. Both bindings now collect before initializing, and the tree reserves its node and spec capacity so a build does not reallocate into a tight heap.
This commit is contained in:
@@ -19,6 +19,10 @@ function ble.init(name) end
|
||||
---Stops the BLE stack and releases its memory.
|
||||
function ble.deinit() end
|
||||
|
||||
---Whether the BLE stack is running.
|
||||
---@return boolean
|
||||
function ble.isInitialized() end
|
||||
|
||||
---Scans for advertising devices.
|
||||
---@param durationMs? integer Defaults to 3000.
|
||||
---@return BleDevice[]? devices
|
||||
|
||||
@@ -131,10 +131,17 @@ public:
|
||||
const char* error = nullptr;
|
||||
uint16_t focus = NONE;
|
||||
|
||||
// Growth doubling needs the old and new buffers live at once, which is the
|
||||
// allocation that fails first on a tight heap. One reservation covers a
|
||||
// typical screen so a build reallocates only if it genuinely gets large.
|
||||
static constexpr size_t RESERVE = 64;
|
||||
|
||||
void reset() {
|
||||
focus = NONE;
|
||||
nodes.clear();
|
||||
specs.clear();
|
||||
nodes.reserve(RESERVE);
|
||||
specs.reserve(RESERVE);
|
||||
labelAt.clear();
|
||||
labels.clear();
|
||||
styles.clear();
|
||||
|
||||
@@ -231,6 +231,7 @@ public:
|
||||
virtual ~BleProvider() = default;
|
||||
virtual Status init(const std::string* name) = 0;
|
||||
virtual void deinit() = 0;
|
||||
virtual bool isInitialized() const = 0;
|
||||
virtual Status scan(int32_t durationMs, std::vector<BleDevice>& devices) = 0;
|
||||
virtual Status connect(const std::string& address) = 0;
|
||||
virtual void disconnect() = 0;
|
||||
|
||||
@@ -13,6 +13,9 @@ namespace {
|
||||
int init(lua_State* state) {
|
||||
std::string name;
|
||||
const bool hasName = optionalString(state, 1, name);
|
||||
// The controller needs a large aggregate allocation; reclaim app garbage
|
||||
// first so a live screen's churn does not fail the init.
|
||||
lua_gc(state, LUA_GCCOLLECT, 0);
|
||||
return pushStatus(
|
||||
state, Runtime::from(state)->ble().init(hasName ? &name : nullptr));
|
||||
}
|
||||
@@ -22,6 +25,11 @@ int deinit(lua_State* state) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int isInitialized(lua_State* state) {
|
||||
lua_pushboolean(state, Runtime::from(state)->ble().isInitialized());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int scan(lua_State* state) {
|
||||
const int32_t durationMs = optionalInt(state, 1, 3000);
|
||||
luaL_argcheck(state, durationMs > 0, 1, "must be positive");
|
||||
@@ -97,6 +105,9 @@ const luaL_Reg FUNCTIONS[] = {
|
||||
{"init", init},
|
||||
// --- Stops the BLE stack and releases its memory.
|
||||
{"deinit", deinit},
|
||||
// --- Whether the BLE stack is running.
|
||||
// @return boolean
|
||||
{"isInitialized", isInitialized},
|
||||
// --- Scans for advertising devices.
|
||||
// @param durationMs integer|nil Defaults to 3000.
|
||||
// @return BleDevice[]|nil devices
|
||||
|
||||
@@ -41,6 +41,9 @@ int connect(lua_State* state) {
|
||||
std::string password;
|
||||
const bool hasSsid = optionalString(state, 1, ssid);
|
||||
const bool hasPassword = optionalString(state, 2, password);
|
||||
// The driver needs ~28KB in aggregate and a live app can be sitting on that
|
||||
// much garbage, which is why a failed connect often succeeds on retry.
|
||||
lua_gc(state, LUA_GCCOLLECT, 0);
|
||||
const Status status = Runtime::from(state)->wifi().connect(
|
||||
hasSsid ? &ssid : nullptr, hasPassword ? &password : nullptr);
|
||||
return pushStatus(state, status);
|
||||
|
||||
@@ -275,9 +275,14 @@ struct Wifi : WifiProvider {
|
||||
struct Ble : BleProvider {
|
||||
int32_t duration = 0;
|
||||
std::string value;
|
||||
bool initialized = false;
|
||||
|
||||
Status init(const std::string*) override { return Status::success(); }
|
||||
void deinit() override {}
|
||||
Status init(const std::string*) override {
|
||||
initialized = true;
|
||||
return Status::success();
|
||||
}
|
||||
void deinit() override { initialized = false; }
|
||||
bool isInitialized() const override { return initialized; }
|
||||
Status scan(int32_t durationMs, std::vector<BleDevice>& devices) override {
|
||||
duration = durationMs;
|
||||
const BleDevice device = {"tag", "aa:bb", -60};
|
||||
|
||||
@@ -96,7 +96,10 @@ int main() {
|
||||
"assert(wifi.connect())");
|
||||
assert(bench.wifi.savedReconnect);
|
||||
|
||||
run(state, "assert(ble.scan()[1].address == 'aa:bb')\n"
|
||||
run(state, "assert(not ble.isInitialized())\n"
|
||||
"assert(ble.init())\n"
|
||||
"assert(ble.isInitialized())\n"
|
||||
"assert(ble.scan()[1].address == 'aa:bb')\n"
|
||||
"assert(#ble.read('svc', 'chr') == 3)\n"
|
||||
"assert(ble.write('svc', 'chr', 'x\\0y'))");
|
||||
assert(bench.ble.duration == 3000);
|
||||
|
||||
Reference in New Issue
Block a user