Skip to content

Commit 491998b

Browse files
Stop persistent operational browse when all controllers are suspended. (#35541)
* Stop persistent operational browse when all controllers are suspended. * Address review comments.
1 parent 7a54490 commit 491998b

7 files changed

+185
-34
lines changed

src/darwin/Framework/CHIP/MTRDeviceController.mm

+34-4
Original file line numberDiff line numberDiff line change
@@ -188,10 +188,6 @@ - (instancetype)initForSubclasses:(BOOL)startSuspended
188188
_shutdownPending = NO;
189189
_assertionLock = OS_UNFAIR_LOCK_INIT;
190190

191-
// All synchronous suspend/resume activity has to be protected by
192-
// @synchronized(self), so that parts of suspend/resume can't
193-
// interleave with each other. Using @synchronized here because
194-
// MTRDevice may call isSuspended.
195191
_suspended = startSuspended;
196192

197193
_nodeIDToDeviceMap = [NSMapTable strongToWeakObjectsMapTable];
@@ -399,13 +395,23 @@ - (void)suspend
399395
{
400396
MTR_LOG("%@ suspending", self);
401397

398+
if (![self isRunning]) {
399+
MTR_LOG_ERROR("%@ not running; can't suspend", self);
400+
return;
401+
}
402+
402403
NSArray * devicesToSuspend;
403404
{
404405
std::lock_guard lock(*self.deviceMapLock);
405406
// Set _suspended under the device map lock. This guarantees that
406407
// for any given device exactly one of two things is true:
407408
// * It is in the snapshot we are creating
408409
// * It is created after we have changed our _suspended state.
410+
if (_suspended) {
411+
MTR_LOG("%@ already suspended", self);
412+
return;
413+
}
414+
409415
_suspended = YES;
410416
devicesToSuspend = [self.nodeIDToDeviceMap objectEnumerator].allObjects;
411417
}
@@ -421,19 +427,36 @@ - (void)suspend
421427
// * CASE sessions in general.
422428
// * Possibly try to see whether we can change our fabric entry to not advertise and restart advertising.
423429
[self _notifyDelegatesOfSuspendState];
430+
431+
[self _controllerSuspended];
432+
}
433+
434+
- (void)_controllerSuspended
435+
{
436+
// Subclass hook; nothing to do.
424437
}
425438

426439
- (void)resume
427440
{
428441
MTR_LOG("%@ resuming", self);
429442

443+
if (![self isRunning]) {
444+
MTR_LOG_ERROR("%@ not running; can't resume", self);
445+
return;
446+
}
447+
430448
NSArray * devicesToResume;
431449
{
432450
std::lock_guard lock(*self.deviceMapLock);
433451
// Set _suspended under the device map lock. This guarantees that
434452
// for any given device exactly one of two things is true:
435453
// * It is in the snapshot we are creating
436454
// * It is created after we have changed our _suspended state.
455+
if (!_suspended) {
456+
MTR_LOG("%@ already not suspended", self);
457+
return;
458+
}
459+
437460
_suspended = NO;
438461
devicesToResume = [self.nodeIDToDeviceMap objectEnumerator].allObjects;
439462
}
@@ -444,6 +467,13 @@ - (void)resume
444467
}
445468

446469
[self _notifyDelegatesOfSuspendState];
470+
471+
[self _controllerResumed];
472+
}
473+
474+
- (void)_controllerResumed
475+
{
476+
// Subclass hook; nothing to do.
447477
}
448478

449479
- (BOOL)matchesPendingShutdownControllerWithOperationalCertificate:(nullable MTRCertificateDERBytes)operationalCertificate andRootCertificate:(nullable MTRCertificateDERBytes)rootCertificate

src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm

+8-10
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ @implementation MTRDeviceControllerFactory {
111111
MTRSessionResumptionStorageBridge * _sessionResumptionStorage;
112112
PersistentStorageOperationalKeystore * _keystore;
113113
Credentials::PersistentStorageOpCertStore * _opCertStore;
114-
MTROperationalBrowser * _operationalBrowser;
114+
std::unique_ptr<MTROperationalBrowser> _operationalBrowser;
115115

116116
// productAttestationAuthorityCertificates and certificationDeclarationCertificates are just copied
117117
// from MTRDeviceControllerFactoryParams.
@@ -223,6 +223,8 @@ - (instancetype)init
223223
_serverEndpointsLock = OS_UNFAIR_LOCK_INIT;
224224
_serverEndpoints = [[NSMutableArray alloc] init];
225225

226+
_operationalBrowser = std::make_unique<MTROperationalBrowser>(self, self->_chipWorkQueue);
227+
226228
return self;
227229
}
228230

@@ -557,12 +559,6 @@ - (MTRDeviceController * _Nullable)_startDeviceController:(MTRDeviceController *
557559
return nil;
558560
}
559561

560-
if ([_controllers count] == 0) {
561-
dispatch_sync(_chipWorkQueue, ^{
562-
self->_operationalBrowser = new MTROperationalBrowser(self, self->_chipWorkQueue);
563-
});
564-
}
565-
566562
// Add the controller to _controllers now, so if we fail partway through its
567563
// startup we will still do the right cleanups.
568564
os_unfair_lock_lock(&_controllersLock);
@@ -943,9 +939,6 @@ - (void)controllerShuttingDown:(MTRDeviceController *)controller
943939
// OtaProviderDelegateBridge uses some services provided by the system
944940
// state without retaining it.
945941
if (_controllers.count == 0) {
946-
delete self->_operationalBrowser;
947-
self->_operationalBrowser = nullptr;
948-
949942
if (_otaProviderDelegateBridge) {
950943
_otaProviderDelegateBridge->Shutdown();
951944
_otaProviderDelegateBridge.reset();
@@ -1246,6 +1239,11 @@ - (PersistentStorageDelegate *)storageDelegate
12461239
return &_groupDataProvider;
12471240
}
12481241

1242+
- (MTROperationalBrowser *)operationalBrowser
1243+
{
1244+
return _operationalBrowser.get();
1245+
}
1246+
12491247
@end
12501248

12511249
@interface MTRDummyStorage : NSObject <MTRStorage>

src/darwin/Framework/CHIP/MTRDeviceControllerFactory_Internal.h

+2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
#import "MTRDefines_Internal.h"
3232
#import "MTRDeviceControllerFactory.h"
33+
#import "MTROperationalBrowser.h"
3334

3435
#include <lib/core/CHIPPersistentStorageDelegate.h>
3536
#include <lib/core/DataModelTypes.h>
@@ -108,6 +109,7 @@ MTR_DIRECT_MEMBERS
108109

109110
@property (readonly) chip::PersistentStorageDelegate * storageDelegate;
110111
@property (readonly) chip::Credentials::GroupDataProvider * groupDataProvider;
112+
@property (readonly, assign) MTROperationalBrowser * operationalBrowser;
111113

112114
@end
113115

src/darwin/Framework/CHIP/MTRDeviceController_Concrete.mm

+30
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,15 @@ - (instancetype)initWithFactory:(MTRDeviceControllerFactory *)factory
330330
self.rootPublicKey = nil;
331331

332332
_storageBehaviorConfiguration = storageBehaviorConfiguration;
333+
334+
// We let the operational browser know about ourselves here, because
335+
// after this point we are guaranteed to have shutDownCppController
336+
// called by the factory.
337+
if (!startSuspended) {
338+
dispatch_async(_chipWorkQueue, ^{
339+
factory.operationalBrowser->ControllerActivated();
340+
});
341+
}
333342
}
334343
return self;
335344
}
@@ -344,6 +353,22 @@ - (BOOL)isRunning
344353
return _cppCommissioner != nullptr;
345354
}
346355

356+
- (void)_controllerSuspended
357+
{
358+
MTRDeviceControllerFactory * factory = _factory;
359+
dispatch_async(_chipWorkQueue, ^{
360+
factory.operationalBrowser->ControllerDeactivated();
361+
});
362+
}
363+
364+
- (void)_controllerResumed
365+
{
366+
MTRDeviceControllerFactory * factory = _factory;
367+
dispatch_async(_chipWorkQueue, ^{
368+
factory.operationalBrowser->ControllerActivated();
369+
});
370+
}
371+
347372
- (BOOL)matchesPendingShutdownControllerWithOperationalCertificate:(nullable MTRCertificateDERBytes)operationalCertificate andRootCertificate:(nullable MTRCertificateDERBytes)rootCertificate
348373
{
349374
if (!operationalCertificate || !rootCertificate) {
@@ -471,6 +496,11 @@ - (void)shutDownCppController
471496
_operationalCredentialsDelegate->SetDeviceCommissioner(nullptr);
472497
}
473498
}
499+
500+
if (!self.suspended) {
501+
_factory.operationalBrowser->ControllerDeactivated();
502+
}
503+
474504
_shutdownPending = NO;
475505
}
476506

src/darwin/Framework/CHIP/MTROperationalBrowser.h

+18-4
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,29 @@
2323
class MTROperationalBrowser
2424
{
2525
public:
26-
// Should be created at a point when the factory starts up the event loop,
27-
// and destroyed when the event loop is stopped.
26+
// Should be created at a point when the dispatch queue is available.
2827
MTROperationalBrowser(MTRDeviceControllerFactory * aFactory, dispatch_queue_t aQueue);
2928

3029
~MTROperationalBrowser();
3130

31+
// ControllerActivated should be called, on the Matter queue, when a
32+
// controller is either started in a non-suspended state or stops being
33+
// suspended.
34+
35+
// ControllerDeactivated should be called, on the Matter queue, when a
36+
// controller is either suspended or shut down while in a non-suspended
37+
// state.
38+
void ControllerActivated();
39+
void ControllerDeactivated();
40+
3241
private:
3342
static void OnBrowse(DNSServiceRef aServiceRef, DNSServiceFlags aFlags, uint32_t aInterfaceId, DNSServiceErrorType aError,
3443
const char * aName, const char * aType, const char * aDomain, void * aContext);
3544

36-
void TryToStartBrowse();
45+
void EnsureBrowse();
46+
void StopBrowse();
3747

38-
MTRDeviceControllerFactory * const mDeviceControllerFactory;
48+
MTRDeviceControllerFactory * const __weak mDeviceControllerFactory;
3949
dispatch_queue_t mQueue;
4050
DNSServiceRef mBrowseRef;
4151

@@ -44,4 +54,8 @@ class MTROperationalBrowser
4454

4555
// If mIsDestroying is true, we're in our destructor, shutting things down.
4656
bool mIsDestroying = false;
57+
58+
// Count of controllers that are currently active; we aim to have a browse
59+
// going while this is nonzero;
60+
size_t mActiveControllerCount = 0;
4761
};

src/darwin/Framework/CHIP/MTROperationalBrowser.mm

+43-14
Original file line numberDiff line numberDiff line change
@@ -37,26 +37,49 @@
3737
: mDeviceControllerFactory(aFactory)
3838
, mQueue(aQueue)
3939
{
40-
// If we fail to start a browse, there's nothing our consumer would do
41-
// differently, so we might as well do this in the constructor.
42-
TryToStartBrowse();
4340
}
4441

45-
void MTROperationalBrowser::TryToStartBrowse()
42+
void MTROperationalBrowser::ControllerActivated()
4643
{
4744
assertChipStackLockedByCurrentThread();
4845

49-
ChipLogProgress(Controller, "Trying to start persistent operational browse");
46+
if (mActiveControllerCount == 0) {
47+
EnsureBrowse();
48+
}
49+
++mActiveControllerCount;
50+
}
51+
52+
void MTROperationalBrowser::ControllerDeactivated()
53+
{
54+
assertChipStackLockedByCurrentThread();
55+
56+
if (mActiveControllerCount == 1) {
57+
StopBrowse();
58+
}
59+
60+
--mActiveControllerCount;
61+
}
62+
63+
void MTROperationalBrowser::EnsureBrowse()
64+
{
65+
assertChipStackLockedByCurrentThread();
66+
67+
if (mInitialized) {
68+
ChipLogProgress(Controller, "%p already has a persistent operational browse running", this);
69+
return;
70+
}
71+
72+
ChipLogProgress(Controller, "%p trying to start persistent operational browse", this);
5073

5174
auto err = DNSServiceCreateConnection(&mBrowseRef);
5275
if (err != kDNSServiceErr_NoError) {
53-
ChipLogError(Controller, "Failed to create connection for persistent operational browse: %" PRId32, err);
76+
ChipLogError(Controller, "%p failed to create connection for persistent operational browse: %" PRId32, this, err);
5477
return;
5578
}
5679

5780
err = DNSServiceSetDispatchQueue(mBrowseRef, mQueue);
5881
if (err != kDNSServiceErr_NoError) {
59-
ChipLogError(Controller, "Failed to set up dispatch queue properly for persistent operational browse: %" PRId32, err);
82+
ChipLogError(Controller, "%p failed to set up dispatch queue properly for persistent operational browse: %" PRId32, this, err);
6083
DNSServiceRefDeallocate(mBrowseRef);
6184
return;
6285
}
@@ -67,11 +90,20 @@
6790
auto browseRef = mBrowseRef; // Mandatory copy because of kDNSServiceFlagsShareConnection.
6891
err = DNSServiceBrowse(&browseRef, kDNSServiceFlagsShareConnection, kDNSServiceInterfaceIndexAny, kOperationalType, domain, OnBrowse, this);
6992
if (err != kDNSServiceErr_NoError) {
70-
ChipLogError(Controller, "Failed to start persistent operational browse for \"%s\" domain: %" PRId32, StringOrNullMarker(domain), err);
93+
ChipLogError(Controller, "%p failed to start persistent operational browse for \"%s\" domain: %" PRId32, this, StringOrNullMarker(domain), err);
7194
}
7295
}
7396
}
7497

98+
void MTROperationalBrowser::StopBrowse()
99+
{
100+
ChipLogProgress(Controller, "%p stopping persistent operational browse", this);
101+
if (mInitialized) {
102+
DNSServiceRefDeallocate(mBrowseRef);
103+
mInitialized = false;
104+
}
105+
}
106+
75107
void MTROperationalBrowser::OnBrowse(DNSServiceRef aServiceRef, DNSServiceFlags aFlags, uint32_t aInterfaceId,
76108
DNSServiceErrorType aError, const char * aName, const char * aType, const char * aDomain, void * aContext)
77109
{
@@ -82,14 +114,13 @@
82114
// We only expect to get notified about our type/domain.
83115
if (aError != kDNSServiceErr_NoError) {
84116
ChipLogError(Controller, "Operational browse failure: %" PRId32, aError);
85-
DNSServiceRefDeallocate(self->mBrowseRef);
86-
self->mInitialized = false;
117+
self->StopBrowse();
87118

88119
// We shouldn't really get callbacks under our destructor, but guard
89120
// against it just in case.
90121
if (!self->mIsDestroying) {
91122
// Try to start a new browse, so we have one going.
92-
self->TryToStartBrowse();
123+
self->EnsureBrowse();
93124
}
94125
return;
95126
}
@@ -116,7 +147,5 @@
116147

117148
mIsDestroying = true;
118149

119-
if (mInitialized) {
120-
DNSServiceRefDeallocate(mBrowseRef);
121-
}
150+
StopBrowse();
122151
}

0 commit comments

Comments
 (0)