From 9c8f0909f4be36100cafd770347221cbe68ff485 Mon Sep 17 00:00:00 2001 From: Manuel de la Pena Date: Wed, 13 Nov 2019 06:06:23 -0500 Subject: [PATCH 1/2] [CoreFoundation] Add Clone method for CFArray. (#7403) When working on https://github.com/xamarin/maccore/issues/940 we noticed that clone the array would be a better approach. The CFArrayCreateCopy add some nice things: * The pointer values from theArray are copied into the new array. * The values are also retained by the new array. * The count of the new array is the same as theArray * The new array uses the same callbacks as theArray. [IMPORTANT] Whith this in, we can have a better fix for https://github.com/xamarin/maccore/issues/940 --- src/CoreFoundation/CFArray.cs | 7 +++++++ tests/xtro-sharpie/common-CoreFoundation.ignore | 1 - 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/CoreFoundation/CFArray.cs b/src/CoreFoundation/CFArray.cs index 7f5df0e79557..d3b9dd5f08c3 100644 --- a/src/CoreFoundation/CFArray.cs +++ b/src/CoreFoundation/CFArray.cs @@ -35,6 +35,8 @@ using ObjCRuntime; using CFIndex = System.nint; +using CFArrayRef = System.IntPtr; +using CFAllocatorRef = System.IntPtr; namespace CoreFoundation { @@ -149,6 +151,11 @@ public static nint GetCount (IntPtr array) { return CFArrayGetCount (array); } + + [DllImport (Constants.CoreFoundationLibrary)] + extern static CFArrayRef CFArrayCreateCopy (CFAllocatorRef allocator, CFArrayRef theArray); + + public CFArray Clone () => new CFArray (CFArrayCreateCopy (IntPtr.Zero, this.Handle), true); } } diff --git a/tests/xtro-sharpie/common-CoreFoundation.ignore b/tests/xtro-sharpie/common-CoreFoundation.ignore index 3db8cadbd88d..ee1d3a96ee1d 100644 --- a/tests/xtro-sharpie/common-CoreFoundation.ignore +++ b/tests/xtro-sharpie/common-CoreFoundation.ignore @@ -334,7 +334,6 @@ !missing-pinvoke! CFArrayApplyFunction is not bound !missing-pinvoke! CFArrayBSearchValues is not bound !missing-pinvoke! CFArrayContainsValue is not bound -!missing-pinvoke! CFArrayCreateCopy is not bound !missing-pinvoke! CFArrayCreateMutable is not bound !missing-pinvoke! CFArrayCreateMutableCopy is not bound !missing-pinvoke! CFArrayExchangeValuesAtIndices is not bound From 48addf81d1af707338813677c78426055deffed2 Mon Sep 17 00:00:00 2001 From: Manuel de la Pena Date: Wed, 13 Nov 2019 17:19:16 -0500 Subject: [PATCH 2/2] [CoreFoundation] Make CFBundle.GetAll thread safe. --- src/CoreFoundation/CFBundle.cs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/CoreFoundation/CFBundle.cs b/src/CoreFoundation/CFBundle.cs index c4ac2db5eaf2..581e73112a13 100644 --- a/src/CoreFoundation/CFBundle.cs +++ b/src/CoreFoundation/CFBundle.cs @@ -103,11 +103,22 @@ public static CFBundle[] GetBundlesFromDirectory (NSUrl directoryUrl, string bun public static CFBundle[] GetAll () { - using (var cfBundles = new CFArray (CFBundleGetAllBundles ())) { - var managedBundles = new CFBundle [cfBundles.Count]; - for (int index = 0; index < cfBundles.Count; index++) { + // as per apple documentation: + // CFBundleGetAllBundles + // + // 'This function is potentially expensive and not thread-safe' + // + // This means, that we should not trust the size of the array, since is a get and + // might be modified by a diff thread. We are going to clone the array and make sure + // that Apple does not modify the array while we work with it. That avoids changes + // in the index or in the bundles returned. + using (var cfBundles = new CFArray (CFBundleGetAllBundles ())) + using (var cfBundlesCopy = cfBundles.Clone () ) { + var bundleCount = cfBundlesCopy.Count; // the property is a C call, calling everytime we loop is not needed + var managedBundles = new CFBundle [bundleCount]; + for (int index = 0; index < bundleCount; index++) { // follow the get rule, we do not own the object - managedBundles [index] = new CFBundle (cfBundles.GetValue (index), false); + managedBundles [index] = new CFBundle (cfBundlesCopy.GetValue (index), false); } return managedBundles; }