@@ -212,7 +212,7 @@ typedef NS_ENUM(NSUInteger, MTRDeviceWorkItemDuplicateTypeID) {
212
212
#define MTRDEVICE_SUBSCRIPTION_LATENCY_NEW_VALUE_WEIGHT (1.0 / 3.0 )
213
213
214
214
@interface MTRDevice_Concrete ()
215
- // protects against concurrent time updates by guarding timeUpdateScheduled flag which manages time updates scheduling,
215
+ // protects against concurrent time updates by guarding the timeUpdateTimer field, which manages time update scheduling,
216
216
// and protects device calls to setUTCTime and setDSTOffset. This can't just be replaced with "lock", because the time
217
217
// update code calls public APIs like readAttributeWithEndpointID:.. (which attempt to take "lock") while holding
218
218
// whatever lock protects the time sync bits.
@@ -266,7 +266,7 @@ @interface MTRDevice_Concrete ()
266
266
267
267
@property (nonatomic ) BOOL expirationCheckScheduled;
268
268
269
- @property (nonatomic ) BOOL timeUpdateScheduled ;
269
+ @property (nonatomic , retain , readwrite , nullable ) dispatch_source_t timeUpdateTimer ;
270
270
271
271
@property (nonatomic ) NSDate * estimatedStartTimeFromGeneralDiagnosticsUpTime;
272
272
@@ -607,8 +607,17 @@ - (void)_setTimeOnDevice
607
607
608
608
- (void )_scheduleNextUpdate : (UInt64 )nextUpdateInSeconds
609
609
{
610
+ os_unfair_lock_assert_owner (&self->_timeSyncLock );
611
+
612
+ auto timerSource = dispatch_source_create (DISPATCH_SOURCE_TYPE_TIMER, 0 , 0 , self.queue );
613
+
614
+ dispatch_source_set_timer (timerSource, dispatch_time (DISPATCH_TIME_NOW, nextUpdateInSeconds * NSEC_PER_SEC), DISPATCH_TIME_FOREVER,
615
+ // Allow 3 seconds of leeway; should be plenty, in practice.
616
+ static_cast <uint64_t >(3 * static_cast <double >(NSEC_PER_SEC)));
617
+
610
618
mtr_weakify (self);
611
- dispatch_after (dispatch_time (DISPATCH_TIME_NOW, (int64_t ) (nextUpdateInSeconds * NSEC_PER_SEC)), self.queue , ^{
619
+ dispatch_source_set_event_handler (timerSource, ^{
620
+ dispatch_source_cancel (timerSource);
612
621
MTR_LOG_DEBUG (" %@ Timer expired, start Device Time Update" , self);
613
622
mtr_strongify (self);
614
623
if (self) {
@@ -618,7 +627,7 @@ - (void)_scheduleNextUpdate:(UInt64)nextUpdateInSeconds
618
627
return ;
619
628
}
620
629
});
621
- self.timeUpdateScheduled = YES ;
630
+ self.timeUpdateTimer = timerSource ;
622
631
MTR_LOG_DEBUG (" %@ Timer Scheduled for next Device Time Update, in %llu seconds" , self, nextUpdateInSeconds);
623
632
}
624
633
@@ -628,7 +637,7 @@ - (void)_scheduleNextUpdate:(UInt64)nextUpdateInSeconds
628
637
- (void )_updateDeviceTimeAndScheduleNextUpdate
629
638
{
630
639
os_unfair_lock_assert_owner (&self->_timeSyncLock );
631
- if (self.timeUpdateScheduled ) {
640
+ if (self.timeUpdateTimer != nil ) {
632
641
MTR_LOG_DEBUG (" %@ Device Time Update already scheduled" , self);
633
642
return ;
634
643
}
@@ -646,14 +655,23 @@ - (void)_performScheduledTimeUpdate
646
655
return ;
647
656
}
648
657
// Device must not be invalidated
649
- if (! self.timeUpdateScheduled ) {
658
+ if (self.timeUpdateTimer == nil ) {
650
659
MTR_LOG_DEBUG (" %@ Device Time Update is no longer scheduled, MTRDevice may have been invalidated." , self);
651
660
return ;
652
661
}
653
- self.timeUpdateScheduled = NO ;
662
+ self.timeUpdateTimer = nil ;
654
663
[self _updateDeviceTimeAndScheduleNextUpdate ];
655
664
}
656
665
666
+ - (void )_cancelTimeUpdateTimer
667
+ {
668
+ std::lock_guard lock (self->_timeSyncLock );
669
+ if (self.timeUpdateTimer != nil ) {
670
+ dispatch_source_cancel (self.timeUpdateTimer );
671
+ self.timeUpdateTimer = nil ;
672
+ }
673
+ }
674
+
657
675
- (NSArray <NSNumber *> *)_endpointsWithTimeSyncClusterServer
658
676
{
659
677
NSArray <NSNumber *> * endpointsOnDevice;
@@ -844,9 +862,7 @@ - (void)invalidate
844
862
845
863
[_asyncWorkQueue invalidate ];
846
864
847
- os_unfair_lock_lock (&self->_timeSyncLock );
848
- _timeUpdateScheduled = NO ;
849
- os_unfair_lock_unlock (&self->_timeSyncLock );
865
+ [self _cancelTimeUpdateTimer ];
850
866
851
867
os_unfair_lock_lock (&self->_lock );
852
868
@@ -1143,7 +1159,7 @@ - (void)_handleSubscriptionEstablished
1143
1159
1144
1160
os_unfair_lock_lock (&self->_timeSyncLock );
1145
1161
1146
- if (! self.timeUpdateScheduled ) {
1162
+ if (self.timeUpdateTimer == nil ) {
1147
1163
[self _scheduleNextUpdate: MTR_DEVICE_TIME_UPDATE_INITIAL_WAIT_TIME_SEC];
1148
1164
}
1149
1165
@@ -4285,6 +4301,8 @@ - (void)controllerSuspended
4285
4301
{
4286
4302
[super controllerSuspended ];
4287
4303
4304
+ [self _cancelTimeUpdateTimer ];
4305
+
4288
4306
std::lock_guard lock (self->_lock );
4289
4307
self.suspended = YES ;
4290
4308
[self _resetSubscriptionWithReasonString: @" Controller suspended" ];
0 commit comments