Amazon IAP Plugin for Xamarin - crash when using TaskCompletionSource

72 views Asked by At

I'm trying to implement a wrapper for the Amazon IAP Plugin for Xamarin. It uses an event based system in the following way:

You can initiate method calls and listen for events. Method calls initiate requests, some of which return a response. Events are asynchronous system-generated messages that are sent in response to method calls to return the requested data to you. See more here

My goal is to wrap this event based system into some API which allows me to use the plugin with tasks, so I can use the async-await syntax. To achieve that I'm using the TaskCompletionSource like in the following example:

public async Task<bool> GetProductInfoAsync(params string[] productIds)
{
    var iapService = AmazonIapV2Impl.Instance;
    var tcs = new TaskCompletionSource<bool>();
    var skus = new SkusInput { Skus = productIds.ToList() };
    var requestId = iapService.GetProductData(skus).RequestId;

    GetProductDataResponseDelegator delegator = null;
    delegator = new GetProductDataResponseDelegator(response => 
    {
        if(response.Id == requestId) {
            var result = GetResultFromResponse(response);
            tcs.SetResult(result);
            //iapService.RemoveGetProductDataResponseListener(delegator.responseDelegate);
        }
    });

    iapService.AddGetProductDataResponseListener(delegator.responseDelegate);
    return await tcs.Task;
}

This code seems to work fine if the method gets called once, but if it gets called two times in a row the app crashes immediately and the only thing printed to the console is the following message..

[mono] Unhandled Exception:
[mono] System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
[mono-rt] [ERROR] FATAL UNHANDLED EXCEPTION: System.InvalidOperationException: Collection was modified; enumeration operation may not execute.

..which kinda makes no sense at all.

So is there something obvious I'm missing here? Or could it be a bug from the plugin?

I have created a repository with the code above so you can reproduce the problem. It's my playground, so please ignore the whole structure of the project and just focus on the classes AmazonIAPService and MainActivity.

Hint 1: The commented line //iapService.RemoveGetProductDataResponseListener(delegator.responseDelegate); causes also a crash with the same message but already at the first call of the method.

Hint 2: The AmazonIAPService contains a commented method which uses await Task.Delay(TimeSpan.FromMilliseconds(1)) and solves the problem from above in a very hacky way which I really don't like.

1

There are 1 answers

0
Emil On

Problem seems to be that those functions have to run asynchronously. Also mentioned here in the doc. So once you run those functions synchronously somehow they throw exception, i dont what is happening in the library but your hacky solution is the actual solution for that. If you write the function as below. it also works.

  PurchaseResponseDelegator delegator = null;
                delegator = new PurchaseResponseDelegator(async response =>
                {
                    await Task.Run(() =>
                    {
                        if (response.RequestId == requestId)
                        {
                            var result = GetPurchaseEventHandler(response);
                            var sucess = taskCompletionSource.TrySetResult(result);        
                            context.RemovePurchaseResponseListener(delegator.responseDelegate);
                        }
                    } );
                });
                // Register for an event
                context.AddPurchaseResponseListener(delegator.responseDelegate);

One other exception I had despite the async-await solution, somehow, it always throws exception for the line taskCompletionSource.SetResult(result); for PurchaseUpdates functions only. if i use instead this line var sucess = taskCompletionSource.TrySetResult(result); it works fine