diff --git a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj index bc90ee8f18..920ea1eefe 100644 --- a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj +++ b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj @@ -442,6 +442,18 @@ 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 */; }; + 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 */; }; + 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 */; }; @@ -2189,6 +2201,18 @@ 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 = ""; }; + 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 = ""; }; + 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 = ""; }; @@ -3560,7 +3584,7 @@ name = Encryption; sourceTree = ""; }; - 10415106241BBB8B00F163CC /* Lock Screen Icon Cache */ = { + 10415106241BBB8B00F163CC /* Icon Cache */ = { isa = PBXGroup; children = ( 10893C142417D78300BA347E /* SDLIconArchiveFile.h */, @@ -3568,7 +3592,7 @@ 10893C182418188600BA347E /* SDLCacheFileManager.h */, 10893C192418188600BA347E /* SDLCacheFileManager.m */, ); - name = "Lock Screen Icon Cache"; + name = "Icon Cache"; sourceTree = ""; }; 109566F42429865500E24F66 /* Lock Screen Icon Cache */ = { @@ -4110,6 +4134,52 @@ 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 = ( + 4A8F465424DC408B003BDAE4 /* SDLLockedMapTable.h */, + 4A8F465524DC408B003BDAE4 /* SDLLockedMapTable.m */, + 4A8F464A24DA0019003BDAE4 /* SDLLockedMutableArray.h */, + 4A8F464B24DA0019003BDAE4 /* SDLLockedMutableArray.m */, + 4A8F464624D9F581003BDAE4 /* SDLLockedMutableDictionary.h */, + 4A8F464724D9F581003BDAE4 /* SDLLockedMutableDictionary.m */, + 4A8F465824DC95C6003BDAE4 /* SDLLockedMutableSet.h */, + 4A8F465924DC95C6003BDAE4 /* SDLLockedMutableSet.m */, + ); + name = "Locked Collections"; + sourceTree = ""; + }; + 4A8F464E24DB3737003BDAE4 /* Utilities */ = { + isa = PBXGroup; + children = ( + 5D61FAD21A84238A00846EE7 /* SDLHexUtility.h */, + 5D61FAD31A84238A00846EE7 /* SDLHexUtility.m */, + ); + name = Utilities; + sourceTree = ""; + }; + 4A8F464F24DB379F003BDAE4 /* Locked Collections */ = { + isa = PBXGroup; + children = ( + 4A8F466624DD92C7003BDAE4 /* SDLLockedMapTableSpec.m */, + 4A8F465024DB37CC003BDAE4 /* SDLLockedMutableDictionarySpec.m */, + 4A8F465224DB439A003BDAE4 /* SDLLockedMutableArraySpec.m */, + 4A8F466424DD92B3003BDAE4 /* SDLLockedMutableSetSpec.m */, + ); + name = "Locked Collections"; + sourceTree = ""; + }; 4A9D02BB2497EEB400FBE99B /* Custom RPC Adapters */ = { isa = PBXGroup; children = ( @@ -4547,13 +4617,14 @@ 5D5934EF1A85160F00687FB9 /* RPCs */ = { isa = PBXGroup; children = ( - 5D5934FF1A851B8400687FB9 /* Superclasses */, 5D5934F41A85165E00687FB9 /* Enums */, + 4A8F463E24D9EE6D003BDAE4 /* Names */, 5D5934F81A8519C300687FB9 /* Notification */, 5D5935041A851E1A00687FB9 /* Payload */, 5D5934F11A85162800687FB9 /* Requests */, 5D5934F21A85163200687FB9 /* Responses */, 5D5934F31A85164500687FB9 /* Structs */, + 5D5934FF1A851B8400687FB9 /* Superclasses */, ); name = RPCs; sourceTree = ""; @@ -5279,8 +5350,9 @@ 5D5934F51A8516C800687FB9 /* Logging */ = { isa = PBXGroup; children = ( - 5DD67CC01E68AE66009CD394 /* Modules */, 5DBF063D1E64BDAE00A5CF03 /* Log Targets */, + 5DD67CC01E68AE66009CD394 /* Modules */, + 4A8F464E24DB3737003BDAE4 /* Utilities */, 5DBF06211E64A83F00A5CF03 /* SDLLogManager.h */, 5DBF06221E64A83F00A5CF03 /* SDLLogManager.m */, 5DBF062B1E64A93A00A5CF03 /* SDLLogFilter.h */, @@ -5298,24 +5370,16 @@ 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 = ""; @@ -5376,6 +5440,8 @@ 5D5934F91A851A8000687FB9 /* Prioritized Objects */ = { isa = PBXGroup; children = ( + 97E26DEA1E807AD70074A3C7 /* SDLMutableDataQueue.h */, + 97E26DEB1E807AD70074A3C7 /* SDLMutableDataQueue.m */, 5D61FB101A84238A00846EE7 /* SDLObjectWithPriority.h */, 5D61FB111A84238A00846EE7 /* SDLObjectWithPriority.m */, 5D61FB521A84238B00846EE7 /* SDLPrioritizedObjectCollection.h */, @@ -5493,11 +5559,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 */, @@ -5574,15 +5640,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 */ = { @@ -5625,8 +5693,6 @@ 5D76E31F1D39731100647CFA /* Utilities */ = { isa = PBXGroup; children = ( - 880723E923A2CFB4003D0489 /* SDLLockScreenRootViewController.h */, - 880723EA23A2CFB4003D0489 /* SDLLockScreenRootViewController.m */, 5D76E3201D39742300647CFA /* SDLViewControllerPresentable.h */, 5D76E3221D39767000647CFA /* SDLLockScreenPresenter.h */, 5D76E3231D39767000647CFA /* SDLLockScreenPresenter.m */, @@ -5845,9 +5911,9 @@ 5DA3F3511BC4477B0026F2D0 /* Developer API */ = { isa = PBXGroup; children = ( - 5DA3F3561BC4480E0026F2D0 /* Utilities */, - 5DBAE0A61D355EF200CE00BF /* Managers */, 5DBAE0A71D355F0C00CE00BF /* Dispatchers */, + 5DBAE0A61D355EF200CE00BF /* Managers */, + 5DA3F3561BC4480E0026F2D0 /* Utilities */, ); name = "Developer API"; sourceTree = ""; @@ -5855,14 +5921,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 = ""; @@ -6067,8 +6133,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 */, ); @@ -6108,8 +6175,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 */, @@ -6733,6 +6801,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 */, @@ -7175,6 +7244,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 */, @@ -7261,9 +7331,11 @@ 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 */, + 4A8F465624DC408B003BDAE4 /* SDLLockedMapTable.h in Headers */, 888DBAEF22D528DE002A0AE2 /* SDLCloseApplicationResponse.h in Headers */, DAC5726C1D11B4840004288B /* SDLTouchManagerDelegate.h in Headers */, 5D61FD3F1A84238C00846EE7 /* SDLPrioritizedObjectCollection.h in Headers */, @@ -7411,7 +7483,7 @@ }; 5D61FA1B1A84237100846EE7 = { CreatedOnToolsVersion = 6.1.1; - LastSwiftMigration = 1150; + LastSwiftMigration = 1160; }; 5D61FA251A84237100846EE7 = { CreatedOnToolsVersion = 6.1.1; @@ -7624,6 +7696,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 */, @@ -7794,6 +7867,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 */, @@ -7865,6 +7939,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 */, @@ -7953,6 +8028,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 */, @@ -8351,6 +8427,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 */, @@ -8381,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 */, @@ -8424,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 */, @@ -8545,6 +8624,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/SDLChoiceSetManager.m b/SmartDeviceLink/SDLChoiceSetManager.m index a8d3468b76..d7e6a5b234 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/SDLLockedDictionary.m b/SmartDeviceLink/SDLLockedDictionary.m new file mode 100644 index 0000000000..67f3707eaf --- /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/SDLLockedMapTable.h b/SmartDeviceLink/SDLLockedMapTable.h new file mode 100644 index 0000000000..dd49634b38 --- /dev/null +++ b/SmartDeviceLink/SDLLockedMapTable.h @@ -0,0 +1,63 @@ +// +// 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 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)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 + +/// 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; + +/// Returns a dictionary representation of the map table. +- (NSDictionary *)dictionaryRepresentation; + +#pragma mark Subscripting +- (void)setObject:(nullable __kindof ObjectType)object forKeyedSubscript:(KeyType)key; +- (nullable __kindof ObjectType)objectForKeyedSubscript:(KeyType)key; + +@end + +NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/SDLLockedMapTable.m b/SmartDeviceLink/SDLLockedMapTable.m new file mode 100644 index 0000000000..8288a0e13e --- /dev/null +++ b/SmartDeviceLink/SDLLockedMapTable.m @@ -0,0 +1,121 @@ +// +// SDLLockedMapTable.m +// SmartDeviceLink +// +// Created by Joel Fischer on 8/6/20. +// Copyright © 2020 smartdevicelink. All rights reserved. +// + +#import "SDLLockedMapTable.h" + +@interface SDLLockedMapTable () + +@property (assign, nonatomic) dispatch_queue_t internalQueue; +@property (assign, nonatomic) const char *internalQueueID; + +@property (strong, nonatomic) NSMapTable *internalMapTable; + +@end + +@implementation SDLLockedMapTable + +#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.internalMapTable objectForKey:key]; +} + +- (void)setObject:(id)object forKeyedSubscript:(id)key { + [self.internalMapTable setObject:object forKey: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/SDLLockedMutableArray.h b/SmartDeviceLink/SDLLockedMutableArray.h new file mode 100644 index 0000000000..c4d9129a96 --- /dev/null +++ b/SmartDeviceLink/SDLLockedMutableArray.h @@ -0,0 +1,70 @@ +// +// 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; + +/// 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 - 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 +- (void)setObject:(__kindof ObjectType)object atIndexedSubscript:(NSUInteger)idx; +- (__kindof ObjectType)objectAtIndexedSubscript:(NSUInteger)idx; + +@end + +NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/SDLLockedMutableArray.m b/SmartDeviceLink/SDLLockedMutableArray.m new file mode 100644 index 0000000000..359a03ce55 --- /dev/null +++ b/SmartDeviceLink/SDLLockedMutableArray.m @@ -0,0 +1,113 @@ +// +// SDLLockedMutableArray.m +// SmartDeviceLink +// +// Created by Joel Fischer on 8/4/20. +// Copyright © 2020 smartdevicelink. All rights reserved. +// + +#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 + +#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); + + _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 + +#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:^{ + [weakSelf.internalArray addObject:object]; + }]; +} + +- (void)insertObject:(id)anObject atIndex:(NSUInteger)index { + __weak typeof(self) weakSelf = self; + [self sdl_runAsyncWithBlock:^{ + [weakSelf.internalArray 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.internalArray[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.h b/SmartDeviceLink/SDLLockedMutableDictionary.h new file mode 100644 index 0000000000..2de9b2c8a8 --- /dev/null +++ b/SmartDeviceLink/SDLLockedMutableDictionary.h @@ -0,0 +1,80 @@ +// +// 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; + +/// 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 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; + +/// 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. +/// +/// 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; + +#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. +/// +/// This will occur synchronously and will not return until the operation completes. +/// @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 new file mode 100644 index 0000000000..0e85a6f9c7 --- /dev/null +++ b/SmartDeviceLink/SDLLockedMutableDictionary.m @@ -0,0 +1,136 @@ +// +// 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 (assign, nonatomic) const char *internalQueueID; + +@property (strong, nonatomic) NSMutableDictionary *internalDict; + +@end + +@implementation SDLLockedMutableDictionary + +- (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); + + _internalDict = [[NSMutableDictionary alloc] init]; + + return self; +} + +#pragma mark - Removing +- (void)removeAllObjects { + __weak typeof(self) weakSelf = self; + [self sdl_runAsyncWithBlock:^{ + [weakSelf.internalDict removeAllObjects]; + }]; +} + +- (void)removeObjectForKey:(id)key { + __weak typeof(self) weakSelf = self; + [self sdl_runAsyncWithBlock:^{ + if ([weakSelf objectForKey:key] != nil) { + [weakSelf.internalDict removeObjectForKey:key]; + } + }]; +} + +#pragma mark - Getting / Setting +- (id)objectForKey:(id)key { + __block id retVal = nil; + [self sdl_runSyncWithBlock:^{ + retVal = [self.internalDict objectForKey:key]; + }]; + + return retVal; +} + +- (void)setObject:(id)object forKey:(id)key { + __weak typeof(self) weakSelf = self; + [self sdl_runAsyncWithBlock:^{ + [weakSelf.internalDict setObject:object forKey:key]; + }]; +} + +#pragma mark Retrieving Information + +- (NSArray> *)allKeys { + __block NSArray> *retVal = nil; + [self sdl_runSyncWithBlock:^{ + retVal = self.internalDict.allKeys; + }]; + + return retVal; +} + +- (NSArray *)allValues { + __block NSArray *retVal = nil; + [self sdl_runSyncWithBlock:^{ + retVal = self.internalDict.allValues; + }]; + + return retVal; +} + +- (NSUInteger)count { + __block NSUInteger retVal = 0; + [self sdl_runSyncWithBlock:^{ + retVal = self.internalDict.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 { + 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/SDLLockedMutableSet.h b/SmartDeviceLink/SDLLockedMutableSet.h new file mode 100644 index 0000000000..757db4a3b3 --- /dev/null +++ b/SmartDeviceLink/SDLLockedMutableSet.h @@ -0,0 +1,60 @@ +// +// 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 - 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 set. +/// +/// This will occur synchronously and will not return until the operation completes. +/// @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 into the set. +/// +/// This will occur asynchronously and may return before the operation completes. +/// @param object The object to add to set. This value must not be nil. +- (void)addObject:(ObjectType)object; + +@end + +NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/SDLLockedMutableSet.m b/SmartDeviceLink/SDLLockedMutableSet.m new file mode 100644 index 0000000000..7bc4b4e6c2 --- /dev/null +++ b/SmartDeviceLink/SDLLockedMutableSet.m @@ -0,0 +1,107 @@ +// +// 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 - Removing +- (void)removeAllObjects { + __weak typeof(self) weakSelf = self; + [self sdl_runAsyncWithBlock:^{ + [weakSelf.internalSet removeAllObjects]; + }]; +} + +#pragma mark Retrieving information +- (NSUInteger)count { + __block NSUInteger retVal = 0; + [self sdl_runSyncWithBlock:^{ + retVal = self.internalSet.count; + }]; + + return retVal; +} + +- (NSSet *)immutableSet { + __block NSSet *retVal = nil; + [self sdl_runSyncWithBlock:^{ + retVal = [self.internalSet copy]; + }]; + + return retVal; +} + +#pragma mark Modifications +- (void)unionSet:(NSMutableSet *)otherSet { + __weak typeof(self) weakSelf = self; + [self sdl_runAsyncWithBlock:^{ + [weakSelf.internalSet unionSet: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) { + 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 dae3e64a2e..13cb9f8c07 100644 --- a/SmartDeviceLink/SDLResponseDispatcher.h +++ b/SmartDeviceLink/SDLResponseDispatcher.h @@ -10,6 +10,8 @@ #import "NSMapTable+Subscripting.h" #import "SDLNotificationConstants.h" +#import "SDLLockedMutableDictionary.h" +#import "SDLLockedMapTable.h" typedef NSNumber SDLRPCCorrelationId; @@ -28,27 +30,27 @@ 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. */ -@property (strong, nonatomic, readonly) NSMutableDictionary *rpcRequestDictionary; +@property (strong, nonatomic, readonly) SDLLockedMutableDictionary *rpcRequestDictionary; /** * 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 7c59566166..8dc7a2516c 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) NSMutableDictionary *rpcRequestDictionary; -@property (strong, nonatomic, readwrite) NSMapTable *commandHandlerMap; -@property (strong, nonatomic, readwrite) NSMapTable *buttonHandlerMap; -@property (strong, nonatomic, readwrite) NSMapTable *customButtonHandlerMap; +@property (strong, nonatomic, readwrite) SDLLockedMapTable *rpcResponseHandlerMap; +@property (strong, nonatomic, readwrite) SDLLockedMutableDictionary *rpcRequestDictionary; +@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]; - _rpcRequestDictionary = [NSMutableDictionary dictionary]; - _commandHandlerMap = [NSMapTable mapTableWithKeyOptions:NSMapTableCopyIn valueOptions:NSMapTableCopyIn]; - _buttonHandlerMap = [NSMapTable mapTableWithKeyOptions:NSMapTableCopyIn valueOptions:NSMapTableCopyIn]; - _customButtonHandlerMap = [NSMapTable mapTableWithKeyOptions:NSMapTableCopyIn valueOptions:NSMapTableCopyIn]; + _rpcResponseHandlerMap = [[SDLLockedMapTable alloc] initWithKeyOptions:NSMapTableCopyIn valueOptions:NSMapTableCopyIn queue:_readWriteQueue]; + _rpcRequestDictionary = [[SDLLockedMutableDictionary alloc] initWithQueue:_readWriteQueue]; + _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 diff --git a/SmartDeviceLink/SDLSubscribeButtonManager.m b/SmartDeviceLink/SDLSubscribeButtonManager.m index 505740227e..1f8e7fc50d 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 4fc1ec6c14..bce392caa1 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; @@ -121,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 = [NSMutableDictionary dictionary]; - [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 @@ -395,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]; @@ -475,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. @@ -581,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]) { @@ -595,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]; } @@ -609,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]; @@ -636,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) { @@ -817,35 +809,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 diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLChoiceSetManagerSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLChoiceSetManagerSpec.m index 47156d1c03..2b1a0a055b 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 0000000000..748e53049a --- /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/SDLLockedMutableArraySpec.m b/SmartDeviceLinkTests/SDLLockedMutableArraySpec.m new file mode 100644 index 0000000000..97819bdfa9 --- /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 0000000000..3ef7569849 --- /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 diff --git a/SmartDeviceLinkTests/UtilitiesSpecs/SDLLockedMutableSetSpec.m b/SmartDeviceLinkTests/UtilitiesSpecs/SDLLockedMutableSetSpec.m new file mode 100644 index 0000000000..29818eebf7 --- /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