Skip to content

Commit

Permalink
fix(IoT): Using custom atomic dictionary for topic listeners (#5415)
Browse files Browse the repository at this point in the history
fix(IoT): Fixing random crash when a connection is attempted just after disconnecting
  • Loading branch information
sebaland authored Aug 1, 2024
1 parent 21daae9 commit d2335a2
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 11 deletions.
12 changes: 6 additions & 6 deletions AWSCore/Utility/AWSSynchronizedMutableDictionary.m
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,13 @@ - (id)objectForKey:(id)aKey {
}

- (void)setObject:(id)anObject forKey:(id)aKey {
dispatch_barrier_async(self.dispatchQueue, ^{
dispatch_barrier_sync(self.dispatchQueue, ^{
[self.dictionary setObject:anObject forKey:aKey];
});
}

- (void)removeObject:(id)object {
dispatch_barrier_async(self.dispatchQueue, ^{
dispatch_barrier_sync(self.dispatchQueue, ^{
for (NSString *key in self.dictionary) {
if (object == self.dictionary[key]) {
[self.dictionary removeObjectForKey:key];
Expand All @@ -91,19 +91,19 @@ - (void)removeObject:(id)object {
}

- (void)removeObjectForKey:(id)aKey {
dispatch_barrier_async(self.dispatchQueue, ^{
dispatch_barrier_sync(self.dispatchQueue, ^{
[self.dictionary removeObjectForKey:aKey];
});
}

- (void)removeAllObjects {
dispatch_barrier_async(self.dispatchQueue, ^{
dispatch_barrier_sync(self.dispatchQueue, ^{
[self.dictionary removeAllObjects];
});
}

- (void)mutateWithBlock:(void (^)(NSMutableDictionary *))block {
dispatch_barrier_async(self.dispatchQueue, ^{
dispatch_barrier_sync(self.dispatchQueue, ^{
block(self.dictionary);
});
}
Expand All @@ -112,7 +112,7 @@ + (void)mutateSyncedDictionaries:(NSArray<AWSSynchronizedMutableDictionary *> *)
AWSSynchronizedMutableDictionary *first = [dictionaries firstObject];
if (!first) { return; }

dispatch_barrier_async(first.dispatchQueue, ^{
dispatch_barrier_sync(first.dispatchQueue, ^{
[dictionaries enumerateObjectsUsingBlock:^(AWSSynchronizedMutableDictionary * _Nonnull atomicDictionary, NSUInteger index, BOOL * _Nonnull stop) {
NSCAssert([first.syncKey isEqual:atomicDictionary.syncKey], @"Sync keys much match");
block(atomicDictionary.instanceKey, atomicDictionary.dictionary);
Expand Down
36 changes: 36 additions & 0 deletions AWSIoT/Internal/AWSIoTAtomicDictionary.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//
// Copyright 2010-2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License").
// You may not use this file except in compliance with the License.
// A copy of the License is located at
//
// http://aws.amazon.com/apache2.0
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface AWSIoTAtomicDictionary<KeyType, ObjectType> : NSObject

@property (readonly, copy) NSArray<KeyType> *allKeys;
@property (readonly, copy) NSArray<ObjectType> *allValues;

/// Create new instance.
- (instancetype)init;

- (id)objectForKey:(id)aKey;
- (void)setObject:(id)anObject forKey:(id <NSCopying>)aKey;

- (void)removeObjectForKey:(id)aKey;
- (void)removeAllObjects;

@end

NS_ASSUME_NONNULL_END
75 changes: 75 additions & 0 deletions AWSIoT/Internal/AWSIoTAtomicDictionary.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//
// Copyright 2010-2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License").
// You may not use this file except in compliance with the License.
// A copy of the License is located at
//
// http://aws.amazon.com/apache2.0
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.
//

#import "AWSIoTAtomicDictionary.h"

@interface AWSIoTAtomicDictionary()

@property (nonatomic, strong) NSMutableDictionary *dictionary;
@property (nonatomic, strong) NSLock *lock;

@end

@implementation AWSIoTAtomicDictionary

- (instancetype)init {
self = [super init];
if (self) {
_lock = [[NSLock alloc] init];
_dictionary = [NSMutableDictionary new];
}
return self;
}

- (NSArray *)allKeys {
[self.lock lock];
NSArray * result = self.dictionary.allKeys;
[self.lock unlock];
return result;
}

- (NSArray *)allValues {
[self.lock lock];
NSArray * result = self.dictionary.allValues;
[self.lock unlock];
return result;
}

- (id)objectForKey:(id)aKey {
[self.lock lock];
id result = [self.dictionary objectForKey:aKey];
[self.lock unlock];
return result;
}

- (void)setObject:(id)anObject forKey:(id)aKey {
[self.lock lock];
[self.dictionary setObject:anObject forKey:aKey];
[self.lock unlock];
}

- (void)removeObjectForKey:(id)aKey {
[self.lock lock];
[self.dictionary removeObjectForKey:aKey];
[self.lock unlock];
}

- (void)removeAllObjects {
[self.lock lock];
[self.dictionary removeAllObjects];
[self.lock unlock];
}

@end
5 changes: 3 additions & 2 deletions AWSIoT/Internal/AWSIoTMQTTClient.m
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#import "AWSMQTTMessage.h"
#import "AWSIoTManager.h"
#import "AWSIoTStreamThread.h"
#import "AWSIoTAtomicDictionary.h"

@implementation AWSIoTMQTTTopicModel
@end
Expand All @@ -38,7 +39,7 @@ @interface AWSIoTMQTTClient() <AWSSRWebSocketDelegate, NSStreamDelegate, AWSMQTT

@property(atomic, assign, readwrite) AWSIoTMQTTStatus mqttStatus;
@property(nonatomic, strong) AWSMQTTSession* session;
@property(nonatomic, strong) AWSSynchronizedMutableDictionary * topicListeners;
@property(nonatomic, strong) AWSIoTAtomicDictionary *topicListeners;

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

- (instancetype)init {
if (self = [super init]) {
_topicListeners = [AWSSynchronizedMutableDictionary new];
_topicListeners = [AWSIoTAtomicDictionary new];
_clientCerts = nil;
_session.delegate = nil;
_session = nil;
Expand Down
8 changes: 8 additions & 0 deletions AWSiOSSDKv2.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,8 @@
68A45BBC2B8D6ADE00A0851E /* AWSDDMultiFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = 68A45BAB2B8D6ADE00A0851E /* AWSDDMultiFormatter.h */; settings = {ATTRIBUTES = (Public, ); }; };
68A45BBF2B8E74F900A0851E /* AWSCLIColor.h in Headers */ = {isa = PBXBuildFile; fileRef = 68A45BBD2B8E74F800A0851E /* AWSCLIColor.h */; settings = {ATTRIBUTES = (Public, ); }; };
68A45BC02B8E74F900A0851E /* AWSCLIColor.m in Sources */ = {isa = PBXBuildFile; fileRef = 68A45BBE2B8E74F900A0851E /* AWSCLIColor.m */; };
68DD11862C5AF52B004E1C37 /* AWSIoTAtomicDictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = 68DD11842C5AF52B004E1C37 /* AWSIoTAtomicDictionary.h */; };
68DD11872C5AF52B004E1C37 /* AWSIoTAtomicDictionary.m in Sources */ = {isa = PBXBuildFile; fileRef = 68DD11852C5AF52B004E1C37 /* AWSIoTAtomicDictionary.m */; };
68EE1A6C2B713D8100B7CF41 /* AWSIoTStreamThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 68EE1A6B2B713D8100B7CF41 /* AWSIoTStreamThread.h */; };
68EE1A6E2B713D8900B7CF41 /* AWSIoTStreamThread.m in Sources */ = {isa = PBXBuildFile; fileRef = 68EE1A6D2B713D8900B7CF41 /* AWSIoTStreamThread.m */; };
6BE9D6AA25A54EBA00AB5C9A /* AWSIotDataManagerRetainTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE9D6A925A54EBA00AB5C9A /* AWSIotDataManagerRetainTests.swift */; };
Expand Down Expand Up @@ -3250,6 +3252,8 @@
68A45BAB2B8D6ADE00A0851E /* AWSDDMultiFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AWSDDMultiFormatter.h; sourceTree = "<group>"; };
68A45BBD2B8E74F800A0851E /* AWSCLIColor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AWSCLIColor.h; sourceTree = "<group>"; };
68A45BBE2B8E74F900A0851E /* AWSCLIColor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AWSCLIColor.m; sourceTree = "<group>"; };
68DD11842C5AF52B004E1C37 /* AWSIoTAtomicDictionary.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AWSIoTAtomicDictionary.h; sourceTree = "<group>"; };
68DD11852C5AF52B004E1C37 /* AWSIoTAtomicDictionary.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AWSIoTAtomicDictionary.m; sourceTree = "<group>"; };
68EE1A6B2B713D8100B7CF41 /* AWSIoTStreamThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AWSIoTStreamThread.h; sourceTree = "<group>"; };
68EE1A6D2B713D8900B7CF41 /* AWSIoTStreamThread.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AWSIoTStreamThread.m; sourceTree = "<group>"; };
6BE9D6A925A54EBA00AB5C9A /* AWSIotDataManagerRetainTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AWSIotDataManagerRetainTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -7209,6 +7213,8 @@
CE9DE6351C6A78D70060793F /* Internal */ = {
isa = PBXGroup;
children = (
68DD11842C5AF52B004E1C37 /* AWSIoTAtomicDictionary.h */,
68DD11852C5AF52B004E1C37 /* AWSIoTAtomicDictionary.m */,
CE9DE6361C6A78D70060793F /* AWSIoTCSR.h */,
CE9DE6371C6A78D70060793F /* AWSIoTCSR.m */,
CE9DE6381C6A78D70060793F /* AWSIoTKeychain.h */,
Expand Down Expand Up @@ -8457,6 +8463,7 @@
CE9DE6501C6A78D70060793F /* AWSIoTDataModel.h in Headers */,
CE9DE6541C6A78D70060793F /* AWSIoTDataService.h in Headers */,
CE9DE64D1C6A78D70060793F /* AWSIoTData.h in Headers */,
68DD11862C5AF52B004E1C37 /* AWSIoTAtomicDictionary.h in Headers */,
CE9DE64E1C6A78D70060793F /* AWSIoTDataManager.h in Headers */,
CE9DE66E1C6A78D70060793F /* AWSMQttTxFlow.h in Headers */,
03427765269D15A400379263 /* AWSIoTMessage.h in Headers */,
Expand Down Expand Up @@ -13393,6 +13400,7 @@
0342776A269D185200379263 /* AWSIoTMessage+AWSMQTTMessage.m in Sources */,
CE9DE66B1C6A78D70060793F /* AWSMQTTMessage.m in Sources */,
CE9DE65D1C6A78D70060793F /* AWSIoTService.m in Sources */,
68DD11872C5AF52B004E1C37 /* AWSIoTAtomicDictionary.m in Sources */,
CE9DE6571C6A78D70060793F /* AWSIoTManager.m in Sources */,
CE9DE6591C6A78D70060793F /* AWSIoTModel.m in Sources */,
CE9DE6691C6A78D70060793F /* AWSMQTTEncoder.m in Sources */,
Expand Down
4 changes: 1 addition & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@

## Unreleased

- **AWSCore**
- Fixing concurrency issues in `AWSSynchronizedMutableDictionary` (#5413)

- **AWSIoT**
- Using custom atomic dictionary for topic listeners (#5415)
- Fixing random crash when a connection is attempted just after disconnecting

## 2.36.6
Expand Down

0 comments on commit d2335a2

Please sign in to comment.