@@ -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,8 +730,9 @@ - (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);
735
+ dispatch_resume(timerSource);
726
736
}
727
737
728
738
// Time Updates are a day apart (this can be changed in the future)
@@ -731,7 +741,7 @@ - (void)_scheduleNextUpdate:(UInt64)nextUpdateInSeconds
731
741
- (void)_updateDeviceTimeAndScheduleNextUpdate
732
742
{
733
743
os_unfair_lock_assert_owner(&self->_timeSyncLock);
734
- if (self.timeUpdateScheduled ) {
744
+ if (self.timeUpdateTimer != nil ) {
735
745
MTR_LOG_DEBUG("%@ Device Time Update already scheduled", self);
736
746
return;
737
747
}
@@ -749,14 +759,23 @@ - (void)_performScheduledTimeUpdate
749
759
return;
750
760
}
751
761
// Device must not be invalidated
752
- if (! self.timeUpdateScheduled ) {
762
+ if (self.timeUpdateTimer == nil ) {
753
763
MTR_LOG_DEBUG("%@ Device Time Update is no longer scheduled, MTRDevice may have been invalidated.", self);
754
764
return;
755
765
}
756
- self.timeUpdateScheduled = NO ;
766
+ self.timeUpdateTimer = nil ;
757
767
[self _updateDeviceTimeAndScheduleNextUpdate];
758
768
}
759
769
770
+ - (void)_cancelTimeUpdateTimer
771
+ {
772
+ std::lock_guard lock(self->_timeSyncLock);
773
+ if (self.timeUpdateTimer != nil) {
774
+ dispatch_source_cancel(self.timeUpdateTimer);
775
+ self.timeUpdateTimer = nil;
776
+ }
777
+ }
778
+
760
779
- (NSArray<NSNumber *> *)_endpointsWithTimeSyncClusterServer
761
780
{
762
781
NSArray<NSNumber *> * endpointsOnDevice;
@@ -961,9 +980,7 @@ - (void)invalidate
961
980
962
981
[_asyncWorkQueue invalidate];
963
982
964
- os_unfair_lock_lock(&self->_timeSyncLock);
965
- _timeUpdateScheduled = NO;
966
- os_unfair_lock_unlock(&self->_timeSyncLock);
983
+ [self _cancelTimeUpdateTimer];
967
984
968
985
os_unfair_lock_lock(&self->_lock);
969
986
@@ -1286,7 +1303,7 @@ - (void)_handleSubscriptionEstablished
1286
1303
1287
1304
os_unfair_lock_lock(&self->_timeSyncLock);
1288
1305
1289
- if (! self.timeUpdateScheduled ) {
1306
+ if (self.timeUpdateTimer == nil ) {
1290
1307
[self _scheduleNextUpdate:MTR_DEVICE_TIME_UPDATE_INITIAL_WAIT_TIME_SEC];
1291
1308
}
1292
1309
@@ -4758,6 +4775,8 @@ - (void)controllerSuspended
4758
4775
{
4759
4776
[super controllerSuspended];
4760
4777
4778
+ [self _cancelTimeUpdateTimer];
4779
+
4761
4780
std::lock_guard lock(self->_lock);
4762
4781
self.suspended = YES;
4763
4782
[self _resetSubscriptionWithReasonString:@"Controller suspended"];
0 commit comments