@@ -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