@stoprocent/noble 2.3.6 → 2.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/common/include/ThreadSafeCallback.h +43 -4
- package/lib/hci-socket/hci.js +20 -11
- package/lib/mac/src/ble_manager.h +1 -0
- package/lib/mac/src/ble_manager.mm +29 -1
- package/package.json +1 -1
- package/prebuilds/darwin-x64+arm64/@stoprocent+noble.node +0 -0
- package/prebuilds/win32-ia32/@stoprocent+noble.node +0 -0
- package/prebuilds/win32-x64/@stoprocent+noble.node +0 -0
|
@@ -72,7 +72,9 @@ inline ThreadSafeCallback::~ThreadSafeCallback() {
|
|
|
72
72
|
|
|
73
73
|
inline void ThreadSafeCallback::call(ArgumentFunction argFunction) {
|
|
74
74
|
auto argFn = new ArgumentFunction(argFunction);
|
|
75
|
-
if
|
|
75
|
+
// Use NonBlockingCall to avoid hanging if environment is destroyed
|
|
76
|
+
// This will queue the callback but not block waiting for it
|
|
77
|
+
if (threadSafeFunction_.NonBlockingCall(argFn) != napi_ok) {
|
|
76
78
|
delete argFn;
|
|
77
79
|
}
|
|
78
80
|
}
|
|
@@ -83,13 +85,50 @@ inline void ThreadSafeCallback::callJsCallback(
|
|
|
83
85
|
Napi::Reference<Napi::Value>* context,
|
|
84
86
|
ArgumentFunction* argFn) {
|
|
85
87
|
|
|
86
|
-
if (argFn
|
|
88
|
+
if (argFn == nullptr) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Check if environment and callback are valid before proceeding
|
|
93
|
+
if (env == nullptr || jsCallback == nullptr || context == nullptr) {
|
|
94
|
+
delete argFn;
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Check if context reference is still valid
|
|
99
|
+
if (context->IsEmpty()) {
|
|
100
|
+
delete argFn;
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
try {
|
|
87
105
|
ArgumentVector args;
|
|
88
106
|
(*argFn)(env, args);
|
|
89
107
|
delete argFn;
|
|
90
108
|
|
|
91
|
-
|
|
92
|
-
|
|
109
|
+
// Get the receiver value and check if it's valid
|
|
110
|
+
Napi::Value receiverValue = context->Value();
|
|
111
|
+
// Check if receiver value is null or undefined (invalid)
|
|
112
|
+
if (receiverValue.IsNull() || receiverValue.IsUndefined()) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Attempt to call the callback with error handling
|
|
117
|
+
// If the environment is being destroyed, this may fail
|
|
118
|
+
// Note: N-API errors don't throw C++ exceptions, so this won't catch
|
|
119
|
+
// napi_open_callback_scope failures, but it helps with other cases
|
|
120
|
+
try {
|
|
121
|
+
jsCallback.Call(receiverValue, args);
|
|
122
|
+
} catch (const std::exception&) {
|
|
123
|
+
// Silently ignore exceptions - environment might be destroyed
|
|
124
|
+
} catch (...) {
|
|
125
|
+
// Catch any other exceptions during callback execution
|
|
93
126
|
}
|
|
127
|
+
} catch (const std::exception&) {
|
|
128
|
+
// If argument building fails, just clean up
|
|
129
|
+
delete argFn;
|
|
130
|
+
} catch (...) {
|
|
131
|
+
// Catch any other exceptions and clean up
|
|
132
|
+
delete argFn;
|
|
94
133
|
}
|
|
95
134
|
}
|
package/lib/hci-socket/hci.js
CHANGED
|
@@ -158,20 +158,29 @@ Hci.prototype.init = function (options) {
|
|
|
158
158
|
this._socket.on('error', this.onSocketError.bind(this));
|
|
159
159
|
this._socket.on('state', this.pollIsDevUp.bind(this));
|
|
160
160
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
this.
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
161
|
+
try {
|
|
162
|
+
// Bind first (either user channel or raw)
|
|
163
|
+
if (this._userChannel) {
|
|
164
|
+
this._socket.bindUser(this._deviceId, this._bindParams);
|
|
165
|
+
} else {
|
|
166
|
+
if (!this._bound) {
|
|
167
|
+
this._socket.bindRaw(this._deviceId, this._bindParams);
|
|
168
|
+
this._bound = true;
|
|
169
|
+
}
|
|
170
170
|
}
|
|
171
|
+
|
|
172
|
+
// Start and reset (common to both paths)
|
|
171
173
|
this._socket.start();
|
|
172
|
-
this._isStarted = true;
|
|
173
174
|
this.reset();
|
|
174
|
-
|
|
175
|
+
|
|
176
|
+
// Set started flag and poll only for non-userChannel
|
|
177
|
+
if (!this._userChannel) {
|
|
178
|
+
this._isStarted = true;
|
|
179
|
+
this.pollIsDevUp();
|
|
180
|
+
}
|
|
181
|
+
} catch (error) {
|
|
182
|
+
debug(`init error: ${error.message}`);
|
|
183
|
+
this.emit('stateChange', 'unsupported');
|
|
175
184
|
}
|
|
176
185
|
};
|
|
177
186
|
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
@property NSMutableDictionary *peripherals;
|
|
17
17
|
@property NSMutableDictionary *mtus;
|
|
18
18
|
@property NSMutableSet *discovered;
|
|
19
|
+
@property (strong) dispatch_source_t stateCheckTimer;
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
- (instancetype)init: (const Napi::Value&) receiver with: (const Napi::Function&) callback;
|
|
@@ -21,6 +21,27 @@
|
|
|
21
21
|
self.discovered = [NSMutableSet set];
|
|
22
22
|
self.peripherals = [NSMutableDictionary dictionaryWithCapacity:10];
|
|
23
23
|
self.mtus = [NSMutableDictionary dictionaryWithCapacity:10];
|
|
24
|
+
|
|
25
|
+
// Set up periodic state checking to handle sleep/wake cycles using GCD timer
|
|
26
|
+
// Check every 2 seconds for state changes on the same dispatch queue
|
|
27
|
+
self.stateCheckTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.dispatchQueue);
|
|
28
|
+
if (self.stateCheckTimer) {
|
|
29
|
+
dispatch_source_set_timer(self.stateCheckTimer,
|
|
30
|
+
dispatch_time(DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC),
|
|
31
|
+
2.0 * NSEC_PER_SEC,
|
|
32
|
+
0.1 * NSEC_PER_SEC); // 100ms leeway
|
|
33
|
+
__weak typeof(self) weakSelf = self;
|
|
34
|
+
dispatch_source_set_event_handler(self.stateCheckTimer, ^{
|
|
35
|
+
__strong typeof(weakSelf) strongSelf = weakSelf;
|
|
36
|
+
if (strongSelf) {
|
|
37
|
+
CBManagerState currentState = strongSelf.centralManager.state;
|
|
38
|
+
if (currentState != strongSelf.lastState) {
|
|
39
|
+
[strongSelf centralManagerDidUpdateState:strongSelf.centralManager];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
dispatch_resume(self.stateCheckTimer);
|
|
44
|
+
}
|
|
24
45
|
}
|
|
25
46
|
return self;
|
|
26
47
|
}
|
|
@@ -519,5 +540,12 @@
|
|
|
519
540
|
return nil;
|
|
520
541
|
}
|
|
521
542
|
|
|
522
|
-
|
|
543
|
+
- (void)dealloc {
|
|
544
|
+
// Clean up GCD timer
|
|
545
|
+
if (self.stateCheckTimer) {
|
|
546
|
+
dispatch_source_cancel(self.stateCheckTimer);
|
|
547
|
+
self.stateCheckTimer = nil;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
523
550
|
|
|
551
|
+
@end
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|