Skip to content

Commit

Permalink
Remove CancellationToken
Browse files Browse the repository at this point in the history
DBus doesn't support cancelling methods. A user may expect
cancelling a method stops it immediately, but in practice
he'll have to wait for the method to complete.

Cancelling a method would only do something when there
were many parallel requests which isn't a common use-case.

We throw NotSupportedException when user tries to add CancellationToken to DBus method or signal.
  • Loading branch information
tmds authored Feb 15, 2017
1 parent 8548c19 commit 35329ec
Show file tree
Hide file tree
Showing 29 changed files with 256 additions and 295 deletions.
36 changes: 18 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,15 +154,15 @@ The `float` type is not part of the D-Bus specification. It was implemented as p

## Methods

A D-Bus method is modeled by a method in the .NET interface. The method must to return `Task` for methods without a return value and `Task<T>` for methods with a return value. Following async naming conventions, we add `Async` to the method name. In case a method has multiple out-arguments, these must be combined in a struct/class as public fields or a C# 7 tuple. The input arguments of the D-Bus method are combined with a final `CancellationToken` parameter.
A D-Bus method is modeled by a method in the .NET interface. The method must to return `Task` for methods without a return value and `Task<T>` for methods with a return value. Following async naming conventions, we add `Async` to the method name. In case a method has multiple out-arguments, these must be combined in a struct/class as public fields or a C# 7 tuple. The input arguments of the D-Bus method are the method parameters.

```
[DBusInterface("org.mpris.MediaPlayer2.TrackList")]
public interface ITrackList
{
// 1 input argument with signature: `ao`
// 1 output parameter with signature `aa{sv}`
Task<IDictionary<string, object>[]> GetTracksMetadataAsync(ObjectPath[] trackIds, CancellationToken cancellationToken = default(CancellationToken));
Task<IDictionary<string, object>[]> GetTracksMetadataAsync(ObjectPath[] trackIds);
}
```

Expand All @@ -178,21 +178,21 @@ struct RetVal
public interface ITrackList
{
// 2 output parameter with signatures `s` and `i`
Task<RetVal> MultipleOutAsync(CancellationToken cancellationToken = default(CancellationToken));
Task<RetVal> MultipleOutAsync();
// 1 output parameter with signatures `(si)`
[ret:Argument]
Task<RetVal> SingleStructOutAsync(CancellationToken cancellationToken = default(CancellationToken));
Task<RetVal> SingleStructOutAsync();
}
// or using C# 7 tuples
[DBusInterface("tmds.dbus.example.structret")]
public interface ITrackList
{
// 2 output parameter with signatures `s` and `i`
Task<(string arg1, int arg2)> MultipleOutAsync(CancellationToken cancellationToken = default(CancellationToken));
Task<(string arg1, int arg2)> MultipleOutAsync();
// 1 output parameter with signatures `(si)`
[ret:Argument]
Task<(string arg1, int arg2)> SingleStructOutAsync(CancellationToken cancellationToken = default(CancellationToken));
Task<(string arg1, int arg2)> SingleStructOutAsync();
}
```

Expand All @@ -203,22 +203,22 @@ In case the return type of a method is `Task<object>` the method may me modeled
public interface ITrackList
{
// user needs to cast, e.g. (ObjectPath)(await FooAsync())
Task<object> FooAsync(CancellationToken cancellationToken = default(CancellationToken));
Task<object> FooAsync();
// return value already casted, e.g. await BarAsync<ObjectPath>()
Task<T> BarAsync<T>(CancellationToken cancellationToken = default(CancellationToken));
Task<T> BarAsync<T>();
}
```

## Signals

A D-Bus signal is modeled by a method in the .NET interface which matches the D-Bus signal name prefixed with `Watch` and sufixed with `Async` suffix. The method needs to return `Task<IDisposable>`. The returned `IDisposable` can be used to unsubscribe from the signal. The method has a handler and a `CancellationToken` parameter. The handler must be of type `Action` for signals without parameters and of type `Action<T>` for methods which do have parameters. In case there are multiple parameters, they must be wrapped in a struct/class as public fields or use a C# 7 tuple. Similar to the method output parameter, the `ArgumentAttribute` can be set on the `Action` to distinguish between a single STRUCT being returned (attribute set) or multiple arguments (no attribute).
A D-Bus signal is modeled by a method in the .NET interface which matches the D-Bus signal name prefixed with `Watch` and sufixed with `Async` suffix. The method needs to return `Task<IDisposable>`. The returned `IDisposable` can be used to unsubscribe from the signal. The method has a handler parameter. The handler must be of type `Action` for signals without parameters and of type `Action<T>` for methods which do have parameters. In case there are multiple parameters, they must be wrapped in a struct/class as public fields or use a C# 7 tuple. Similar to the method output parameter, the `ArgumentAttribute` can be set on the `Action` to distinguish between a single STRUCT being returned (attribute set) or multiple arguments (no attribute).

```
[DBusInterface("org.freedesktop.NetworkManager")]
public interface INetworkManager : IDBusObject
{
// DeviceAdded signal with a single ObjectPath argument
Task<IDisposable> WatchDeviceAddedAsync(Action<ObjectPath> handler, CancellationToken cancellationToken = default(CancellationToken))));
Task<IDisposable> WatchDeviceAddedAsync(Action<ObjectPath> handler)));
}
```

Expand All @@ -230,10 +230,10 @@ Properties are defined per interface and accessed using `org.freedesktop.DBus.Pr
[DBusInterface("org.mpris.MediaPlayer2.TrackList")]
public interface ITrackList
{
Task<T> GetAsync<T>(string prop, CancellationToken cancellationToken = default(CancellationToken));
Task<IDictionary<string, object>> GetAllAsync(CancellationToken cancellationToken = default(CancellationToken));
Task SetAsync(string prop, object val, CancellationToken cancellationToken = default(CancellationToken));
Task<IDisposable> WatchPropertiesAsync(Action<PropertyChanges> handler, CancellationToken cancellationToken = default(CancellationToken));
Task<T> GetAsync<T>(string prop);
Task<IDictionary<string, object>> GetAllAsync();
Task SetAsync(string prop, object val);
Task<IDisposable> WatchPropertiesAsync(Action<PropertyChanges> handler);
}
```

Expand All @@ -247,9 +247,9 @@ class TrackListProperties
static class TrackListPropertyExtensions
{
public static Task<ObjectPath[]> GetTracksAsync(this ITrackList trackList, CancellationToken cancellationToken = default(CancellationToken))
public static Task<ObjectPath[]> GetTracksAsync(this ITrackList trackList)
{
return trackList.GetAsync<ObjectPath[]>(nameof(TrackListProperties.Tracks), cancellationToken);
return trackList.GetAsync<ObjectPath[]>(nameof(TrackListProperties.Tracks));
}
}
```
Expand All @@ -267,7 +267,7 @@ class TrackListProperties
public interface ITrackList
{
// ...
Task<TrackListProperties> GetAllAsync(CancellationToken cancellationToken = default(CancellationToken));
Task<TrackListProperties> GetAllAsync();
// ...
}
```
Expand Down Expand Up @@ -355,7 +355,7 @@ As explained in this document, we can model this interface as shown in the next
[DBusInterface("org.freedesktop.DBus.Introspectable")]
public interface IIntrospectable : IDBusObject
{
Task<string> IntrospectAsync(CancellationToken cancellationToken = default(CancellationToken));
Task<string> IntrospectAsync();
}
```

Expand Down
2 changes: 1 addition & 1 deletion samples/Introspect/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace Introspect
[DBusInterface("org.freedesktop.DBus.Introspectable")]
public interface IIntrospectable : IDBusObject
{
Task<string> IntrospectAsync(CancellationToken cancellationToken = default(CancellationToken));
Task<string> IntrospectAsync();
}

public class Program
Expand Down
28 changes: 14 additions & 14 deletions samples/MediaPlayerRemote/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ class TrackListProperties
[DBusInterface("org.mpris.MediaPlayer2.TrackList")]
public interface ITrackList : IDBusObject
{
Task<IDictionary<string, object>[]> GetTracksMetadataAsync(ObjectPath[] trackIds, CancellationToken cancellationToken = default(CancellationToken));
Task<IDictionary<string, object>[]> GetTracksMetadataAsync(ObjectPath[] trackIds);

Task<IDictionary<string, object>> GetAllAsync(CancellationToken cancellationToken = default(CancellationToken));
Task<T> GetAsync<T>(string prop, CancellationToken cancellationToken = default(CancellationToken));
Task SetAsync(string prop, object val, CancellationToken cancellationToken = default(CancellationToken));
Task<IDisposable> WatchPropertiesAsync(Action<PropertyChanges> handler, CancellationToken cancellationToken = default(CancellationToken));
Task<IDictionary<string, object>> GetAllAsync();
Task<T> GetAsync<T>(string prop);
Task SetAsync(string prop, object val);
Task<IDisposable> WatchPropertiesAsync(Action<PropertyChanges> handler);
}

class PlayerProperties
Expand All @@ -35,20 +35,20 @@ class PlayerProperties
[DBusInterface("org.mpris.MediaPlayer2.Player")]
public interface IPlayer : IDBusObject
{
Task PlayPauseAsync(CancellationToken cancellationToken = default(CancellationToken));
Task NextAsync(CancellationToken cancellationToken = default(CancellationToken));
Task PreviousAsync(CancellationToken cancellationToken = default(CancellationToken));

Task<IDictionary<string, object>> GetAllAsync(CancellationToken cancellationToken = default(CancellationToken));
Task<T> GetAsync<T>(string prop, CancellationToken cancellationToken = default(CancellationToken));
Task SetAsync(string prop, object val, CancellationToken cancellationToken = default(CancellationToken));
Task<IDisposable> WatchPropertiesAsync(Action<PropertyChanges> handler, CancellationToken cancellationToken = default(CancellationToken));
Task PlayPauseAsync();
Task NextAsync();
Task PreviousAsync();

Task<IDictionary<string, object>> GetAllAsync();
Task<T> GetAsync<T>(string prop);
Task SetAsync(string prop, object val);
Task<IDisposable> WatchPropertiesAsync(Action<PropertyChanges> handler);
}

[DBusInterface("org.mpris.MediaPlayer2")]
public interface IMediaPlayer : IPlayer, ITrackList
{
Task QuitAsync(CancellationToken cancellationToken = default(CancellationToken));
Task QuitAsync();
}

public class Program
Expand Down
10 changes: 5 additions & 5 deletions samples/NetworkManager/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,12 @@ public IEnumerator<KeyValuePair<string,object>> GetEnumerator()
[DBusInterface("org.freedesktop.NetworkManager")]
public interface INetworkManager : IDBusObject
{
Task<ObjectPath[]> GetDevicesAsync(CancellationToken cancellationToken = default(CancellationToken));
Task<NetworkManagerProperties> GetAllAsync(CancellationToken cancellationToken = default(CancellationToken));
Task<ObjectPath[]> GetDevicesAsync();
Task<NetworkManagerProperties> GetAllAsync();

Task<object> GetAsync(string prop, CancellationToken cancellationToken = default(CancellationToken));
Task SetAsync(string prop, object val, CancellationToken cancellationToken = default(CancellationToken));
Task<IDisposable> WatchPropertiesAsync(Action<PropertyChanges> handler, CancellationToken cancellationToken = default(CancellationToken));
Task<object> GetAsync(string prop);
Task SetAsync(string prop, object val);
Task<IDisposable> WatchPropertiesAsync(Action<PropertyChanges> handler);
}

public class Program
Expand Down
10 changes: 5 additions & 5 deletions src/Tmds.DBus/CodeGen/DBusAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,14 @@ public void CompleteRegistration()
}
}

public async Task WatchSignalsAsync(CancellationToken cancellationToken)
public async Task WatchSignalsAsync()
{
var tasks = StartWatchingSignals(cancellationToken);
var tasks = StartWatchingSignals();
IEnumerable<IDisposable> signalDisposables = null;

try
{
Task.WaitAll(tasks, cancellationToken);
Task.WaitAll(tasks);
signalDisposables = tasks.Select(task => task.Result);

if (signalDisposables.Contains(null))
Expand Down Expand Up @@ -149,7 +149,7 @@ static protected internal string GetPropertyAddKey(string iface, string member,
return $"org.freedesktop.DBus.Properties.{iface}.{member}.s{signature?.Value}";
}

public Task<Message> HandleMethodCall(Message methodCall, CancellationToken cancellationToken)
public Task<Message> HandleMethodCall(Message methodCall)
{
var key = GetMethodLookupKey(methodCall.Header.Interface, methodCall.Header.Member, methodCall.Header.Signature);
MethodCallHandler handler = null;
Expand Down Expand Up @@ -199,7 +199,7 @@ public Task<Message> HandleMethodCall(Message methodCall, CancellationToken canc
}
}

protected internal virtual Task<IDisposable>[] StartWatchingSignals(CancellationToken cancellationToken)
protected internal virtual Task<IDisposable>[] StartWatchingSignals()
{
return Array.Empty<Task<IDisposable>>();
}
Expand Down
16 changes: 2 additions & 14 deletions src/Tmds.DBus/CodeGen/DBusAdapterTypeBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ internal class DBusAdapterTypeBuilder
private static readonly MethodInfo s_writerWriteString = typeof(MessageWriter).GetMethod(nameof(MessageWriter.WriteString), BindingFlags.Instance | BindingFlags.Public);
private static readonly MethodInfo s_writerSetSkipNextStructPadding = typeof(MessageWriter).GetMethod(nameof(MessageWriter.SetSkipNextStructPadding), BindingFlags.Instance | BindingFlags.Public);
private static readonly FieldInfo s_setTypeIntrospectionField = typeof(DBusAdapter).GetField(nameof(DBusAdapter._typeIntrospection), BindingFlags.Instance | BindingFlags.NonPublic);
private static readonly Type s_cancellationTokenType = typeof(CancellationToken);
private static readonly Type s_taskOfMessageType = typeof(Task<Message>);
private static readonly Type s_nullableSignatureType = typeof(Signature?);
private static readonly Type s_action2GenericType = typeof(Action<,>);
Expand Down Expand Up @@ -99,7 +98,7 @@ private void ImplementStartWatchingSignals(IList<InterfaceDescription> interface

foreach (var signal in signals)
{
// signals[i] = Watch((IDbusInterface)this, SendSignalAction, CancellationToken)
// signals[i] = Watch((IDbusInterface)this, SendSignalAction)
ilg.Emit(OpCodes.Dup); // signals
ilg.Emit(OpCodes.Ldc_I4, idx); // i

Expand All @@ -119,11 +118,6 @@ private void ImplementStartWatchingSignals(IList<InterfaceDescription> interface
ilg.Emit(OpCodes.Newobj, signal.ActionType.GetConstructors()[0]);
}

{
// CancellationToken
ilg.Emit(OpCodes.Ldarg_1);
}

ilg.Emit(OpCodes.Callvirt, signal.MethodInfo);
}

Expand Down Expand Up @@ -294,7 +288,7 @@ private MethodInfo GenMethodHandler(string key, MethodDescription dbusMethod, bo
ilg.Emit(OpCodes.Ldarg_0);
// Message
ilg.Emit(OpCodes.Ldarg_2);
// Task = (IDbusInterface)object.CallMethod(arguments, new CancellationToken)
// Task = (IDbusInterface)object.CallMethod(arguments)
{
// (IIinterface)object
ilg.Emit(OpCodes.Ldarg_1);
Expand Down Expand Up @@ -324,12 +318,6 @@ private MethodInfo GenMethodHandler(string key, MethodDescription dbusMethod, bo
}
}

// CancellationToken
LocalBuilder cancellationToken = ilg.DeclareLocal(s_cancellationTokenType);
ilg.Emit(OpCodes.Ldloca_S, cancellationToken);
ilg.Emit(OpCodes.Initobj, s_cancellationTokenType);
ilg.Emit(OpCodes.Ldloc, cancellationToken);

// Call method
ilg.Emit(OpCodes.Callvirt, dbusMethod.MethodInfo);
}
Expand Down
Loading

0 comments on commit 35329ec

Please sign in to comment.