Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CoreFoundation] Implement missing dispatch API. Fixes #4606. #4967

Merged
merged 9 commits into from
Oct 17, 2018
181 changes: 180 additions & 1 deletion src/CoreFoundation/Dispatch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,17 @@ public enum DispatchQueuePriority : int {
Low = -2,
Background = Int16.MinValue
}

// dispatch_qos_class_t is defined in usr/include/dispatch/queue.h, but redirects to qos_class_t
// the qos_class_t enum is defined in usr/include/sys/qos.h (typed as 'unsigned int')
public enum DispatchQualityOfService : uint {
UserInteractive = 0x21,
UserInitiated = 0x19,
Default = 0x15,
Utility = 0x11,
Background = 0x09,
Unspecified = 0x00,
}

public abstract class DispatchObject : NativeObject
{
Expand Down Expand Up @@ -140,6 +151,22 @@ public void SetTargetQueue (DispatchQueue queue)

[DllImport (Constants.libcLibrary)]
internal extern static void dispatch_suspend (IntPtr o);

[Mac (10,12)]
[iOS (10,0)]
[TV (10,0)]
[Watch (3,0)]
public void Activate ()
{
dispatch_activate (GetCheckedHandle ());
}

[DllImport (Constants.libcLibrary)]
[Mac (10,12)]
[iOS (10,0)]
[TV (10,0)]
[Watch (3,0)]
extern static void dispatch_activate (/* dispatch_object_t */ IntPtr @object);
#endif // !COREBUILD
}

Expand Down Expand Up @@ -177,6 +204,15 @@ public DispatchQueue (string label, bool concurrent)
throw new Exception ("Error creating dispatch queue");
}

[Mac (10,12)]
[iOS (10,0)]
[TV (10,0)]
[Watch (3,0)]
public DispatchQueue (string label, Attributes attributes, DispatchQueue target = null)
: base (dispatch_queue_create_with_target (label, attributes?.Create () ?? IntPtr.Zero, target.GetHandle ()), true)
{
}

//
// Properties and methods
//
Expand Down Expand Up @@ -320,6 +356,14 @@ static void static_dispatcher_iterations_to_managed (IntPtr context, IntPtr coun

}

internal static readonly dispatch_callback_t free_gchandle = static_free_gchandle;

[MonoPInvokeCallback (typeof (dispatch_callback_t))]
static void static_free_gchandle (IntPtr context)
{
GCHandle.FromIntPtr (context).Free ();
}

public void DispatchAsync (Action action)
{
if (action == null)
Expand All @@ -343,7 +387,15 @@ public void DispatchBarrierAsync (Action action)

dispatch_barrier_async_f (Handle, (IntPtr) GCHandle.Alloc (Tuple.Create (action, this)), static_dispatch);
}


public void DispatchBarrierSync (Action action)
{
if (action == null)
throw new ArgumentNullException (nameof (action));

dispatch_barrier_sync_f (Handle, (IntPtr) GCHandle.Alloc (Tuple.Create (action, this)), static_dispatch);
}

public void DispatchAfter (DispatchTime when, Action action)
{
if (action == null)
Expand All @@ -359,12 +411,50 @@ public void Submit (Action<int> action, long times)
dispatch_apply_f ((IntPtr) times, Handle, (IntPtr) GCHandle.Alloc (Tuple.Create (action, this)), static_dispatch_iterations);
}

public void SetSpecific (IntPtr key, object context)
{
dispatch_queue_set_specific (GetCheckedHandle (), key, (IntPtr) GCHandle.Alloc (context), free_gchandle);
}

public object GetSpecific (IntPtr key)
{
GCHandle gchandle = (GCHandle) dispatch_queue_get_specific (GetCheckedHandle (), key);
return gchandle.Target;
}

[Mac (10,10)]
[iOS (8,0)]
public DispatchQualityOfService GetQualityOfService (out int relative_priority)
{
unsafe {
fixed (int* rel_pri = &relative_priority)
return dispatch_queue_get_qos_class (Handle, rel_pri);
}
}

[Mac (10,10)]
[iOS (8,0)]
public DispatchQualityOfService QualityOfService {
get {
unsafe {
return dispatch_queue_get_qos_class (Handle, null);
}
}
}

//
// Native methods
//
[DllImport (Constants.libcLibrary)]
extern static IntPtr dispatch_queue_create (string label, IntPtr attr);

[Mac (10,12)]
[iOS (10,0)]
[TV (10,0)]
[Watch (3,0)]
[DllImport (Constants.libcLibrary)]
extern static IntPtr dispatch_queue_create_with_target (string label, IntPtr attr, IntPtr target);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing availability attributes


[DllImport (Constants.libcLibrary)]
extern static void dispatch_async_f (IntPtr queue, IntPtr context, dispatch_callback_t dispatch);

Expand All @@ -374,6 +464,9 @@ public void Submit (Action<int> action, long times)
[DllImport (Constants.libcLibrary)]
extern static void dispatch_barrier_async_f (IntPtr queue, IntPtr context, dispatch_callback_t dispatch);

[DllImport(Constants.libcLibrary)]
extern static void dispatch_barrier_sync_f (IntPtr queue, IntPtr context, dispatch_callback_t dispatch);

[DllImport (Constants.libcLibrary)]
extern static void dispatch_after_f (/* dispath_time_t */ ulong time, IntPtr queue, IntPtr context, dispatch_callback_t dispatch);

Expand All @@ -388,6 +481,17 @@ public void Submit (Action<int> action, long times)
// this returns a "const char*" so we cannot make a string out of it since it will be freed (and crash)
extern static IntPtr dispatch_queue_get_label (IntPtr queue);

[DllImport(Constants.libcLibrary)]
extern static void dispatch_queue_set_specific (IntPtr queue, /* const void* */ IntPtr key, /* void *_Nullable */ IntPtr context, dispatch_callback_t /* _Nullable */ destructor);

[DllImport(Constants.libcLibrary)]
extern static IntPtr dispatch_queue_get_specific (IntPtr queue, /* const void* */ IntPtr key);

[Mac (10,10)]
[iOS (8,0)]
[DllImport (Constants.libcLibrary)]
unsafe extern static /* dispatch_qos_class_t */ DispatchQualityOfService dispatch_queue_get_qos_class (/* dispatch_queue_t */ IntPtr queue, /* int *_Nullable */ int* relative_priority);

public override bool Equals (object other)
{
DispatchQueue o = other as DispatchQueue;
Expand Down Expand Up @@ -428,6 +532,81 @@ public static void MainIteration ()
dispatch_main ();
}
#endif

public class Attributes
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some members, like IsInitiallyInactive (but I did not check all of them) are missing availability. Not sure if there's a minimum on the type

{
public bool Concurrent { get; set; }

[Mac (10,12)]
[iOS (10,0)]
[TV (10,0)]
[Watch (3,0)]
public bool IsInitiallyInactive { get; set; }

[Mac (10,12)]
[iOS (10,0)]
[TV (10,0)]
[Watch (3,0)]
public AutoreleaseFrequency? AutoreleaseFrequency { get; set; }

[Mac (10,10)]
[iOS (8,0)]
public int RelativePriority { get; set; }

[Mac (10,10)]
[iOS (8,0)]
public DispatchQualityOfService? QualityOfService { get; set; }

internal IntPtr Create ()
{
IntPtr rv = IntPtr.Zero;

if (Concurrent)
rv = DispatchQueue.ConcurrentQueue;

if (IsInitiallyInactive)
rv = dispatch_queue_attr_make_initially_inactive (rv);

if (AutoreleaseFrequency.HasValue)
rv = dispatch_queue_attr_make_with_autorelease_frequency (rv, (nuint) (ulong) AutoreleaseFrequency.Value);

if (QualityOfService.HasValue)
rv = dispatch_queue_attr_make_with_qos_class (rv, QualityOfService.Value, RelativePriority);

return rv;
}

[Mac (10,12)]
[iOS (10,0)]
[TV (10,0)]
[Watch (3,0)]
[DllImport (Constants.libcLibrary)]
static extern /* dispatch_queue_attr_t */ IntPtr dispatch_queue_attr_make_initially_inactive (/* dispatch_queue_attr_t _Nullable */ IntPtr attr);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing availability attributes
(maybe others p/invoke too, not checked)


[Mac (10,12)]
[iOS (10,0)]
[TV (10,0)]
[Watch (3,0)]
[DllImport (Constants.libcLibrary)]
static extern /* dispatch_queue_attr_t */ IntPtr dispatch_queue_attr_make_with_autorelease_frequency (/* dispatch_queue_attr_t _Nullable */ IntPtr attr, /* dispatch_autorelease_frequency_t */ nuint frequency);

[Mac (10,10)]
[iOS (8,0)]
[DllImport (Constants.libcLibrary)]
static extern /* dispatch_queue_attr_t */ IntPtr dispatch_queue_attr_make_with_qos_class (/* dispatch_queue_attr_t _Nullable */ IntPtr attr, /* dispatch_qos_class_t */ DispatchQualityOfService qos_class, int relative_priority);
}

[Mac (10,12)]
[iOS (10,0)]
[TV (10,0)]
[Watch (3,0)]
[Native]
public enum AutoreleaseFrequency : ulong /* unsigned long */
{
Inherit = 0,
WorkItem = 1,
Never = 2,
}
#endif // !COREBUILD
}

Expand Down
124 changes: 124 additions & 0 deletions tests/monotouch-test/CoreFoundation/DispatchQueueTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
//
// Unit tests for DispatchQueue
//
// Authors:
// Rolf Bjarne Kvinge <rolf@xamarin.com>
//
// Copyright 2018 Microsoft Corp. All rights reserved.
//

using System;
using System.IO;
#if XAMCORE_2_0
using CoreFoundation;
using Foundation;
using ObjCRuntime;
#if MONOMAC
using AppKit;
#else
using UIKit;
#endif
#else
using MonoTouch.CoreFoundation;
using MonoTouch.Foundation;
using MonoTouch.ObjCRuntime;
using MonoTouch.UIKit;
#endif
using NUnit.Framework;
using System.Drawing;
using System.Threading;

#if XAMCORE_2_0
using RectangleF = CoreGraphics.CGRect;
using SizeF = CoreGraphics.CGSize;
using PointF = CoreGraphics.CGPoint;
#else
using nfloat=global::System.Single;
using nint=global::System.Int32;
using nuint=global::System.UInt32;
#endif

namespace MonoTouchFixtures.CoreFoundation
{

[TestFixture]
[Preserve(AllMembers = true)]
public class DispatchQueueTests
{
[Test]
public void CtorWithAttributes ()
{
TestRuntime.AssertXcodeVersion (8, 0);

using (var queue = new DispatchQueue ("1", new DispatchQueue.Attributes
{
AutoreleaseFrequency = DispatchQueue.AutoreleaseFrequency.Inherit,
}))
{
Assert.AreNotEqual (IntPtr.Zero, queue.Handle, "Handle 1");
}

using (var queue = new DispatchQueue ("2", new DispatchQueue.Attributes
{
IsInitiallyInactive = true,
}))
{
queue.Activate (); // must activate the queue before it can be released according to Apple's documentation
Assert.AreNotEqual (IntPtr.Zero, queue.Handle, "Handle 2");
}

using (var queue = new DispatchQueue ("3", new DispatchQueue.Attributes
{
QualityOfService = DispatchQualityOfService.Utility,
}))
{
Assert.AreNotEqual (IntPtr.Zero, queue.Handle, "Handle 3");
Assert.AreEqual (DispatchQualityOfService.Utility, queue.QualityOfService, "QualityOfService 3");
}

using (var target_queue = new DispatchQueue ("4 - target")) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that seems to be one of the new .ctor
so an Xcode check would be needed

using (var queue = new DispatchQueue ("4", new DispatchQueue.Attributes
{
QualityOfService = DispatchQualityOfService.Background,
AutoreleaseFrequency = DispatchQueue.AutoreleaseFrequency.WorkItem,
RelativePriority = -1,
}, target_queue))
{
Assert.AreNotEqual (IntPtr.Zero, queue.Handle, "Handle 4");
Assert.AreEqual (DispatchQualityOfService.Background, queue.GetQualityOfService (out var relative_priority), "QualityOfService 4");
Assert.AreEqual (-1, relative_priority, "RelativePriority 4");
}
}
}

[Test]
public void Specific ()
{
using (var queue = new DispatchQueue ("Specific"))
{
var key = (IntPtr) 0x31415926;
queue.SetSpecific (key, "hello world");
Assert.AreEqual ("hello world", queue.GetSpecific (key), "Key");
}
}

[Test]
public void DispatchBarrierSync ()
{
using (var queue = new DispatchQueue ("DispatchBarrierSync")) {
var called = false;
queue.DispatchBarrierSync(() =>
{
called = true;
});
Assert.IsTrue (called, "Called");
}
}

[Test]
public void MainQueue ()
{
Assert.AreEqual (DispatchQueue.CurrentQueue, DispatchQueue.MainQueue, "MainQueue");
}
}
}
Loading