From c37370638109786560292b79b23241405c7f223f Mon Sep 17 00:00:00 2001 From: Manuel de la Pena Date: Wed, 13 Nov 2019 14:23:09 -0500 Subject: [PATCH] [CoreFoundation] CFBundle.GetAll better thread safe. The initial solution fixed the rance condition in which the index changed, but that did not guarantee that we would get the correct bundles. We now clone the CFArray (which also clones the callbacks set to the array) and iterate over it to make sure Apple does not do evil tings while we are iteraing. Better Fixes: https://github.com/xamarin/maccore/issues/940 --- src/CoreFoundation/CFBundle.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/CoreFoundation/CFBundle.cs b/src/CoreFoundation/CFBundle.cs index 5989890bd407..581e73112a13 100644 --- a/src/CoreFoundation/CFBundle.cs +++ b/src/CoreFoundation/CFBundle.cs @@ -109,14 +109,16 @@ public static CFBundle[] GetAll () // '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 just use the size returned by the first - // count to avoid a System.IndexOutOfRangeException when looping - using (var cfBundles = new CFArray (CFBundleGetAllBundles ())) { - var bundleCount = cfBundles.Count; + // 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; }