Bug 8106 - Parallel.ForEach and AsParallel().ForAll() Fail for All Reference Types
Summary: Parallel.ForEach and AsParallel().ForAll() Fail for All Reference Types
Status: RESOLVED FIXED
Alias: None
Product: iOS
Classification: Xamarin
Component: Xamarin.iOS.dll ()
Version: 6.0.x
Hardware: Macintosh Mac OS
: High normal
Target Milestone: 6.4 (async)
Assignee: Marek Safar
URL:
Depends on:
Blocks:
 
Reported: 2012-10-31 13:38 UTC by Isaac.Eckert
Modified: 2013-07-08 18:31 UTC (History)
7 users (show)

Tags:
Is this bug a regression?: ---
Last known good build:


Attachments
Solution Files (3.04 MB, application/zip)
2012-10-31 13:38 UTC, Isaac.Eckert
Details


Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED FIXED

Description Isaac.Eckert 2012-10-31 13:38:01 UTC
Created attachment 2822 [details]
Solution Files

Parallel.ForEach() and AsParallel().ForAll() both fail with a JIT error when attempting to run on an iOS 6.0 device.

This appears to happen because a piece of internal code is attempting to use a generic dictionary.  Unfortunately, it appears the error cannot be avoided when enumerating over an IEnumerable of any reference type except String.  

The internal code currently has a special exception for strings that avoids the bug.  Strings use a specialized partitioner that does not rely on the JIT feature.

Below is a short example and the resulting error and call stack.  I have also attached a compressed copy of the solution.


Example: 

public class ValueAndSquare
    { 
      public float Value { get; set; }
      public float Square { get; set; }
    }

ValueAndSquare[] values = {new ValueAndSquare()...}

System.Threading.Tasks.Parallel.ForEach(values, value=> {
        value.Square = value.Value * value.Value;
      });

Result: Attempting to JIT compile method 'System.Collections.Generic.KeyValuePair`2<long, ParallelTest.ParallelTestViewController/ValueAndSquare>:.ctor (long,ParallelTest.ParallelTestViewController/ValueAndSquare)' while running with --aot-only.

Call Stack:

System.AggregateException: One or more errors occured ---> System.Exception: Attempting to JIT compile method 'System.Collections.Generic.KeyValuePair`2<long, ParallelTest.ParallelTestViewController/ValueAndSquare>:.ctor (long,ParallelTest.ParallelTestViewController/ValueAndSquare)' while running with --aot-only. See http://docs.xamarin.com/ios/about/limitations for more information.

  at System.Collections.Concurrent.Partitioners.ListPartitioner`1+<GetEnumeratorForRangeInternal>c__IteratorA[ParallelTest.ParallelTestViewController+ValueAndSquare].MoveNext () [0x00000] in <filename unknown>:0
  at System.Collections.Concurrent.OrderablePartitioner`1+ProxyEnumerator[ParallelTest.ParallelTestViewController+ValueAndSquare].MoveNext () [0x00000] in <filename unknown>:0
  at System.Threading.Tasks.Parallel+<ForEach>c__AnonStorey35`2[ParallelTest.ParallelTestViewController+ValueAndSquare,System.Object].<>m__32 () [0x00000] in <filename unknown>:0
  at System.Threading.Tasks.TaskActionInvoker+ActionInvoke.Invoke (System.Threading.Tasks.Task owner, System.Object state, System.Threading.Tasks.Task context) [0x00000] in /Developer/MonoTouch/Source/mono/mcs/class/corlib/System.Threading.Tasks/TaskActionInvoker.cs:70
  at System.Threading.Tasks.Task.InnerInvoke () [0x00028] in /Developer/MonoTouch/Source/mono/mcs/class/corlib/System.Threading.Tasks/Task.cs:478
  at System.Threading.Tasks.Task.ThreadStart () [0x0007b] in /Developer/MonoTouch/Source/mono/mcs/class/corlib/System.Threading.Tasks/Task.cs:390
  --- End of inner exception stack trace ---
  at System.Threading.Tasks.Parallel.HandleExceptions (IEnumerable`1 tasks, System.Threading.Tasks.ExternalInfos infos) [0x00068] in /Developer/MonoTouch/Source/mono/mcs/class/corlib/System.Threading.Tasks/Parallel.cs:85
  at System.Threading.Tasks.Parallel.ForEach[ValueAndSquare,Object] (System.Func`2 enumerable, System.Threading.Tasks.ParallelOptions options, System.Func`1 init, System.Func`4 action, System.Action`1 destruct) [0x00000] in <filename unknown>:0
  at System.Threading.Tasks.Parallel.ForEach[ValueAndSquare,Object] (System.Collections.Concurrent.Partitioner`1 source, System.Threading.Tasks.ParallelOptions parallelOptions, System.Func`1 localInit, System.Func`4 body, System.Action`1 localFinally) [0x00000] in <filename unknown>:0
  at System.Threading.Tasks.Parallel.ForEach[ValueAndSquare] (IEnumerable`1 source, System.Action`1 body) [0x00000] in <filename unknown>:0
  at ParallelTest.ParallelTestViewController.ViewDidLoad () [0x000e1] in /Users/eckertia/Projects/ParallelTest/ParallelTest/ParallelTestViewController.cs:74
  at MonoTouch.UIKit.UIWindow.MakeKeyAndVisible () [0x00008] in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIWindow.g.cs:124
  at ParallelTest.AppDelegate.FinishedLaunching (MonoTouch.UIKit.UIApplication app, MonoTouch.Foundation.NSDictionary options) [0x00031] in /Users/eckertia/Projects/ParallelTest/ParallelTest/AppDelegate.cs:60
  at MonoTouch.UIKit.UIApplication.Main (System.String[] args, System.String principalClassName, System.String delegateClassName) [0x0004c] in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIApplication.cs:38
  at ParallelTest.Application.Main (System.String[] args) [0x00000] in /Users/eckertia/Projects/ParallelTest/ParallelTest/Main.cs:44
Comment 1 Isaac.Eckert 2012-11-14 16:51:56 UTC
Here is a workaround to get the example to work:

//by declaring the generic type with parameters in code can I avoid JITting
KeyValuePair<long, ValueAndSquare> avoidJIT= new KeyValuePair<long, ValueAndSquare>();

System.Threading.Tasks.Parallel.ForEach(values, value=> {
        value.Square = value.Value * value.Value;
      });


Thanks
Comment 2 Marek Safar 2013-01-17 11:53:32 UTC
This is due to current AOT limitation. 

Unfortunately KeyValuePair<long, TSource> is part if public .NET API in OrderablePartitioner class and we cannot really change it.

The best "hack" I could figure out is to add to all ForEach/ForAll overloads (there is many of then) default parameter to force closed type injection to user code. Something like

-               public static ParallelLoopResult ForEach<TSource> (IEnumerable<TSource> source, Action<TSource> body)
+               public static ParallelLoopResult ForEach<TSource> (IEnumerable<TSource> source, Action<TSource> body,
+#if MONOTOUCH
+            KeyValuePair<long, TSource> aotToken = default (KeyValuePair<long, TSource>)
+#endif
+                                                                                                                  

I am not sure it's worth it when the workaround is quite simple and we should have generic solution soon.
Comment 3 Miguel de Icaza [MSFT] 2013-02-11 22:09:54 UTC
Marek, can we close this one, since the patch was added to the latest 6.0.xx series?

Do we have a test case in our unit testing framework for this?
Comment 4 Marek Safar 2013-02-12 11:58:21 UTC
Yes test was added.

Only Parallel.ForEach was fixed, AsParallel().ForAll() is still failing because it's an extension method over generic interface.