Skip to content

Commit

Permalink
[CoreFoundation] Implement missing dispatch API. Fixes #4606. (#4967)
Browse files Browse the repository at this point in the history
* [CoreFoundation] Add DispatchQueue.DispatchBarrierSync.

* [CoreFoundation] Bind dispatch_queue_[set|get]_specific.

* [CoreFoundation] Bind dispatch_queue_get_qos_class.

* [CoreFoundation] Bind dispatch_queue_create_with_target.

* Add tests.

* Update xtro.

* Add missing availability attributes.

* [tests] Do a version check before testing new API.

* Remove redundant code.
  • Loading branch information
rolfbjarne committed Oct 17, 2018
1 parent 6751f27 commit ee1f7dc
Show file tree
Hide file tree
Showing 3 changed files with 313 additions and 1 deletion.
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);

[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
{
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);

[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")) {
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

1 comment on commit ee1f7dc

@xamarin-release-manager
Copy link
Collaborator

Choose a reason for hiding this comment

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

Jenkins job (on internal Jenkins) succeeded

Build succeeded
API Diff (from stable)
ℹ️ API Diff (from PR only) (please review changes)
Generator Diff (no change)
Test run succeeded

Please sign in to comment.