Notice (2018-05-24): bugzilla.xamarin.com is now in
Please join us on
Visual Studio Developer Community and in the
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
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.
our mac application has suffered from this crash, randomly/occasionally for quite some time. we never reported it because we could not produce a reliable test case. regardless of that fact, i'm reporting it now because i'm banging my head against the wall to no avail.
our code is calling:
NSDate.FromTimeIntervalSinceNow((double)wait / 1000.0)
where "wait" is some integer greater than zero.
we call this in our ui eventloop (i.e. very often) and only once in a while (like once every few weeks) we get:
Exception Source: MonoMac
Exception Type: System.InvalidCastException
Exception Target Site: NSDate.FromTimeIntervalSinceNow
Exception Message: Cannot cast from source type to destination type.
the crash happens where the result of Runtime.GetNSObject() is cast to NSDate, as though GetNSObject() is, in some case, returning an object of the wrong type. i can find no documented case where dateWithTimeIntervalSinceNow: returns something other than an NSDate; therefore, the only possibility is that the managed object -> native object caching in Runtime.cs is at fault.
my guess is that the object_map cache in is holding stale values. our occasional crash happens when a newly allocated native NSDate ends up with an address that matches a stale cache item. then the runtime returns the wrong object.
i have a theory but i'm uncertain how to fix. can someone comment / take it from here?
i'm going to start running with --debug=casts so that next time this occurs we'll see what the source type in the invalid cast is. i'll report back next time i reproduce!
Please provide a test case.
During MonoSpace we theorized that this scenario could happen and would love to see a test case.
i would love to, miguel, but i'm unable. every test case i've attempted has run for days without crashing.
i've instrumented Runtime.cs to log activity related to the object_map. next time i reproduce i'll report details.
using --debug=casts and some trace in Runtime.cs i was able to trace the source of the problem to an NSObject subclass in our code. that class is around from back when we used monobjc.
--debug=casts told me that the object that was being cast to an NSDate was actually an instance of our NSObject subclass (let's call it MyObject).
the trace in Runtime.cs showed that when MyObject was deallocated its pointer would be unregistered from the objects_map but then immediately registered again (at the same address). for some reason the managed object was remaining associated with that memory address even though the native object was gone (and that address available for reuse).
i still don't understand why the object was being re-registered, but i found the code that triggers it. MyObject was implementing Dealloc, like so:
public void Dealloc()
// some code here
regardless of what is in the body of Dealloc, just it's presence caused this weird behavior. when i removed it, the weird re-register behavior went away and with it the InvalidCastException.
i replaced the dealloc business with an override of NSObject.Dispose. i believe at the time this code was written monobjc required implementing dealloc to do clean up. i understand that's not the way to do it with monomac, but i am still curious why it causes this odd behavior and whether that behavior is a bug. anyone care to comment?
Created attachment 166 [details]
demo that reproduces the cast exception
I've encountered this bug too, only without using any custom classes. The NSNumber.From*() methods seem to be enough to trigger it. The simplest way to recreate it I've found is to create a console project, add a reference to MonoMac and put the following code in Main():
using (NSAutoreleasePool pool = new NSAutoreleasePool())
NSObject obj = new NSObject();
NSNumber num = NSNumber.FromInt32 (42);
This causes the InvalidCastException every time for me, using MonoDevelop 2.8.1 and the MonoMac build supplied with it on OS X 10.7.2 and Mono 2.10.6.
After some further investigation, I think in my case it's a different bug. The NSNumber class is not added to Class.type_map unless one of its constructors is called. Adding a call to new NSNumber() before the call to NSNumber.FromInt32() prevents the exception.
If the new NSObject() call is omitted, Class.LookUp() enters an infinite loop suggesting that the root cause is the same as that of bug 408.
Would you mind opening a separate bug for the NSNumber case, as the original bug reported is a misuse of the MonoMac API? I am closing this bug due to the original source, while your case is different.
We take over memory management in MonoMac and MonoTouch, which is why hooking up to dealloc is interfering with us. You *could* go down that route if you wanted to, but it would require you to understand entirely the Runtime.cs and provide all the same code that MonoMac already does for you to avoid stepping on MonoMac's feet.
The right approach as you noted is to override Dispose (bool disposing) which is the right .NET way of doing those things.
I have opened bug 1825 for the problem I was experiencing.