@@ -27,6 +27,8 @@ @interface AWSIoTStreamThread()
2727@property (nonatomic , assign ) NSTimeInterval defaultRunLoopTimeInterval;
2828@property (nonatomic , assign ) BOOL isRunning;
2929@property (nonatomic , assign ) BOOL shouldDisconnect;
30+ @property (nonatomic , strong ) dispatch_queue_t serialQueue;
31+ @property (nonatomic , assign ) BOOL didCleanUp;
3032@end
3133
3234@implementation AWSIoTStreamThread
@@ -40,34 +42,42 @@ - (nonnull instancetype)initWithSession:(nonnull AWSMQTTSession *)session
4042 outputStream: nil ];
4143}
4244
43- -(instancetype )initWithSession : (nonnull AWSMQTTSession *)session
44- decoderInputStream : (nonnull NSInputStream *)decoderInputStream
45- encoderOutputStream : (nonnull NSOutputStream *)encoderOutputStream
46- outputStream : (nullable NSOutputStream *)outputStream ; {
45+ - (instancetype )initWithSession : (nonnull AWSMQTTSession *)session
46+ decoderInputStream : (nonnull NSInputStream *)decoderInputStream
47+ encoderOutputStream : (nonnull NSOutputStream *)encoderOutputStream
48+ outputStream : (nullable NSOutputStream *)outputStream {
4749 if (self = [super init ]) {
4850 _session = session;
4951 _decoderInputStream = decoderInputStream;
5052 _encoderOutputStream = encoderOutputStream;
5153 _outputStream = outputStream;
5254 _defaultRunLoopTimeInterval = 10 ;
5355 _shouldDisconnect = NO ;
56+ _serialQueue = dispatch_queue_create (" com.amazonaws.iot.streamthread.syncQueue" , DISPATCH_QUEUE_SERIAL);
57+ _didCleanUp = NO ;
5458 }
5559 return self;
5660}
5761
5862- (void )main {
63+ if (self.isRunning ) {
64+ AWSDDLogWarn (@" Attempted to start a thread that is already running: [%@ ]" , self);
65+ return ;
66+ }
67+
5968 AWSDDLogVerbose (@" Started execution of Thread: [%@ ]" , self);
6069 // This is invoked in a new thread by the webSocketDidOpen method or by the Connect method. Get the runLoop from the thread.
6170 self.runLoopForStreamsThread = [NSRunLoop currentRunLoop ];
6271
6372 // Setup a default timer to ensure that the RunLoop always has atleast one timer on it. This is to prevent the while loop
6473 // below to spin in tight loop when all input sources and session timers are shutdown during a reconnect sequence.
74+ __weak typeof (self) weakSelf = self;
6575 self.defaultRunLoopTimer = [[NSTimer alloc ] initWithFireDate: [NSDate dateWithTimeIntervalSinceNow: 60.0 ]
66- interval: 60.0
67- target: self
68- selector: @selector ( timerHandler: )
69- userInfo: nil
70- repeats: YES ];
76+ interval: 60.0
77+ repeats: YES
78+ block: ^( NSTimer * _Nonnull timer) {
79+ AWSDDLogVerbose ( @" Default run loop timer executed on Thread: [ %@ ]. isRunning = %@ . isCancelled = %@ " , weakSelf, weakSelf. isRunning ? @" YES " : @" NO " , weakSelf. isCancelled ? @" YES " : @" NO " );
80+ } ];
7181 [self .runLoopForStreamsThread addTimer: self .defaultRunLoopTimer
7282 forMode: NSDefaultRunLoopMode ];
7383
@@ -82,7 +92,7 @@ - (void)main {
8292 [self .session connectToInputStream: self .decoderInputStream
8393 outputStream: self .encoderOutputStream];
8494
85- while (self. isRunning && !self. isCancelled ) {
95+ while ([ self shouldContinueRunning ] ) {
8696 // This will continue run until the thread is cancelled
8797 // Run one cycle of the runloop. This will return after a input source event or timer event is processed
8898 [self .runLoopForStreamsThread runMode: NSDefaultRunLoopMode
@@ -94,60 +104,76 @@ - (void)main {
94104 AWSDDLogVerbose (@" Finished execution of Thread: [%@ ]" , self);
95105}
96106
107+ - (BOOL )shouldContinueRunning {
108+ __block BOOL shouldRun;
109+ dispatch_sync (self.serialQueue , ^{
110+ shouldRun = self.isRunning && !self.isCancelled && self.defaultRunLoopTimer != nil ;
111+ });
112+ return shouldRun;
113+ }
114+
97115- (void )cancel {
98116 AWSDDLogVerbose (@" Issued Cancel on thread [%@ ]" , (NSThread *)self);
99- self.isRunning = NO ;
100- [super cancel ];
117+ dispatch_sync (self.serialQueue , ^{
118+ self.isRunning = NO ;
119+ [super cancel ];
120+ });
101121}
102122
103123- (void )cancelAndDisconnect : (BOOL )shouldDisconnect {
104124 AWSDDLogVerbose (@" Issued Cancel and Disconnect = [%@ ] on thread [%@ ]" , shouldDisconnect ? @" YES" : @" NO" , (NSThread *)self);
105- self.shouldDisconnect = shouldDisconnect;
106- self.isRunning = NO ;
107- [super cancel ];
125+ dispatch_sync (self.serialQueue , ^{
126+ self.shouldDisconnect = shouldDisconnect;
127+ self.isRunning = NO ;
128+ [super cancel ];
129+ });
108130}
109131
110132- (void )cleanUp {
111- if (self.defaultRunLoopTimer ) {
112- [self .defaultRunLoopTimer invalidate ];
113- self.defaultRunLoopTimer = nil ;
114- }
115-
116- if (self.shouldDisconnect ) {
117- if (self.session ) {
118- [self .session close ];
119- self.session = nil ;
133+ dispatch_sync (self.serialQueue , ^{
134+ if (self.didCleanUp ) {
135+ AWSDDLogVerbose (@" Clean up already called for thread: [%@ ]" , (NSThread *)self);
136+ return ;
120137 }
121138
122- if (self.outputStream ) {
123- self.outputStream .delegate = nil ;
124- [self .outputStream close ];
125- [self .outputStream removeFromRunLoop: self .runLoopForStreamsThread
126- forMode: NSDefaultRunLoopMode ];
127- self.outputStream = nil ;
139+ self.didCleanUp = YES ;
140+ if (self.defaultRunLoopTimer ) {
141+ [self .defaultRunLoopTimer invalidate ];
142+ self.defaultRunLoopTimer = nil ;
128143 }
129144
130- if (self.decoderInputStream ) {
131- [self .decoderInputStream close ];
132- self.decoderInputStream = nil ;
145+ if (self.shouldDisconnect ) {
146+ if (self.session ) {
147+ [self .session close ];
148+ self.session = nil ;
149+ }
150+
151+ if (self.outputStream ) {
152+ self.outputStream .delegate = nil ;
153+ [self .outputStream close ];
154+ [self .outputStream removeFromRunLoop: self .runLoopForStreamsThread
155+ forMode: NSDefaultRunLoopMode ];
156+ self.outputStream = nil ;
157+ }
158+
159+ if (self.decoderInputStream ) {
160+ [self .decoderInputStream close ];
161+ self.decoderInputStream = nil ;
162+ }
163+
164+ if (self.encoderOutputStream ) {
165+ [self .encoderOutputStream close ];
166+ self.encoderOutputStream = nil ;
167+ }
168+ } else {
169+ AWSDDLogVerbose (@" Skipping disconnect for thread: [%@ ]" , (NSThread *)self);
133170 }
134171
135- if (self.encoderOutputStream ) {
136- [ self .encoderOutputStream close ] ;
137- self.encoderOutputStream = nil ;
172+ if (self.onStop ) {
173+ self.onStop () ;
174+ self.onStop = nil ;
138175 }
139- } else {
140- AWSDDLogVerbose (@" Skipping disconnect for thread: [%@ ]" , (NSThread *)self);
141- }
142-
143- if (self.onStop ) {
144- self.onStop ();
145- self.onStop = nil ;
146- }
147- }
148-
149- - (void )timerHandler : (NSTimer *)theTimer {
150- AWSDDLogVerbose (@" Default run loop timer executed on Thread: [%@ ]. isRunning = %@ . isCancelled = %@ " , self, self.isRunning ? @" YES" : @" NO" , self.isCancelled ? @" YES" : @" NO" );
176+ });
151177}
152178
153179@end
0 commit comments