[Darwin] Fix kqueue/kevent bindings to actually work. (#1871)

* [Darwin] Fix kqueue/kevent bindings to actually work.

Fix kqueue/kevent bindings to actually work. The P/Invoke signatures were
badly broken (missing an argument), so there's no way this could ever have
worked.

Additionally obsoletes kevent signatures that return bool (they're ignoring
vital information: how many events were actually returned from kevent), and
add overloads that do the right thing.

* [Darwin] Improve input validation and add more tests.
This commit is contained in:
Rolf Bjarne Kvinge 2017-03-15 07:50:05 +01:00 коммит произвёл GitHub
Родитель 78052430ab
Коммит 2380389278
3 изменённых файлов: 221 добавлений и 7 удалений

Просмотреть файл

@ -45,7 +45,11 @@ namespace XamCore.Darwin {
public IntPtr /* uintptr_tr */ Ident;
public EventFilter /* int16_t */ Filter;
public EventFlags /* uint16_t */ Flags;
public uint /* uint16_t */ FilterFlags;
#if XAMCORE_4_0
public FilterFlags /* uint32_t */ FilterFlags;
#else
public uint /* uint32_t */ FilterFlags;
#endif
public IntPtr /* uintptr_t */ Data;
public IntPtr /* void */ UserData;
}
@ -175,11 +179,74 @@ namespace XamCore.Darwin {
}
[DllImport (Constants.SystemLibrary)]
unsafe extern static int /* int */ kevent (KernelEvent *changeList, int /* int */ nChanges, KernelEvent *eventList, int /* int */ nEvents, IntPtr timeout);
unsafe extern static int /* int */ kevent (int kq, KernelEvent *changeList, int /* int */ nChanges, KernelEvent *eventList, int /* int */ nEvents, IntPtr timeout);
[DllImport (Constants.SystemLibrary)]
unsafe extern static int /* int */ kevent (KernelEvent *changeList, int /* int */ nChanges, KernelEvent *eventList, int /* int */ nEvents, ref TimeSpec timeout);
unsafe extern static int /* int */ kevent (int kq, KernelEvent *changeList, int /* int */ nChanges, KernelEvent *eventList, int /* int */ nEvents, ref TimeSpec timeout);
public int KEvent (KernelEvent[] changeList, KernelEvent[] eventList, TimeSpan? timeout = null)
{
if (changeList == null)
throw new ArgumentNullException (nameof (changeList));
if (eventList == null)
throw new ArgumentNullException (nameof (eventList));
if (changeList.Length < 1)
throw new ArgumentOutOfRangeException ("eventList must contain at least one element", nameof (eventList));
if (eventList.Length < 1)
throw new ArgumentOutOfRangeException ("changeList must contain at least one element", nameof (changeList));
return KEvent (changeList, changeList.Length, eventList, eventList.Length, ToTimespec (timeout));
}
public unsafe int KEvent (KernelEvent[] changeList, int nChanges, KernelEvent[] eventList, int nEvents, TimeSpec? timeout = null)
{
if (changeList == null)
throw new ArgumentNullException (nameof (changeList));
if (eventList == null)
throw new ArgumentNullException (nameof (eventList));
if (changeList.Length < 1)
throw new ArgumentOutOfRangeException ("eventList must contain at least one element", nameof (eventList));
if (eventList.Length < 1)
throw new ArgumentOutOfRangeException ("changeList must contain at least one element", nameof (changeList));
if (changeList.Length < nChanges)
throw new ArgumentOutOfRangeException ("nChanges is larger than the number of elements in changeList", nameof (nChanges));
if (eventList.Length < nEvents)
throw new ArgumentOutOfRangeException ("nEvents is larger than the number of elements in eventList", nameof (nEvents));
unsafe {
fixed (KernelEvent *cp = &changeList [0])
fixed (KernelEvent *ep = &eventList [0]) {
if (timeout == null) {
return kevent (handle, cp, nChanges, ep, nEvents, IntPtr.Zero);
} else {
TimeSpec ts = timeout.Value;
return kevent (handle, cp, nChanges, ep, nEvents, ref ts);
}
}
}
}
static TimeSpec? ToTimespec (TimeSpan? ts)
{
if (ts == null)
return null;
var rv = new TimeSpec ();
rv.Seconds = (nint) ts.Value.TotalSeconds;
rv.NanoSeconds = (nint) (ts.Value.Milliseconds * 1000000L);
return rv;
}
#if !XAMCORE_4_0
[Obsolete ("Use any of the overloads that return an int to get how many events were returned from kevent.")]
public bool KEvent (KernelEvent [] changeList, int nChanges, KernelEvent [] eventList, int nEvents, ref TimeSpec timeOut)
{
if (changeList != null && changeList.Length < nChanges)
@ -191,10 +258,11 @@ namespace XamCore.Darwin {
unsafe {
fixed (KernelEvent *cp = &changeList [0])
fixed (KernelEvent *ep = &eventList [0])
return kevent (cp, nChanges, ep, nEvents, ref timeOut) != -1;
return kevent (handle, cp, nChanges, ep, nEvents, ref timeOut) != -1;
}
}
[Obsolete ("Use any of the overloads that return an int to get how many events were returned from kevent.")]
public bool KEvent (KernelEvent [] changeList, int nChanges, KernelEvent [] eventList, int nEvents)
{
if (changeList != null && changeList.Length < nChanges)
@ -206,25 +274,36 @@ namespace XamCore.Darwin {
unsafe {
fixed (KernelEvent *cp = &changeList [0])
fixed (KernelEvent *ep = &eventList [0])
return kevent (cp, nChanges, ep, nEvents, IntPtr.Zero) != -1;
return kevent (handle, cp, nChanges, ep, nEvents, IntPtr.Zero) != -1;
}
}
[Obsolete ("Use any of the overloads that return an int to get how many events were returned from kevent.")]
public bool KEvent (KernelEvent [] changeList, KernelEvent [] eventList, ref TimeSpec timeOut)
{
unsafe {
fixed (KernelEvent *cp = &changeList [0])
fixed (KernelEvent *ep = &eventList [0])
return kevent (cp, changeList != null ? changeList.Length : 0, ep, eventList != null ? eventList.Length : 0, ref timeOut) != -1;
return kevent (handle, cp, changeList != null ? changeList.Length : 0, ep, eventList != null ? eventList.Length : 0, ref timeOut) != -1;
}
}
#endif
#if XAMCORE_4_0
public int KEvent (KernelEvent [] changeList, KernelEvent [] eventList)
#else
[Obsolete ("Use any of the overloads that return an int to get how many events were returned from kevent.")]
public bool KEvent (KernelEvent [] changeList, KernelEvent [] eventList)
#endif
{
unsafe {
fixed (KernelEvent *cp = &changeList [0])
fixed (KernelEvent *ep = &eventList [0])
return kevent (cp, changeList != null ? changeList.Length : 0, ep, eventList != null ? eventList.Length : 0, IntPtr.Zero) != -1;
#if XAMCORE_4_0
return kevent (handle, cp, changeList != null ? changeList.Length : 0, ep, eventList != null ? eventList.Length : 0, IntPtr.Zero);
#else
return kevent (handle, cp, changeList != null ? changeList.Length : 0, ep, eventList != null ? eventList.Length : 0, IntPtr.Zero) != -1;
#endif
}
}
}

Просмотреть файл

@ -107,6 +107,7 @@
<Compile Include="src\CoreAnimation\CALayer.cs" />
<Compile Include="src\CoreAnimation\CAOpenGLLayer.cs" />
<Compile Include="src\CoreImage\CIFilter.cs" />
<Compile Include="src\Darwin\KernelNotificationTest.cs" />
<Compile Include="src\DelegateAndDataSourceTest.cs" />
<Compile Include="src\DerivedEventTest.cs" />
<Compile Include="src\EveryFrameworkSmokeTest.cs" />

Просмотреть файл

@ -0,0 +1,134 @@
using System;
using System.Diagnostics;
#if XAMCORE_2_0
using Darwin;
#else
using MonoMac.Darwin;
#endif
using NUnit.Framework;
namespace apitest
{
[TestFixture]
public class KernelNotificationTest
{
KernelEvent [] CreateEvents (Process process)
{
return new KernelEvent [] {
new KernelEvent {
Ident = (IntPtr) process.Id,
Filter = EventFilter.Proc,
Flags = EventFlags.Add,
FilterFlags = (uint) (FilterFlags.ProcExit),
}
};
}
[Test]
public void KEvent ()
{
// (KernelEvent[], KernelEvent[])
using (var sleep = Process.Start ("/bin/sleep", "0.5")) {
using (var kqueue = new KernelQueue ()) {
var events = CreateEvents (sleep);
Assert.IsTrue (kqueue.KEvent (events, events), "kevent");
}
}
// (KernelEvent[], KernelEvent[], TimeSpan?)
using (var sleep = Process.Start ("/bin/sleep", "0.5")) {
using (var kqueue = new KernelQueue ()) {
var events = CreateEvents (sleep);
Assert.AreEqual (1, kqueue.KEvent (events, events, TimeSpan.FromSeconds (5)), "kevent");
}
}
// (KernelEvent[], KernelEvent[], TimeSpan?)
using (var sleep = Process.Start ("/bin/sleep", "0.5")) {
using (var kqueue = new KernelQueue ()) {
var events = CreateEvents (sleep);
Assert.AreEqual (1, kqueue.KEvent (events, events, null), "kevent");
}
}
// (KernelEvent[], int, KernelEvent[], int, TimeSpec?)
using (var sleep = Process.Start ("/bin/sleep", "0.5")) {
using (var kqueue = new KernelQueue ()) {
var events = CreateEvents (sleep);
TimeSpec ts = new TimeSpec
{
Seconds = 5,
};
Assert.AreEqual (1, kqueue.KEvent (events, events.Length, events, events.Length, ts), "kevent");
}
}
// (KernelEvent[], int, KernelEvent[], int, TimeSpec?)
using (var sleep = Process.Start ("/bin/sleep", "0.5")) {
using (var kqueue = new KernelQueue ()) {
var events = CreateEvents (sleep);
Assert.AreEqual (1, kqueue.KEvent (events, events.Length, events, events.Length, null), "kevent");
}
}
// (KernelEvent[], int, KernelEvent[], int, ref TimeSpec)
using (var sleep = Process.Start ("/bin/sleep", "0.5")) {
using (var kqueue = new KernelQueue ()) {
var events = CreateEvents (sleep);
TimeSpec ts = new TimeSpec
{
Seconds = 5,
};
Assert.IsTrue (kqueue.KEvent (events, events.Length, events, events.Length, ref ts), "kevent");
}
}
// (KernelEvent[], KernelEvent[], ref TimeSpec)
using (var sleep = Process.Start ("/bin/sleep", "0.5")) {
using (var kqueue = new KernelQueue ()) {
var events = CreateEvents (sleep);
TimeSpec ts = new TimeSpec
{
Seconds = 5,
};
Assert.IsTrue (kqueue.KEvent (events, events, ref ts), "kevent");
}
}
// (KernelEvent[], int, KernelEvent[], int)
using (var sleep = Process.Start ("/bin/sleep", "0.5")) {
using (var kqueue = new KernelQueue ()) {
var events = CreateEvents (sleep);
Assert.IsTrue (kqueue.KEvent (events, events.Length, events, events.Length), "kevent");
}
}
}
[Test]
public void KEventExceptions ()
{
using (var sleep = Process.Start ("/bin/sleep", "0")) {
using (var kqueue = new KernelQueue ()) {
var events = CreateEvents (sleep);
var empty = new KernelEvent [0];
Assert.Throws<ArgumentNullException> (() => kqueue.KEvent (null, events, null), "a1");
Assert.Throws<ArgumentNullException> (() => kqueue.KEvent (events, null, null), "a2");
Assert.Throws<ArgumentNullException> (() => kqueue.KEvent (null, null, null), "a3");
Assert.Throws<ArgumentOutOfRangeException> (() => kqueue.KEvent (events, empty, null), "a4");
Assert.Throws<ArgumentOutOfRangeException> (() => kqueue.KEvent (empty, events, null), "a5");
Assert.Throws<ArgumentNullException> (() => kqueue.KEvent (null, 1, events, 1, null), "b1");
Assert.Throws<ArgumentNullException> (() => kqueue.KEvent (events, 1, null, 1, null), "b2");
Assert.Throws<ArgumentNullException> (() => kqueue.KEvent (null, 1, null, 1, null), "b3");
Assert.Throws<ArgumentOutOfRangeException> (() => kqueue.KEvent (events, 1, empty, 1, null), "b4");
Assert.Throws<ArgumentOutOfRangeException> (() => kqueue.KEvent (empty, 1, events, 1, null), "b5");
Assert.Throws<ArgumentOutOfRangeException> (() => kqueue.KEvent (events, 1, events, 2, null), "b6");
Assert.Throws<ArgumentOutOfRangeException> (() => kqueue.KEvent (events, 2, events, 1, null), "b7");
}
}
}
}
}