Skip to content

Commit d2335a2

Browse files
authored
fix(IoT): Using custom atomic dictionary for topic listeners (#5415)
fix(IoT): Fixing random crash when a connection is attempted just after disconnecting
1 parent 21daae9 commit d2335a2

File tree

6 files changed

+129
-11
lines changed

6 files changed

+129
-11
lines changed

AWSCore/Utility/AWSSynchronizedMutableDictionary.m

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,13 @@ - (id)objectForKey:(id)aKey {
7474
}
7575

7676
- (void)setObject:(id)anObject forKey:(id)aKey {
77-
dispatch_barrier_async(self.dispatchQueue, ^{
77+
dispatch_barrier_sync(self.dispatchQueue, ^{
7878
[self.dictionary setObject:anObject forKey:aKey];
7979
});
8080
}
8181

8282
- (void)removeObject:(id)object {
83-
dispatch_barrier_async(self.dispatchQueue, ^{
83+
dispatch_barrier_sync(self.dispatchQueue, ^{
8484
for (NSString *key in self.dictionary) {
8585
if (object == self.dictionary[key]) {
8686
[self.dictionary removeObjectForKey:key];
@@ -91,19 +91,19 @@ - (void)removeObject:(id)object {
9191
}
9292

9393
- (void)removeObjectForKey:(id)aKey {
94-
dispatch_barrier_async(self.dispatchQueue, ^{
94+
dispatch_barrier_sync(self.dispatchQueue, ^{
9595
[self.dictionary removeObjectForKey:aKey];
9696
});
9797
}
9898

9999
- (void)removeAllObjects {
100-
dispatch_barrier_async(self.dispatchQueue, ^{
100+
dispatch_barrier_sync(self.dispatchQueue, ^{
101101
[self.dictionary removeAllObjects];
102102
});
103103
}
104104

105105
- (void)mutateWithBlock:(void (^)(NSMutableDictionary *))block {
106-
dispatch_barrier_async(self.dispatchQueue, ^{
106+
dispatch_barrier_sync(self.dispatchQueue, ^{
107107
block(self.dictionary);
108108
});
109109
}
@@ -112,7 +112,7 @@ + (void)mutateSyncedDictionaries:(NSArray<AWSSynchronizedMutableDictionary *> *)
112112
AWSSynchronizedMutableDictionary *first = [dictionaries firstObject];
113113
if (!first) { return; }
114114

115-
dispatch_barrier_async(first.dispatchQueue, ^{
115+
dispatch_barrier_sync(first.dispatchQueue, ^{
116116
[dictionaries enumerateObjectsUsingBlock:^(AWSSynchronizedMutableDictionary * _Nonnull atomicDictionary, NSUInteger index, BOOL * _Nonnull stop) {
117117
NSCAssert([first.syncKey isEqual:atomicDictionary.syncKey], @"Sync keys much match");
118118
block(atomicDictionary.instanceKey, atomicDictionary.dictionary);
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//
2+
// Copyright 2010-2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License").
5+
// You may not use this file except in compliance with the License.
6+
// A copy of the License is located at
7+
//
8+
// http://aws.amazon.com/apache2.0
9+
//
10+
// or in the "license" file accompanying this file. This file is distributed
11+
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
// express or implied. See the License for the specific language governing
13+
// permissions and limitations under the License.
14+
//
15+
16+
#import <Foundation/Foundation.h>
17+
18+
NS_ASSUME_NONNULL_BEGIN
19+
20+
@interface AWSIoTAtomicDictionary<KeyType, ObjectType> : NSObject
21+
22+
@property (readonly, copy) NSArray<KeyType> *allKeys;
23+
@property (readonly, copy) NSArray<ObjectType> *allValues;
24+
25+
/// Create new instance.
26+
- (instancetype)init;
27+
28+
- (id)objectForKey:(id)aKey;
29+
- (void)setObject:(id)anObject forKey:(id <NSCopying>)aKey;
30+
31+
- (void)removeObjectForKey:(id)aKey;
32+
- (void)removeAllObjects;
33+
34+
@end
35+
36+
NS_ASSUME_NONNULL_END
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//
2+
// Copyright 2010-2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License").
5+
// You may not use this file except in compliance with the License.
6+
// A copy of the License is located at
7+
//
8+
// http://aws.amazon.com/apache2.0
9+
//
10+
// or in the "license" file accompanying this file. This file is distributed
11+
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
// express or implied. See the License for the specific language governing
13+
// permissions and limitations under the License.
14+
//
15+
16+
#import "AWSIoTAtomicDictionary.h"
17+
18+
@interface AWSIoTAtomicDictionary()
19+
20+
@property (nonatomic, strong) NSMutableDictionary *dictionary;
21+
@property (nonatomic, strong) NSLock *lock;
22+
23+
@end
24+
25+
@implementation AWSIoTAtomicDictionary
26+
27+
- (instancetype)init {
28+
self = [super init];
29+
if (self) {
30+
_lock = [[NSLock alloc] init];
31+
_dictionary = [NSMutableDictionary new];
32+
}
33+
return self;
34+
}
35+
36+
- (NSArray *)allKeys {
37+
[self.lock lock];
38+
NSArray * result = self.dictionary.allKeys;
39+
[self.lock unlock];
40+
return result;
41+
}
42+
43+
- (NSArray *)allValues {
44+
[self.lock lock];
45+
NSArray * result = self.dictionary.allValues;
46+
[self.lock unlock];
47+
return result;
48+
}
49+
50+
- (id)objectForKey:(id)aKey {
51+
[self.lock lock];
52+
id result = [self.dictionary objectForKey:aKey];
53+
[self.lock unlock];
54+
return result;
55+
}
56+
57+
- (void)setObject:(id)anObject forKey:(id)aKey {
58+
[self.lock lock];
59+
[self.dictionary setObject:anObject forKey:aKey];
60+
[self.lock unlock];
61+
}
62+
63+
- (void)removeObjectForKey:(id)aKey {
64+
[self.lock lock];
65+
[self.dictionary removeObjectForKey:aKey];
66+
[self.lock unlock];
67+
}
68+
69+
- (void)removeAllObjects {
70+
[self.lock lock];
71+
[self.dictionary removeAllObjects];
72+
[self.lock unlock];
73+
}
74+
75+
@end

AWSIoT/Internal/AWSIoTMQTTClient.m

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#import "AWSMQTTMessage.h"
2626
#import "AWSIoTManager.h"
2727
#import "AWSIoTStreamThread.h"
28+
#import "AWSIoTAtomicDictionary.h"
2829

2930
@implementation AWSIoTMQTTTopicModel
3031
@end
@@ -38,7 +39,7 @@ @interface AWSIoTMQTTClient() <AWSSRWebSocketDelegate, NSStreamDelegate, AWSMQTT
3839

3940
@property(atomic, assign, readwrite) AWSIoTMQTTStatus mqttStatus;
4041
@property(nonatomic, strong) AWSMQTTSession* session;
41-
@property(nonatomic, strong) AWSSynchronizedMutableDictionary * topicListeners;
42+
@property(nonatomic, strong) AWSIoTAtomicDictionary *topicListeners;
4243

4344
@property(atomic, assign) BOOL userDidIssueDisconnect; //Flag to indicate if requestor has issued a disconnect
4445
@property(atomic, assign) BOOL userDidIssueConnect; //Flag to indicate if requestor has issued a connect
@@ -91,7 +92,7 @@ @implementation AWSIoTMQTTClient
9192

9293
- (instancetype)init {
9394
if (self = [super init]) {
94-
_topicListeners = [AWSSynchronizedMutableDictionary new];
95+
_topicListeners = [AWSIoTAtomicDictionary new];
9596
_clientCerts = nil;
9697
_session.delegate = nil;
9798
_session = nil;

AWSiOSSDKv2.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,8 @@
635635
68A45BBC2B8D6ADE00A0851E /* AWSDDMultiFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = 68A45BAB2B8D6ADE00A0851E /* AWSDDMultiFormatter.h */; settings = {ATTRIBUTES = (Public, ); }; };
636636
68A45BBF2B8E74F900A0851E /* AWSCLIColor.h in Headers */ = {isa = PBXBuildFile; fileRef = 68A45BBD2B8E74F800A0851E /* AWSCLIColor.h */; settings = {ATTRIBUTES = (Public, ); }; };
637637
68A45BC02B8E74F900A0851E /* AWSCLIColor.m in Sources */ = {isa = PBXBuildFile; fileRef = 68A45BBE2B8E74F900A0851E /* AWSCLIColor.m */; };
638+
68DD11862C5AF52B004E1C37 /* AWSIoTAtomicDictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = 68DD11842C5AF52B004E1C37 /* AWSIoTAtomicDictionary.h */; };
639+
68DD11872C5AF52B004E1C37 /* AWSIoTAtomicDictionary.m in Sources */ = {isa = PBXBuildFile; fileRef = 68DD11852C5AF52B004E1C37 /* AWSIoTAtomicDictionary.m */; };
638640
68EE1A6C2B713D8100B7CF41 /* AWSIoTStreamThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 68EE1A6B2B713D8100B7CF41 /* AWSIoTStreamThread.h */; };
639641
68EE1A6E2B713D8900B7CF41 /* AWSIoTStreamThread.m in Sources */ = {isa = PBXBuildFile; fileRef = 68EE1A6D2B713D8900B7CF41 /* AWSIoTStreamThread.m */; };
640642
6BE9D6AA25A54EBA00AB5C9A /* AWSIotDataManagerRetainTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE9D6A925A54EBA00AB5C9A /* AWSIotDataManagerRetainTests.swift */; };
@@ -3250,6 +3252,8 @@
32503252
68A45BAB2B8D6ADE00A0851E /* AWSDDMultiFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AWSDDMultiFormatter.h; sourceTree = "<group>"; };
32513253
68A45BBD2B8E74F800A0851E /* AWSCLIColor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AWSCLIColor.h; sourceTree = "<group>"; };
32523254
68A45BBE2B8E74F900A0851E /* AWSCLIColor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AWSCLIColor.m; sourceTree = "<group>"; };
3255+
68DD11842C5AF52B004E1C37 /* AWSIoTAtomicDictionary.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AWSIoTAtomicDictionary.h; sourceTree = "<group>"; };
3256+
68DD11852C5AF52B004E1C37 /* AWSIoTAtomicDictionary.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AWSIoTAtomicDictionary.m; sourceTree = "<group>"; };
32533257
68EE1A6B2B713D8100B7CF41 /* AWSIoTStreamThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AWSIoTStreamThread.h; sourceTree = "<group>"; };
32543258
68EE1A6D2B713D8900B7CF41 /* AWSIoTStreamThread.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AWSIoTStreamThread.m; sourceTree = "<group>"; };
32553259
6BE9D6A925A54EBA00AB5C9A /* AWSIotDataManagerRetainTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AWSIotDataManagerRetainTests.swift; sourceTree = "<group>"; };
@@ -7209,6 +7213,8 @@
72097213
CE9DE6351C6A78D70060793F /* Internal */ = {
72107214
isa = PBXGroup;
72117215
children = (
7216+
68DD11842C5AF52B004E1C37 /* AWSIoTAtomicDictionary.h */,
7217+
68DD11852C5AF52B004E1C37 /* AWSIoTAtomicDictionary.m */,
72127218
CE9DE6361C6A78D70060793F /* AWSIoTCSR.h */,
72137219
CE9DE6371C6A78D70060793F /* AWSIoTCSR.m */,
72147220
CE9DE6381C6A78D70060793F /* AWSIoTKeychain.h */,
@@ -8457,6 +8463,7 @@
84578463
CE9DE6501C6A78D70060793F /* AWSIoTDataModel.h in Headers */,
84588464
CE9DE6541C6A78D70060793F /* AWSIoTDataService.h in Headers */,
84598465
CE9DE64D1C6A78D70060793F /* AWSIoTData.h in Headers */,
8466+
68DD11862C5AF52B004E1C37 /* AWSIoTAtomicDictionary.h in Headers */,
84608467
CE9DE64E1C6A78D70060793F /* AWSIoTDataManager.h in Headers */,
84618468
CE9DE66E1C6A78D70060793F /* AWSMQttTxFlow.h in Headers */,
84628469
03427765269D15A400379263 /* AWSIoTMessage.h in Headers */,
@@ -13393,6 +13400,7 @@
1339313400
0342776A269D185200379263 /* AWSIoTMessage+AWSMQTTMessage.m in Sources */,
1339413401
CE9DE66B1C6A78D70060793F /* AWSMQTTMessage.m in Sources */,
1339513402
CE9DE65D1C6A78D70060793F /* AWSIoTService.m in Sources */,
13403+
68DD11872C5AF52B004E1C37 /* AWSIoTAtomicDictionary.m in Sources */,
1339613404
CE9DE6571C6A78D70060793F /* AWSIoTManager.m in Sources */,
1339713405
CE9DE6591C6A78D70060793F /* AWSIoTModel.m in Sources */,
1339813406
CE9DE6691C6A78D70060793F /* AWSMQTTEncoder.m in Sources */,

CHANGELOG.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@
22

33
## Unreleased
44

5-
- **AWSCore**
6-
- Fixing concurrency issues in `AWSSynchronizedMutableDictionary` (#5413)
7-
85
- **AWSIoT**
6+
- Using custom atomic dictionary for topic listeners (#5415)
97
- Fixing random crash when a connection is attempted just after disconnecting
108

119
## 2.36.6

0 commit comments

Comments
 (0)