Bug 19288 - new NSNUMBER(bool,int) fails randomly with NaN on the background thread.
Summary: new NSNUMBER(bool,int) fails randomly with NaN on the background thread.
Status: VERIFIED FIXED
Alias: None
Product: iOS
Classification: Xamarin
Component: XI runtime ()
Version: master
Hardware: PC Mac OS
: --- normal
Target Milestone: Untriaged
Assignee: Bugzilla
URL:
Depends on:
Blocks:
 
Reported: 2014-04-24 17:00 UTC by Paul Auman
Modified: 2014-05-30 11:03 UTC (History)
7 users (show)

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


Attachments
Test Project for NSNumber issue. (13.78 KB, application/zip)
2014-04-24 17:00 UTC, Paul Auman
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:
VERIFIED FIXED

Description Paul Auman 2014-04-24 17:00:14 UTC
Created attachment 6652 [details]
Test Project for NSNumber issue.

The original problem presented itself when importing large amounts of data on a background thread. NSNumber would randomly return a nil object (i.e. NaN).

See attached MT project that produces the fault over a large number of iterations.

Given :

bool boolShouldAlwaysBeTrue = true; 

Results :

NSNumber number = boolShouldAlwaysBeTrue;  - Work 100% of the time
NSNumber number = new NSNumber(boolShouldAlwaysBeTrue); - Fails 1349 times, 452752 iterations.
NSNumber number = NSNumber.FromBoolean(boolShouldAlwaysBeTrue)  - Work 100% of the time, if it is NOT preceded by a new NSNumber();
NSNumber number = NSNumber.FromBoolean(boolShouldAlwaysBeTrue)  - Fails 452, 699234 iterations.if it is preceded by a new NSNumber();

This holds true for all NSNumber( int, int32, ..) except float. And this issues seems to be limited to the background thread.

The example is simple to SHOW that the issue is reproducible. The side effects in a large scale product are numerous. We are in the late stages of multiyear production push and this reproducible RANDOM issue will generate such issues as :

NSDictionary options = NSDictionary.FromObjectAndKey(NSNumber.FromBoolean(true) , new NSString("NSReadOnlyPersistentStoreOption"));
            
EXCEPTION : Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSPlaceholderDictionary initWithObjects:forKeys:count:]: attempt to insert nil object from objects[0]'

The NSNumber.FromBoolean(true) is returning nil ?

or

A non optional NSManagedObject property of type NSNumber when will generate the following exception :

The operation couldn’t be completed. (Cocoa error 1560.)

 NSDetailedErrors =     (
       "Error Domain=NSCocoaErrorDomain Code=1570 \"The operation couldn\U2019t be completed. (Cocoa error 1570.)\" UserInfo=0x15baaf80 {NSValidationErrorObject=<Trip: 0x161c6ea0> (entity: Trip; id: 0x165a0e80 <x-coredata://7BA2B12A-95FC-49E0-9EBA-F31C697FD602/Trip/p115> ; data: {\n    active = nil;

In this case active is a non optional property set with NSNumber.FromBool(true);


As stated in the demo project the behavior seems to be limited to the background thread and when we alloc/init a nsobject (in this sample it is NSNumber). 

A search of the bugzilla returned a possible match of similar behavior Bug 7723.

Please feel free to contact me directly for any information you need.

Paul Auman
Auman Software, LLC
727-688-1631
Comment 1 Sebastien Pouliot 2014-04-24 17:35:19 UTC
In general we have a valid Handle (and always the same, it's a singleton for True) and an "infinite" retainCount (can't be released).

> ? nsnumber_fromboolean.Handle
0x17ba350
> ? nsnumber_fromboolean.RetainCount
2147483647

but when this fails we have:

> ? nsnumber_fromboolean.Handle
0x0
> ? nsnumber_fromboolean.RetainCount
0

Same happens with boehm or sgen, with (or without) NRC.
Comment 2 Sebastien Pouliot 2014-04-24 19:59:13 UTC
The problem can be seen with just the first case:

	NSNumber new_nsnumber = new NSNumber(boolShouldAlwaysBeTrue);
	Console.WriteLine ("{0} - handle {1}", i, new_nsnumber.Handle);
	if(new_nsnumber.BoolValue==false) {
		Console.WriteLine ("{0} - handle {1}", i, new_nsnumber.Handle);
		failedAttemptsWithNewNSNumber++;
        }

The Handle gets reset to 0 after the .ctor call, either the first or second CWL. i.e. sometimes I get:

2014-04-24 19:30:39.484 NSNumberProblems[53801:5c03] 517 - handle 0
2014-04-24 19:30:39.485 NSNumberProblems[53801:5c03] 517 - handle 0

and other times:

2014-04-24 19:30:39.484 NSNumberProblems[53801:5c03] 517 - handle 24879952
2014-04-24 19:30:39.485 NSNumberProblems[53801:5c03] 517 - handle 0

It seems related only to (Apple-optimized) singleton instances, i.e. NSNumber bools (cheaper than to recreate them every time), some integers but not floats values (not worth it).

I _think_ that we, somewhere, set the Handle to 0 in a case where multiple managed object points to the same native object. That's not an issue in itself unless, at the same time, we return this object again to be reused (based on it's identical Handle - just before it's set to 0). IOW it's unique somewhere and it's not unique somewhere else).
Comment 4 Miguel de Icaza [MSFT] 2014-04-25 16:09:08 UTC
Sebastien is on to something;   It seems like the call to initWithBool returns a new handle, and Objective-C is releasing our handle behind our backs.

While trying to repro this, I got this very strange behavior from Xcode.   Calling alloc without init on the same statement makes Xcode flicker with value updates, and the process starts consuming memory and displaying error messages non-stop:

http://screencast.com/t/tyqkmJe5
Comment 5 Rolf Bjarne Kvinge [MSFT] 2014-04-25 17:22:18 UTC
Fixed.

Thanks for the perfect test case!

monotouch/master: 4a32e5a533e173db07dbd511ac83f68b5641cdaa.
Comment 6 Rolf Bjarne Kvinge [MSFT] 2014-04-25 17:24:31 UTC
QA: unit test was added as well.
Comment 7 narayanp 2014-05-30 11:03:39 UTC
I have checked this issue with following builds:

All Mac 
X.S 5.1 (Build 261)
Git revision: f5f2b5e52eeab3d8ec5c7cc0e5cd59997992f99b
X.iOS 7.2.99.403

I am successfully able to run attached sample on iOS Simulator and iOS Device.