Bug 6460 - performSelector:withObject:afterDelay improperly bound
Summary: performSelector:withObject:afterDelay improperly bound
Status: RESOLVED FIXED
Alias: None
Product: MonoMac
Classification: Desktop
Component: Bindings ()
Version: unspecified
Hardware: Macintosh Mac OS
: --- normal
Target Milestone: ---
Assignee: Bugzilla
URL:
Depends on:
Blocks:
 
Reported: 2012-08-07 22:05 UTC by steven.orth
Modified: 2012-08-15 16:14 UTC (History)
4 users (show)

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

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 steven.orth 2012-08-07 22:05:28 UTC
The binding for NSObject.PerformSelector(selector, data, delay) is incorrectly using a float value for delay rather than a double. This could potentially affect other bindings to methods / properties that use NSTimeInterval, which is a double. Have not done a pass through the Mono APIs to check for this.

IT SEEMS THAT THE SAME BUG MAY BE PRESENT FOR MonoTouch AS WELL -- have not verified this, though - but the same P/Invoke calls are made, it seems.

Initially noticed this after upgrading to MonoDevelop 3.0.3.5 -- unsure if this was due to the update, or if "something" changed and a latent problem was exposed -- our code had not changed when this bug was detected.

The changed behavior was that some (but not all) selectors were not called at all  -- nor were custom data objects passed to the uncalled selectors ever GC'd. I suspect that four of the bytes in the expected double precision 'delay' value were garbage, and happened to result in extremely large values for 'delay'. Also observed that when requesting a long delay, e.g. 10 seconds, selectors that were called were definitely *not* called after 10 sec -- usually they fired right away, or not at all.

After making the change described below, selectors seem to be called after the expected delay, and are not missed.

Docs for performSelector:withObject:afterDelay -->

https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/nsobject_Class/Reference/Reference.html#//apple_ref/occ/instm/NSObject/performSelector:withObject:afterDelay:

As a workaround, you can do the following...
A) Use BeginInvokeOnUIThread() instead if 'delay' is not necessary, and it's OK to execute selector on UI thread
B) Patch PerformSelector():
  1) Add necessary P/Invoke calls to pass delay as a double (aren't in the Messaging class)
  2) For classes you're interested in using NSObject.PerformSelector, override PerformSelector and call the new entry points.

SAMPLE CODE:

    // Add P/Invoke for objc_msgSend / objc_msgSendSuper that accept double arg
    internal static class MyMessaging
    {
        [DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
        public static extern void void_objc_msgSend_intptr_intptr_Double(IntPtr receiver, IntPtr selector, IntPtr arg1, IntPtr arg2, double arg3);

        [DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSendSuper")]
        public static extern void void_objc_msgSendSuper_intptr_intptr_Double(IntPtr receiver, IntPtr selector, IntPtr arg1, IntPtr arg2, double arg3);
    }

    // Patch PerformSelector
    public partial MyClass
    {
        private static IntPtr selPerformSelectorWithObjectAfterDelay = MonoMac.ObjCRuntime.Selector.GetHandle ("performSelector:withObject:afterDelay:");

        public override void PerformSelector (MonoMac.ObjCRuntime.Selector sel, NSObject obj, float delay)
        {
            if (this.IsDirectBinding)
            {
                MyMessaging.void_objc_msgSend_intptr_intptr_Double(this.Handle, selPerformSelectorWithObjectAfterDelay, sel.Handle, (obj != null) ? obj.Handle : IntPtr.Zero, delay);
            }
            else
            {
                MyMessaging.void_objc_msgSendSuper_intptr_intptr_Double(this.SuperHandle, selPerformSelectorWithObjectAfterDelay, sel.Handle, (obj != null) ? obj.Handle : IntPtr.Zero, delay);
            }
        }
    }
Comment 1 Martin Baulig 2012-08-15 15:39:40 UTC
I just fixed that in monomac commit 7d9f63.  Long delays are working fine after doing the float->double change, I tested several times with 0, 1, 3, 5, 10, 15 and 45 seconds.

Monotouch is not done yet, but I'll ask someone from the monotouch team to have a look at it.
Comment 2 Miguel de Icaza [MSFT] 2012-08-15 16:14:41 UTC
This was a typo in MonoMac, MonoTouch did not have that problem.