From 3d7b8a70c50ae9f59669bb8eacb25eb96a72c961 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Tue, 4 Aug 2020 16:47:00 -0400 Subject: [PATCH 1/9] In progress work on locked collections --- SmartDeviceLink-iOS.xcodeproj/project.pbxproj | 90 +++++++++++++------ SmartDeviceLink/SDLLockedDictionary.m | 32 +++++++ SmartDeviceLink/SDLLockedMutableArray.h | 35 ++++++++ SmartDeviceLink/SDLLockedMutableArray.m | 13 +++ SmartDeviceLink/SDLLockedMutableDictionary.h | 33 +++++++ SmartDeviceLink/SDLLockedMutableDictionary.m | 71 +++++++++++++++ 6 files changed, 245 insertions(+), 29 deletions(-) create mode 100644 SmartDeviceLink/SDLLockedDictionary.m create mode 100644 SmartDeviceLink/SDLLockedMutableArray.h create mode 100644 SmartDeviceLink/SDLLockedMutableArray.m create mode 100644 SmartDeviceLink/SDLLockedMutableDictionary.h create mode 100644 SmartDeviceLink/SDLLockedMutableDictionary.m diff --git a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj index 435b4e720..d6af97ae2 100644 --- a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj +++ b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj @@ -439,6 +439,10 @@ 4A457DD924A5137100386CBA /* SDLLifecycleProtocolHandlerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A457DD824A5137100386CBA /* SDLLifecycleProtocolHandlerSpec.m */; }; 4A4AD8A424894260008FC414 /* TestOldConfigurationUpdateManagerDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A4AD8A324894260008FC414 /* TestOldConfigurationUpdateManagerDelegate.m */; }; 4A4AD8A724894270008FC414 /* TestNewConfigurationUpdateManagerDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A4AD8A624894270008FC414 /* TestNewConfigurationUpdateManagerDelegate.m */; }; + 4A8F464824D9F581003BDAE4 /* SDLLockedMutableDictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A8F464624D9F581003BDAE4 /* SDLLockedMutableDictionary.h */; }; + 4A8F464924D9F581003BDAE4 /* SDLLockedMutableDictionary.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A8F464724D9F581003BDAE4 /* SDLLockedMutableDictionary.m */; }; + 4A8F464C24DA0019003BDAE4 /* SDLLockedMutableArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A8F464A24DA0019003BDAE4 /* SDLLockedMutableArray.h */; }; + 4A8F464D24DA0019003BDAE4 /* SDLLockedMutableArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A8F464B24DA0019003BDAE4 /* SDLLockedMutableArray.m */; }; 4A99D00E247576B7009B43E6 /* SDLTextField+ScreenManagerExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A99D00C247576B7009B43E6 /* SDLTextField+ScreenManagerExtensions.h */; }; 4A99D00F247576B7009B43E6 /* SDLTextField+ScreenManagerExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A99D00D247576B7009B43E6 /* SDLTextField+ScreenManagerExtensions.m */; }; 4A99D0122475773C009B43E6 /* SDLImageField+ScreenManagerExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A99D0102475773C009B43E6 /* SDLImageField+ScreenManagerExtensions.h */; }; @@ -2183,6 +2187,10 @@ 4A4AD8A324894260008FC414 /* TestOldConfigurationUpdateManagerDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = TestOldConfigurationUpdateManagerDelegate.m; path = DevAPISpecs/TestOldConfigurationUpdateManagerDelegate.m; sourceTree = ""; }; 4A4AD8A524894270008FC414 /* TestNewConfigurationUpdateManagerDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TestNewConfigurationUpdateManagerDelegate.h; sourceTree = ""; }; 4A4AD8A624894270008FC414 /* TestNewConfigurationUpdateManagerDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TestNewConfigurationUpdateManagerDelegate.m; sourceTree = ""; }; + 4A8F464624D9F581003BDAE4 /* SDLLockedMutableDictionary.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDLLockedMutableDictionary.h; sourceTree = ""; }; + 4A8F464724D9F581003BDAE4 /* SDLLockedMutableDictionary.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLLockedMutableDictionary.m; sourceTree = ""; }; + 4A8F464A24DA0019003BDAE4 /* SDLLockedMutableArray.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDLLockedMutableArray.h; sourceTree = ""; }; + 4A8F464B24DA0019003BDAE4 /* SDLLockedMutableArray.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLLockedMutableArray.m; sourceTree = ""; }; 4A99D00C247576B7009B43E6 /* SDLTextField+ScreenManagerExtensions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SDLTextField+ScreenManagerExtensions.h"; sourceTree = ""; }; 4A99D00D247576B7009B43E6 /* SDLTextField+ScreenManagerExtensions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "SDLTextField+ScreenManagerExtensions.m"; sourceTree = ""; }; 4A99D0102475773C009B43E6 /* SDLImageField+ScreenManagerExtensions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SDLImageField+ScreenManagerExtensions.h"; sourceTree = ""; }; @@ -3554,7 +3562,7 @@ name = Encryption; sourceTree = ""; }; - 10415106241BBB8B00F163CC /* Lock Screen Icon Cache */ = { + 10415106241BBB8B00F163CC /* Icon Cache */ = { isa = PBXGroup; children = ( 10893C142417D78300BA347E /* SDLIconArchiveFile.h */, @@ -3562,7 +3570,7 @@ 10893C182418188600BA347E /* SDLCacheFileManager.h */, 10893C192418188600BA347E /* SDLCacheFileManager.m */, ); - name = "Lock Screen Icon Cache"; + name = "Icon Cache"; sourceTree = ""; }; 109566F42429865500E24F66 /* Lock Screen Icon Cache */ = { @@ -4103,6 +4111,28 @@ name = Utilities; sourceTree = ""; }; + 4A8F463E24D9EE6D003BDAE4 /* Names */ = { + isa = PBXGroup; + children = ( + 883C22C6222ED84D00939C4C /* SDLRPCFunctionNames.h */, + 883C22C7222ED84D00939C4C /* SDLRPCFunctionNames.m */, + 5D61FB0F1A84238A00846EE7 /* SDLRPCParameterNames.h */, + DA0C46AC1DCD35080001F2A8 /* SDLRPCParameterNames.m */, + ); + name = Names; + sourceTree = ""; + }; + 4A8F463F24D9F39D003BDAE4 /* Locked Collections */ = { + isa = PBXGroup; + children = ( + 4A8F464624D9F581003BDAE4 /* SDLLockedMutableDictionary.h */, + 4A8F464724D9F581003BDAE4 /* SDLLockedMutableDictionary.m */, + 4A8F464A24DA0019003BDAE4 /* SDLLockedMutableArray.h */, + 4A8F464B24DA0019003BDAE4 /* SDLLockedMutableArray.m */, + ); + name = "Locked Collections"; + sourceTree = ""; + }; 4A9D02BB2497EEB400FBE99B /* Custom RPC Adapters */ = { isa = PBXGroup; children = ( @@ -4540,13 +4570,14 @@ 5D5934EF1A85160F00687FB9 /* RPCs */ = { isa = PBXGroup; children = ( - 5D5934FF1A851B8400687FB9 /* Superclasses */, + 5D5934F41A85165E00687FB9 /* Enums */, + 4A8F463E24D9EE6D003BDAE4 /* Names */, + 5D5934F81A8519C300687FB9 /* Notification */, 5D5935041A851E1A00687FB9 /* Payload */, 5D5934F11A85162800687FB9 /* Requests */, 5D5934F21A85163200687FB9 /* Responses */, 5D5934F31A85164500687FB9 /* Structs */, - 5D5934F41A85165E00687FB9 /* Enums */, - 5D5934F81A8519C300687FB9 /* Notification */, + 5D5934FF1A851B8400687FB9 /* Superclasses */, ); name = RPCs; sourceTree = ""; @@ -5289,24 +5320,18 @@ 5D5934F61A85189500687FB9 /* Utilities */ = { isa = PBXGroup; children = ( - 8803DCED22C2B84B00FBB7CE /* SDLBackgroundTaskManager.h */, - 8803DCEE22C2B84B00FBB7CE /* SDLBackgroundTaskManager.m */, - 97E26DEA1E807AD70074A3C7 /* SDLMutableDataQueue.h */, - 97E26DEB1E807AD70074A3C7 /* SDLMutableDataQueue.m */, E9C32B831AB20B2900F283AF /* @categories */, + 4A8F463F24D9F39D003BDAE4 /* Locked Collections */, 5D5934F91A851A8000687FB9 /* Prioritized Objects */, 5D535DC31B72473800CF7760 /* SDLGlobals.h */, 5D535DC41B72473800CF7760 /* SDLGlobals.m */, 5D61FAD21A84238A00846EE7 /* SDLHexUtility.h */, 5D61FAD31A84238A00846EE7 /* SDLHexUtility.m */, - 883C22C6222ED84D00939C4C /* SDLRPCFunctionNames.h */, - 883C22C7222ED84D00939C4C /* SDLRPCFunctionNames.m */, - 5D61FB0F1A84238A00846EE7 /* SDLRPCParameterNames.h */, - DA0C46AC1DCD35080001F2A8 /* SDLRPCParameterNames.m */, + DA0C46AE1DCD41E30001F2A8 /* SDLMacros.h */, E9C32B8E1AB20BA200F283AF /* SDLTimer.h */, E9C32B8F1AB20BA200F283AF /* SDLTimer.m */, - DA0C46AE1DCD41E30001F2A8 /* SDLMacros.h */, - 10415106241BBB8B00F163CC /* Lock Screen Icon Cache */, + 5DD60D96221C5D7D00A82A4F /* SDLVersion.h */, + 5DD60D97221C5D7D00A82A4F /* SDLVersion.m */, ); name = Utilities; sourceTree = ""; @@ -5367,6 +5392,8 @@ 5D5934F91A851A8000687FB9 /* Prioritized Objects */ = { isa = PBXGroup; children = ( + 97E26DEA1E807AD70074A3C7 /* SDLMutableDataQueue.h */, + 97E26DEB1E807AD70074A3C7 /* SDLMutableDataQueue.m */, 5D61FB101A84238A00846EE7 /* SDLObjectWithPriority.h */, 5D61FB111A84238A00846EE7 /* SDLObjectWithPriority.m */, 5D61FB521A84238B00846EE7 /* SDLPrioritizedObjectCollection.h */, @@ -5484,11 +5511,11 @@ isa = PBXGroup; children = ( 5DA3F3511BC4477B0026F2D0 /* Developer API */, - 5D5934F61A85189500687FB9 /* Utilities */, 5D5934F51A8516C800687FB9 /* Logging */, 5D5934EF1A85160F00687FB9 /* RPCs */, 5D5934EE1A85160900687FB9 /* Protocol */, 5D5934F01A85161A00687FB9 /* Transport */, + 5D5934F61A85189500687FB9 /* Utilities */, 5D61FA201A84237100846EE7 /* SmartDeviceLink.h */, 5D1665A01CF5D5DA00CC4CA1 /* .gitignore */, 5D1665A11CF5D5DA00CC4CA1 /* .travis.yml */, @@ -5565,15 +5592,17 @@ name = Categories; sourceTree = ""; }; - 5D6F7A301BC5B7100070BF37 /* Lock Screen UI */ = { + 5D6F7A301BC5B7100070BF37 /* UI */ = { isa = PBXGroup; children = ( 5D6F7A3D1BC811FC0070BF37 /* SDLAssets.xcassets */, 5D616B481D552F7A00553F6B /* SDLLockScreen.storyboard */, + 880723E923A2CFB4003D0489 /* SDLLockScreenRootViewController.h */, + 880723EA23A2CFB4003D0489 /* SDLLockScreenRootViewController.m */, 5D6F7A331BC5B9B60070BF37 /* SDLLockScreenViewController.h */, 5D6F7A341BC5B9B60070BF37 /* SDLLockScreenViewController.m */, ); - name = "Lock Screen UI"; + name = UI; sourceTree = ""; }; 5D75960A22972F620013207C /* Utilities */ = { @@ -5616,8 +5645,6 @@ 5D76E31F1D39731100647CFA /* Utilities */ = { isa = PBXGroup; children = ( - 880723E923A2CFB4003D0489 /* SDLLockScreenRootViewController.h */, - 880723EA23A2CFB4003D0489 /* SDLLockScreenRootViewController.m */, 5D76E3201D39742300647CFA /* SDLViewControllerPresentable.h */, 5D76E3221D39767000647CFA /* SDLLockScreenPresenter.h */, 5D76E3231D39767000647CFA /* SDLLockScreenPresenter.m */, @@ -5836,9 +5863,9 @@ 5DA3F3511BC4477B0026F2D0 /* Developer API */ = { isa = PBXGroup; children = ( - 5DA3F3561BC4480E0026F2D0 /* Utilities */, - 5DBAE0A61D355EF200CE00BF /* Managers */, 5DBAE0A71D355F0C00CE00BF /* Dispatchers */, + 5DBAE0A61D355EF200CE00BF /* Managers */, + 5DA3F3561BC4480E0026F2D0 /* Utilities */, ); name = "Developer API"; sourceTree = ""; @@ -5846,14 +5873,14 @@ 5DA3F3561BC4480E0026F2D0 /* Utilities */ = { isa = PBXGroup; children = ( - 5DFFB9141BD7C89700DB3F04 /* SDLConnectionManagerType.h */, - EE6CBF872064CAEE00EEE0CA /* SDLStreamingProtocolDelegate.h */, + 5DA3F3571BC448160026F2D0 /* Categories */, 5D616B501D59042B00553F6B /* Errors */, 5DA3F35C1BC4484B0026F2D0 /* Notifications */, - 5DA3F3571BC448160026F2D0 /* Categories */, 5D6008871BE3ED470094A505 /* State Machine */, - 5DD60D96221C5D7D00A82A4F /* SDLVersion.h */, - 5DD60D97221C5D7D00A82A4F /* SDLVersion.m */, + 8803DCED22C2B84B00FBB7CE /* SDLBackgroundTaskManager.h */, + 8803DCEE22C2B84B00FBB7CE /* SDLBackgroundTaskManager.m */, + 5DFFB9141BD7C89700DB3F04 /* SDLConnectionManagerType.h */, + EE6CBF872064CAEE00EEE0CA /* SDLStreamingProtocolDelegate.h */, ); name = Utilities; sourceTree = ""; @@ -6099,8 +6126,9 @@ isa = PBXGroup; children = ( 5D0A7376203F0C8F0001595D /* Configuration */, - 5D6F7A301BC5B7100070BF37 /* Lock Screen UI */, + 10415106241BBB8B00F163CC /* Icon Cache */, 4A9D02C02497F9AA00FBE99B /* Status Manager */, + 5D6F7A301BC5B7100070BF37 /* UI */, 5D76E31F1D39731100647CFA /* Utilities */, 5D4D67B21D30161600468B4A /* SDLLockScreenManager.h */, 5D4D67B31D30161600468B4A /* SDLLockScreenManager.m */, @@ -6724,6 +6752,7 @@ 9FE2470D22D77A5A00F8D2FC /* SDLDeleteWindowResponse.h in Headers */, 9FE2470922D77A3600F8D2FC /* SDLDeleteWindow.h in Headers */, 9FE2471122D77AA400F8D2FC /* SDLCreateWindowResponse.h in Headers */, + 4A8F464C24DA0019003BDAE4 /* SDLLockedMutableArray.h in Headers */, 9FD334E022DC6E7500F62736 /* SDLCreateWindow.h in Headers */, 8BA12B1122DCCE1F00371E82 /* SDLUnpublishAppService.h in Headers */, 8BA12B1522DCEACB00371E82 /* SDLUnpublishAppServiceResponse.h in Headers */, @@ -7251,6 +7280,7 @@ 5D7F87EB1CE3C1A1002DD7C4 /* SDLDeleteFileOperation.h in Headers */, 5D61FCFC1A84238C00846EE7 /* SDLRPCParameterNames.h in Headers */, DA9F7E8F1DCC04C000ACAE48 /* SDLUnsubscribeWayPointsResponse.h in Headers */, + 4A8F464824D9F581003BDAE4 /* SDLLockedMutableDictionary.h in Headers */, 5D61FCFD1A84238C00846EE7 /* SDLObjectWithPriority.h in Headers */, 4A3BA4E1248EB133003E56B8 /* SDLLifecycleSyncPDataHandler.h in Headers */, 88EF8EBD22D8FE5800CB06C2 /* SDLCancelInteractionResponse.h in Headers */, @@ -7401,7 +7431,7 @@ }; 5D61FA1B1A84237100846EE7 = { CreatedOnToolsVersion = 6.1.1; - LastSwiftMigration = 1150; + LastSwiftMigration = 1160; }; 5D61FA251A84237100846EE7 = { CreatedOnToolsVersion = 6.1.1; @@ -7614,6 +7644,7 @@ 5D61FD261A84238C00846EE7 /* SDLPerformAudioPassThru.m in Sources */, 1EAA471E203410BB000FE74B /* SDLAudioControlCapabilities.m in Sources */, 88BCEA932266250B00BB7E70 /* SDLIAPControlSession.m in Sources */, + 4A8F464924D9F581003BDAE4 /* SDLLockedMutableDictionary.m in Sources */, 5D61FC971A84238C00846EE7 /* SDLECallConfirmationStatus.m in Sources */, 8816772D222097C3001FACFF /* SDLNavigationServiceData.m in Sources */, 1E5AD04D1F1F79640029B8AF /* SDLDefrostZone.m in Sources */, @@ -7784,6 +7815,7 @@ 5DD8406320FCD6C10082CE04 /* SDLElectronicParkBrakeStatus.m in Sources */, 88EF8EB822D8E02E00CB06C2 /* SDLCancelInteraction.m in Sources */, 8BBEA6071F324165003EEA26 /* SDLMetadataType.m in Sources */, + 4A8F464D24DA0019003BDAE4 /* SDLLockedMutableArray.m in Sources */, 5DA150C82271FDC20032928D /* SDLSoftButtonTransitionOperation.m in Sources */, 9FE2471222D77AA400F8D2FC /* SDLCreateWindowResponse.m in Sources */, 5D61FDBC1A84238C00846EE7 /* SDLSystemAction.m in Sources */, diff --git a/SmartDeviceLink/SDLLockedDictionary.m b/SmartDeviceLink/SDLLockedDictionary.m new file mode 100644 index 000000000..67f3707ea --- /dev/null +++ b/SmartDeviceLink/SDLLockedDictionary.m @@ -0,0 +1,32 @@ +// +// SDLLockedDictionary.m +// SmartDeviceLink +// +// Created by Joel Fischer on 8/4/20. +// Copyright © 2020 smartdevicelink. All rights reserved. +// + +#import "SDLLockedMutableDictionary.h" + +@interface SDLLockedMutableDictionary () + +@property (strong, nonatomic) NSDictionary *internalDict; + +@end + +@implementation SDLLockedMutableDictionary + +- (instancetype)init { + self = [super init]; + if (!self) { return nil; } + + _internalDict = [[NSDictionary alloc] init]; + + return self; +} + +- (void)setObject:(ObjectType)object forKey:(KeyType )key { + +} + +@end diff --git a/SmartDeviceLink/SDLLockedMutableArray.h b/SmartDeviceLink/SDLLockedMutableArray.h new file mode 100644 index 000000000..5d0d5d01b --- /dev/null +++ b/SmartDeviceLink/SDLLockedMutableArray.h @@ -0,0 +1,35 @@ +// +// SDLLockedMutableArray.h +// SmartDeviceLink +// +// Created by Joel Fischer on 8/4/20. +// Copyright © 2020 smartdevicelink. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface SDLLockedMutableArray : NSObject + +#pragma mark - Initializers +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithSerialQueue:(dispatch_queue_t)queue; + +#pragma mark - Removing +- (void)removeObjectForKey:(KeyType)key; +- (void)removeAllObjects; + +#pragma mark - Getting / Setting +- (void)addObject:(ObjectType)object; +- (void)insertObject:(ObjectType)anObject atIndex:(NSUInteger)index; +- (void)removeLastObject; +- (void)removeObjectAtIndex:(NSUInteger)index; + +#pragma mark Subscripting +- (void)setObject:(ObjectType)object atIndexedSubscript:(KeyType)key; +- (ObjectType)objectAtIndexedSubscript:(KeyType)key; + +@end + +NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/SDLLockedMutableArray.m b/SmartDeviceLink/SDLLockedMutableArray.m new file mode 100644 index 000000000..5b994c688 --- /dev/null +++ b/SmartDeviceLink/SDLLockedMutableArray.m @@ -0,0 +1,13 @@ +// +// SDLLockedMutableArray.m +// SmartDeviceLink +// +// Created by Joel Fischer on 8/4/20. +// Copyright © 2020 smartdevicelink. All rights reserved. +// + +#import "SDLLockedMutableArray.h" + +@implementation SDLLockedMutableArray + +@end diff --git a/SmartDeviceLink/SDLLockedMutableDictionary.h b/SmartDeviceLink/SDLLockedMutableDictionary.h new file mode 100644 index 000000000..f03da5724 --- /dev/null +++ b/SmartDeviceLink/SDLLockedMutableDictionary.h @@ -0,0 +1,33 @@ +// +// SDLLockedDictionary.h +// SmartDeviceLink +// +// Created by Joel Fischer on 8/4/20. +// Copyright © 2020 smartdevicelink. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface SDLLockedMutableDictionary : NSObject + +#pragma mark - Initializers +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithSerialQueue:(dispatch_queue_t)queue; + +#pragma mark - Removing +- (void)removeObjectForKey:(KeyType)key; +- (void)removeAllObjects; + +#pragma mark - Getting / Setting +- (ObjectType)objectForKey:(KeyType)key; +- (void)setObject:(ObjectType)object forKey:(KeyType)key; + +#pragma mark Subscripting +- (void)setObject:(ObjectType)object atIndexedSubscript:(KeyType)key; +- (ObjectType)objectAtIndexedSubscript:(KeyType)key; + +@end + +NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/SDLLockedMutableDictionary.m b/SmartDeviceLink/SDLLockedMutableDictionary.m new file mode 100644 index 000000000..03da8bb7f --- /dev/null +++ b/SmartDeviceLink/SDLLockedMutableDictionary.m @@ -0,0 +1,71 @@ +// +// SDLLockedDictionary.m +// SmartDeviceLink +// +// Created by Joel Fischer on 8/4/20. +// Copyright © 2020 smartdevicelink. All rights reserved. +// + +#import "SDLLockedMutableDictionary.h" + +@interface SDLLockedMutableDictionary () + +@property (assign, nonatomic) dispatch_queue_t internalQueue; +@property (strong, nonatomic) NSMutableDictionary *internalDict; + +@end + +@implementation SDLLockedMutableDictionary + +- (instancetype)initWithSerialQueue:(dispatch_queue_t)queue { + self = [super init]; + if (!self) { return nil; } + + _internalQueue = queue; + _internalDict = [[NSMutableDictionary alloc] init]; + + return self; +} + +#pragma mark - Removing +- (void)removeAllObjects { + __weak typeof(self) weakSelf = self; + dispatch_async(_internalQueue, ^{ + [weakSelf.internalDict removeAllObjects]; + }); +} + +- (void)removeObjectForKey:(id)key { + __weak typeof(self) weakSelf = self; + dispatch_async(_internalQueue, ^{ + [weakSelf.internalDict removeObjectForKey:key]; + }); +} + +#pragma mark - Getting / Setting +- (id)objectForKey:(id)key { + __block id retVal = nil; + dispatch_sync(_internalQueue, ^{ + retVal = _internalDict[key]; + }); + + return retVal; +} + +- (void)setObject:(id)object forKey:(id)key { + __weak typeof(self) weakSelf = self; + dispatch_async(_internalQueue, ^{ + weakSelf.internalDict[key] = object; + }); +} + +#pragma mark Subscripting +- (id)objectAtIndexedSubscript:(id)key { + return [self objectForKey:key]; +} + +- (void)setObject:(id)object atIndexedSubscript:(id)key { + [self setObject:object forKey:key]; +} + +@end From ccfb255457edc27ea26cdcc95e23f5754966861e Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 5 Aug 2020 09:33:44 -0400 Subject: [PATCH 2/9] Adding some documentation * switch to barrier_async calls --- SmartDeviceLink/SDLLockedMutableArray.h | 11 ++++---- SmartDeviceLink/SDLLockedMutableDictionary.h | 27 +++++++++++++++++++- SmartDeviceLink/SDLLockedMutableDictionary.m | 12 ++++----- 3 files changed, 37 insertions(+), 13 deletions(-) diff --git a/SmartDeviceLink/SDLLockedMutableArray.h b/SmartDeviceLink/SDLLockedMutableArray.h index 5d0d5d01b..ea7b626c6 100644 --- a/SmartDeviceLink/SDLLockedMutableArray.h +++ b/SmartDeviceLink/SDLLockedMutableArray.h @@ -14,21 +14,20 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Initializers - (instancetype)init NS_UNAVAILABLE; + - (instancetype)initWithSerialQueue:(dispatch_queue_t)queue; #pragma mark - Removing -- (void)removeObjectForKey:(KeyType)key; -- (void)removeAllObjects; +- (void)removeLastObject; +- (void)removeObjectAtIndex:(NSUInteger)index; #pragma mark - Getting / Setting - (void)addObject:(ObjectType)object; - (void)insertObject:(ObjectType)anObject atIndex:(NSUInteger)index; -- (void)removeLastObject; -- (void)removeObjectAtIndex:(NSUInteger)index; #pragma mark Subscripting -- (void)setObject:(ObjectType)object atIndexedSubscript:(KeyType)key; -- (ObjectType)objectAtIndexedSubscript:(KeyType)key; +- (void)setObject:(ObjectType)object atIndexedSubscript:(NSUInteger)idx; +- (ObjectType)objectAtIndexedSubscript:(NSUInteger)idx; @end diff --git a/SmartDeviceLink/SDLLockedMutableDictionary.h b/SmartDeviceLink/SDLLockedMutableDictionary.h index f03da5724..2aaf7ba50 100644 --- a/SmartDeviceLink/SDLLockedMutableDictionary.h +++ b/SmartDeviceLink/SDLLockedMutableDictionary.h @@ -14,14 +14,39 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Initializers - (instancetype)init NS_UNAVAILABLE; -- (instancetype)initWithSerialQueue:(dispatch_queue_t)queue; + +/// Create a new locked mutable dictionary with a given dispatch queue. All calls will be reader/writer locked on the queue so that only one operation will occur at a time. +/// @param queue The queue to use. It can be either a serial or concurrent queue. +- (instancetype)initWithQueue:(dispatch_queue_t)queue; + #pragma mark - Removing + +/// Removes a given key and its associated value from the dictionary. +/// +/// This will occur asynchronously and will return before the operation completes. +/// @param key The key to search for and remove the associated object. Does nothing if key does not exist. - (void)removeObjectForKey:(KeyType)key; + +/// Empties the dictionary of its entries. +/// +/// This will occur asynchronously and will return before the operation completes. - (void)removeAllObjects; + #pragma mark - Getting / Setting + +/// Returns the value associated with a given key. +/// +/// This will occur synchronously and will not return until the operation completes. +/// @param key The key for which to return the corresponding value. +/// @return The value associated with aKey, or nil if no value is associated with aKey. - (ObjectType)objectForKey:(KeyType)key; + +/// Adds a given key-value pair to the dictionary. +/// +/// @param object The value for aKey. A strong reference to the object is maintained by the dictionary. +/// @param key The key for value. The key is copied (using copyWithZone:; keys must conform to the NSCopying protocol). If aKey already exists in the dictionary, anObject takes its place. - (void)setObject:(ObjectType)object forKey:(KeyType)key; #pragma mark Subscripting diff --git a/SmartDeviceLink/SDLLockedMutableDictionary.m b/SmartDeviceLink/SDLLockedMutableDictionary.m index 03da8bb7f..34dd2d5c9 100644 --- a/SmartDeviceLink/SDLLockedMutableDictionary.m +++ b/SmartDeviceLink/SDLLockedMutableDictionary.m @@ -17,7 +17,7 @@ @interface SDLLockedMutableDictionary () @implementation SDLLockedMutableDictionary -- (instancetype)initWithSerialQueue:(dispatch_queue_t)queue { +- (instancetype)initWithQueue:(dispatch_queue_t)queue { self = [super init]; if (!self) { return nil; } @@ -30,14 +30,14 @@ - (instancetype)initWithSerialQueue:(dispatch_queue_t)queue { #pragma mark - Removing - (void)removeAllObjects { __weak typeof(self) weakSelf = self; - dispatch_async(_internalQueue, ^{ + dispatch_barrier_async(_internalQueue, ^{ [weakSelf.internalDict removeAllObjects]; }); } - (void)removeObjectForKey:(id)key { __weak typeof(self) weakSelf = self; - dispatch_async(_internalQueue, ^{ + dispatch_barrier_async(_internalQueue, ^{ [weakSelf.internalDict removeObjectForKey:key]; }); } @@ -46,7 +46,7 @@ - (void)removeObjectForKey:(id)key { - (id)objectForKey:(id)key { __block id retVal = nil; dispatch_sync(_internalQueue, ^{ - retVal = _internalDict[key]; + retVal = [_internalDict objectForKey:key]; }); return retVal; @@ -54,8 +54,8 @@ - (id)objectForKey:(id)key { - (void)setObject:(id)object forKey:(id)key { __weak typeof(self) weakSelf = self; - dispatch_async(_internalQueue, ^{ - weakSelf.internalDict[key] = object; + dispatch_barrier_async(_internalQueue, ^{ + [weakSelf.internalDict setObject:object forKey:key]; }); } From a8ea635c5663af15255e8b42155629b2d607eedb Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 5 Aug 2020 11:05:28 -0400 Subject: [PATCH 3/9] Clarify some documentation --- SmartDeviceLink/SDLLockedMutableDictionary.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/SmartDeviceLink/SDLLockedMutableDictionary.h b/SmartDeviceLink/SDLLockedMutableDictionary.h index 2aaf7ba50..b9f6d4f13 100644 --- a/SmartDeviceLink/SDLLockedMutableDictionary.h +++ b/SmartDeviceLink/SDLLockedMutableDictionary.h @@ -16,6 +16,7 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)init NS_UNAVAILABLE; /// Create a new locked mutable dictionary with a given dispatch queue. All calls will be reader/writer locked on the queue so that only one operation will occur at a time. +/// If the /// @param queue The queue to use. It can be either a serial or concurrent queue. - (instancetype)initWithQueue:(dispatch_queue_t)queue; @@ -24,7 +25,7 @@ NS_ASSUME_NONNULL_BEGIN /// Removes a given key and its associated value from the dictionary. /// -/// This will occur asynchronously and will return before the operation completes. +/// This will occur asynchronously and may return before the operation completes. /// @param key The key to search for and remove the associated object. Does nothing if key does not exist. - (void)removeObjectForKey:(KeyType)key; @@ -44,7 +45,8 @@ NS_ASSUME_NONNULL_BEGIN - (ObjectType)objectForKey:(KeyType)key; /// Adds a given key-value pair to the dictionary. -/// +/// +/// This will occur asynchronously and may return before the operation completes. /// @param object The value for aKey. A strong reference to the object is maintained by the dictionary. /// @param key The key for value. The key is copied (using copyWithZone:; keys must conform to the NSCopying protocol). If aKey already exists in the dictionary, anObject takes its place. - (void)setObject:(ObjectType)object forKey:(KeyType)key; From 2f82932a9097d31325ea95152a1c8ba080cfe252 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 5 Aug 2020 13:15:00 -0400 Subject: [PATCH 4/9] Adding mutable array implementation * Updating locking on mutable dictionary to not crash if called from its own queue --- SmartDeviceLink/SDLLockedMutableArray.h | 2 +- SmartDeviceLink/SDLLockedMutableArray.m | 87 ++++++++++++++++++++ SmartDeviceLink/SDLLockedMutableDictionary.m | 41 +++++++-- 3 files changed, 120 insertions(+), 10 deletions(-) diff --git a/SmartDeviceLink/SDLLockedMutableArray.h b/SmartDeviceLink/SDLLockedMutableArray.h index ea7b626c6..bcef1e4df 100644 --- a/SmartDeviceLink/SDLLockedMutableArray.h +++ b/SmartDeviceLink/SDLLockedMutableArray.h @@ -18,7 +18,7 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithSerialQueue:(dispatch_queue_t)queue; #pragma mark - Removing -- (void)removeLastObject; +- (void)removeAllObjects; - (void)removeObjectAtIndex:(NSUInteger)index; #pragma mark - Getting / Setting diff --git a/SmartDeviceLink/SDLLockedMutableArray.m b/SmartDeviceLink/SDLLockedMutableArray.m index 5b994c688..acb53bdf3 100644 --- a/SmartDeviceLink/SDLLockedMutableArray.m +++ b/SmartDeviceLink/SDLLockedMutableArray.m @@ -8,6 +8,93 @@ #import "SDLLockedMutableArray.h" +@interface SDLLockedMutableArray () + +@property (assign, nonatomic) dispatch_queue_t internalQueue; +@property (assign, nonatomic) const char *internalQueueID; + +@property (strong, nonatomic) NSMutableArray *internalArray; + +@end + @implementation SDLLockedMutableArray +- (instancetype)initWithSerialQueue:(dispatch_queue_t)queue { + self = [super init]; + if (!self) { return nil; } + + _internalQueue = queue; + _internalQueueID = [[NSUUID alloc] init].UUIDString.UTF8String; + dispatch_queue_set_specific(_internalQueue, _internalQueueID, (void *)_internalQueueID, NULL); + + _internalArray = [[NSMutableArray alloc] init]; + + return self; +} + +#pragma mark - Removing +- (void)removeAllObjects { + __weak typeof(self) weakSelf = self; + [self sdl_runAsyncWithBlock:^{ + [weakSelf.internalArray removeAllObjects]; + }]; +} + +- (void)removeObjectAtIndex:(NSUInteger)index { + __weak typeof(self) weakSelf = self; + [self sdl_runAsyncWithBlock:^{ + [weakSelf.internalArray removeObjectAtIndex:index]; + }]; +} + +#pragma mark - Getting / Setting +- (void)addObject:(id)object { + __weak typeof(self) weakSelf = self; + [self sdl_runAsyncWithBlock:^{ + [weakSelf addObject:object]; + }]; +} + +- (void)insertObject:(id)anObject atIndex:(NSUInteger)index { + __weak typeof(self) weakSelf = self; + [self sdl_runAsyncWithBlock:^{ + [weakSelf insertObject:anObject atIndex:index]; + }]; +} + +#pragma mark Subscripting +- (id)objectAtIndexedSubscript:(NSUInteger)idx { + __block id retVal = nil; + [self sdl_runSyncWithBlock:^{ + retVal = self.internalArray[idx]; + }]; + + return retVal; +} + +- (void)setObject:(id)object atIndexedSubscript:(NSUInteger)idx { + __weak typeof(self) weakSelf = self; + [self sdl_runAsyncWithBlock:^{ + weakSelf[idx] = object; + }]; +} + + +# pragma mark - Utilities +- (void)sdl_runSyncWithBlock:(void (^)(void))block { + if (dispatch_get_specific(_internalQueueID) != NULL) { + block(); + } else { + dispatch_sync(_internalQueue, block); + } +} + +- (void)sdl_runAsyncWithBlock:(void (^)(void))block { + if (dispatch_get_specific(_internalQueueID) != NULL) { + block(); + } else { + dispatch_barrier_async(_internalQueue, block); + } +} + @end diff --git a/SmartDeviceLink/SDLLockedMutableDictionary.m b/SmartDeviceLink/SDLLockedMutableDictionary.m index 34dd2d5c9..cf676daed 100644 --- a/SmartDeviceLink/SDLLockedMutableDictionary.m +++ b/SmartDeviceLink/SDLLockedMutableDictionary.m @@ -11,6 +11,8 @@ @interface SDLLockedMutableDictionary () @property (assign, nonatomic) dispatch_queue_t internalQueue; +@property (assign, nonatomic) const char *internalQueueID; + @property (strong, nonatomic) NSMutableDictionary *internalDict; @end @@ -22,6 +24,9 @@ - (instancetype)initWithQueue:(dispatch_queue_t)queue { if (!self) { return nil; } _internalQueue = queue; + _internalQueueID = [[NSUUID alloc] init].UUIDString.UTF8String; + dispatch_queue_set_specific(_internalQueue, _internalQueueID, (void *)_internalQueueID, NULL); + _internalDict = [[NSMutableDictionary alloc] init]; return self; @@ -30,33 +35,33 @@ - (instancetype)initWithQueue:(dispatch_queue_t)queue { #pragma mark - Removing - (void)removeAllObjects { __weak typeof(self) weakSelf = self; - dispatch_barrier_async(_internalQueue, ^{ + [self sdl_runAsyncWithBlock:^{ [weakSelf.internalDict removeAllObjects]; - }); + }]; } - (void)removeObjectForKey:(id)key { __weak typeof(self) weakSelf = self; - dispatch_barrier_async(_internalQueue, ^{ + [self sdl_runAsyncWithBlock:^{ [weakSelf.internalDict removeObjectForKey:key]; - }); + }]; } #pragma mark - Getting / Setting - (id)objectForKey:(id)key { __block id retVal = nil; - dispatch_sync(_internalQueue, ^{ - retVal = [_internalDict objectForKey:key]; - }); + [self sdl_runSyncWithBlock:^{ + retVal = [self.internalDict objectForKey:key]; + }]; return retVal; } - (void)setObject:(id)object forKey:(id)key { __weak typeof(self) weakSelf = self; - dispatch_barrier_async(_internalQueue, ^{ + [self sdl_runAsyncWithBlock:^{ [weakSelf.internalDict setObject:object forKey:key]; - }); + }]; } #pragma mark Subscripting @@ -68,4 +73,22 @@ - (void)setObject:(id)object atIndexedSubscript:(id)key { [self setObject:object forKey:key]; } + +# pragma mark - Utilities +- (void)sdl_runSyncWithBlock:(void (^)(void))block { + if (dispatch_get_specific(_internalQueueID) != NULL) { + block(); + } else { + dispatch_sync(_internalQueue, block); + } +} + +- (void)sdl_runAsyncWithBlock:(void (^)(void))block { + if (dispatch_get_specific(_internalQueueID) != NULL) { + block(); + } else { + dispatch_barrier_async(_internalQueue, block); + } +} + @end From 813d2c3d63077c43e1a54994ebd59d2d1b2ecd5f Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 5 Aug 2020 14:25:18 -0400 Subject: [PATCH 5/9] Add documentation and `count` to locked array --- SmartDeviceLink/SDLLockedMutableArray.h | 36 ++++++++++++++++++++ SmartDeviceLink/SDLLockedMutableArray.m | 13 +++++++ SmartDeviceLink/SDLLockedMutableDictionary.h | 2 +- 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/SmartDeviceLink/SDLLockedMutableArray.h b/SmartDeviceLink/SDLLockedMutableArray.h index bcef1e4df..0c5b76160 100644 --- a/SmartDeviceLink/SDLLockedMutableArray.h +++ b/SmartDeviceLink/SDLLockedMutableArray.h @@ -15,14 +15,50 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Initializers - (instancetype)init NS_UNAVAILABLE; +/// Create a new locked mutable array with a given dispatch queue. All calls will be reader/writer locked on the queue so that only one operation will occur at a time. +/// +/// @param queue The queue to use. It can be either a serial or concurrent queue. - (instancetype)initWithSerialQueue:(dispatch_queue_t)queue; #pragma mark - Removing +/// Empties the array of its entries. +/// +/// This will occur asynchronously and may return before the operation completes. - (void)removeAllObjects; + +/// Removes the object at `index`. +/// +/// To fill the gap, all elements beyond index are moved by subtracting 1 from their index. +/// +/// This will occur asynchronously and may return before the operation completes. +/// @param index The index from which to remove the object in the array. The value must not exceed the bounds of the array. - (void)removeObjectAtIndex:(NSUInteger)index; #pragma mark - Getting / Setting + +#pragma mark Retrieving information + +/// The number of objects in the array. +/// +/// This will occur synchronously and will not return until the operation completes. +/// @return The number of objects in the array +- (NSUInteger)count; + +#pragma mark Adding Objects +/// Inserts a given object at the end of the array. +/// +/// This will occur asynchronously and may return before the operation completes. +/// @param object The object to add to the end of the array’s content. This value must not be nil. - (void)addObject:(ObjectType)object; + +/// Inserts a given object into the array’s contents at a given index. +/// +/// If index is already occupied, the objects at index and beyond are shifted by adding 1 to their indices to make room. +/// Note that NSArray objects are not like C arrays. That is, even though you specify a size when you create an array, the specified size is regarded as a “hint”; the actual size of the array is still 0. This means that you cannot insert an object at an index greater than the current count of an array. For example, if an array contains two objects, its size is 2, so you can add objects at indices 0, 1, or 2. Index 3 is illegal and out of bounds; if you try to add an object at index 3 (when the size of the array is 2), NSMutableArray raises an exception. +/// +/// This will occur asynchronously and may return before the operation completes. +/// @param anObject The object to add to the array's content. This value must not be nil. +/// @param index The index in the array at which to insert anObject. This value must not be greater than the count of elements in the array. - (void)insertObject:(ObjectType)anObject atIndex:(NSUInteger)index; #pragma mark Subscripting diff --git a/SmartDeviceLink/SDLLockedMutableArray.m b/SmartDeviceLink/SDLLockedMutableArray.m index acb53bdf3..537061c9f 100644 --- a/SmartDeviceLink/SDLLockedMutableArray.m +++ b/SmartDeviceLink/SDLLockedMutableArray.m @@ -19,6 +19,7 @@ @interface SDLLockedMutableArray () @implementation SDLLockedMutableArray +#pragma mark - Initializers - (instancetype)initWithSerialQueue:(dispatch_queue_t)queue { self = [super init]; if (!self) { return nil; } @@ -48,6 +49,18 @@ - (void)removeObjectAtIndex:(NSUInteger)index { } #pragma mark - Getting / Setting + +#pragma mark Retrieving information +- (NSUInteger)count { + __block NSUInteger retVal = 0; + [self sdl_runSyncWithBlock:^{ + retVal = self.internalArray.count; + }]; + + return retVal; +} + +#pragma mark Adding Objects - (void)addObject:(id)object { __weak typeof(self) weakSelf = self; [self sdl_runAsyncWithBlock:^{ diff --git a/SmartDeviceLink/SDLLockedMutableDictionary.h b/SmartDeviceLink/SDLLockedMutableDictionary.h index b9f6d4f13..a2e9841db 100644 --- a/SmartDeviceLink/SDLLockedMutableDictionary.h +++ b/SmartDeviceLink/SDLLockedMutableDictionary.h @@ -16,7 +16,7 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)init NS_UNAVAILABLE; /// Create a new locked mutable dictionary with a given dispatch queue. All calls will be reader/writer locked on the queue so that only one operation will occur at a time. -/// If the +/// /// @param queue The queue to use. It can be either a serial or concurrent queue. - (instancetype)initWithQueue:(dispatch_queue_t)queue; From 27686830e244d5c101491daa6ac6235eb3f2f96c Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Wed, 5 Aug 2020 16:10:12 -0400 Subject: [PATCH 6/9] Add tests and additional methods and fixes --- SmartDeviceLink-iOS.xcodeproj/project.pbxproj | 32 ++++++- SmartDeviceLink/SDLLockedMutableArray.h | 2 +- SmartDeviceLink/SDLLockedMutableArray.m | 8 +- SmartDeviceLink/SDLLockedMutableDictionary.h | 10 +- SmartDeviceLink/SDLLockedMutableDictionary.m | 27 +++++- .../SDLLockedMutableArraySpec.m | 73 +++++++++++++++ .../SDLLockedMutableDictionarySpec.m | 93 +++++++++++++++++++ 7 files changed, 230 insertions(+), 15 deletions(-) create mode 100644 SmartDeviceLinkTests/SDLLockedMutableArraySpec.m create mode 100644 SmartDeviceLinkTests/SDLLockedMutableDictionarySpec.m diff --git a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj index d6af97ae2..8e62c9cbd 100644 --- a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj +++ b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj @@ -443,6 +443,8 @@ 4A8F464924D9F581003BDAE4 /* SDLLockedMutableDictionary.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A8F464724D9F581003BDAE4 /* SDLLockedMutableDictionary.m */; }; 4A8F464C24DA0019003BDAE4 /* SDLLockedMutableArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A8F464A24DA0019003BDAE4 /* SDLLockedMutableArray.h */; }; 4A8F464D24DA0019003BDAE4 /* SDLLockedMutableArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A8F464B24DA0019003BDAE4 /* SDLLockedMutableArray.m */; }; + 4A8F465124DB37CC003BDAE4 /* SDLLockedMutableDictionarySpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A8F465024DB37CC003BDAE4 /* SDLLockedMutableDictionarySpec.m */; }; + 4A8F465324DB439A003BDAE4 /* SDLLockedMutableArraySpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A8F465224DB439A003BDAE4 /* SDLLockedMutableArraySpec.m */; }; 4A99D00E247576B7009B43E6 /* SDLTextField+ScreenManagerExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A99D00C247576B7009B43E6 /* SDLTextField+ScreenManagerExtensions.h */; }; 4A99D00F247576B7009B43E6 /* SDLTextField+ScreenManagerExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A99D00D247576B7009B43E6 /* SDLTextField+ScreenManagerExtensions.m */; }; 4A99D0122475773C009B43E6 /* SDLImageField+ScreenManagerExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A99D0102475773C009B43E6 /* SDLImageField+ScreenManagerExtensions.h */; }; @@ -2191,6 +2193,8 @@ 4A8F464724D9F581003BDAE4 /* SDLLockedMutableDictionary.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLLockedMutableDictionary.m; sourceTree = ""; }; 4A8F464A24DA0019003BDAE4 /* SDLLockedMutableArray.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDLLockedMutableArray.h; sourceTree = ""; }; 4A8F464B24DA0019003BDAE4 /* SDLLockedMutableArray.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLLockedMutableArray.m; sourceTree = ""; }; + 4A8F465024DB37CC003BDAE4 /* SDLLockedMutableDictionarySpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLLockedMutableDictionarySpec.m; sourceTree = ""; }; + 4A8F465224DB439A003BDAE4 /* SDLLockedMutableArraySpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLLockedMutableArraySpec.m; sourceTree = ""; }; 4A99D00C247576B7009B43E6 /* SDLTextField+ScreenManagerExtensions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SDLTextField+ScreenManagerExtensions.h"; sourceTree = ""; }; 4A99D00D247576B7009B43E6 /* SDLTextField+ScreenManagerExtensions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "SDLTextField+ScreenManagerExtensions.m"; sourceTree = ""; }; 4A99D0102475773C009B43E6 /* SDLImageField+ScreenManagerExtensions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SDLImageField+ScreenManagerExtensions.h"; sourceTree = ""; }; @@ -4133,6 +4137,24 @@ name = "Locked Collections"; sourceTree = ""; }; + 4A8F464E24DB3737003BDAE4 /* Utilities */ = { + isa = PBXGroup; + children = ( + 5D61FAD21A84238A00846EE7 /* SDLHexUtility.h */, + 5D61FAD31A84238A00846EE7 /* SDLHexUtility.m */, + ); + name = Utilities; + sourceTree = ""; + }; + 4A8F464F24DB379F003BDAE4 /* Locked Collections */ = { + isa = PBXGroup; + children = ( + 4A8F465024DB37CC003BDAE4 /* SDLLockedMutableDictionarySpec.m */, + 4A8F465224DB439A003BDAE4 /* SDLLockedMutableArraySpec.m */, + ); + name = "Locked Collections"; + sourceTree = ""; + }; 4A9D02BB2497EEB400FBE99B /* Custom RPC Adapters */ = { isa = PBXGroup; children = ( @@ -5301,8 +5323,9 @@ 5D5934F51A8516C800687FB9 /* Logging */ = { isa = PBXGroup; children = ( - 5DD67CC01E68AE66009CD394 /* Modules */, 5DBF063D1E64BDAE00A5CF03 /* Log Targets */, + 5DD67CC01E68AE66009CD394 /* Modules */, + 4A8F464E24DB3737003BDAE4 /* Utilities */, 5DBF06211E64A83F00A5CF03 /* SDLLogManager.h */, 5DBF06221E64A83F00A5CF03 /* SDLLogManager.m */, 5DBF062B1E64A93A00A5CF03 /* SDLLogFilter.h */, @@ -5325,8 +5348,6 @@ 5D5934F91A851A8000687FB9 /* Prioritized Objects */, 5D535DC31B72473800CF7760 /* SDLGlobals.h */, 5D535DC41B72473800CF7760 /* SDLGlobals.m */, - 5D61FAD21A84238A00846EE7 /* SDLHexUtility.h */, - 5D61FAD31A84238A00846EE7 /* SDLHexUtility.m */, DA0C46AE1DCD41E30001F2A8 /* SDLMacros.h */, E9C32B8E1AB20BA200F283AF /* SDLTimer.h */, E9C32B8F1AB20BA200F283AF /* SDLTimer.m */, @@ -6085,8 +6106,9 @@ isa = PBXGroup; children = ( 5DEE55BE1B8509A5004F0D0F /* HTTP Connection */, - 5DB92D2B1AC4A32A00C15BB0 /* Prioritized Objects */, 109566F42429865500E24F66 /* Lock Screen Icon Cache */, + 4A8F464F24DB379F003BDAE4 /* Locked Collections */, + 5DB92D2B1AC4A32A00C15BB0 /* Prioritized Objects */, 5DB92D231AC47B2C00C15BB0 /* SDLHexUtilitySpec.m */, 5DC978251B7A38640012C2F1 /* SDLGlobalsSpec.m */, ); @@ -8371,6 +8393,7 @@ 1EAA47582035AFD9000FE74B /* SDLHMISettingsControlDataSpec.m in Sources */, 162E83161A9BDE8B00906325 /* SDLOnHashChangeSpec.m in Sources */, 8886EB982111F4FA008294A5 /* SDLFileManagerConfigurationSpec.m in Sources */, + 4A8F465324DB439A003BDAE4 /* SDLLockedMutableArraySpec.m in Sources */, 162E82FE1A9BDE8B00906325 /* SDLTBTStateSpec.m in Sources */, 5D5DBF0B1D48E5E600D4F914 /* SDLLockScreenViewControllerSnapshotTests.m in Sources */, 5DB1BCD41D243A8E002FFC37 /* SDLListFilesOperationSpec.m in Sources */, @@ -8565,6 +8588,7 @@ 162E82CF1A9BDE8A00906325 /* SDLBitsPerSampleSpec.m in Sources */, 883581B022D659BE00405C42 /* SDLCloseApplicationResponseSpec.m in Sources */, 162E831E1A9BDE8B00906325 /* SDLOnTBTClientStateSpec.m in Sources */, + 4A8F465124DB37CC003BDAE4 /* SDLLockedMutableDictionarySpec.m in Sources */, 162E83351A9BDE8B00906325 /* SDLReadDIDSpec.m in Sources */, 5DF40B28208FDA9700DD6FDA /* SDLVoiceCommandManagerSpec.m in Sources */, 88B3BFA020DA8FD000943565 /* SDLFuelTypeSpec.m in Sources */, diff --git a/SmartDeviceLink/SDLLockedMutableArray.h b/SmartDeviceLink/SDLLockedMutableArray.h index 0c5b76160..3d0b745f4 100644 --- a/SmartDeviceLink/SDLLockedMutableArray.h +++ b/SmartDeviceLink/SDLLockedMutableArray.h @@ -18,7 +18,7 @@ NS_ASSUME_NONNULL_BEGIN /// Create a new locked mutable array with a given dispatch queue. All calls will be reader/writer locked on the queue so that only one operation will occur at a time. /// /// @param queue The queue to use. It can be either a serial or concurrent queue. -- (instancetype)initWithSerialQueue:(dispatch_queue_t)queue; +- (instancetype)initWithQueue:(dispatch_queue_t)queue; #pragma mark - Removing /// Empties the array of its entries. diff --git a/SmartDeviceLink/SDLLockedMutableArray.m b/SmartDeviceLink/SDLLockedMutableArray.m index 537061c9f..359a03ce5 100644 --- a/SmartDeviceLink/SDLLockedMutableArray.m +++ b/SmartDeviceLink/SDLLockedMutableArray.m @@ -20,7 +20,7 @@ @interface SDLLockedMutableArray () @implementation SDLLockedMutableArray #pragma mark - Initializers -- (instancetype)initWithSerialQueue:(dispatch_queue_t)queue { +- (instancetype)initWithQueue:(dispatch_queue_t)queue { self = [super init]; if (!self) { return nil; } @@ -64,14 +64,14 @@ - (NSUInteger)count { - (void)addObject:(id)object { __weak typeof(self) weakSelf = self; [self sdl_runAsyncWithBlock:^{ - [weakSelf addObject:object]; + [weakSelf.internalArray addObject:object]; }]; } - (void)insertObject:(id)anObject atIndex:(NSUInteger)index { __weak typeof(self) weakSelf = self; [self sdl_runAsyncWithBlock:^{ - [weakSelf insertObject:anObject atIndex:index]; + [weakSelf.internalArray insertObject:anObject atIndex:index]; }]; } @@ -88,7 +88,7 @@ - (id)objectAtIndexedSubscript:(NSUInteger)idx { - (void)setObject:(id)object atIndexedSubscript:(NSUInteger)idx { __weak typeof(self) weakSelf = self; [self sdl_runAsyncWithBlock:^{ - weakSelf[idx] = object; + weakSelf.internalArray[idx] = object; }]; } diff --git a/SmartDeviceLink/SDLLockedMutableDictionary.h b/SmartDeviceLink/SDLLockedMutableDictionary.h index a2e9841db..09297f594 100644 --- a/SmartDeviceLink/SDLLockedMutableDictionary.h +++ b/SmartDeviceLink/SDLLockedMutableDictionary.h @@ -52,8 +52,14 @@ NS_ASSUME_NONNULL_BEGIN - (void)setObject:(ObjectType)object forKey:(KeyType)key; #pragma mark Subscripting -- (void)setObject:(ObjectType)object atIndexedSubscript:(KeyType)key; -- (ObjectType)objectAtIndexedSubscript:(KeyType)key; +- (void)setObject:(nullable ObjectType)object forKeyedSubscript:(KeyType)key; +- (nullable ObjectType)objectForKeyedSubscript:(KeyType)key; + +/// The number of objects in the dictionary. +/// +/// This will occur synchronously and will not return until the operation completes. +/// @return The number of objects in the dictionary. +- (NSUInteger)count; @end diff --git a/SmartDeviceLink/SDLLockedMutableDictionary.m b/SmartDeviceLink/SDLLockedMutableDictionary.m index cf676daed..8cfe3c134 100644 --- a/SmartDeviceLink/SDLLockedMutableDictionary.m +++ b/SmartDeviceLink/SDLLockedMutableDictionary.m @@ -65,12 +65,31 @@ - (void)setObject:(id)object forKey:(id)key { } #pragma mark Subscripting -- (id)objectAtIndexedSubscript:(id)key { - return [self objectForKey:key]; +- (id)objectForKeyedSubscript:(id)key { + __block id retVal = nil; + [self sdl_runSyncWithBlock:^{ + retVal = self.internalDict[key]; + }]; + + return retVal; } -- (void)setObject:(id)object atIndexedSubscript:(id)key { - [self setObject:object forKey:key]; +- (void)setObject:(id)object forKeyedSubscript:(id)key { + __weak typeof(self) weakSelf = self; + [self sdl_runAsyncWithBlock:^{ + weakSelf.internalDict[key] = object; + }]; +} + +#pragma mark Retrieving Information + +- (NSUInteger)count { + __block NSUInteger retVal = 0; + [self sdl_runSyncWithBlock:^{ + retVal = self.internalDict.count; + }]; + + return retVal; } diff --git a/SmartDeviceLinkTests/SDLLockedMutableArraySpec.m b/SmartDeviceLinkTests/SDLLockedMutableArraySpec.m new file mode 100644 index 000000000..97819bdfa --- /dev/null +++ b/SmartDeviceLinkTests/SDLLockedMutableArraySpec.m @@ -0,0 +1,73 @@ +// +// SDLLockedMutableArraySpec.m +// SmartDeviceLinkTests +// +// Created by Joel Fischer on 8/5/20. +// Copyright © 2020 smartdevicelink. All rights reserved. +// + +#import +#import + +#import "SDLLockedMutableArray.h" + +QuickSpecBegin(SDLLockedMutableArraySpec) + +describe(@"a locked dictionary", ^{ + __block dispatch_queue_t testQueue = NULL; + __block SDLLockedMutableArray *testArray = nil; + + __block NSString *objectString = @"testObj"; + + beforeEach(^{ + testQueue = dispatch_queue_create("com.testqueue", DISPATCH_QUEUE_SERIAL); + testArray = [[SDLLockedMutableArray alloc] initWithQueue:testQueue]; + }); + + context(@"on a different queue", ^{ + it(@"should add, retrieve, and remove an object repeatedly", ^{ + for(int i = 0; i < 5000; i++) { + [testArray addObject:objectString]; + (void)testArray[0]; + [testArray removeObjectAtIndex:0]; + expect(testArray.count).to(equal(0)); + } + }); + + it(@"should remove all objects properly", ^{ + for(int i = 0; i < 5000; i++) { + [testArray addObject:objectString]; + } + + expect(testArray.count).to(equal(5000)); + [testArray removeAllObjects]; + expect(testArray.count).to(equal(0)); + }); + + it(@"should insert objects at the front properly", ^{ + for(int i = 0; i < 5000; i++) { + [testArray insertObject:@(i) atIndex:0]; + } + + expect(testArray.count).to(equal(5000)); + expect(testArray[4999]).to(equal(@0)); + expect(testArray[0]).to(equal(@4999)); + }); + }); + + context(@"on the same queue", ^{ + it(@"should add, retrieve, and remove an object repeatedly", ^{ + dispatch_sync(testQueue, ^{ + for(int i = 0; i < 5000; i++) { + [testArray addObject:objectString]; + (void)testArray[0]; + [testArray removeObjectAtIndex:0]; + expect(testArray.count).to(equal(0)); + } + }); + }); + }); +}); + +QuickSpecEnd + diff --git a/SmartDeviceLinkTests/SDLLockedMutableDictionarySpec.m b/SmartDeviceLinkTests/SDLLockedMutableDictionarySpec.m new file mode 100644 index 000000000..3ef756984 --- /dev/null +++ b/SmartDeviceLinkTests/SDLLockedMutableDictionarySpec.m @@ -0,0 +1,93 @@ +// +// SDLLockedMutableDictionarySpec.m +// SmartDeviceLinkTests +// +// Created by Joel Fischer on 8/5/20. +// Copyright © 2020 smartdevicelink. All rights reserved. +// + +#import +#import + +#import "SDLLockedMutableDictionary.h" + +QuickSpecBegin(SDLLockedMutableDictionarySpec) + +describe(@"a locked dictionary", ^{ + __block dispatch_queue_t testQueue = NULL; + __block SDLLockedMutableDictionary *testDict = nil; + + __block NSString *objectString = @"testObj"; + __block NSString *keyString = @"testKey"; + + beforeEach(^{ + testQueue = dispatch_queue_create("com.testqueue", DISPATCH_QUEUE_SERIAL); + testDict = [[SDLLockedMutableDictionary alloc] initWithQueue:testQueue]; + }); + + context(@"on a different queue", ^{ + context(@"not using subscripting", ^{ + it(@"should set, get, and remove a key-value pair repeatedly", ^{ + for(int i = 0; i < 5000; i++) { + [testDict setObject:objectString forKey:keyString]; + [testDict objectForKey:keyString]; + [testDict removeObjectForKey:keyString]; + + expect(testDict.count).to(equal(0)); + } + }); + }); + context(@"using subscripting", ^{ + it(@"should set, get, and remove a key-value pair repeatedly", ^{ + for(int i = 0; i < 5000; i++) { + testDict[keyString] = objectString; + (void)testDict[keyString]; + testDict[keyString] = nil; + + expect(testDict.count).to(equal(0)); + } + }); + }); + + it(@"should remove all objects properly", ^{ + for(int i = 0; i < 5000; i++) { + [testDict setObject:@(i) forKey:@(i)]; + } + + expect(testDict.count).to(equal(5000)); + [testDict removeAllObjects]; + expect(testDict.count).to(equal(0)); + }); + }); + + context(@"on the same queue", ^{ + context(@"not using subscripting", ^{ + it(@"should set, get, and remove a key-value pair repeatedly", ^{ + dispatch_sync(testQueue, ^{ + for(int i = 0; i < 5000; i++) { + [testDict setObject:objectString forKey:keyString]; + [testDict objectForKey:keyString]; + [testDict removeObjectForKey:keyString]; + + expect(testDict.count).to(equal(0)); + } + }); + }); + }); + context(@"using subscripting", ^{ + it(@"should set, get, and remove a key-value pair repeatedly", ^{ + dispatch_sync(testQueue, ^{ + for(int i = 0; i < 5000; i++) { + testDict[keyString] = objectString; + (void)testDict[keyString]; + testDict[keyString] = nil; + + expect(testDict.count).to(equal(0)); + } + }); + }); + }); + }); +}); + +QuickSpecEnd From e4dba026c277f3273ec47df89afd3d6adea86199 Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Thu, 6 Aug 2020 10:40:29 -0400 Subject: [PATCH 7/9] Additional methods to locked dictionary * Add locked map table * Integrate to SystemCapabilityManager * Start integration on response dispatcher --- SmartDeviceLink-iOS.xcodeproj/project.pbxproj | 12 ++++- SmartDeviceLink/SDLLockedMapTable.h | 25 +++++++++++ SmartDeviceLink/SDLLockedMapTable.m | 23 ++++++++++ SmartDeviceLink/SDLLockedMutableDictionary.h | 20 +++++++-- SmartDeviceLink/SDLLockedMutableDictionary.m | 38 +++++++++++----- SmartDeviceLink/SDLResponseDispatcher.h | 4 +- SmartDeviceLink/SDLResponseDispatcher.m | 4 +- SmartDeviceLink/SDLSystemCapabilityManager.m | 44 ++++--------------- 8 files changed, 116 insertions(+), 54 deletions(-) create mode 100644 SmartDeviceLink/SDLLockedMapTable.h create mode 100644 SmartDeviceLink/SDLLockedMapTable.m diff --git a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj index 838b49efb..1b04d9298 100644 --- a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj +++ b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj @@ -448,6 +448,8 @@ 4A8F464D24DA0019003BDAE4 /* SDLLockedMutableArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A8F464B24DA0019003BDAE4 /* SDLLockedMutableArray.m */; }; 4A8F465124DB37CC003BDAE4 /* SDLLockedMutableDictionarySpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A8F465024DB37CC003BDAE4 /* SDLLockedMutableDictionarySpec.m */; }; 4A8F465324DB439A003BDAE4 /* SDLLockedMutableArraySpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A8F465224DB439A003BDAE4 /* SDLLockedMutableArraySpec.m */; }; + 4A8F465624DC408B003BDAE4 /* SDLLockedMapTable.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A8F465424DC408B003BDAE4 /* SDLLockedMapTable.h */; }; + 4A8F465724DC408B003BDAE4 /* SDLLockedMapTable.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A8F465524DC408B003BDAE4 /* SDLLockedMapTable.m */; }; 4A99D00E247576B7009B43E6 /* SDLTextField+ScreenManagerExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A99D00C247576B7009B43E6 /* SDLTextField+ScreenManagerExtensions.h */; }; 4A99D00F247576B7009B43E6 /* SDLTextField+ScreenManagerExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A99D00D247576B7009B43E6 /* SDLTextField+ScreenManagerExtensions.m */; }; 4A99D0122475773C009B43E6 /* SDLImageField+ScreenManagerExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A99D0102475773C009B43E6 /* SDLImageField+ScreenManagerExtensions.h */; }; @@ -2201,6 +2203,8 @@ 4A8F464B24DA0019003BDAE4 /* SDLLockedMutableArray.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLLockedMutableArray.m; sourceTree = ""; }; 4A8F465024DB37CC003BDAE4 /* SDLLockedMutableDictionarySpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLLockedMutableDictionarySpec.m; sourceTree = ""; }; 4A8F465224DB439A003BDAE4 /* SDLLockedMutableArraySpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLLockedMutableArraySpec.m; sourceTree = ""; }; + 4A8F465424DC408B003BDAE4 /* SDLLockedMapTable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDLLockedMapTable.h; sourceTree = ""; }; + 4A8F465524DC408B003BDAE4 /* SDLLockedMapTable.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLLockedMapTable.m; sourceTree = ""; }; 4A99D00C247576B7009B43E6 /* SDLTextField+ScreenManagerExtensions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SDLTextField+ScreenManagerExtensions.h"; sourceTree = ""; }; 4A99D00D247576B7009B43E6 /* SDLTextField+ScreenManagerExtensions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "SDLTextField+ScreenManagerExtensions.m"; sourceTree = ""; }; 4A99D0102475773C009B43E6 /* SDLImageField+ScreenManagerExtensions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SDLImageField+ScreenManagerExtensions.h"; sourceTree = ""; }; @@ -4136,10 +4140,12 @@ 4A8F463F24D9F39D003BDAE4 /* Locked Collections */ = { isa = PBXGroup; children = ( - 4A8F464624D9F581003BDAE4 /* SDLLockedMutableDictionary.h */, - 4A8F464724D9F581003BDAE4 /* SDLLockedMutableDictionary.m */, 4A8F464A24DA0019003BDAE4 /* SDLLockedMutableArray.h */, 4A8F464B24DA0019003BDAE4 /* SDLLockedMutableArray.m */, + 4A8F464624D9F581003BDAE4 /* SDLLockedMutableDictionary.h */, + 4A8F464724D9F581003BDAE4 /* SDLLockedMutableDictionary.m */, + 4A8F465424DC408B003BDAE4 /* SDLLockedMapTable.h */, + 4A8F465524DC408B003BDAE4 /* SDLLockedMapTable.m */, ); name = "Locked Collections"; sourceTree = ""; @@ -7316,6 +7322,7 @@ 5D61FCFD1A84238C00846EE7 /* SDLObjectWithPriority.h in Headers */, 4A3BA4E1248EB133003E56B8 /* SDLLifecycleSyncPDataHandler.h in Headers */, 88EF8EBD22D8FE5800CB06C2 /* SDLCancelInteractionResponse.h in Headers */, + 4A8F465624DC408B003BDAE4 /* SDLLockedMapTable.h in Headers */, 888DBAEF22D528DE002A0AE2 /* SDLCloseApplicationResponse.h in Headers */, DAC5726C1D11B4840004288B /* SDLTouchManagerDelegate.h in Headers */, 5D61FD3F1A84238C00846EE7 /* SDLPrioritizedObjectCollection.h in Headers */, @@ -7919,6 +7926,7 @@ 88EF8EBE22D8FE5800CB06C2 /* SDLCancelInteractionResponse.m in Sources */, 5DE372A21ACB2ED300849FAA /* SDLHMICapabilities.m in Sources */, 5D61FDD41A84238C00846EE7 /* SDLTouchEvent.m in Sources */, + 4A8F465724DC408B003BDAE4 /* SDLLockedMapTable.m in Sources */, 5D61FD881A84238C00846EE7 /* SDLSetGlobalProperties.m in Sources */, 5D61FC7F1A84238C00846EE7 /* SDLDeleteSubMenu.m in Sources */, 5D61FCE91A84238C00846EE7 /* SDLLanguage.m in Sources */, diff --git a/SmartDeviceLink/SDLLockedMapTable.h b/SmartDeviceLink/SDLLockedMapTable.h new file mode 100644 index 000000000..a83e15c8a --- /dev/null +++ b/SmartDeviceLink/SDLLockedMapTable.h @@ -0,0 +1,25 @@ +// +// SDLLockedMapTable.h +// SmartDeviceLink +// +// Created by Joel Fischer on 8/6/20. +// Copyright © 2020 smartdevicelink. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface SDLLockedMapTable : NSObject + +#pragma mark - Initializers +- (instancetype)init NS_UNAVAILABLE; + +/// Create a new locked mutable dictionary with a given dispatch queue. All calls will be reader/writer locked on the queue so that only one operation will occur at a time. +/// +/// @param queue The queue to use. It can be either a serial or concurrent queue. +- (instancetype)initWithQueue:(dispatch_queue_t)queue; + +@end + +NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/SDLLockedMapTable.m b/SmartDeviceLink/SDLLockedMapTable.m new file mode 100644 index 000000000..202ead625 --- /dev/null +++ b/SmartDeviceLink/SDLLockedMapTable.m @@ -0,0 +1,23 @@ +// +// SDLLockedMapTable.m +// SmartDeviceLink +// +// Created by Joel Fischer on 8/6/20. +// Copyright © 2020 smartdevicelink. All rights reserved. +// + +#import "SDLLockedMapTable.h" + +@interface SDLLockedMapTable () + +@property (strong, nonatomic) NSMapTable *internalMapTable; + +@end + +@implementation SDLLockedMapTable + +- (instancetype)initWithQueue:(dispatch_queue_t)queue { + +} + +@end diff --git a/SmartDeviceLink/SDLLockedMutableDictionary.h b/SmartDeviceLink/SDLLockedMutableDictionary.h index 09297f594..2de9b2c8a 100644 --- a/SmartDeviceLink/SDLLockedMutableDictionary.h +++ b/SmartDeviceLink/SDLLockedMutableDictionary.h @@ -51,9 +51,19 @@ NS_ASSUME_NONNULL_BEGIN /// @param key The key for value. The key is copied (using copyWithZone:; keys must conform to the NSCopying protocol). If aKey already exists in the dictionary, anObject takes its place. - (void)setObject:(ObjectType)object forKey:(KeyType)key; -#pragma mark Subscripting -- (void)setObject:(nullable ObjectType)object forKeyedSubscript:(KeyType)key; -- (nullable ObjectType)objectForKeyedSubscript:(KeyType)key; +#pragma mark Retrieving Information + +/// An array of the keys in the dictionary +/// +/// This will occur synchronously and will not return until the operation completes. +/// @return An array of the key objects in the dictionary +- (NSArray> *)allKeys; + +/// An array of the values in the dictionary +/// +/// This will occur synchronously and will not return until the operation completes. +/// @return An array of the value objects in the dictionary +- (NSArray *)allValues; /// The number of objects in the dictionary. /// @@ -61,6 +71,10 @@ NS_ASSUME_NONNULL_BEGIN /// @return The number of objects in the dictionary. - (NSUInteger)count; +#pragma mark Subscripting +- (void)setObject:(nullable ObjectType)object forKeyedSubscript:(KeyType)key; +- (nullable ObjectType)objectForKeyedSubscript:(KeyType)key; + @end NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/SDLLockedMutableDictionary.m b/SmartDeviceLink/SDLLockedMutableDictionary.m index 8cfe3c134..5a57441a2 100644 --- a/SmartDeviceLink/SDLLockedMutableDictionary.m +++ b/SmartDeviceLink/SDLLockedMutableDictionary.m @@ -64,24 +64,25 @@ - (void)setObject:(id)object forKey:(id)key { }]; } -#pragma mark Subscripting -- (id)objectForKeyedSubscript:(id)key { - __block id retVal = nil; +#pragma mark Retrieving Information + +- (NSArray> *)allKeys { + __block NSArray> *retVal = nil; [self sdl_runSyncWithBlock:^{ - retVal = self.internalDict[key]; + retVal = self.internalDict.allKeys; }]; return retVal; } -- (void)setObject:(id)object forKeyedSubscript:(id)key { - __weak typeof(self) weakSelf = self; - [self sdl_runAsyncWithBlock:^{ - weakSelf.internalDict[key] = object; +- (NSArray *)allValues { + __block NSArray *retVal = nil; + [self sdl_runSyncWithBlock:^{ + retVal = self.internalDict.allValues; }]; -} -#pragma mark Retrieving Information + return retVal; +} - (NSUInteger)count { __block NSUInteger retVal = 0; @@ -92,6 +93,23 @@ - (NSUInteger)count { return retVal; } +#pragma mark Subscripting +- (id)objectForKeyedSubscript:(id)key { + __block id retVal = nil; + [self sdl_runSyncWithBlock:^{ + retVal = self.internalDict[key]; + }]; + + return retVal; +} + +- (void)setObject:(id)object forKeyedSubscript:(id)key { + __weak typeof(self) weakSelf = self; + [self sdl_runAsyncWithBlock:^{ + weakSelf.internalDict[key] = object; + }]; +} + # pragma mark - Utilities - (void)sdl_runSyncWithBlock:(void (^)(void))block { diff --git a/SmartDeviceLink/SDLResponseDispatcher.h b/SmartDeviceLink/SDLResponseDispatcher.h index dae3e64a2..8050298ce 100644 --- a/SmartDeviceLink/SDLResponseDispatcher.h +++ b/SmartDeviceLink/SDLResponseDispatcher.h @@ -11,6 +11,8 @@ #import "NSMapTable+Subscripting.h" #import "SDLNotificationConstants.h" +#import "SDLLockedMutableDictionary.h" + typedef NSNumber SDLRPCCorrelationId; typedef NSNumber SDLAddCommandCommandId; @@ -33,7 +35,7 @@ NS_ASSUME_NONNULL_BEGIN /** * Holds a dictionary of RPC request correlation ids and their corresponding RPC request. */ -@property (strong, nonatomic, readonly) NSMutableDictionary *rpcRequestDictionary; +@property (strong, nonatomic, readonly) SDLLockedMutableDictionary *rpcRequestDictionary; /** * Holds a map of command ids and their corresponding blocks. diff --git a/SmartDeviceLink/SDLResponseDispatcher.m b/SmartDeviceLink/SDLResponseDispatcher.m index 7c5956616..44977c5a3 100644 --- a/SmartDeviceLink/SDLResponseDispatcher.m +++ b/SmartDeviceLink/SDLResponseDispatcher.m @@ -43,7 +43,7 @@ @interface SDLResponseDispatcher () @property (copy, nonatomic) dispatch_queue_t readWriteQueue; @property (strong, nonatomic, readwrite) NSMapTable *rpcResponseHandlerMap; -@property (strong, nonatomic, readwrite) NSMutableDictionary *rpcRequestDictionary; +@property (strong, nonatomic, readwrite) SDLLockedMutableDictionary *rpcRequestDictionary; @property (strong, nonatomic, readwrite) NSMapTable *commandHandlerMap; @property (strong, nonatomic, readwrite) NSMapTable *buttonHandlerMap; @property (strong, nonatomic, readwrite) NSMapTable *customButtonHandlerMap; @@ -72,7 +72,7 @@ - (instancetype)initWithNotificationDispatcher:(nullable id)dispatcher { } _rpcResponseHandlerMap = [NSMapTable mapTableWithKeyOptions:NSMapTableCopyIn valueOptions:NSMapTableCopyIn]; - _rpcRequestDictionary = [NSMutableDictionary dictionary]; + _rpcRequestDictionary = [[SDLLockedMutableDictionary alloc] initWithQueue:_readWriteQueue]; _commandHandlerMap = [NSMapTable mapTableWithKeyOptions:NSMapTableCopyIn valueOptions:NSMapTableCopyIn]; _buttonHandlerMap = [NSMapTable mapTableWithKeyOptions:NSMapTableCopyIn valueOptions:NSMapTableCopyIn]; _customButtonHandlerMap = [NSMapTable mapTableWithKeyOptions:NSMapTableCopyIn valueOptions:NSMapTableCopyIn]; diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.m b/SmartDeviceLink/SDLSystemCapabilityManager.m index 4fc1ec6c1..8befacfbc 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.m +++ b/SmartDeviceLink/SDLSystemCapabilityManager.m @@ -22,6 +22,7 @@ #import "SDLGlobals.h" #import "SDLHMICapabilities.h" #import "SDLImageField+ScreenManagerExtensions.h" +#import "SDLLockedMutableDictionary.h" #import "SDLLogMacros.h" #import "SDLNavigationCapability.h" #import "SDLNotificationConstants.h" @@ -71,11 +72,11 @@ @interface SDLSystemCapabilityManager () @property (nullable, strong, nonatomic, readwrite) SDLSeatLocationCapability *seatLocationCapability; @property (nullable, strong, nonatomic, readwrite) SDLDriverDistractionCapability *driverDistractionCapability; -@property (nullable, strong, nonatomic) NSMutableDictionary *appServicesCapabilitiesDictionary; +@property (nullable, strong, nonatomic) SDLLockedMutableDictionary *appServicesCapabilitiesDictionary; @property (assign, nonatomic, readwrite) BOOL supportsSubscriptions; -@property (strong, nonatomic) NSMutableDictionary *> *capabilityObservers; -@property (strong, nonatomic) NSMutableDictionary *> *subscriptionStatus; +@property (strong, nonatomic) SDLLockedMutableDictionary *> *capabilityObservers; +@property (strong, nonatomic) SDLLockedMutableDictionary *> *subscriptionStatus; @property (assign, nonatomic) BOOL shouldConvertDeprecatedDisplayCapabilities; @property (strong, nonatomic) SDLHMILevel currentHMILevel; @@ -102,10 +103,10 @@ - (instancetype)initWithConnectionManager:(id)manager _connectionManager = manager; _shouldConvertDeprecatedDisplayCapabilities = YES; - _appServicesCapabilitiesDictionary = [NSMutableDictionary dictionary]; + _appServicesCapabilitiesDictionary = [[SDLLockedMutableDictionary alloc] initWithQueue:_readWriteQueue]; - _capabilityObservers = [NSMutableDictionary dictionary]; - _subscriptionStatus = [NSMutableDictionary dictionary]; + _capabilityObservers = [[SDLLockedMutableDictionary alloc] initWithQueue:_readWriteQueue]; + _subscriptionStatus = [[SDLLockedMutableDictionary alloc] initWithQueue:_readWriteQueue]; _currentHMILevel = SDLHMILevelNone; @@ -143,7 +144,7 @@ - (void)stop { self.supportsSubscriptions = NO; - self.appServicesCapabilitiesDictionary = [NSMutableDictionary dictionary]; + [self.appServicesCapabilitiesDictionary removeAllObjects]; [self.capabilityObservers removeAllObjects]; [self.subscriptionStatus removeAllObjects]; @@ -817,35 +818,6 @@ - (void)sdl_runSyncOnQueue:(void (^)(void))block { } } -#pragma mark Getters - -- (NSMutableDictionary *> *)capabilityObservers { - __block NSMutableDictionary *> *dict = nil; - [self sdl_runSyncOnQueue:^{ - dict = self->_capabilityObservers; - }]; - - return dict; -} - -- (NSMutableDictionary *> *)subscriptionStatus { - __block NSMutableDictionary *> *dict = nil; - [self sdl_runSyncOnQueue:^{ - dict = self->_subscriptionStatus; - }]; - - return dict; -} - -- (nullable NSMutableDictionary *)appServicesCapabilitiesDictionary { - __block NSMutableDictionary *dict = nil; - [self sdl_runSyncOnQueue:^{ - dict = self->_appServicesCapabilitiesDictionary; - }]; - - return dict; -} - @end NS_ASSUME_NONNULL_END From d022dd94ff1c258b7b9bddea5778f2bfeb09878e Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Thu, 6 Aug 2020 16:06:46 -0400 Subject: [PATCH 8/9] Integrate with response dispatcher * Creating mutable set --- SmartDeviceLink-iOS.xcodeproj/project.pbxproj | 12 +- SmartDeviceLink/SDLLockedMapTable.h | 26 +- SmartDeviceLink/SDLLockedMapTable.m | 102 +++++++- SmartDeviceLink/SDLLockedMutableDictionary.m | 7 +- SmartDeviceLink/SDLLockedMutableSet.h | 43 ++++ SmartDeviceLink/SDLLockedMutableSet.m | 83 +++++++ SmartDeviceLink/SDLResponseDispatcher.h | 10 +- SmartDeviceLink/SDLResponseDispatcher.m | 232 ++++++------------ 8 files changed, 348 insertions(+), 167 deletions(-) create mode 100644 SmartDeviceLink/SDLLockedMutableSet.h create mode 100644 SmartDeviceLink/SDLLockedMutableSet.m diff --git a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj index 1b04d9298..4b13f42d7 100644 --- a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj +++ b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj @@ -450,6 +450,8 @@ 4A8F465324DB439A003BDAE4 /* SDLLockedMutableArraySpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A8F465224DB439A003BDAE4 /* SDLLockedMutableArraySpec.m */; }; 4A8F465624DC408B003BDAE4 /* SDLLockedMapTable.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A8F465424DC408B003BDAE4 /* SDLLockedMapTable.h */; }; 4A8F465724DC408B003BDAE4 /* SDLLockedMapTable.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A8F465524DC408B003BDAE4 /* SDLLockedMapTable.m */; }; + 4A8F465A24DC95C6003BDAE4 /* SDLLockedMutableSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A8F465824DC95C6003BDAE4 /* SDLLockedMutableSet.h */; }; + 4A8F465B24DC95C6003BDAE4 /* SDLLockedMutableSet.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A8F465924DC95C6003BDAE4 /* SDLLockedMutableSet.m */; }; 4A99D00E247576B7009B43E6 /* SDLTextField+ScreenManagerExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A99D00C247576B7009B43E6 /* SDLTextField+ScreenManagerExtensions.h */; }; 4A99D00F247576B7009B43E6 /* SDLTextField+ScreenManagerExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A99D00D247576B7009B43E6 /* SDLTextField+ScreenManagerExtensions.m */; }; 4A99D0122475773C009B43E6 /* SDLImageField+ScreenManagerExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A99D0102475773C009B43E6 /* SDLImageField+ScreenManagerExtensions.h */; }; @@ -2205,6 +2207,8 @@ 4A8F465224DB439A003BDAE4 /* SDLLockedMutableArraySpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLLockedMutableArraySpec.m; sourceTree = ""; }; 4A8F465424DC408B003BDAE4 /* SDLLockedMapTable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDLLockedMapTable.h; sourceTree = ""; }; 4A8F465524DC408B003BDAE4 /* SDLLockedMapTable.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLLockedMapTable.m; sourceTree = ""; }; + 4A8F465824DC95C6003BDAE4 /* SDLLockedMutableSet.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDLLockedMutableSet.h; sourceTree = ""; }; + 4A8F465924DC95C6003BDAE4 /* SDLLockedMutableSet.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLLockedMutableSet.m; sourceTree = ""; }; 4A99D00C247576B7009B43E6 /* SDLTextField+ScreenManagerExtensions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SDLTextField+ScreenManagerExtensions.h"; sourceTree = ""; }; 4A99D00D247576B7009B43E6 /* SDLTextField+ScreenManagerExtensions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "SDLTextField+ScreenManagerExtensions.m"; sourceTree = ""; }; 4A99D0102475773C009B43E6 /* SDLImageField+ScreenManagerExtensions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SDLImageField+ScreenManagerExtensions.h"; sourceTree = ""; }; @@ -4140,12 +4144,14 @@ 4A8F463F24D9F39D003BDAE4 /* Locked Collections */ = { isa = PBXGroup; children = ( + 4A8F465424DC408B003BDAE4 /* SDLLockedMapTable.h */, + 4A8F465524DC408B003BDAE4 /* SDLLockedMapTable.m */, 4A8F464A24DA0019003BDAE4 /* SDLLockedMutableArray.h */, 4A8F464B24DA0019003BDAE4 /* SDLLockedMutableArray.m */, 4A8F464624D9F581003BDAE4 /* SDLLockedMutableDictionary.h */, 4A8F464724D9F581003BDAE4 /* SDLLockedMutableDictionary.m */, - 4A8F465424DC408B003BDAE4 /* SDLLockedMapTable.h */, - 4A8F465524DC408B003BDAE4 /* SDLLockedMapTable.m */, + 4A8F465824DC95C6003BDAE4 /* SDLLockedMutableSet.h */, + 4A8F465924DC95C6003BDAE4 /* SDLLockedMutableSet.m */, ); name = "Locked Collections"; sourceTree = ""; @@ -7232,6 +7238,7 @@ 4A3BA4E724901794003E56B8 /* SDLLifecycleMobileHMIStateHandler.h in Headers */, 5D61FD5D1A84238C00846EE7 /* SDLRegisterAppInterface.h in Headers */, 5D61FC9A1A84238C00846EE7 /* SDLEmergencyEvent.h in Headers */, + 4A8F465A24DC95C6003BDAE4 /* SDLLockedMutableSet.h in Headers */, 5D61FC651A84238C00846EE7 /* SDLCompassDirection.h in Headers */, 5D61FC8E1A84238C00846EE7 /* SDLDimension.h in Headers */, 5D61FD6B1A84238C00846EE7 /* SDLRPCMessageType.h in Headers */, @@ -8015,6 +8022,7 @@ DA4F47961E771AA100FC809E /* SDLEnum.m in Sources */, 5D61FDD61A84238C00846EE7 /* SDLTouchEventCapabilities.m in Sources */, 5DA49CE61F1EA83300E65FC5 /* SDLControlFramePayloadRPCStartService.m in Sources */, + 4A8F465B24DC95C6003BDAE4 /* SDLLockedMutableSet.m in Sources */, 5DA102A51D4122C700C15826 /* NSMutableDictionary+SafeRemove.m in Sources */, 5DD60D99221C5D7D00A82A4F /* SDLVersion.m in Sources */, 5D61FCF11A84238C00846EE7 /* SDLLockScreenStatusManager.m in Sources */, diff --git a/SmartDeviceLink/SDLLockedMapTable.h b/SmartDeviceLink/SDLLockedMapTable.h index a83e15c8a..67bb5a85d 100644 --- a/SmartDeviceLink/SDLLockedMapTable.h +++ b/SmartDeviceLink/SDLLockedMapTable.h @@ -17,8 +17,32 @@ NS_ASSUME_NONNULL_BEGIN /// Create a new locked mutable dictionary with a given dispatch queue. All calls will be reader/writer locked on the queue so that only one operation will occur at a time. /// +/// @param keyOptions A bit field that specifies the options for the keys in the map table. For possible values, see NSMapTableOptions. +/// @param valueOptions A bit field that specifies the options for the values in the map table. For possible values, see NSMapTableOptions. /// @param queue The queue to use. It can be either a serial or concurrent queue. -- (instancetype)initWithQueue:(dispatch_queue_t)queue; +- (instancetype)initWithKeyOptions:(NSPointerFunctionsOptions)keyOptions valueOptions:(NSPointerFunctionsOptions)valueOptions queue:(dispatch_queue_t)queue; + +#pragma mark - Removing +- (void)removeAllObjects; +- (void)removeObjectForKey:(nullable KeyType)aKey; + +#pragma mark - Getting / Setting +- (nullable ObjectType)objectForKey:(nullable KeyType)aKey; +- (void)setObject:(nullable ObjectType)anObject forKey:(nullable KeyType)aKey; + +#pragma mark Retrieving Information + +/// The number of objects in the dictionary. +/// +/// This will occur synchronously and will not return until the operation completes. +/// @return The number of objects in the dictionary. +- (NSUInteger)count; + +- (NSDictionary *)dictionaryRepresentation; + +#pragma mark Subscripting +- (void)setObject:(nullable ObjectType)object forKeyedSubscript:(KeyType)key; +- (nullable ObjectType)objectForKeyedSubscript:(KeyType)key; @end diff --git a/SmartDeviceLink/SDLLockedMapTable.m b/SmartDeviceLink/SDLLockedMapTable.m index 202ead625..cd78786b3 100644 --- a/SmartDeviceLink/SDLLockedMapTable.m +++ b/SmartDeviceLink/SDLLockedMapTable.m @@ -10,14 +10,112 @@ @interface SDLLockedMapTable () +@property (assign, nonatomic) dispatch_queue_t internalQueue; +@property (assign, nonatomic) const char *internalQueueID; + @property (strong, nonatomic) NSMapTable *internalMapTable; @end @implementation SDLLockedMapTable -- (instancetype)initWithQueue:(dispatch_queue_t)queue { - +#pragma mark - Initializers + +- (instancetype)initWithKeyOptions:(NSPointerFunctionsOptions)keyOptions valueOptions:(NSPointerFunctionsOptions)valueOptions queue:(dispatch_queue_t)queue { + self = [super init]; + if (!self) { return nil; } + + _internalQueue = queue; + _internalQueueID = [[NSUUID alloc] init].UUIDString.UTF8String; + dispatch_queue_set_specific(_internalQueue, _internalQueueID, (void *)_internalQueueID, NULL); + + _internalMapTable = [NSMapTable mapTableWithKeyOptions:keyOptions valueOptions:valueOptions]; + + return self; +} + +#pragma mark - Removing + +- (void)removeAllObjects { + __weak typeof(self) weakSelf = self; + [self sdl_runAsyncWithBlock:^{ + [weakSelf.internalMapTable removeAllObjects]; + }]; +} + +- (void)removeObjectForKey:(id)key { + __weak typeof(self) weakSelf = self; + [self sdl_runAsyncWithBlock:^{ + if ([weakSelf objectForKey:key] != nil) { + [weakSelf.internalMapTable removeObjectForKey:key]; + } + }]; +} + +#pragma mark - Getting / Setting + +- (id)objectForKey:(id)key { + __block id retVal = nil; + [self sdl_runSyncWithBlock:^{ + retVal = [self.internalMapTable objectForKey:key]; + }]; + + return retVal; +} + +- (void)setObject:(id)object forKey:(id)key { + __weak typeof(self) weakSelf = self; + [self sdl_runAsyncWithBlock:^{ + [weakSelf.internalMapTable setObject:object forKey:key]; + }]; +} + +#pragma mark Subscripting + +- (id)objectForKeyedSubscript:(id)key { + return [self objectForKey:key]; +} + +- (void)setObject:(id)object forKeyedSubscript:(id)key { + [self setObject:object forKeyedSubscript:key]; +} + +#pragma mark Retrieving Information + +- (NSUInteger)count { + __block NSUInteger retVal = 0; + [self sdl_runSyncWithBlock:^{ + retVal = self.internalMapTable.count; + }]; + + return retVal; +} + +- (NSDictionary *)dictionaryRepresentation { + __block NSDictionary *retVal = nil; + [self sdl_runSyncWithBlock:^{ + retVal = self.internalMapTable.dictionaryRepresentation; + }]; + + return retVal; +} + +# pragma mark - Utilities + +- (void)sdl_runSyncWithBlock:(void (^)(void))block { + if (dispatch_get_specific(_internalQueueID) != NULL) { + block(); + } else { + dispatch_sync(_internalQueue, block); + } +} + +- (void)sdl_runAsyncWithBlock:(void (^)(void))block { + if (dispatch_get_specific(_internalQueueID) != NULL) { + block(); + } else { + dispatch_barrier_async(_internalQueue, block); + } } @end diff --git a/SmartDeviceLink/SDLLockedMutableDictionary.m b/SmartDeviceLink/SDLLockedMutableDictionary.m index 5a57441a2..0e85a6f9c 100644 --- a/SmartDeviceLink/SDLLockedMutableDictionary.m +++ b/SmartDeviceLink/SDLLockedMutableDictionary.m @@ -43,7 +43,9 @@ - (void)removeAllObjects { - (void)removeObjectForKey:(id)key { __weak typeof(self) weakSelf = self; [self sdl_runAsyncWithBlock:^{ - [weakSelf.internalDict removeObjectForKey:key]; + if ([weakSelf objectForKey:key] != nil) { + [weakSelf.internalDict removeObjectForKey:key]; + } }]; } @@ -93,7 +95,9 @@ - (NSUInteger)count { return retVal; } + #pragma mark Subscripting + - (id)objectForKeyedSubscript:(id)key { __block id retVal = nil; [self sdl_runSyncWithBlock:^{ @@ -112,6 +116,7 @@ - (void)setObject:(id)object forKeyedSubscript:(id)key { # pragma mark - Utilities + - (void)sdl_runSyncWithBlock:(void (^)(void))block { if (dispatch_get_specific(_internalQueueID) != NULL) { block(); diff --git a/SmartDeviceLink/SDLLockedMutableSet.h b/SmartDeviceLink/SDLLockedMutableSet.h new file mode 100644 index 000000000..f2160ab7b --- /dev/null +++ b/SmartDeviceLink/SDLLockedMutableSet.h @@ -0,0 +1,43 @@ +// +// SDLLockedMutableSet.h +// SmartDeviceLink +// +// Created by Joel Fischer on 8/6/20. +// Copyright © 2020 smartdevicelink. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface SDLLockedMutableSet : NSObject + +/// Create a new locked mutable array with a given dispatch queue. All calls will be reader/writer locked on the queue so that only one operation will occur at a time. +/// +/// @param queue The queue to use. It can be either a serial or concurrent queue. +- (instancetype)initWithQueue:(dispatch_queue_t)queue; + +#pragma mark - Getting / Setting + +#pragma mark Retrieving information + +/// The number of objects in the array. +/// +/// This will occur synchronously and will not return until the operation completes. +/// @return The number of objects in the array +- (NSUInteger)count; + +#pragma mark Adding Objects +/// Inserts a given object at the end of the array. +/// +/// This will occur asynchronously and may return before the operation completes. +/// @param object The object to add to the end of the array’s content. This value must not be nil. +- (void)addObject:(ObjectType)object; + +#pragma mark Subscripting +- (void)setObject:(ObjectType)object atIndexedSubscript:(NSUInteger)idx; +- (ObjectType)objectAtIndexedSubscript:(NSUInteger)idx; + +@end + +NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/SDLLockedMutableSet.m b/SmartDeviceLink/SDLLockedMutableSet.m new file mode 100644 index 000000000..28c1dead0 --- /dev/null +++ b/SmartDeviceLink/SDLLockedMutableSet.m @@ -0,0 +1,83 @@ +// +// SDLLockedMutableSet.m +// SmartDeviceLink +// +// Created by Joel Fischer on 8/6/20. +// Copyright © 2020 smartdevicelink. All rights reserved. +// + +#import "SDLLockedMutableSet.h" + +@interface SDLLockedMutableSet () + +@property (assign, nonatomic) dispatch_queue_t internalQueue; +@property (assign, nonatomic) const char *internalQueueID; + +@property (strong, nonatomic) NSMutableSet *internalSet; + +@end + +@implementation SDLLockedMutableSet + +#pragma mark - Initializers + +- (instancetype)initWithQueue:(dispatch_queue_t)queue { + self = [super init]; + if (!self) { return nil; } + + _internalQueue = queue; + _internalQueueID = [[NSUUID alloc] init].UUIDString.UTF8String; + dispatch_queue_set_specific(_internalQueue, _internalQueueID, (void *)_internalQueueID, NULL); + + _internalSet = [[NSMutableSet alloc] init]; + + return self; +} + + +#pragma mark - Getting / Setting + +#pragma mark Retrieving information +- (NSUInteger)count { + __block NSUInteger retVal = 0; + [self sdl_runSyncWithBlock:^{ + retVal = self.internalSet.count; + }]; + + return retVal; +} + +#pragma mark Adding Objects +- (void)addObject:(id)object { + __weak typeof(self) weakSelf = self; + [self sdl_runAsyncWithBlock:^{ + [weakSelf.internalSet addObject:object]; + }]; +} + +#pragma mark Modifications +- (void)unionSet:(NSMutableSet *)otherSet { + __weak typeof(self) weakSelf = self; + [self sdl_runAsyncWithBlock:^{ + [weakSelf.internalSet unionSet:otherSet]; + }]; +} + +# pragma mark - Utilities +- (void)sdl_runSyncWithBlock:(void (^)(void))block { + if (dispatch_get_specific(_internalQueueID) != NULL) { + block(); + } else { + dispatch_sync(_internalQueue, block); + } +} + +- (void)sdl_runAsyncWithBlock:(void (^)(void))block { + if (dispatch_get_specific(_internalQueueID) != NULL) { + block(); + } else { + dispatch_barrier_async(_internalQueue, block); + } +} + +@end diff --git a/SmartDeviceLink/SDLResponseDispatcher.h b/SmartDeviceLink/SDLResponseDispatcher.h index 8050298ce..13cb9f8c0 100644 --- a/SmartDeviceLink/SDLResponseDispatcher.h +++ b/SmartDeviceLink/SDLResponseDispatcher.h @@ -10,8 +10,8 @@ #import "NSMapTable+Subscripting.h" #import "SDLNotificationConstants.h" - #import "SDLLockedMutableDictionary.h" +#import "SDLLockedMapTable.h" typedef NSNumber SDLRPCCorrelationId; @@ -30,7 +30,7 @@ NS_ASSUME_NONNULL_BEGIN /** * Holds a map of RPC request correlation ids and corresponding blocks. */ -@property (strong, nonatomic, readonly) NSMapTable *rpcResponseHandlerMap; +@property (strong, nonatomic, readonly) SDLLockedMapTable *rpcResponseHandlerMap; /** * Holds a dictionary of RPC request correlation ids and their corresponding RPC request. @@ -40,17 +40,17 @@ NS_ASSUME_NONNULL_BEGIN /** * Holds a map of command ids and their corresponding blocks. */ -@property (strong, nonatomic, readonly) NSMapTable *commandHandlerMap; +@property (strong, nonatomic, readonly) SDLLockedMapTable *commandHandlerMap; /** * Holds a map of button names and their corresponding blocks. */ -@property (strong, nonatomic, readonly) NSMapTable *buttonHandlerMap; +@property (strong, nonatomic, readonly) SDLLockedMapTable *buttonHandlerMap; /** * Holds a map of soft button ids and their corresponding blocks. */ -@property (strong, nonatomic, readonly) NSMapTable *customButtonHandlerMap; +@property (strong, nonatomic, readonly) SDLLockedMapTable *customButtonHandlerMap; /** * Holds an audio pass thru block. diff --git a/SmartDeviceLink/SDLResponseDispatcher.m b/SmartDeviceLink/SDLResponseDispatcher.m index 44977c5a3..8dc7a2516 100644 --- a/SmartDeviceLink/SDLResponseDispatcher.m +++ b/SmartDeviceLink/SDLResponseDispatcher.m @@ -42,11 +42,11 @@ @interface SDLResponseDispatcher () @property (copy, nonatomic) dispatch_queue_t readWriteQueue; -@property (strong, nonatomic, readwrite) NSMapTable *rpcResponseHandlerMap; +@property (strong, nonatomic, readwrite) SDLLockedMapTable *rpcResponseHandlerMap; @property (strong, nonatomic, readwrite) SDLLockedMutableDictionary *rpcRequestDictionary; -@property (strong, nonatomic, readwrite) NSMapTable *commandHandlerMap; -@property (strong, nonatomic, readwrite) NSMapTable *buttonHandlerMap; -@property (strong, nonatomic, readwrite) NSMapTable *customButtonHandlerMap; +@property (strong, nonatomic, readwrite) SDLLockedMapTable *commandHandlerMap; +@property (strong, nonatomic, readwrite) SDLLockedMapTable *buttonHandlerMap; +@property (strong, nonatomic, readwrite) SDLLockedMapTable *customButtonHandlerMap; @property (strong, nonatomic, readwrite, nullable) SDLAudioPassThruHandler audioPassThruHandler; @end @@ -71,11 +71,11 @@ - (instancetype)initWithNotificationDispatcher:(nullable id)dispatcher { _readWriteQueue = [SDLGlobals sharedGlobals].sdlProcessingQueue; } - _rpcResponseHandlerMap = [NSMapTable mapTableWithKeyOptions:NSMapTableCopyIn valueOptions:NSMapTableCopyIn]; + _rpcResponseHandlerMap = [[SDLLockedMapTable alloc] initWithKeyOptions:NSMapTableCopyIn valueOptions:NSMapTableCopyIn queue:_readWriteQueue]; _rpcRequestDictionary = [[SDLLockedMutableDictionary alloc] initWithQueue:_readWriteQueue]; - _commandHandlerMap = [NSMapTable mapTableWithKeyOptions:NSMapTableCopyIn valueOptions:NSMapTableCopyIn]; - _buttonHandlerMap = [NSMapTable mapTableWithKeyOptions:NSMapTableCopyIn valueOptions:NSMapTableCopyIn]; - _customButtonHandlerMap = [NSMapTable mapTableWithKeyOptions:NSMapTableCopyIn valueOptions:NSMapTableCopyIn]; + _commandHandlerMap = [[SDLLockedMapTable alloc] initWithKeyOptions:NSMapTableCopyIn valueOptions:NSMapTableCopyIn queue:_readWriteQueue]; + _buttonHandlerMap = [[SDLLockedMapTable alloc] initWithKeyOptions:NSMapTableCopyIn valueOptions:NSMapTableCopyIn queue:_readWriteQueue]; + _customButtonHandlerMap = [[SDLLockedMapTable alloc] initWithKeyOptions:NSMapTableCopyIn valueOptions:NSMapTableCopyIn queue:_readWriteQueue]; // Responses for (SDLNotificationName responseName in [SDLNotificationConstants allResponseNames]) { @@ -100,7 +100,6 @@ - (instancetype)initWithNotificationDispatcher:(nullable id)dispatcher { #pragma mark - Storage - (void)storeRequest:(SDLRPCRequest *)request handler:(nullable SDLResponseHandler)handler { - __weak typeof(self) weakself = self; NSNumber *correlationId = request.correlationID; // Check for RPCs that require an extra handler @@ -109,11 +108,9 @@ - (void)storeRequest:(SDLRPCRequest *)request handler:(nullable SDLResponseHandl if (addCommand.cmdID == nil) { @throw [NSException sdl_missingIdException]; } + if (addCommand.handler != nil) { - [self sdl_runAsyncOnQueue:^{ - __strong typeof(weakself) strongself = weakself; - strongself->_commandHandlerMap[addCommand.cmdID] = addCommand.handler; - }]; + self.commandHandlerMap[addCommand.cmdID] = addCommand.handler; } } else if ([request isKindOfClass:[SDLSubscribeButton class]]) { // Convert SDLButtonName to NSString, since it doesn't conform to @@ -123,10 +120,7 @@ - (void)storeRequest:(SDLRPCRequest *)request handler:(nullable SDLResponseHandl @throw [NSException sdl_missingIdException]; } if (subscribeButton.handler != nil) { - [self sdl_runAsyncOnQueue:^{ - __strong typeof(weakself) strongself = weakself; - strongself->_buttonHandlerMap[buttonName] = subscribeButton.handler; - }]; + self.buttonHandlerMap[buttonName] = subscribeButton.handler; } } else if ([request isKindOfClass:[SDLAlert class]]) { SDLAlert *alert = (SDLAlert *)request; @@ -139,49 +133,36 @@ - (void)storeRequest:(SDLRPCRequest *)request handler:(nullable SDLResponseHandl [self sdl_addToCustomButtonHandlerMap:show.softButtons]; } else if ([request isKindOfClass:[SDLPerformAudioPassThru class]]) { SDLPerformAudioPassThru *performAudioPassThru = (SDLPerformAudioPassThru *)request; - - [self sdl_runAsyncOnQueue:^{ - __strong typeof(weakself) strongself = weakself; - strongself->_audioPassThruHandler = performAudioPassThru.audioDataHandler; - }]; + self.audioPassThruHandler = performAudioPassThru.audioDataHandler; } - // Always store the request, it's needed in some cases whether or not there was a handler (e.g. DeleteCommand). - [self sdl_runAsyncOnQueue:^{ - __strong typeof(weakself) strongself = weakself; - - strongself->_rpcRequestDictionary[correlationId] = request; - if (handler != nil) { - strongself->_rpcResponseHandlerMap[correlationId] = handler; - } - }]; + self.rpcRequestDictionary[correlationId] = request; + if (handler != nil) { + self.rpcResponseHandlerMap[correlationId] = handler; + } } - (void)clear { - __weak typeof(self) weakself = self; - __block NSArray *handlers = nil; __block NSArray *requests = nil; - [self sdl_runSyncOnQueue:^{ - __strong typeof(weakself) strongself = weakself; - NSMutableArray *handlerArray = [NSMutableArray array]; - NSMutableArray *requestArray = [NSMutableArray array]; - - // When we get disconnected we have to delete all existing responseHandlers as they are not valid anymore - for (SDLRPCCorrelationId *correlationID in strongself->_rpcResponseHandlerMap.dictionaryRepresentation) { - SDLResponseHandler responseHandler = strongself->_rpcResponseHandlerMap[correlationID]; - SDLRPCRequest *request = strongself->_rpcRequestDictionary[correlationID]; - - if (responseHandler != NULL) { - [handlerArray addObject:responseHandler]; - [requestArray addObject:request]; - } + NSMutableArray *handlerArray = [NSMutableArray array]; + NSMutableArray *requestArray = [NSMutableArray array]; + + // When we get disconnected we have to delete all existing responseHandlers as they are not valid anymore + for (SDLRPCCorrelationId *correlationID in self.rpcResponseHandlerMap.dictionaryRepresentation.allKeys) { + SDLResponseHandler responseHandler = self.rpcResponseHandlerMap[correlationID]; + SDLRPCRequest *request = self.rpcRequestDictionary[correlationID]; + + if (responseHandler != NULL) { + [handlerArray addObject:responseHandler]; + [requestArray addObject:request]; } + } - handlers = [handlerArray copy]; - requests = [requestArray copy]; - }]; + handlers = [handlerArray copy]; + requests = [requestArray copy]; + // When we have our list, first we'll call all of them to make sure they get a callback for (NSUInteger i = 0; i < handlers.count; i++) { SDLResponseHandler responseHandler = handlers[i]; SDLRPCRequest *request = requests[i]; @@ -189,30 +170,22 @@ - (void)clear { responseHandler(request, nil, [NSError sdl_lifecycle_notConnectedError]); } - [self sdl_runAsyncOnQueue:^{ - __strong typeof(weakself) strongself = weakself; - - [strongself->_rpcRequestDictionary removeAllObjects]; - [strongself->_rpcResponseHandlerMap removeAllObjects]; - [strongself->_commandHandlerMap removeAllObjects]; - [strongself->_buttonHandlerMap removeAllObjects]; - [strongself->_customButtonHandlerMap removeAllObjects]; - strongself->_audioPassThruHandler = nil; - }]; + [self.rpcRequestDictionary removeAllObjects]; + [self.rpcResponseHandlerMap removeAllObjects]; + [self.commandHandlerMap removeAllObjects]; + [self.buttonHandlerMap removeAllObjects]; + [self.customButtonHandlerMap removeAllObjects]; + self.audioPassThruHandler = nil; } - (void)sdl_addToCustomButtonHandlerMap:(NSArray *)softButtons { - __weak typeof(self) weakself = self; for (SDLSoftButton *sb in softButtons) { if (sb.softButtonID == nil) { @throw [NSException sdl_missingIdException]; } if (sb.handler != nil) { - [self sdl_runAsyncOnQueue:^{ - __strong typeof(weakself) strongself = weakself; - strongself->_customButtonHandlerMap[sb.softButtonID] = sb.handler; - }]; + self.customButtonHandlerMap[sb.softButtonID] = sb.handler; } } } @@ -222,7 +195,6 @@ - (void)sdl_addToCustomButtonHandlerMap:(NSArray *)softButtons // Called by notifications - (void)sdl_runHandlersForResponse:(SDLRPCResponseNotification *)notification { - __weak typeof(self) weakself = self; if (![notification isResponseKindOfClass:[SDLRPCResponse class]]) { return; } @@ -234,45 +206,38 @@ - (void)sdl_runHandlersForResponse:(SDLRPCResponseNotification *)notification { error = [NSError sdl_lifecycle_rpcErrorWithDescription:response.resultCode andReason:response.info]; } - __block SDLResponseHandler handler = nil; - __block SDLRPCRequest *request = nil; - [self sdl_runSyncOnQueue:^{ - handler = self->_rpcResponseHandlerMap[response.correlationID]; - request = self->_rpcRequestDictionary[response.correlationID]; - }]; + __block SDLResponseHandler handler = self.rpcResponseHandlerMap[response.correlationID]; + __block SDLRPCRequest *request = self.rpcRequestDictionary[response.correlationID]; // Find the appropriate request completion handler, remove the request and response handler - [self sdl_runAsyncOnQueue:^{ - __strong typeof(weakself) strongself = weakself; - [strongself->_rpcRequestDictionary safeRemoveObjectForKey:response.correlationID]; - [strongself->_rpcResponseHandlerMap safeRemoveObjectForKey:response.correlationID]; - - // If we errored on the response, the delete / unsubscribe was unsuccessful - if (error == nil) { - // If it's a DeleteCommand, UnsubscribeButton, or PerformAudioPassThru we need to remove handlers for the corresponding RPCs - if ([response isKindOfClass:[SDLDeleteCommandResponse class]]) { - SDLDeleteCommand *deleteCommandRequest = (SDLDeleteCommand *)request; - NSNumber *deleteCommandId = deleteCommandRequest.cmdID; - [strongself->_commandHandlerMap safeRemoveObjectForKey:deleteCommandId]; - } else if ([response isKindOfClass:[SDLUnsubscribeButtonResponse class]]) { - SDLUnsubscribeButton *unsubscribeButtonRequest = (SDLUnsubscribeButton *)request; - SDLButtonName unsubscribeButtonName = unsubscribeButtonRequest.buttonName; - [strongself->_buttonHandlerMap safeRemoveObjectForKey:unsubscribeButtonName]; - } else if ([response isKindOfClass:[SDLPerformAudioPassThruResponse class]]) { - strongself->_audioPassThruHandler = nil; - } + [self.rpcRequestDictionary removeObjectForKey:response.correlationID]; + [self.rpcResponseHandlerMap removeObjectForKey:response.correlationID]; + + // If we errored on the response, the delete / unsubscribe was unsuccessful + if (error == nil) { + // If it's a DeleteCommand, UnsubscribeButton, or PerformAudioPassThru we need to remove handlers for the corresponding RPCs + if ([response isKindOfClass:[SDLDeleteCommandResponse class]]) { + SDLDeleteCommand *deleteCommandRequest = (SDLDeleteCommand *)request; + NSNumber *deleteCommandId = deleteCommandRequest.cmdID; + [self.commandHandlerMap removeObjectForKey:deleteCommandId]; + } else if ([response isKindOfClass:[SDLUnsubscribeButtonResponse class]]) { + SDLUnsubscribeButton *unsubscribeButtonRequest = (SDLUnsubscribeButton *)request; + SDLButtonName unsubscribeButtonName = unsubscribeButtonRequest.buttonName; + [self.buttonHandlerMap removeObjectForKey:unsubscribeButtonName]; + } else if ([response isKindOfClass:[SDLPerformAudioPassThruResponse class]]) { + self.audioPassThruHandler = nil; } + } - dispatch_async([SDLGlobals sharedGlobals].sdlProcessingQueue, ^{ - // Run the response handler - if (handler) { - if (!response.success.boolValue) { - SDLLogW(@"Request failed: %@, response: %@, error: %@", request, response, error); - } - handler(request, response, error); + dispatch_async([SDLGlobals sharedGlobals].sdlProcessingQueue, ^{ + // Run the response handler + if (handler) { + if (!response.success.boolValue) { + SDLLogW(@"Request failed: %@, response: %@, error: %@", request, response, error); } - }); - }]; + handler(request, response, error); + } + }); } #pragma mark Command @@ -322,9 +287,9 @@ - (void)sdl_runHandlerForButton:(SDLRPCNotificationNotification *)notification { handler(rpcNotification, nil); } } - + #pragma mark Audio Pass Thru - + - (void)sdl_runHandlerForAudioPassThru:(SDLRPCNotificationNotification *)notification { SDLOnAudioPassThru *onAudioPassThruNotification = notification.notification; @@ -344,70 +309,25 @@ - (void)sdl_runSyncOnQueue:(void (^)(void))block { } } -- (void)sdl_runAsyncOnQueue:(void (^)(void))block { - if (dispatch_get_specific(SDLProcessingQueueName) != nil) { - block(); - } else { - dispatch_async(self.readWriteQueue, block); - } -} - #pragma mark Getters -- (NSMapTable *)rpcResponseHandlerMap { - __block NSMapTable *map = nil; - [self sdl_runSyncOnQueue:^{ - map = self->_rpcResponseHandlerMap; - }]; - - return map; -} - -- (NSMutableDictionary *)rpcRequestDictionary { - __block NSMutableDictionary *dict = nil; - [self sdl_runSyncOnQueue:^{ - dict = self->_rpcRequestDictionary; - }]; - - return dict; -} - -- (NSMapTable *)commandHandlerMap { - __block NSMapTable *map = nil; - [self sdl_runSyncOnQueue:^{ - map = self->_commandHandlerMap; - }]; - - return map; -} - -- (NSMapTable *)buttonHandlerMap { - __block NSMapTable *map = nil; - [self sdl_runSyncOnQueue:^{ - map = self->_buttonHandlerMap; - }]; - - return map; -} - -- (NSMapTable *)customButtonHandlerMap { - __block NSMapTable *map = nil; - [self sdl_runSyncOnQueue:^{ - map = self->_customButtonHandlerMap; - }]; - - return map; -} - - (nullable SDLAudioPassThruHandler)audioPassThruHandler { + __weak typeof(self) weakSelf = self; __block SDLAudioPassThruHandler audioPassThruHandler = nil; [self sdl_runSyncOnQueue:^{ - audioPassThruHandler = self->_audioPassThruHandler; + audioPassThruHandler = weakSelf.audioPassThruHandler; }]; return audioPassThruHandler; } +- (void)setAudioPassThruHandler:(nullable SDLAudioPassThruHandler)audioPassThruHandler { + __weak typeof(self) weakSelf = self; + dispatch_async(_readWriteQueue, ^{ + weakSelf.audioPassThruHandler = audioPassThruHandler; + }); +} + @end NS_ASSUME_NONNULL_END From 11950f8c54958fc366be7cdd10b250fe5ffea33d Mon Sep 17 00:00:00 2001 From: Joel Fischer Date: Fri, 7 Aug 2020 12:03:35 -0400 Subject: [PATCH 9/9] Fixes and updates --- SmartDeviceLink-iOS.xcodeproj/project.pbxproj | 8 ++ SmartDeviceLink/SDLChoiceSetManager.m | 54 +++------ SmartDeviceLink/SDLLockedMapTable.h | 18 ++- SmartDeviceLink/SDLLockedMapTable.m | 4 +- SmartDeviceLink/SDLLockedMutableArray.h | 4 +- SmartDeviceLink/SDLLockedMutableSet.h | 33 ++++-- SmartDeviceLink/SDLLockedMutableSet.m | 34 +++++- SmartDeviceLink/SDLSubscribeButtonManager.m | 41 ++----- SmartDeviceLink/SDLSystemCapabilityManager.m | 109 ++++++++---------- .../DevAPISpecs/SDLChoiceSetManagerSpec.m | 44 ++++--- SmartDeviceLinkTests/SDLLockedMapTableSpec.m | 103 +++++++++++++++++ .../UtilitiesSpecs/SDLLockedMutableSetSpec.m | 63 ++++++++++ 12 files changed, 351 insertions(+), 164 deletions(-) create mode 100644 SmartDeviceLinkTests/SDLLockedMapTableSpec.m create mode 100644 SmartDeviceLinkTests/UtilitiesSpecs/SDLLockedMutableSetSpec.m diff --git a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj index 4b13f42d7..920ea1eef 100644 --- a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj +++ b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj @@ -452,6 +452,8 @@ 4A8F465724DC408B003BDAE4 /* SDLLockedMapTable.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A8F465524DC408B003BDAE4 /* SDLLockedMapTable.m */; }; 4A8F465A24DC95C6003BDAE4 /* SDLLockedMutableSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A8F465824DC95C6003BDAE4 /* SDLLockedMutableSet.h */; }; 4A8F465B24DC95C6003BDAE4 /* SDLLockedMutableSet.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A8F465924DC95C6003BDAE4 /* SDLLockedMutableSet.m */; }; + 4A8F466524DD92B3003BDAE4 /* SDLLockedMutableSetSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A8F466424DD92B3003BDAE4 /* SDLLockedMutableSetSpec.m */; }; + 4A8F466724DD92C7003BDAE4 /* SDLLockedMapTableSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A8F466624DD92C7003BDAE4 /* SDLLockedMapTableSpec.m */; }; 4A99D00E247576B7009B43E6 /* SDLTextField+ScreenManagerExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A99D00C247576B7009B43E6 /* SDLTextField+ScreenManagerExtensions.h */; }; 4A99D00F247576B7009B43E6 /* SDLTextField+ScreenManagerExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A99D00D247576B7009B43E6 /* SDLTextField+ScreenManagerExtensions.m */; }; 4A99D0122475773C009B43E6 /* SDLImageField+ScreenManagerExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A99D0102475773C009B43E6 /* SDLImageField+ScreenManagerExtensions.h */; }; @@ -2209,6 +2211,8 @@ 4A8F465524DC408B003BDAE4 /* SDLLockedMapTable.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLLockedMapTable.m; sourceTree = ""; }; 4A8F465824DC95C6003BDAE4 /* SDLLockedMutableSet.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDLLockedMutableSet.h; sourceTree = ""; }; 4A8F465924DC95C6003BDAE4 /* SDLLockedMutableSet.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLLockedMutableSet.m; sourceTree = ""; }; + 4A8F466424DD92B3003BDAE4 /* SDLLockedMutableSetSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLLockedMutableSetSpec.m; path = UtilitiesSpecs/SDLLockedMutableSetSpec.m; sourceTree = ""; }; + 4A8F466624DD92C7003BDAE4 /* SDLLockedMapTableSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLLockedMapTableSpec.m; sourceTree = ""; }; 4A99D00C247576B7009B43E6 /* SDLTextField+ScreenManagerExtensions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SDLTextField+ScreenManagerExtensions.h"; sourceTree = ""; }; 4A99D00D247576B7009B43E6 /* SDLTextField+ScreenManagerExtensions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "SDLTextField+ScreenManagerExtensions.m"; sourceTree = ""; }; 4A99D0102475773C009B43E6 /* SDLImageField+ScreenManagerExtensions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SDLImageField+ScreenManagerExtensions.h"; sourceTree = ""; }; @@ -4168,8 +4172,10 @@ 4A8F464F24DB379F003BDAE4 /* Locked Collections */ = { isa = PBXGroup; children = ( + 4A8F466624DD92C7003BDAE4 /* SDLLockedMapTableSpec.m */, 4A8F465024DB37CC003BDAE4 /* SDLLockedMutableDictionarySpec.m */, 4A8F465224DB439A003BDAE4 /* SDLLockedMutableArraySpec.m */, + 4A8F466424DD92B3003BDAE4 /* SDLLockedMutableSetSpec.m */, ); name = "Locked Collections"; sourceTree = ""; @@ -8452,6 +8458,7 @@ 162E834C1A9BDE8B00906325 /* SDLAlertResponseSpec.m in Sources */, 1680B11B1A9CD7AD00DBD79E /* SDLFunctionIDSpec.m in Sources */, 5DB1BCDA1D243D85002FFC37 /* SDLStateMachineSpec.m in Sources */, + 4A8F466524DD92B3003BDAE4 /* SDLLockedMutableSetSpec.m in Sources */, 7538765622DCAF5400FE8484 /* SDLShowAppMenuSpec.m in Sources */, 8831FA4B2202402B00B8FFB7 /* SDLAppServicesCapabilitiesSpec.m in Sources */, 5D4346731E6F617D00B639C6 /* TestLogTarget.m in Sources */, @@ -8495,6 +8502,7 @@ 162E835F1A9BDE8B00906325 /* SDLResetGlobalPropertiesResponseSpec.m in Sources */, 1EE8C4381F347C7300FDC2CF /* SDLRadioBandSpec.m in Sources */, 162E835E1A9BDE8B00906325 /* SDLRegisterAppInterfaceResponseSpec.m in Sources */, + 4A8F466724DD92C7003BDAE4 /* SDLLockedMapTableSpec.m in Sources */, 162E835A1A9BDE8B00906325 /* SDLPerformAudioPassThruResponseSpec.m in Sources */, 883468FA234BBBE1003F51E5 /* SDLStreamingVideoScaleManagerSpec.m in Sources */, 162E83501A9BDE8B00906325 /* SDLDeleteFileResponseSpec.m in Sources */, diff --git a/SmartDeviceLink/SDLChoiceSetManager.m b/SmartDeviceLink/SDLChoiceSetManager.m index a8d3468b7..d7e6a5b23 100644 --- a/SmartDeviceLink/SDLChoiceSetManager.m +++ b/SmartDeviceLink/SDLChoiceSetManager.m @@ -23,6 +23,7 @@ #import "SDLGlobals.h" #import "SDLHMILevel.h" #import "SDLKeyboardProperties.h" +#import "SDLLockedMutableSet.h" #import "SDLLogMacros.h" #import "SDLOnHMIStatus.h" #import "SDLPerformInteraction.h" @@ -69,9 +70,9 @@ @interface SDLChoiceSetManager() @property (copy, nonatomic, nullable) SDLHMILevel currentHMILevel; @property (copy, nonatomic, nullable) SDLWindowCapability *currentWindowCapability; -@property (strong, nonatomic) NSMutableSet *preloadedMutableChoices; +@property (strong, nonatomic) SDLLockedMutableSet *preloadedMutableChoices; @property (strong, nonatomic, readonly) NSSet *pendingPreloadChoices; -@property (strong, nonatomic) NSMutableSet *pendingMutablePreloadChoices; +@property (strong, nonatomic) SDLLockedMutableSet *pendingMutablePreloadChoices; @property (strong, nonatomic, nullable) SDLChoiceSet *pendingPresentationSet; @property (strong, nonatomic, nullable) SDLAsynchronousOperation *pendingPresentOperation; @@ -104,8 +105,8 @@ - (instancetype)initWithConnectionManager:(id)connecti _readWriteQueue = [SDLGlobals sharedGlobals].sdlProcessingQueue; } - _preloadedMutableChoices = [NSMutableSet set]; - _pendingMutablePreloadChoices = [NSMutableSet set]; + _preloadedMutableChoices = [[SDLLockedMutableSet alloc] initWithQueue:_readWriteQueue]; + _pendingMutablePreloadChoices = [[SDLLockedMutableSet alloc] initWithQueue:_readWriteQueue]; _nextChoiceId = ChoiceCellIdMin; _nextCancelId = ChoiceCellCancelIdMin; @@ -181,8 +182,8 @@ - (void)didEnterStateShutdown { [self.transactionQueue cancelAllOperations]; self.transactionQueue = [self sdl_newTransactionQueue]; - _preloadedMutableChoices = [NSMutableSet set]; - _pendingMutablePreloadChoices = [NSMutableSet set]; + [_preloadedMutableChoices removeAllObjects]; + [_pendingMutablePreloadChoices removeAllObjects]; _pendingPresentationSet = nil; _vrOptional = YES; @@ -230,11 +231,8 @@ - (void)preloadChoices:(NSArray *)choices withCompletionHandler } NSMutableSet *choicesToUpload = [[self sdl_choicesToBeUploadedWithArray:choices] mutableCopy]; - - [self sdl_runSyncOnQueue:^{ - [choicesToUpload minusSet:self.preloadedMutableChoices]; - [choicesToUpload minusSet:self.pendingMutablePreloadChoices]; - }]; + [choicesToUpload minusSet:self.preloadedMutableChoices.immutableSet]; + [choicesToUpload minusSet:self.pendingMutablePreloadChoices.immutableSet]; if (choicesToUpload.count == 0) { SDLLogD(@"All choices already preloaded. No need to perform a preload"); @@ -248,9 +246,7 @@ - (void)preloadChoices:(NSArray *)choices withCompletionHandler [self sdl_updateIdsOnChoices:choicesToUpload]; // Add the preload cells to the pending preloads - [self sdl_runSyncOnQueue:^{ - [self.pendingMutablePreloadChoices unionSet:choicesToUpload]; - }]; + [self.pendingMutablePreloadChoices unionSet:choicesToUpload]; // Upload pending preloads // For backward compatibility with Gen38Inch display type head units @@ -275,11 +271,8 @@ - (void)preloadChoices:(NSArray *)choices withCompletionHandler return; } - [strongSelf sdl_runSyncOnQueue:^{ - __strong typeof(weakSelf) strongSelf = weakSelf; - [strongSelf.preloadedMutableChoices unionSet:choicesToUpload]; - [strongSelf.pendingMutablePreloadChoices minusSet:choicesToUpload]; - }]; + [strongSelf.preloadedMutableChoices unionSet:choicesToUpload]; + [strongSelf.pendingMutablePreloadChoices minusSet:choicesToUpload]; }; [self.transactionQueue addOperation:preloadOp]; } @@ -307,9 +300,7 @@ - (void)deleteChoices:(NSArray *)choices { } // Remove the cells from pending and delete choices - [self sdl_runSyncOnQueue:^{ - [self.pendingMutablePreloadChoices minusSet:cellsToBeRemovedFromPending]; - }]; + [self.pendingMutablePreloadChoices minusSet:cellsToBeRemovedFromPending]; for (SDLAsynchronousOperation *op in self.transactionQueue.operations) { if (![op isMemberOfClass:[SDLPreloadChoicesOperation class]]) { continue; } @@ -341,10 +332,7 @@ - (void)deleteChoices:(NSArray *)choices { return; } - [strongSelf sdl_runSyncOnQueue:^{ - __strong typeof(weakSelf) strongSelf = weakSelf; - [strongSelf.preloadedMutableChoices minusSet:cellsToBeDeleted]; - }]; + [strongSelf.preloadedMutableChoices minusSet:cellsToBeDeleted]; }; [self.transactionQueue addOperation:deleteOp]; } @@ -521,21 +509,11 @@ - (SDLKeyboardProperties *)sdl_defaultKeyboardConfiguration { #pragma mark - Getters - (NSSet *)preloadedChoices { - __block NSSet *set = nil; - [self sdl_runSyncOnQueue:^{ - set = [self->_preloadedMutableChoices copy]; - }]; - - return set; + return self.preloadedMutableChoices.immutableSet; } - (NSSet *)pendingPreloadChoices { - __block NSSet *set = nil; - [self sdl_runSyncOnQueue:^{ - set = [self->_pendingMutablePreloadChoices copy]; - }]; - - return set; + return self.pendingMutablePreloadChoices.immutableSet; } - (UInt16)nextChoiceId { diff --git a/SmartDeviceLink/SDLLockedMapTable.h b/SmartDeviceLink/SDLLockedMapTable.h index 67bb5a85d..dd49634b3 100644 --- a/SmartDeviceLink/SDLLockedMapTable.h +++ b/SmartDeviceLink/SDLLockedMapTable.h @@ -23,11 +23,24 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithKeyOptions:(NSPointerFunctionsOptions)keyOptions valueOptions:(NSPointerFunctionsOptions)valueOptions queue:(dispatch_queue_t)queue; #pragma mark - Removing + +/// Empties the map table of its entries. - (void)removeAllObjects; + +/// Removes a given key and its associated value from the map table. +/// @param aKey The key to remove. - (void)removeObjectForKey:(nullable KeyType)aKey; #pragma mark - Getting / Setting + +/// Returns a the value associated with a given key. +/// @param aKey The key for which to return the corresponding value. +/// @return The value associated with aKey, or nil if no value is associated with aKey. - (nullable ObjectType)objectForKey:(nullable KeyType)aKey; + +/// Adds a given key-value pair to the map table. +/// @param anObject The value for aKey +/// @param aKey The key for anObject - (void)setObject:(nullable ObjectType)anObject forKey:(nullable KeyType)aKey; #pragma mark Retrieving Information @@ -38,11 +51,12 @@ NS_ASSUME_NONNULL_BEGIN /// @return The number of objects in the dictionary. - (NSUInteger)count; +/// Returns a dictionary representation of the map table. - (NSDictionary *)dictionaryRepresentation; #pragma mark Subscripting -- (void)setObject:(nullable ObjectType)object forKeyedSubscript:(KeyType)key; -- (nullable ObjectType)objectForKeyedSubscript:(KeyType)key; +- (void)setObject:(nullable __kindof ObjectType)object forKeyedSubscript:(KeyType)key; +- (nullable __kindof ObjectType)objectForKeyedSubscript:(KeyType)key; @end diff --git a/SmartDeviceLink/SDLLockedMapTable.m b/SmartDeviceLink/SDLLockedMapTable.m index cd78786b3..8288a0e13 100644 --- a/SmartDeviceLink/SDLLockedMapTable.m +++ b/SmartDeviceLink/SDLLockedMapTable.m @@ -73,11 +73,11 @@ - (void)setObject:(id)object forKey:(id)key { #pragma mark Subscripting - (id)objectForKeyedSubscript:(id)key { - return [self objectForKey:key]; + return [self.internalMapTable objectForKey:key]; } - (void)setObject:(id)object forKeyedSubscript:(id)key { - [self setObject:object forKeyedSubscript:key]; + [self.internalMapTable setObject:object forKey:key]; } #pragma mark Retrieving Information diff --git a/SmartDeviceLink/SDLLockedMutableArray.h b/SmartDeviceLink/SDLLockedMutableArray.h index 3d0b745f4..c4d9129a9 100644 --- a/SmartDeviceLink/SDLLockedMutableArray.h +++ b/SmartDeviceLink/SDLLockedMutableArray.h @@ -62,8 +62,8 @@ NS_ASSUME_NONNULL_BEGIN - (void)insertObject:(ObjectType)anObject atIndex:(NSUInteger)index; #pragma mark Subscripting -- (void)setObject:(ObjectType)object atIndexedSubscript:(NSUInteger)idx; -- (ObjectType)objectAtIndexedSubscript:(NSUInteger)idx; +- (void)setObject:(__kindof ObjectType)object atIndexedSubscript:(NSUInteger)idx; +- (__kindof ObjectType)objectAtIndexedSubscript:(NSUInteger)idx; @end diff --git a/SmartDeviceLink/SDLLockedMutableSet.h b/SmartDeviceLink/SDLLockedMutableSet.h index f2160ab7b..757db4a3b 100644 --- a/SmartDeviceLink/SDLLockedMutableSet.h +++ b/SmartDeviceLink/SDLLockedMutableSet.h @@ -19,25 +19,42 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Getting / Setting +#pragma mark - Removing + +/// Empties the set of its entries. +/// +/// This will occur asynchronously and may return before the operation completes. +- (void)removeAllObjects; + #pragma mark Retrieving information -/// The number of objects in the array. +/// The number of objects in the set. /// /// This will occur synchronously and will not return until the operation completes. -/// @return The number of objects in the array +/// @return The number of objects in the set - (NSUInteger)count; +/// Retrieve an immutable set version of this mutable set at the current point. +- (NSSet *)immutableSet; + +#pragma mark Modifications + +/// Adds each object in another given set to the receiving set, if not present. +/// @param otherSet The set of objects to add to the receiving set. +- (void)unionSet:(NSSet *)otherSet; + +/// Removes each object in another given set from the receiving set, if present. +/// @param otherSet The set of objects to remove from the receiving set. +- (void)minusSet:(NSSet *)otherSet; + #pragma mark Adding Objects -/// Inserts a given object at the end of the array. + +/// Inserts a given object into the set. /// /// This will occur asynchronously and may return before the operation completes. -/// @param object The object to add to the end of the array’s content. This value must not be nil. +/// @param object The object to add to set. This value must not be nil. - (void)addObject:(ObjectType)object; -#pragma mark Subscripting -- (void)setObject:(ObjectType)object atIndexedSubscript:(NSUInteger)idx; -- (ObjectType)objectAtIndexedSubscript:(NSUInteger)idx; - @end NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/SDLLockedMutableSet.m b/SmartDeviceLink/SDLLockedMutableSet.m index 28c1dead0..7bc4b4e6c 100644 --- a/SmartDeviceLink/SDLLockedMutableSet.m +++ b/SmartDeviceLink/SDLLockedMutableSet.m @@ -37,6 +37,14 @@ - (instancetype)initWithQueue:(dispatch_queue_t)queue { #pragma mark - Getting / Setting +#pragma mark - Removing +- (void)removeAllObjects { + __weak typeof(self) weakSelf = self; + [self sdl_runAsyncWithBlock:^{ + [weakSelf.internalSet removeAllObjects]; + }]; +} + #pragma mark Retrieving information - (NSUInteger)count { __block NSUInteger retVal = 0; @@ -47,12 +55,13 @@ - (NSUInteger)count { return retVal; } -#pragma mark Adding Objects -- (void)addObject:(id)object { - __weak typeof(self) weakSelf = self; - [self sdl_runAsyncWithBlock:^{ - [weakSelf.internalSet addObject:object]; +- (NSSet *)immutableSet { + __block NSSet *retVal = nil; + [self sdl_runSyncWithBlock:^{ + retVal = [self.internalSet copy]; }]; + + return retVal; } #pragma mark Modifications @@ -63,6 +72,21 @@ - (void)unionSet:(NSMutableSet *)otherSet { }]; } +- (void)minusSet:(NSSet *)otherSet { + __weak typeof(self) weakSelf = self; + [self sdl_runAsyncWithBlock:^{ + [weakSelf.internalSet minusSet:otherSet]; + }]; +} + +#pragma mark Adding Objects +- (void)addObject:(id)object { + __weak typeof(self) weakSelf = self; + [self sdl_runAsyncWithBlock:^{ + [weakSelf.internalSet addObject:object]; + }]; +} + # pragma mark - Utilities - (void)sdl_runSyncWithBlock:(void (^)(void))block { if (dispatch_get_specific(_internalQueueID) != NULL) { diff --git a/SmartDeviceLink/SDLSubscribeButtonManager.m b/SmartDeviceLink/SDLSubscribeButtonManager.m index 505740227..1f8e7fc50 100644 --- a/SmartDeviceLink/SDLSubscribeButtonManager.m +++ b/SmartDeviceLink/SDLSubscribeButtonManager.m @@ -11,6 +11,7 @@ #import "SDLError.h" #import "SDLGlobals.h" #import "SDLHMIPermissions.h" +#import "SDLLockedMutableDictionary.h" #import "SDLLogMacros.h" #import "SDLOnButtonPress.h" #import "SDLOnHMIStatus.h" @@ -30,7 +31,7 @@ @interface SDLSubscribeButtonManager() @property (weak, nonatomic) id connectionManager; -@property (strong, nonatomic) NSMutableDictionary *> *subscribeButtonObservers; +@property (strong, nonatomic) SDLLockedMutableDictionary *> *subscribeButtonObservers; @property (copy, nonatomic) dispatch_queue_t readWriteQueue; @end @@ -50,7 +51,7 @@ - (instancetype)initWithConnectionManager:(id)connecti } _connectionManager = connectionManager; - _subscribeButtonObservers = [NSMutableDictionary dictionary]; + _subscribeButtonObservers = [[SDLLockedMutableDictionary alloc] initWithQueue:_readWriteQueue]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_handleButtonEvent:) name:SDLDidReceiveButtonEventNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_handleButtonPress:) name:SDLDidReceiveButtonPressNotification object:nil]; @@ -61,11 +62,7 @@ - (instancetype)initWithConnectionManager:(id)connecti - (void)start { } - (void)stop { - __weak typeof(self) weakSelf = self; - [self sdl_runSyncOnQueue:^{ - __strong typeof(weakSelf) strongself = weakSelf; - [strongself->_subscribeButtonObservers removeAllObjects]; - }]; + [self.subscribeButtonObservers removeAllObjects]; } #pragma mark - Subscribe @@ -129,17 +126,13 @@ - (BOOL)sdl_isSubscribedObserver:(id)observer forButtonName:(SDLButton /// @param subscribedObserver The observer /// @param buttonName The name of the button - (void)sdl_addSubscribedObserver:(SDLSubscribeButtonObserver *)subscribedObserver forButtonName:(SDLButtonName)buttonName { - __weak typeof(self) weakSelf = self; - [self sdl_runSyncOnQueue:^{ - __strong typeof(weakSelf) strongSelf = weakSelf; - if (strongSelf.subscribeButtonObservers[buttonName] == nil) { - SDLLogV(@"Adding first subscriber for button: %@", buttonName); - strongSelf.subscribeButtonObservers[buttonName] = [NSMutableArray arrayWithObject:subscribedObserver]; - } else { - SDLLogV(@"Adding another subscriber for button: %@", buttonName); - [strongSelf.subscribeButtonObservers[buttonName] addObject:subscribedObserver]; - } - }]; + if (self.subscribeButtonObservers[buttonName] == nil) { + SDLLogV(@"Adding first subscriber for button: %@", buttonName); + self.subscribeButtonObservers[buttonName] = [NSMutableArray arrayWithObject:subscribedObserver]; + } else { + SDLLogV(@"Adding another subscriber for button: %@", buttonName); + [self.subscribeButtonObservers[buttonName] addObject:subscribedObserver]; + } } #pragma mark Send Subscribe Request @@ -204,6 +197,7 @@ - (void)sdl_removeSubscribedObserver:(id)observer forButtonName:(SDLBu for (NSUInteger i = 0; i < strongSelf.subscribeButtonObservers[buttonName].count; i++) { SDLSubscribeButtonObserver *subscribedObserver = (SDLSubscribeButtonObserver *)strongSelf.subscribeButtonObservers[buttonName][i]; if (subscribedObserver.observer != observer) { continue; } + // Okay to mutate because we will break immediately afterward [strongSelf.subscribeButtonObservers[buttonName] removeObjectAtIndex:i]; break; @@ -295,17 +289,6 @@ - (void)sdl_runSyncOnQueue:(void (^)(void))block { } } -#pragma mark - Getters - -- (NSMutableDictionary *> *)subscribeButtonObservers { - __block NSMutableDictionary *> *dict = nil; - [self sdl_runSyncOnQueue:^{ - dict = self->_subscribeButtonObservers; - }]; - - return dict; -} - @end NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.m b/SmartDeviceLink/SDLSystemCapabilityManager.m index 8befacfbc..bce392caa 100644 --- a/SmartDeviceLink/SDLSystemCapabilityManager.m +++ b/SmartDeviceLink/SDLSystemCapabilityManager.m @@ -122,35 +122,33 @@ - (void)start { } */ - (void)stop { SDLLogD(@"System Capability manager stopped"); - [self sdl_runSyncOnQueue:^{ - self.displayCapabilities = nil; - self.displays = nil; - self.hmiCapabilities = nil; - self.softButtonCapabilities = nil; - self.buttonCapabilities = nil; - self.presetBankCapabilities = nil; - self.hmiZoneCapabilities = nil; - self.speechCapabilities = nil; - self.prerecordedSpeechCapabilities = nil; - self.vrCapability = NO; - self.audioPassThruCapabilities = nil; - self.pcmStreamCapability = nil; - self.navigationCapability = nil; - self.phoneCapability = nil; - self.videoStreamingCapability = nil; - self.remoteControlCapability = nil; - self.seatLocationCapability = nil; - self.driverDistractionCapability = nil; - - self.supportsSubscriptions = NO; - - [self.appServicesCapabilitiesDictionary removeAllObjects]; - [self.capabilityObservers removeAllObjects]; - [self.subscriptionStatus removeAllObjects]; - - self.currentHMILevel = SDLHMILevelNone; - self.shouldConvertDeprecatedDisplayCapabilities = YES; - }]; + self.displayCapabilities = nil; + self.displays = nil; + self.hmiCapabilities = nil; + self.softButtonCapabilities = nil; + self.buttonCapabilities = nil; + self.presetBankCapabilities = nil; + self.hmiZoneCapabilities = nil; + self.speechCapabilities = nil; + self.prerecordedSpeechCapabilities = nil; + self.vrCapability = NO; + self.audioPassThruCapabilities = nil; + self.pcmStreamCapability = nil; + self.navigationCapability = nil; + self.phoneCapability = nil; + self.videoStreamingCapability = nil; + self.remoteControlCapability = nil; + self.seatLocationCapability = nil; + self.driverDistractionCapability = nil; + + self.supportsSubscriptions = NO; + + [self.appServicesCapabilitiesDictionary removeAllObjects]; + [self.capabilityObservers removeAllObjects]; + [self.subscriptionStatus removeAllObjects]; + + self.currentHMILevel = SDLHMILevelNone; + self.shouldConvertDeprecatedDisplayCapabilities = YES; } #pragma mark - Getters @@ -396,9 +394,7 @@ - (void)sdl_sendGetSystemCapabilityWithType:(SDLSystemCapabilityType)type subscr SDLLogD(@"GetSystemCapability response succeeded, type: %@, response: %@", type, getSystemCapabilityResponse); if (![weakself.subscriptionStatus[type] isEqualToNumber:subscribe] && weakself.supportsSubscriptions) { - [self sdl_runSyncOnQueue:^{ - weakself.subscriptionStatus[type] = subscribe; - }]; + weakself.subscriptionStatus[type] = subscribe; } [weakself sdl_saveSystemCapability:getSystemCapabilityResponse.systemCapability error:error completionHandler:handler]; @@ -476,13 +472,14 @@ - (BOOL)sdl_saveSystemCapability:(nullable SDLSystemCapability *)systemCapabilit - (void)sdl_saveAppServicesCapabilitiesUpdate:(SDLAppServicesCapabilities *)newCapabilities { SDLLogV(@"Saving app services capability update with new capabilities: %@", newCapabilities); - for (SDLAppServiceCapability *capability in newCapabilities.appServices) { - // If the capability has been removed, delete the saved capability; otherwise just update with the new capability - SDLAppServiceCapability *newCapability = [capability.updateReason isEqualToEnum:SDLServiceUpdateRemoved] ? nil : capability; - [self sdl_runSyncOnQueue:^{ + [self sdl_runSyncOnQueue:^{ + for (SDLAppServiceCapability *capability in newCapabilities.appServices) { + // If the capability has been removed, delete the saved capability; otherwise just update with the new capability + SDLAppServiceCapability *newCapability = [capability.updateReason isEqualToEnum:SDLServiceUpdateRemoved] ? nil : capability; + self.appServicesCapabilitiesDictionary[capability.updatedAppServiceRecord.serviceID] = newCapability; - }]; - } + } + }]; } /// Save a new new-style `DisplayCapability` update (only contains the delta) that was received by merging it with the existing version. @@ -582,10 +579,7 @@ - (BOOL)subscribeToCapabilityType:(SDLSystemCapabilityType)type withObserver:(id if (self.capabilityObservers[type] == nil) { SDLLogD(@"This is the first subscription to capability type: %@, sending a GetSystemCapability with subscribe true", type); - - [self sdl_runSyncOnQueue:^{ - self.capabilityObservers[type] = [NSMutableArray arrayWithObject:observerObject]; - }]; + self.capabilityObservers[type] = [NSMutableArray arrayWithObject:observerObject]; // We don't want to send this for the displays type because that's automatically subscribed if (![type isEqualToEnum:SDLSystemCapabilityTypeDisplays]) { @@ -596,10 +590,7 @@ - (BOOL)subscribeToCapabilityType:(SDLSystemCapabilityType)type withObserver:(id } } else { // Store the observer and call it immediately with the cached value - [self sdl_runSyncOnQueue:^{ - [self.capabilityObservers[type] addObject:observerObject]; - }]; - + [self.capabilityObservers[type] addObject:observerObject]; [self sdl_invokeObserver:observerObject withCapabilityType:type capability:[self sdl_cachedCapabilityForType:type] error:nil]; } @@ -610,24 +601,23 @@ - (BOOL)subscribeToCapabilityType:(SDLSystemCapabilityType)type withObserver:(id - (void)unsubscribeFromCapabilityType:(SDLSystemCapabilityType)type withObserver:(id)observer { SDLLogD(@"Unsubscribing from capability type: %@", type); - for (SDLSystemCapabilityObserver *capabilityObserver in self.capabilityObservers[type]) { - if ([observer isEqual:capabilityObserver.observer] && self.capabilityObservers[type] != nil) { - [self sdl_runSyncOnQueue:^{ + [self sdl_runSyncOnQueue:^{ + for (SDLSystemCapabilityObserver *capabilityObserver in self.capabilityObservers[type]) { + if ([observer isEqual:capabilityObserver.observer] && self.capabilityObservers[type] != nil) { [self.capabilityObservers[type] removeObject:capabilityObserver]; - }]; - - [self sdl_removeNilObserversAndUnsubscribeIfNecessary]; - break; + [self sdl_removeNilObserversAndUnsubscribeIfNecessary]; + break; + } } - } + }]; } - (void)sdl_removeNilObserversAndUnsubscribeIfNecessary { SDLLogV(@"Checking for nil observers and removing them, then checking for subscriptions we don't need and unsubscribing."); // Loop through our observers - for (SDLSystemCapabilityType key in self.capabilityObservers.allKeys) { - for (SDLSystemCapabilityObserver *observer in self.capabilityObservers[key]) { - [self sdl_runSyncOnQueue:^{ + [self sdl_runSyncOnQueue:^{ + for (SDLSystemCapabilityType key in self.capabilityObservers.allKeys) { + for (SDLSystemCapabilityObserver *observer in self.capabilityObservers[key]) { // If an observer object is nil, remove it if (observer.observer == nil) { [self.capabilityObservers[key] removeObject:observer]; @@ -637,9 +627,10 @@ - (void)sdl_removeNilObserversAndUnsubscribeIfNecessary { if (self.capabilityObservers[key].count == 0) { [self.capabilityObservers removeObjectForKey:key]; } - }]; + + } } - } + }]; // If we don't support subscriptions, we don't want to unsubscribe by sending an RPC below if (!self.supportsSubscriptions) { diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLChoiceSetManagerSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLChoiceSetManagerSpec.m index 47156d1c0..2b1a0a055 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLChoiceSetManagerSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLChoiceSetManagerSpec.m @@ -15,6 +15,7 @@ #import "SDLHMILevel.h" #import "SDLKeyboardDelegate.h" #import "SDLKeyboardProperties.h" +#import "SDLLockedMutableSet.h" #import "SDLNotificationConstants.h" #import "SDLOnHMIStatus.h" #import "SDLPreloadChoicesOperation.h" @@ -48,14 +49,15 @@ @interface SDLChoiceSetManager() @property (strong, nonatomic, readonly) SDLStateMachine *stateMachine; @property (strong, nonatomic) NSOperationQueue *transactionQueue; +@property (copy, nonatomic) dispatch_queue_t readWriteQueue; @property (copy, nonatomic, nullable) SDLHMILevel currentHMILevel; @property (copy, nonatomic, nullable) SDLSystemContext currentSystemContext; @property (copy, nonatomic, nullable) SDLWindowCapability *currentWindowCapability; -@property (strong, nonatomic) NSMutableSet *preloadedMutableChoices; +@property (strong, nonatomic) SDLLockedMutableSet *preloadedMutableChoices; @property (strong, nonatomic, readonly) NSSet *pendingPreloadChoices; -@property (strong, nonatomic) NSMutableSet *pendingMutablePreloadChoices; +@property (strong, nonatomic) SDLLockedMutableSet *pendingMutablePreloadChoices; @property (strong, nonatomic, nullable) SDLChoiceSet *pendingPresentationSet; @property (strong, nonatomic, nullable) SDLAsynchronousOperation *pendingPresentOperation; @@ -219,8 +221,8 @@ - (void)sdl_displayCapabilityDidUpdate:(SDLSystemCapability *)systemCapability; expect(testManager.vrOptional).to(beTrue()); expect(testManager.currentHMILevel).to(equal(SDLHMILevelNone)); expect(testManager.pendingPresentationSet).to(beNil()); - expect(testManager.preloadedMutableChoices).to(beEmpty()); - expect(testManager.pendingMutablePreloadChoices).to(beEmpty()); + expect(testManager.preloadedMutableChoices.count).to(equal(0)); + expect(testManager.pendingMutablePreloadChoices.count).to(equal(0)); }); }); }); @@ -233,7 +235,8 @@ - (void)sdl_displayCapabilityDidUpdate:(SDLSystemCapability *)systemCapability; describe(@"preloading choices", ^{ context(@"when some choices are already uploaded", ^{ beforeEach(^{ - testManager.preloadedMutableChoices = [NSMutableSet setWithArray:@[testCell1]]; + testManager.preloadedMutableChoices = [[SDLLockedMutableSet alloc] initWithQueue:testManager.readWriteQueue]; + [testManager.preloadedMutableChoices addObject:testCell1]; [testManager preloadChoices:@[testCell1, testCell2, testCell3] withCompletionHandler:^(NSError * _Nullable error) { }]; @@ -257,7 +260,8 @@ - (void)sdl_displayCapabilityDidUpdate:(SDLSystemCapability *)systemCapability; context(@"when some choices are already pending", ^{ beforeEach(^{ - testManager.pendingMutablePreloadChoices = [NSMutableSet setWithArray:@[testCell1]]; + testManager.pendingMutablePreloadChoices = [[SDLLockedMutableSet alloc] initWithQueue:testManager.readWriteQueue]; + [testManager.pendingMutablePreloadChoices addObject:testCell1]; [testManager preloadChoices:@[testCell1, testCell2, testCell3] withCompletionHandler:^(NSError * _Nullable error) { }]; @@ -281,10 +285,10 @@ - (void)sdl_displayCapabilityDidUpdate:(SDLSystemCapability *)systemCapability; context(@"when the manager shuts down during preloading", ^{ beforeEach(^{ - testManager.pendingMutablePreloadChoices = [NSMutableSet setWithArray:@[testCell1]]; + testManager.pendingMutablePreloadChoices = [[SDLLockedMutableSet alloc] initWithQueue:testManager.readWriteQueue]; + [testManager.pendingMutablePreloadChoices addObject:testCell1]; - [testManager preloadChoices:@[testCell1, testCell2, testCell3] withCompletionHandler:^(NSError * _Nullable error) { - }]; + [testManager preloadChoices:@[testCell1, testCell2, testCell3] withCompletionHandler:^(NSError * _Nullable error) {}]; }); it(@"should leave the list of pending and uploaded choice items empty when the operation finishes", ^{ @@ -294,8 +298,8 @@ - (void)sdl_displayCapabilityDidUpdate:(SDLSystemCapability *)systemCapability; expect(testManager.transactionQueue.operations.firstObject).to(beAnInstanceOf([SDLPreloadChoicesOperation class])); [testManager.stateMachine setToState:SDLChoiceManagerStateShutdown fromOldState:nil callEnterTransition:NO]; - testManager.pendingMutablePreloadChoices = [NSMutableSet set]; - testManager.preloadedMutableChoices = [NSMutableSet set]; + [testManager.pendingMutablePreloadChoices removeAllObjects]; + [testManager.preloadedMutableChoices removeAllObjects]; SDLPreloadChoicesOperation *testOp = testManager.transactionQueue.operations.firstObject; testOp.completionBlock(); @@ -320,7 +324,8 @@ - (void)sdl_displayCapabilityDidUpdate:(SDLSystemCapability *)systemCapability; testManager.pendingPresentOperation = pendingPresentOp; testManager.pendingPresentationSet = [[SDLChoiceSet alloc] initWithTitle:@"Test" delegate:choiceDelegate choices:@[testCell1]]; - testManager.preloadedMutableChoices = [NSMutableSet setWithObject:testCell1]; + testManager.preloadedMutableChoices = [[SDLLockedMutableSet alloc] initWithQueue:testManager.readWriteQueue]; + [testManager.preloadedMutableChoices addObject:testCell1]; [testManager deleteChoices:@[testCell1, testCell2, testCell3]]; }); @@ -341,10 +346,10 @@ - (void)sdl_displayCapabilityDidUpdate:(SDLSystemCapability *)systemCapability; beforeEach(^{ pendingPreloadOp = [[SDLPreloadChoicesOperation alloc] init]; - [testManager.transactionQueue addOperation:pendingPreloadOp]; - testManager.pendingMutablePreloadChoices = [NSMutableSet setWithObject:testCell1]; + testManager.pendingMutablePreloadChoices = [[SDLLockedMutableSet alloc] initWithQueue:testManager.readWriteQueue]; + [testManager.pendingMutablePreloadChoices addObject:testCell1]; [testManager deleteChoices:@[testCell1, testCell2, testCell3]]; }); @@ -365,7 +370,8 @@ - (void)sdl_displayCapabilityDidUpdate:(SDLSystemCapability *)systemCapability; OCMStub(pendingPresentOp.choiceSet.choices).andReturn([NSSet setWithArray:@[testCell1]]); testManager.pendingPresentOperation = pendingPresentOp; testManager.pendingPresentationSet = [[SDLChoiceSet alloc] initWithTitle:@"Test" delegate:choiceDelegate choices:@[testCell1]]; - testManager.preloadedMutableChoices = [NSMutableSet setWithObject:testCell1]; + testManager.preloadedMutableChoices = [[SDLLockedMutableSet alloc] initWithQueue:testManager.readWriteQueue]; + [testManager.preloadedMutableChoices addObject:testCell1]; [testManager deleteChoices:@[testCell1, testCell2, testCell3]]; }); @@ -377,8 +383,8 @@ - (void)sdl_displayCapabilityDidUpdate:(SDLSystemCapability *)systemCapability; OCMVerify([choiceDelegate choiceSet:[OCMArg any] didReceiveError:[OCMArg any]]); [testManager.stateMachine setToState:SDLChoiceManagerStateShutdown fromOldState:nil callEnterTransition:NO]; - testManager.pendingMutablePreloadChoices = [NSMutableSet set]; - testManager.preloadedMutableChoices = [NSMutableSet set]; + [testManager.pendingMutablePreloadChoices removeAllObjects]; + [testManager.preloadedMutableChoices removeAllObjects]; testManager.transactionQueue.operations.lastObject.completionBlock(); @@ -480,8 +486,8 @@ - (void)sdl_displayCapabilityDidUpdate:(SDLSystemCapability *)systemCapability; expect(testManager.transactionQueue.operations.lastObject).to(beAnInstanceOf([SDLPresentChoiceSetOperation class])); [testManager.stateMachine setToState:SDLChoiceManagerStateShutdown fromOldState:nil callEnterTransition:NO]; - testManager.pendingMutablePreloadChoices = [NSMutableSet set]; - testManager.preloadedMutableChoices = [NSMutableSet set]; + [testManager.pendingMutablePreloadChoices removeAllObjects]; + [testManager.preloadedMutableChoices removeAllObjects]; testManager.transactionQueue.operations.lastObject.completionBlock(); diff --git a/SmartDeviceLinkTests/SDLLockedMapTableSpec.m b/SmartDeviceLinkTests/SDLLockedMapTableSpec.m new file mode 100644 index 000000000..748e53049 --- /dev/null +++ b/SmartDeviceLinkTests/SDLLockedMapTableSpec.m @@ -0,0 +1,103 @@ +// +// SDLLockedMapTableSpec.m +// SmartDeviceLinkTests +// +// Created by Joel Fischer on 8/7/20. +// Copyright © 2020 smartdevicelink. All rights reserved. +// + +#import + +#import +#import + +#import "SDLLockedMapTable.h" + +QuickSpecBegin(SDLLockedMapTableSpec) + +describe(@"a locked map table", ^{ + __block dispatch_queue_t testQueue = NULL; + __block SDLLockedMapTable *testMapTable = nil; + + __block NSString *objectString = @"testObj"; + __block NSString *keyString = @"testKey"; + + beforeEach(^{ + testQueue = dispatch_queue_create("com.testqueue", DISPATCH_QUEUE_SERIAL); + testMapTable = [[SDLLockedMapTable alloc] initWithKeyOptions:NSMapTableStrongMemory valueOptions:NSMapTableStrongMemory queue:testQueue]; + }); + + context(@"on a different queue", ^{ + context(@"not using subscripting", ^{ + it(@"should set, get, and remove a key-value pair repeatedly", ^{ + for(int i = 0; i < 5000; i++) { + [testMapTable setObject:objectString forKey:keyString]; + [testMapTable objectForKey:keyString]; + [testMapTable removeObjectForKey:keyString]; + + expect(testMapTable.count).to(equal(0)); + } + }); + }); + context(@"using subscripting", ^{ + it(@"should set, get, and remove a key-value pair repeatedly", ^{ + for(int i = 0; i < 5000; i++) { + testMapTable[keyString] = objectString; + (void)testMapTable[keyString]; + testMapTable[keyString] = nil; + + expect(testMapTable.count).to(equal(0)); + } + }); + }); + + it(@"should remove all objects properly", ^{ + for(int i = 0; i < 5000; i++) { + [testMapTable setObject:@(i) forKey:@(i)]; + } + + expect(testMapTable.count).to(equal(5000)); + [testMapTable removeAllObjects]; + expect(testMapTable.count).to(equal(0)); + }); + + it(@"should create a dictionary representation properly", ^{ + [testMapTable setObject:@1 forKey:@1]; + + NSDictionary *mapDict = testMapTable.dictionaryRepresentation; + expect(mapDict).toNot(beNil()); + expect(mapDict).to(haveCount(1)); + }); + }); + + context(@"on the same queue", ^{ + context(@"not using subscripting", ^{ + it(@"should set, get, and remove a key-value pair repeatedly", ^{ + dispatch_sync(testQueue, ^{ + for(int i = 0; i < 5000; i++) { + [testMapTable setObject:objectString forKey:keyString]; + [testMapTable objectForKey:keyString]; + [testMapTable removeObjectForKey:keyString]; + + expect(testMapTable.count).to(equal(0)); + } + }); + }); + }); + context(@"using subscripting", ^{ + it(@"should set, get, and remove a key-value pair repeatedly", ^{ + dispatch_sync(testQueue, ^{ + for(int i = 0; i < 5000; i++) { + testMapTable[keyString] = objectString; + (void)testMapTable[keyString]; + testMapTable[keyString] = nil; + + expect(testMapTable.count).to(equal(0)); + } + }); + }); + }); + }); +}); + +QuickSpecEnd diff --git a/SmartDeviceLinkTests/UtilitiesSpecs/SDLLockedMutableSetSpec.m b/SmartDeviceLinkTests/UtilitiesSpecs/SDLLockedMutableSetSpec.m new file mode 100644 index 000000000..29818eebf --- /dev/null +++ b/SmartDeviceLinkTests/UtilitiesSpecs/SDLLockedMutableSetSpec.m @@ -0,0 +1,63 @@ +// +// SDLLockedMutableSetSpec.m +// SmartDeviceLinkTests +// +// Created by Joel Fischer on 8/7/20. +// Copyright © 2020 smartdevicelink. All rights reserved. +// + +#import + +#import +#import + +#import "SDLLockedMutableSet.h" + +QuickSpecBegin(SDLLockedMutableSetSpec) + +describe(@"a locked set", ^{ + __block dispatch_queue_t testQueue = NULL; + __block SDLLockedMutableSet *testSet = nil; + + __block NSString *objectString = @"testObj"; + + beforeEach(^{ + testQueue = dispatch_queue_create("com.testqueue", DISPATCH_QUEUE_SERIAL); + testSet = [[SDLLockedMutableSet alloc] initWithQueue:testQueue]; + }); + + context(@"on a different queue", ^{ + it(@"should add, retrieve, and remove an object repeatedly", ^{ + for(int i = 0; i < 5000; i++) { + [testSet addObject:objectString]; + [testSet removeAllObjects]; + expect(testSet.count).to(equal(0)); + } + }); + + it(@"should remove all objects properly", ^{ + for(int i = 0; i < 5000; i++) { + [testSet addObject:[NSString stringWithFormat:@"%@", @(i)]]; + } + + expect(testSet.count).to(equal(5000)); + [testSet removeAllObjects]; + expect(testSet.count).to(equal(0)); + }); + }); + + context(@"on the same queue", ^{ + it(@"should add, retrieve, and remove an object repeatedly", ^{ + dispatch_sync(testQueue, ^{ + for(int i = 0; i < 5000; i++) { + [testSet addObject:objectString]; + [testSet removeAllObjects]; + expect(testSet.count).to(equal(0)); + } + }); + }); + }); +}); + + +QuickSpecEnd