1

In my application I notify multiple devices. If a device is not reachable and is not responding within a few seconds, i want to cancel the call.

My code is:
await characteristic0.WriteClientCharacteristicConfigurationDescriptorAsync (GattClientCharacteristicConfigurationDescriptorValue.Notify);

Now after some research i found out that usually one can pass a CancelationToken (_ct) and do this:

First create an operation containing the call:
IAsyncOperation<GattCommunicationStatus> operation = characteristic0.WriteClientCharacteristicConfigurationDescriptorAsync (GattClientCharacteristicConfigurationDescriptorValue.Notify);

Then creating a task with a CancellationToken:
Task<GattCommunicationStatus> task = operation.AsTask(_ct);

And then await it:
GattCommunicationStatus status = await task;

Now, the thing is even though the IsCancellationRequested-Property of the CancellationToken is set to true. The call wont stop.
And, the Device is getting Notified after the first line already! Isn't that supposed to happen after the call to await??

Do i make a mistake with the Token or is this a bigger thing?

EDIT
After the conversation with @Andrii Litvinov i put some more code to my initial description of the problem. Here is the whole method:

public async Task<GattCommunicationStatus> NotifyDevice(DeviceInformationDisplay deviceInfo, CancellationToken _ct)
        {
            try
            {
                BluetoothLEDevice device = await BluetoothLEDevice.FromIdAsync(deviceInfo.Id);
                service = device.GetGattService(new Guid(Service_UUID));
                characteristic0 = service.GetCharacteristics(new Guid(Characteristic_0_UUID)).First();

                characteristic0.ValueChanged += characteristic0ValueChanged;
                GattCommunicationStatus status = await characteristic0.WriteClientCharacteristicConfigurationDescriptorAsync(GattClientCharacteristicConfigurationDescriptorValue.Notify);
                _ct.Register(() =>
                {
                // trying to cancel the operation somehow
                });
                IAsyncOperation<GattCommunicationStatus> operation = characteristic0.WriteClientCharacteristicConfigurationDescriptorAsync(GattClientCharacteristicConfigurationDescriptorValue.Notify);
                Task<GattCommunicationStatus> task = operation.AsTask(_ct);
                GattCommunicationStatus status_1 = await task;

                if (!elapsedTimeWatcher.IsRunning)
                {
                    elapsedTimeWatcher.Start();
                }

                else
                {
                    elapsedTimeWatcher.Restart();
                }
                return status;
            }
            catch(OperationCanceledException e)
            {
                return GattCommunicationStatus.Unreachable;
            }
            catch (Exception e)
            {
                return GattCommunicationStatus.Unreachable;
            }

        }
Laurence
  • 95
  • 11
  • 1
    Cancellation is *cooperative*. The operation to be cancelled needs to *know about* a cancellation token and *observe* the request for cancellation. If a long-running operation doesn't have an overload that *accepts* a cancellation token, just having one in its vicinity won't do anything. – Damien_The_Unbeliever May 11 '17 at 07:41
  • So you're saying that if `WriteClientCharacteristicConfigurationDescriptorAsync` doesnt have an overload with a CancellationToken, adding the `AsTask`, wont change anything? So is there no way to cancel this call? – Laurence May 11 '17 at 07:44
  • Doesn't look like it. – Damien_The_Unbeliever May 11 '17 at 07:44
  • Is there a way to kill it? In an uncooperative way? – Laurence May 11 '17 at 07:49
  • Have you tried Close method on `operation.Close()`? Can you emulate operation that takes longer time and can be canceled? It looks like operation is already completed when you await in `await task`. – Andrii Litvinov May 11 '17 at 12:28
  • But where should i cancel the operation from? Just added some more code to the original question to show you what i wanted to do. – Laurence May 11 '17 at 12:34

1 Answers1

0

Based on the docs you are doing it right:

try
{
...

IAsyncOperation<GattCommunicationStatus> operation =
    characteristic0.WriteClientCharacteristicConfigurationDescriptorAsync(
        GattClientCharacteristicConfigurationDescriptorValue.Notify);
return await operation.AsTask(_ct);

...
}
...

This code should register to token and call operation.Cancel() behind the scene.

Can you set a token to lower value to see if operation is actually cancelled? How much time does it normally take to execute the method?

Try:

CancellationTokenSource cts = new CancellationTokenSource(10);
await NotifyDevice(BLEDevice, cts.Token);

Or even lower values.

Andrii Litvinov
  • 12,402
  • 3
  • 52
  • 59
  • This is the weird thing! From this line `op = characteristic0.WriteClientCharacteristicConfigurationDescriptorAsync(GattClientCharacteristicConfigurationDescriptorValue.Notify);` To this line: `Task task = op.AsTask(cts.Token);` Takes 7 (!!) seconds... – Laurence May 11 '17 at 13:39
  • Okay, probably it's because operation cannot be cancelled before device reply or internal timeout occurs. I suppose after 7 seconds it will be cancelled successfully. And will work fine altogether with solution from another answer. If it blocks the UI you could consider to run it with `Task.Run`. – Andrii Litvinov May 11 '17 at 13:47
  • Yeah i guess so, i will work on the other method again and try to make the tasks work. Thx so much! – Laurence May 11 '17 at 13:48
  • You are welcome. And if any of answers help you to solve the issue consider to accept those. – Andrii Litvinov May 11 '17 at 13:51