@@ -277,7 +277,7 @@ typedef NS_ENUM(NSUInteger, MTRDeviceWorkItemDuplicateTypeID) {
277
277
#define MTRDEVICE_SUBSCRIPTION_LATENCY_NEW_VALUE_WEIGHT (1.0 / 3.0)
278
278
279
279
@interface MTRDevice_Concrete ()
280
- // protects against concurrent time updates by guarding timeUpdateScheduled flag which manages time updates scheduling,
280
+ // protects against concurrent time updates by guarding the timeUpdateTimer field, which manages time update scheduling,
281
281
// and protects device calls to setUTCTime and setDSTOffset. This can't just be replaced with "lock", because the time
282
282
// update code calls public APIs like readAttributeWithEndpointID:.. (which attempt to take "lock") while holding
283
283
// whatever lock protects the time sync bits.
@@ -332,7 +332,7 @@ @interface MTRDevice_Concrete ()
332
332
333
333
@property (nonatomic) BOOL expirationCheckScheduled;
334
334
335
- @property (nonatomic) BOOL timeUpdateScheduled ;
335
+ @property (nonatomic, retain, readwrite, nullable) dispatch_source_t timeUpdateTimer ;
336
336
337
337
@property (nonatomic) NSDate * estimatedStartTimeFromGeneralDiagnosticsUpTime;
338
338
@@ -710,8 +710,17 @@ - (void)_setTimeOnDevice
710
710
711
711
- (void)_scheduleNextUpdate:(UInt64)nextUpdateInSeconds
712
712
{
713
+ os_unfair_lock_assert_owner(&self->_timeSyncLock);
714
+
715
+ auto timerSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.queue);
716
+
717
+ dispatch_source_set_timer(timerSource, dispatch_time(DISPATCH_TIME_NOW, nextUpdateInSeconds * NSEC_PER_SEC), DISPATCH_TIME_FOREVER,
718
+ // Allow 3 seconds of leeway; should be plenty, in practice.
719
+ static_cast<uint64_t>(3 * static_cast<double>(NSEC_PER_SEC)));
720
+
713
721
mtr_weakify(self);
714
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t) (nextUpdateInSeconds * NSEC_PER_SEC)), self.queue, ^{
722
+ dispatch_source_set_event_handler(timerSource, ^{
723
+ dispatch_source_cancel(timerSource);
715
724
mtr_strongify(self);
716
725
MTR_LOG_DEBUG("%@ Timer expired, start Device Time Update", self);
717
726
if (self) {
@@ -721,7 +730,7 @@ - (void)_scheduleNextUpdate:(UInt64)nextUpdateInSeconds
721
730
return;
722
731
}
723
732
});
724
- self.timeUpdateScheduled = YES ;
733
+ self.timeUpdateTimer = timerSource ;
725
734
MTR_LOG_DEBUG("%@ Timer Scheduled for next Device Time Update, in %llu seconds", self, nextUpdateInSeconds);
726
735
}
727
736
@@ -731,7 +740,7 @@ - (void)_scheduleNextUpdate:(UInt64)nextUpdateInSeconds
731
740
- (void)_updateDeviceTimeAndScheduleNextUpdate
732
741
{
733
742
os_unfair_lock_assert_owner(&self->_timeSyncLock);
734
- if (self.timeUpdateScheduled ) {
743
+ if (self.timeUpdateTimer != nil ) {
735
744
MTR_LOG_DEBUG("%@ Device Time Update already scheduled", self);
736
745
return;
737
746
}
@@ -749,14 +758,23 @@ - (void)_performScheduledTimeUpdate
749
758
return;
750
759
}
751
760
// Device must not be invalidated
752
- if (! self.timeUpdateScheduled ) {
761
+ if (self.timeUpdateTimer == nil ) {
753
762
MTR_LOG_DEBUG("%@ Device Time Update is no longer scheduled, MTRDevice may have been invalidated.", self);
754
763
return;
755
764
}
756
- self.timeUpdateScheduled = NO ;
765
+ self.timeUpdateTimer = nil ;
757
766
[self _updateDeviceTimeAndScheduleNextUpdate];
758
767
}
759
768
769
+ - (void)_cancelTimeUpdateTimer
770
+ {
771
+ std::lock_guard lock(self->_timeSyncLock);
772
+ if (self.timeUpdateTimer != nil) {
773
+ dispatch_source_cancel(self.timeUpdateTimer);
774
+ self.timeUpdateTimer = nil;
775
+ }
776
+ }
777
+
760
778
- (NSArray<NSNumber *> *)_endpointsWithTimeSyncClusterServer
761
779
{
762
780
NSArray<NSNumber *> * endpointsOnDevice;
@@ -961,9 +979,7 @@ - (void)invalidate
961
979
962
980
[_asyncWorkQueue invalidate];
963
981
964
- os_unfair_lock_lock(&self->_timeSyncLock);
965
- _timeUpdateScheduled = NO;
966
- os_unfair_lock_unlock(&self->_timeSyncLock);
982
+ [self _cancelTimeUpdateTimer];
967
983
968
984
os_unfair_lock_lock(&self->_lock);
969
985
@@ -1286,7 +1302,7 @@ - (void)_handleSubscriptionEstablished
1286
1302
1287
1303
os_unfair_lock_lock(&self->_timeSyncLock);
1288
1304
1289
- if (! self.timeUpdateScheduled ) {
1305
+ if (self.timeUpdateTimer == nil ) {
1290
1306
[self _scheduleNextUpdate:MTR_DEVICE_TIME_UPDATE_INITIAL_WAIT_TIME_SEC];
1291
1307
}
1292
1308
@@ -4758,6 +4774,8 @@ - (void)controllerSuspended
4758
4774
{
4759
4775
[super controllerSuspended];
4760
4776
4777
+ [self _cancelTimeUpdateTimer];
4778
+
4761
4779
std::lock_guard lock(self->_lock);
4762
4780
self.suspended = YES;
4763
4781
[self _resetSubscriptionWithReasonString:@"Controller suspended"];
0 commit comments