Skip to content

Commit ee6c9e9

Browse files
Take CASE BUSY delay into account when scheduling retries in Matter.framework. (#32920)
1 parent e05d08e commit ee6c9e9

6 files changed

+46
-25
lines changed

src/darwin/Framework/CHIP/MTRBaseDevice.mm

+2-2
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ - (void)subscribeWithQueue:(dispatch_queue_t)queue
365365

366366
[self.deviceController getSessionForNode:self.nodeID
367367
completion:^(ExchangeManager * _Nullable exchangeManager, const Optional<SessionHandle> & session,
368-
NSError * _Nullable error) {
368+
NSError * _Nullable error, NSNumber * _Nullable retryDelay) {
369369
if (error != nil) {
370370
dispatch_async(queue, ^{
371371
errorHandler(error);
@@ -1603,7 +1603,7 @@ - (void)subscribeToAttributePaths:(NSArray<MTRAttributeRequestPath *> * _Nullabl
16031603
[self.deviceController
16041604
getSessionForNode:self.nodeID
16051605
completion:^(ExchangeManager * _Nullable exchangeManager, const Optional<SessionHandle> & session,
1606-
NSError * _Nullable error) {
1606+
NSError * _Nullable error, NSNumber * _Nullable retryDelay) {
16071607
if (error != nil) {
16081608
dispatch_async(queue, ^{
16091609
reportHandler(nil, error);

src/darwin/Framework/CHIP/MTRCallbackBridgeBase.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ class MTRCallbackBridgeBase {
8383

8484
[device.deviceController getSessionForCommissioneeDevice:device.nodeID
8585
completion:^(chip::Messaging::ExchangeManager * exchangeManager,
86-
const chip::Optional<chip::SessionHandle> & session, NSError * error) {
86+
const chip::Optional<chip::SessionHandle> & session, NSError * _Nullable error, NSNumber * _Nullable retryDelay) {
8787
MaybeDoAction(exchangeManager, session, error);
8888
}];
8989
}
@@ -93,8 +93,8 @@ class MTRCallbackBridgeBase {
9393
LogRequestStart();
9494

9595
[controller getSessionForNode:nodeID
96-
completion:^(chip::Messaging::ExchangeManager * exchangeManager,
97-
const chip::Optional<chip::SessionHandle> & session, NSError * error) {
96+
completion:^(chip::Messaging::ExchangeManager * _Nullable exchangeManager,
97+
const chip::Optional<chip::SessionHandle> & session, NSError * _Nullable error, NSNumber * _Nullable retryDelay) {
9898
MaybeDoAction(exchangeManager, session, error);
9999
}];
100100
}

src/darwin/Framework/CHIP/MTRDevice.mm

+20-7
Original file line numberDiff line numberDiff line change
@@ -771,7 +771,7 @@ - (void)_handleResubscriptionNeeded
771771
[self _changeState:MTRDeviceStateUnknown];
772772
}
773773

774-
- (void)_handleSubscriptionReset
774+
- (void)_handleSubscriptionReset:(NSNumber * _Nullable)retryDelay
775775
{
776776
std::lock_guard lock(_lock);
777777
// if there is no delegate then also do not retry
@@ -790,17 +790,29 @@ - (void)_handleSubscriptionReset
790790

791791
self.reattemptingSubscription = YES;
792792

793+
NSTimeInterval secondsToWait;
793794
if (_lastSubscriptionAttemptWait < MTRDEVICE_SUBSCRIPTION_ATTEMPT_MIN_WAIT_SECONDS) {
794795
_lastSubscriptionAttemptWait = MTRDEVICE_SUBSCRIPTION_ATTEMPT_MIN_WAIT_SECONDS;
796+
secondsToWait = _lastSubscriptionAttemptWait;
797+
} else if (retryDelay != nil) {
798+
// The device responded but is currently busy. Reset our backoff
799+
// counter, so that we don't end up waiting for a long time if the next
800+
// attempt fails for some reason, and retry after whatever time period
801+
// the device told us to use.
802+
_lastSubscriptionAttemptWait = 0;
803+
secondsToWait = retryDelay.doubleValue;
804+
MTR_LOG_INFO("%@ resetting resubscribe attempt counter, and delaying by the server-provided delay: %f",
805+
self, secondsToWait);
795806
} else {
796807
_lastSubscriptionAttemptWait *= 2;
797808
if (_lastSubscriptionAttemptWait > MTRDEVICE_SUBSCRIPTION_ATTEMPT_MAX_WAIT_SECONDS) {
798809
_lastSubscriptionAttemptWait = MTRDEVICE_SUBSCRIPTION_ATTEMPT_MAX_WAIT_SECONDS;
799810
}
811+
secondsToWait = _lastSubscriptionAttemptWait;
800812
}
801813

802-
MTR_LOG_DEFAULT("%@ scheduling to reattempt subscription in %u seconds", self, _lastSubscriptionAttemptWait);
803-
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t) (_lastSubscriptionAttemptWait * NSEC_PER_SEC)), self.queue, ^{
814+
MTR_LOG_DEFAULT("%@ scheduling to reattempt subscription in %f seconds", self, secondsToWait);
815+
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t) (secondsToWait * NSEC_PER_SEC)), self.queue, ^{
804816
os_unfair_lock_lock(&self->_lock);
805817
[self _reattemptSubscriptionNowIfNeeded];
806818
os_unfair_lock_unlock(&self->_lock);
@@ -1133,12 +1145,13 @@ - (void)_setupSubscription
11331145
[_deviceController
11341146
getSessionForNode:_nodeID.unsignedLongLongValue
11351147
completion:^(chip::Messaging::ExchangeManager * _Nullable exchangeManager,
1136-
const chip::Optional<chip::SessionHandle> & session, NSError * _Nullable error) {
1148+
const chip::Optional<chip::SessionHandle> & session, NSError * _Nullable error,
1149+
NSNumber * _Nullable retryDelay) {
11371150
if (error != nil) {
11381151
MTR_LOG_ERROR("%@ getSessionForNode error %@", self, error);
11391152
dispatch_async(self.queue, ^{
11401153
[self _handleSubscriptionError:error];
1141-
[self _handleSubscriptionReset];
1154+
[self _handleSubscriptionReset:retryDelay];
11421155
});
11431156
return;
11441157
}
@@ -1193,7 +1206,7 @@ - (void)_setupSubscription
11931206

11941207
dispatch_async(self.queue, ^{
11951208
// OnDone
1196-
[self _handleSubscriptionReset];
1209+
[self _handleSubscriptionReset:nil];
11971210
});
11981211
os_unfair_lock_unlock(&self->_lock);
11991212
},
@@ -1300,7 +1313,7 @@ - (void)_setupSubscription
13001313
MTR_LOG_ERROR("%@ SendAutoResubscribeRequest error %@", self, error);
13011314
dispatch_async(self.queue, ^{
13021315
[self _handleSubscriptionError:error];
1303-
[self _handleSubscriptionReset];
1316+
[self _handleSubscriptionReset:nil];
13041317
});
13051318

13061319
return;

src/darwin/Framework/CHIP/MTRDeviceConnectionBridge.h

+8-4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#import <Foundation/Foundation.h>
2121

22+
#include <app/OperationalSessionSetup.h>
2223
#include <controller/CHIPDeviceController.h>
2324
#include <lib/core/ReferenceCounted.h>
2425
#include <messaging/ExchangeMgr.h>
@@ -27,9 +28,12 @@
2728
NS_ASSUME_NONNULL_BEGIN
2829

2930
// Either exchangeManager will be non-nil and session will have a value, or
30-
// error will be non-nil.
31+
// error will be non-nil. If error is non-nil, retryDelay might be non-nil. In
32+
// that case it will contain an NSTimeInterval indicating how long consumers
33+
// should delay before trying again (e.g. based on the information communicated
34+
// in a BUSY response).
3135
typedef void (^MTRInternalDeviceConnectionCallback)(chip::Messaging::ExchangeManager * _Nullable exchangeManager,
32-
const chip::Optional<chip::SessionHandle> & session, NSError * _Nullable error);
36+
const chip::Optional<chip::SessionHandle> & session, NSError * _Nullable error, NSNumber * _Nullable retryDelay);
3337

3438
/**
3539
* Helper to establish or look up a CASE session and hand its session
@@ -62,11 +66,11 @@ class MTRDeviceConnectionBridge : public chip::ReferenceCounted<MTRDeviceConnect
6266
private:
6367
MTRInternalDeviceConnectionCallback mCompletionHandler;
6468
chip::Callback::Callback<chip::OnDeviceConnected> mOnConnected;
65-
chip::Callback::Callback<chip::OnDeviceConnectionFailure> mOnConnectFailed;
69+
chip::Callback::Callback<chip::OperationalSessionSetup::OnSetupFailure> mOnConnectFailed;
6670

6771
static void OnConnected(
6872
void * context, chip::Messaging::ExchangeManager & exchangeMgr, const chip::SessionHandle & sessionHandle);
69-
static void OnConnectionFailure(void * context, const chip::ScopedNodeId & peerId, CHIP_ERROR error);
73+
static void OnConnectionFailure(void * context, const chip::OperationalSessionSetup::ConnnectionFailureInfo & failureInfo);
7074
};
7175

7276
NS_ASSUME_NONNULL_END

src/darwin/Framework/CHIP/MTRDeviceConnectionBridge.mm

+7-3
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,17 @@
2323
void * context, chip::Messaging::ExchangeManager & exchangeMgr, const chip::SessionHandle & sessionHandle)
2424
{
2525
auto * object = static_cast<MTRDeviceConnectionBridge *>(context);
26-
object->mCompletionHandler(&exchangeMgr, chip::MakeOptional<chip::SessionHandle>(*sessionHandle->AsSecureSession()), nil);
26+
object->mCompletionHandler(&exchangeMgr, chip::MakeOptional<chip::SessionHandle>(*sessionHandle->AsSecureSession()), nil, nil);
2727
object->Release();
2828
}
2929

30-
void MTRDeviceConnectionBridge::OnConnectionFailure(void * context, const chip::ScopedNodeId & peerId, CHIP_ERROR error)
30+
void MTRDeviceConnectionBridge::OnConnectionFailure(void * context, const chip::OperationalSessionSetup::ConnnectionFailureInfo & failureInfo)
3131
{
32+
NSNumber * retryDelay;
33+
if (failureInfo.requestedBusyDelay.HasValue()) {
34+
retryDelay = @(static_cast<double>(failureInfo.requestedBusyDelay.Value().count()) / MSEC_PER_SEC);
35+
}
3236
auto * object = static_cast<MTRDeviceConnectionBridge *>(context);
33-
object->mCompletionHandler(nil, chip::NullOptional, [MTRError errorForCHIPErrorCode:error]);
37+
object->mCompletionHandler(nil, chip::NullOptional, [MTRError errorForCHIPErrorCode:failureInfo.error], retryDelay);
3438
object->Release();
3539
}

src/darwin/Framework/CHIP/MTRDeviceController.mm

+6-6
Original file line numberDiff line numberDiff line change
@@ -1205,7 +1205,7 @@ - (void)getSessionForNode:(chip::NodeId)nodeID completion:(MTRInternalDeviceConn
12051205
connectionBridge->connect(commissioner, nodeID);
12061206
}
12071207
errorHandler:^(NSError * error) {
1208-
completion(nullptr, chip::NullOptional, error);
1208+
completion(nullptr, chip::NullOptional, error, nil);
12091209
}];
12101210
}
12111211

@@ -1216,20 +1216,20 @@ - (void)getSessionForCommissioneeDevice:(chip::NodeId)deviceID completion:(MTRIn
12161216
chip::CommissioneeDeviceProxy * deviceProxy;
12171217
CHIP_ERROR err = commissioner->GetDeviceBeingCommissioned(deviceID, &deviceProxy);
12181218
if (err != CHIP_NO_ERROR) {
1219-
completion(nullptr, chip::NullOptional, [MTRError errorForCHIPErrorCode:err]);
1219+
completion(nullptr, chip::NullOptional, [MTRError errorForCHIPErrorCode:err], nil);
12201220
return;
12211221
}
12221222

12231223
chip::Optional<chip::SessionHandle> session = deviceProxy->GetSecureSession();
12241224
if (!session.HasValue() || !session.Value()->AsSecureSession()->IsPASESession()) {
1225-
completion(nullptr, chip::NullOptional, [MTRError errorForCHIPErrorCode:CHIP_ERROR_INCORRECT_STATE]);
1225+
completion(nullptr, chip::NullOptional, [MTRError errorForCHIPErrorCode:CHIP_ERROR_INCORRECT_STATE], nil);
12261226
return;
12271227
}
12281228

1229-
completion(deviceProxy->GetExchangeManager(), session, nil);
1229+
completion(deviceProxy->GetExchangeManager(), session, nil, nil);
12301230
}
12311231
errorHandler:^(NSError * error) {
1232-
completion(nullptr, chip::NullOptional, error);
1232+
completion(nullptr, chip::NullOptional, error, nil);
12331233
}];
12341234
}
12351235

@@ -1609,7 +1609,7 @@ - (BOOL)getBaseDevice:(uint64_t)deviceID queue:(dispatch_queue_t)queue completio
16091609
// that we are running.
16101610
[self getSessionForNode:deviceID
16111611
completion:^(chip::Messaging::ExchangeManager * _Nullable exchangeManager,
1612-
const chip::Optional<chip::SessionHandle> & session, NSError * _Nullable error) {
1612+
const chip::Optional<chip::SessionHandle> & session, NSError * _Nullable error, NSNumber * _Nullable retryDelay) {
16131613
// Create an MTRBaseDevice for the node id involved, now that our
16141614
// CASE session is primed. We don't actually care about the session
16151615
// information here.

0 commit comments

Comments
 (0)