Bug 1579 - Error occurs using NSOperationQueue and custom NSOperation
Summary: Error occurs using NSOperationQueue and custom NSOperation
Status: RESOLVED FIXED
Alias: None
Product: iOS
Classification: Xamarin
Component: General ()
Version: 5.0
Hardware: Macintosh Mac OS
: --- normal
Target Milestone: Untriaged
Assignee: Bugzilla
URL:
Depends on:
Blocks:
 
Reported: 2011-10-19 11:39 UTC by Sergio Fadda
Modified: 2011-10-20 20:25 UTC (History)
4 users (show)

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


Attachments
MonoDevelop project w/ test case (6.12 KB, application/zip)
2011-10-19 12:37 UTC, Jeffrey Stedfast
Details
updated log with backtrace of each retain call too (11.75 KB, text/plain)
2011-10-20 12:28 UTC, Jeffrey Stedfast
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 Sergio Fadda 2011-10-19 11:39:15 UTC
Environment:
- MT 5.0
- MonoDevelop 2.8.1
- Mono 2.10.6
- XCode 4.2

The issue occurs only when the application runs in iOS 5 (both physical device and simulator); running under 4.3 (both physical device and simulator) works fine!

The following program:

[CODE]
using System;
using System.Drawing;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
using System.Collections.Generic;

namespace MTFailure
{
	public class Application
	{
		static void Main (string[] args)
		{
			UIApplication.Main (args, null, "AppDelegate");
		}
	}
	
	public class MyOperation : NSOperation
	{
		public MyOperation()
			: base()
		{
		}
		
		public override void Main ()
		{
			// Do nothing!!!
		}
	}
	
	[Register("AppDelegate")]
	public class AppDelegate : UIApplicationDelegate
	{
		private MonoTouch.UIKit.UIWindow _window;
		private UINavigationController _navCtrl;
		
		public override bool FinishedLaunching (UIApplication app, NSDictionary options)
		{
			_window = new UIWindow(UIScreen.MainScreen.Bounds);
			_window.BackgroundColor = UIColor.White;
			
			_navCtrl = new UINavigationController();
			_navCtrl.PushViewController(CreatePage(), true);
			
			_window.AddSubview(_navCtrl.View);
			_window.MakeKeyAndVisible ();
	
			return true;
		}
		
		protected UIViewController CreatePage()
		{
			UIViewController result = new UIViewController();
			UIView pageView = new UIView(UIScreen.MainScreen.Bounds);
			UIButton btn = UIButton.FromType(UIButtonType.RoundedRect);
			
			btn.Frame = new RectangleF(10, 10, 300, 37);
			btn.TouchUpInside += HandleBtnTouchUpInside;
			btn.SetTitle("Touch me!!!", UIControlState.Normal);
			
			pageView.AddSubview(btn);
			
			result.View = pageView;
			
			return result;
		}

		private NSOperationQueue _queue = new NSOperationQueue();
		
		void HandleBtnTouchUpInside (object sender, EventArgs e)
		{
			// Enqueue the operation
			_queue.AddOperation(new MyOperation());
		}

		// This method is required in iPhoneOS 3.0
		public override void OnActivated (UIApplication application)
		{
		}
	}
}
[/CODE]

crashes after tap (can be necessary more than one tap) the button; two possible error messages can appear:

[CODE]
Stacktrace:

  at (wrapper managed-to-native) MonoTouch.ObjCRuntime.Messaging.void_objc_msgSend (intptr,intptr) <IL 0x00024, 0xffffffff>
  at MonoTouch.Foundation.NSObject/MonoTouch_Disposer.Drain (MonoTouch.Foundation.NSObject) [0x0006a] in /Developer/MonoTouch/Source/monotouch/src/Foundation/NSObject.cs:321
  at (wrapper runtime-invoke) <Module>.runtime_invoke_void__this___object (object,intptr,intptr,intptr) <IL 0x00052, 0xffffffff>
  at (wrapper managed-to-native) MonoTouch.UIKit.UIApplication.UIApplicationMain (int,string[],intptr,intptr) <IL 0x0009f, 0xffffffff>
  at MonoTouch.UIKit.UIApplication.Main (string[],string,string) [0x00042] in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIApplication.cs:29
  at MTFailure.Application.Main (string[]) [0x00000] in /Users/sergiofadda/Projects/MonoTest/GenericsFailure/GenericsFailure/Main.cs:13
  at (wrapper runtime-invoke) <Module>.runtime_invoke_void_object (object,intptr,intptr,intptr) <IL 0x00050, 0xffffffff>

Native stacktrace:

	0   MTFailure                           0x000d5c18 mono_handle_native_sigsegv + 408
	1   MTFailure                           0x00012fcf mono_sigsegv_signal_handler + 351
	2   libsystem_c.dylib                   0x9435859b _sigtramp + 43
	3   ???                                 0xffffffff 0x0 + 4294967295
	4   ???                                 0x0c63108c 0x0 + 207818892
	5   ???                                 0x06fc4046 0x0 + 117194822
	6   MTFailure                           0x00012d1f mono_jit_runtime_invoke + 1407
	7   MTFailure                           0x0020a33a mono_runtime_invoke + 170
	8   MTFailure                           0x002ca0f5 monotouch_trampoline + 3381
	9   CoreFoundation                      0x01805e72 -[NSObject performSelector:withObject:] + 66
	10  Foundation                          0x006a79ef __NSThreadPerformPerform + 254
	11  CoreFoundation                      0x017d897f __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 15
	12  CoreFoundation                      0x0173bb73 __CFRunLoopDoSources0 + 243
	13  CoreFoundation                      0x0173b454 __CFRunLoopRun + 1012
	14  CoreFoundation                      0x0173adb4 CFRunLoopRunSpecific + 212
	15  CoreFoundation                      0x0173accb CFRunLoopRunInMode + 123
	16  GraphicsServices                    0x022a2879 GSEventRunModal + 207
	17  GraphicsServices                    0x022a293e GSEventRun + 114
	18  UIKit                               0x009dea9b UIApplicationMain + 1175
	19  ???                                 0x095a1405 0x0 + 156898309
	20  ???                                 0x095a0688 0x0 + 156894856
	21  ???                                 0x095a0380 0x0 + 156894080
	22  ???                                 0x095a04d6 0x0 + 156894422
	23  MTFailure                           0x00012d1f mono_jit_runtime_invoke + 1407
	24  MTFailure                           0x0020a33a mono_runtime_invoke + 170
	25  MTFailure                           0x0020cf61 mono_runtime_exec_main + 705
	26  MTFailure                           0x0020c1b1 mono_runtime_run_main + 929
	27  MTFailure                           0x000a78cf mono_jit_exec + 239
	28  MTFailure                           0x00004cca main + 5194
	29  MTFailure                           0x00002085 start + 53

=================================================================
Got a SIGSEGV while executing native code. This usually indicates
a fatal error in the mono runtime or one of the native libraries 
used by your application.
=================================================================
[/CODE]

or

[CODE]
Stacktrace:


Native stacktrace:

	0   MTFailure                           0x000d5c18 mono_handle_native_sigsegv + 408
	1   MTFailure                           0x00012fcf mono_sigsegv_signal_handler + 351
	2   libsystem_c.dylib                   0x9435859b _sigtramp + 43
	3   ???                                 0xffffffff 0x0 + 4294967295
	4   libobjc.A.dylib                     0x0199dc09 _class_getVariable + 99
	5   libobjc.A.dylib                     0x0199415f object_getInstanceVariable + 56
	6   MTFailure                           0x002cc543 monotouch_release_trampoline + 419
	7   Foundation                          0x00747d16 __release_object_op + 37
	8   libdispatch.dylib                   0x0214cc7b _dispatch_async_f_redirect_invoke + 146
	9   libdispatch.dylib                   0x0214c4e6 _dispatch_worker_thread2 + 284
	10  libsystem_c.dylib                   0x94302b24 _pthread_wqthread + 346
	11  libsystem_c.dylib                   0x943046fe start_wqthread + 30

=================================================================
Got a SIGSEGV while executing native code. This usually indicates
a fatal error in the mono runtime or one of the native libraries 
used by your application.
=================================================================
[/CODE]
Comment 1 Jeffrey Stedfast 2011-10-19 12:37:23 UTC
Created attachment 722 [details]
MonoDevelop project w/ test case

Looks like something is corrupting memory. For some reason, getIvar() is trying to access a very suspicious memory address.

Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_PROTECTION_FAILURE at address: 0x00000014
[Switching to process 8750 thread 0x10f]
0x036bcc66 in getIvar ()
(gdb) thread apply all bt

Thread 10 (process 8750):
#0  0x962fb412 in __workq_kernreturn ()
#1  0x962fb9a8 in _pthread_wqthread ()
#2  0x962fb5c6 in start_wqthread ()

Thread 9 (process 8750):
#0  0x036bcc66 in getIvar ()
#1  0x036bcc09 in _class_getVariable ()
#2  0x036b315f in object_getInstanceVariable ()
#3  0x002f08b3 in monotouch_release_trampoline (this=0x9c59b10, sel=0x36cd405) at monotouch-glue.m:802
#4  0x018edd16 in __release_object_op ()
#5  0x03885c7b in _dispatch_async_f_redirect_invoke ()
#6  0x038854e6 in _dispatch_worker_thread2 ()
#7  0x962fb781 in _pthread_wqthread ()
#8  0x962fb5c6 in start_wqthread ()

Thread 8 (process 8750):
#0  0x962fb5a8 in start_wqthread ()
#1  0x00000000 in ?? ()

Thread 7 (process 8750):
#0  0x962d5afa in mach_msg_trap ()
#1  0x962d6267 in mach_msg ()
#2  0x012ab13a in __CFRunLoopServiceMachPort ()
#3  0x0120e605 in __CFRunLoopRun ()
#4  0x0120ddb4 in CFRunLoopRunSpecific ()
#5  0x0120dccb in CFRunLoopRunInMode ()
#6  0x074ab220 in RunWebThread ()
#7  0x96303259 in _pthread_start ()
#8  0x963030de in thread_start ()

Thread 6 (process 8750):
#0  0x96303aa2 in __semwait_signal ()
#1  0x9632f9c5 in nanosleep$UNIX2003 ()
#2  0x9632f903 in usleep$UNIX2003 ()
#3  0x002e9d74 in monotouch_pump_gc (context=0x0) at monotouch-glue.m:428
#4  0x96303259 in _pthread_start ()
#5  0x963030de in thread_start ()

Thread 5 (process 8750):
#0  0x963130d6 in recvfrom$UNIX2003 ()
#1  0x96332eab in recv$UNIX2003 ()
#2  0x001013fa in recv_length (fd=7, buf=0xb0388eb9, len=11, flags=0) at debugger-agent.c:996
#3  0x00112071 in debugger_thread (arg=0x0) at debugger-agent.c:7136
#4  0x002abe1e in thread_start_routine (args=0xa84712c) at wthreads.c:287
#5  0x002e5d68 in GC_start_routine (arg=0x9a6af60) at pthread_support.c:1468
#6  0x96303259 in _pthread_start ()
#7  0x963030de in thread_start ()

Thread 4 (process 8750):
#0  0x962d5b36 in semaphore_wait_trap ()
#1  0x002bb382 in mono_sem_wait (sem=0x3c4694, alertable=1) at mono-semaphore.c:115
#2  0x0019d312 in finalizer_thread (unused=0x0) at gc.c:1070
#3  0x00266a11 in start_wrapper_internal (data=0x9c191b0) at threads.c:783
#4  0x00266ac7 in start_wrapper (data=0x9c191b0) at threads.c:831
#5  0x002abe1e in thread_start_routine (args=0xa847034) at wthreads.c:287
#6  0x002e5d68 in GC_start_routine (arg=0x9a6af60) at pthread_support.c:1468
#7  0x96303259 in _pthread_start ()
#8  0x963030de in thread_start ()

Thread 3 (process 8750):
#0  0x962d5afa in mach_msg_trap ()
#1  0x962d6267 in mach_msg ()
#2  0x00152461 in mach_exception_thread (arg=0x0) at mini-darwin.c:132
#3  0x002e5d68 in GC_start_routine (arg=0x9a6af60) at pthread_support.c:1468
#4  0x96303259 in _pthread_start ()
#5  0x963030de in thread_start ()

Thread 2 (process 8750):
#0  0x962fc382 in kevent ()
#1  0x03886373 in _dispatch_mgr_invoke ()
#2  0x03884cd0 in _dispatch_mgr_thread ()

Thread 1 (process 8750):
#0  0x962d5afa in mach_msg_trap ()
#1  0x962d6267 in mach_msg ()
#2  0x012ab13a in __CFRunLoopServiceMachPort ()
#3  0x0120e580 in __CFRunLoopRun ()
#4  0x0120ddb4 in CFRunLoopRunSpecific ()
#5  0x0120dccb in CFRunLoopRunInMode ()
#6  0x0489f879 in GSEventRunModal ()
#7  0x0489f93e in GSEventRun ()
#8  0x022a6a9b in UIApplicationMain ()
#9  0x09ffca85 in ?? ()
#10 0x09ffaf30 in ?? ()
#11 0x09ffac28 in ?? ()
#12 0x09ffad7e in ?? ()
#13 0x000118ff in mono_jit_runtime_invoke (method=0xa056e34, obj=0x0, params=0xbfffe6e8, exc=0x0) at mini.c:5770
#14 0x0021ff4a in mono_runtime_invoke (method=0xa056e34, obj=0x0, params=0xbfffe6e8, exc=0x0) at object.c:2757
#15 0x00222c81 in mono_runtime_exec_main (method=0xa056e34, args=0xe005660, exc=0x0) at object.c:3940
#16 0x00221e91 in mono_runtime_run_main (method=0xa056e34, argc=0, argv=0xbfffe890, exc=0x0) at object.c:3562
#17 0x000ad4ef in mono_jit_exec (domain=0x9a6fe00, assembly=0x990b200, argc=1, argv=0xbfffe88c) at driver.c:1102
#18 0x002f409f in main (argc=2, argv=0xbfffea54) at template.m:2476
(gdb)
Comment 2 Jeffrey Stedfast 2011-10-19 12:46:54 UTC
I'm actually able to reproduce this on the iPhone 4.3 simulator as well.
Comment 3 Jeffrey Stedfast 2011-10-19 13:35:26 UTC
A workaround for this bug seems to be to use the NSOperationQueue.AddOperation (NSAction) version of the API instead.


so... you could do something like this:

NSOperationQueue _queue = new NSOperationQueue ();
void HandleBtnTouchUpInside (object sender, EventArgs e)
{
    _queue.AddOperation (DeferredActionOnMainThread);
}

void DeferredActionOnMainThread ()
{
   InvokeOnMainThread (DeferredAction);
}
Comment 4 Jeffrey Stedfast 2011-10-19 15:49:21 UTC
Okay, so what is going on here is that 'release' is called on the MyUIOperation when it has a refcount of 2, it calls super release (afterwards, refcount is *still* 2) and then we fetch & free the gchandle.

then 'release' is called on the same object again (refcount still 2!), we call super release (afterwards, refcount is *still* 2), fetch the gchandle (which is now 0 because of the previous release), and no-op because there's no gchandle to free.

so far so good (except that the refcount isn't dropping as expected).

but then release gets called on the MyUIOperation *again*, still the reported refcount is 2, this time we crash hard when we call super release.


log:

monotouch_release_trampoline (OperationQueue.AppDelegate+MyUIOperation 0xb964370 Handle=194397040) refs=2
	updated ref_count is now 2
	gchandle destroyed
monotouch_release_trampoline (OperationQueue.AppDelegate+MyUIOperation 0xb964370 Handle=194397040) refs=2
	updated ref_count is now 2
	no gchandle for this object
monotouch_release_trampoline (OperationQueue.AppDelegate+MyUIOperation 0xb964370 Handle=194397040) refs=2
*crash*
Comment 5 Jeffrey Stedfast 2011-10-19 18:15:19 UTC
Wrote an Objective-C version of this app to see what is going on...

if you don't implement Start(), then NSOperation's start method is invoked which first refs itself, then calls main(), then unrefs itself.

Somehow this is confusing MonoTouch's release trampoline, but I don't understand why.



Interestingly, if I implement Start() on MyUIOperation and avoid calling base.Start() (and instead just call Main()), then the crash is avoided.
Comment 6 Jeffrey Stedfast 2011-10-19 18:54:11 UTC
this is interesting:


In the ObjC case, when [NSOperation start] is called, the refcount is 2.
  - when main() is called, the refcount is 3 (as expected, because start() refs itself before calling main)
  - then start unrefs itself after calling main, bringing the refcount back to 2.
  - the operation then gets unreffed twice, freeing the operation.


in the C# case, here's what happens:
  - init refcount is 1, we add it to the queue, we intercept 'retain' and the current refcount is 1, we call super retain and the resulting refcount is *still* 1
  - start is invoked, it refs itself, we intercept 'retain' and the current refcount is 1, we call super retain and the resulting refcount is *still* 1
  - main is invoked and we do our thing
  - control returns back to start, it release's itself, we intercept 'release' and the current refcount is 2, we call super release and the refcount is *still* 2
  - the operation then gets unreffed twice just like the ObjC case, except now it crashes because we should have had a refcount of 3, but it was 2 at the end of main().


are retains and releases async or something?
Comment 8 Sergio Fadda 2011-10-20 04:06:51 UTC
Good!
Only a question: I don't understand if the workaround based on overriding the 'Start' method can have side effects (such as memory leaks)... is it safe?
I've tried it on my project and now everything works!!!
Comment 9 Jeffrey Stedfast 2011-10-20 08:35:14 UTC
Looks like I was misreading the log last night. Re-examining shows that we aren't getting a retain call when the start method is invoked for some reason, but clearly it *thinks* it called retain, because it is calling release immediately after the main method returns.


Sergio: it shouldn't cause any leaks because all it does is prevent that unmatched call to release, so you should be good.
Comment 11 Jeffrey Stedfast 2011-10-20 18:33:25 UTC
This is an update.

According to the documentation, implementing a Start() method has plenty of extra complexities like raising notifications, a prohibition on ever calling the base.Start() and a series of other oddities that are requires for a complete NSOperation to be functional.

The documentation strongly recommends that for simple cases, you should use NSInvocationOperation (the old per-block style API) or NSBlockOperation.

So you should be using NSBlockOperation, like this:

var myOp = NSBlockOperation.Create (delegate {....});
myQueue.AddOperation (myOp);

Or alternatively, you can use the simpler:

myQueue.AddOperation (delegate {...});

Which uses the block directly.
Comment 13 Miguel de Icaza [MSFT] 2011-10-20 20:25:17 UTC
fixed, not a bug.