JsonConvert.DeserializeObject and ThreadAbortedException

In a Xamarin project, I have a PCL library with the following code.

Let's define a ConcurrentQueue<SyncRequest>

. Why a consumer was added when initializing an object Task

:

_syncConsumer = new Task(
                ProcessSyncQueue,
                _syncConsumerCancellationTokenSource.Token);
_syncConsumer.Start();

      

The method ProcessSyncQueue

checks the sync queue and calls the method GetSyncableEntity

:

private async void ProcessSyncQueue()
{
    while (true)
    {
         SyncRequest syncRequest;
         if (_syncQueue.TryDequeue(out syncRequest))
         {
             var syncableEntity = GetSyncableEntity(syncRequest);
         }
    }
}

      

GetSyncableEntity, in turn, performs Json deserialization:

private T GetSyncableEntity(SyncRequest syncRequest)
{
    T syncableEntity = default(T);

    try
    {
       syncableEntity = JsonConvert.DeserializeObject<T>(syncRequest.SynchronizationContent);
    }
    catch (Exception e)
    {

    }

    return syncableEntity;
 }

      

At this point we get ThreadAbortedException

with the message "Thread was an aborted". StackTrace:

   at Newtonsoft.Json.JsonTextReader.FinishReadStringIntoBuffer(Int32 charPos, Int32 initialPosition, Int32 lastWritePosition)
   at Newtonsoft.Json.JsonTextReader.ReadStringIntoBuffer(Char quote)
   at Newtonsoft.Json.JsonTextReader.ParseProperty()
   at Newtonsoft.Json.JsonTextReader.ParseObject()
   at Newtonsoft.Json.JsonTextReader.Read()
   at Newtonsoft.Json.JsonReader.ReadAndAssert()
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
   at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
   at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
   at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings)
   at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value)

      

Can anyone help us understand what's going on and how to deserialize it?

UPDATE: I am posting a bit more code as the reviewers suggested I remove CancellationTokenSource

, use Task.Run

to initialize the consumer and await

that. And created some test implementation like this:

    protected void RequestSynchronizationFor(
        string synchronizationKey,
        T entity)
    {
        if (!_isInitialized)
        {
            InitializeSyncRequestsQueue();
        }

        _syncQueue.Enqueue(GetSyncRequest(synchronizationKey, entity));
    }

      

So we are requesting an entity to synchronize the method RequestSynchronizationFor

. If it's a cold run, we initialize the queue from the caller's db InitializeSyncRequestsQueue

and wait for the Task.Run

consumer thread.

    private async void InitializeSyncRequestsQueue()
    {
        var syncRequests = GetSyncedRequests();

        foreach (var syncRequest in syncRequests)
        {
            _syncQueue.Enqueue(syncRequest);
        }

        await Task.Run(ProcessSyncQueue);
    }

      

The consumer task, as before, does the same:

 private async Task ProcessSyncQueue()
    {
        while (true)
        {
            SyncRequest syncRequest;
            if (_syncQueue.TryDequeue(out syncRequest))
            {
                var syncableEntity = GetSyncableEntity(syncRequest);
            }
        }
    }

      

Still has the same exception. Not sure if that makes sense, but I'm running the code from a unit test. Any suggestions?

UPDATE2:

After I made the changes that I posted in the first "UPDATE", the call stack was changed as well:

   at Newtonsoft.Json.JsonSerializer.get_MetadataPropertyHandling()
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
   at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
   at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
   at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings)
   at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value)

      

UPDATE 3: I am fetching all the code in a fake service and still have the same exception when trying to deserialize:

public class JsonDeserializeService<T>
{
    private readonly bool _isInitialized;

    private readonly ConcurrentQueue<SyncRequest> _syncQueue;

    public JsonDeserializeService()
    {
        _isInitialized = false;
        _syncQueue = new ConcurrentQueue<SyncRequest>();
    }

    public void RequestSynchronizationFor(
        string synchronizationKey,
        T entity)
    {
        if (!_isInitialized)
        {
            InitializeSyncRequestsQueue();
        }

        _syncQueue.Enqueue(GetSyncRequest(synchronizationKey, entity));
    }

    private async void InitializeSyncRequestsQueue()
    {
        var syncRequests = Enumerable.Empty<SyncRequest>();

        foreach (var syncRequest in syncRequests)
        {
            _syncQueue.Enqueue(syncRequest);
        }

        await Task.Run(ProcessSyncQueue);
    }

    private async Task ProcessSyncQueue()
    {
        while (true)
        {
            SyncRequest syncRequest;
            if (_syncQueue.TryDequeue(out syncRequest))
            {
                var syncableEntity = GetSyncableEntity(syncRequest);
            }
        }
    }

    private T GetSyncableEntity(SyncRequest syncRequest)
    {
        T syncableEntity = default(T);

        try
        {
            syncableEntity = JsonConvert.DeserializeObject<T>(syncRequest.SynchronizationContent);
        }
        catch (Exception e)
        {
        }

        return syncableEntity;
    }

    private SyncRequest GetSyncRequest(string synchronizationKey, T entity)
    {
        return new SyncRequest()
        {
            SynchronizationContent = JsonConvert.SerializeObject(entity),
            SynchronizationDelayUntil = DateTime.Now
        };
    }
}

      

Disabled from unit test:

    public void Syncable_Service_Should_Not_Generate_Exception()
    {
        var syncService = new JsonDeserializeService<FakeSyncableEntity>();
        syncService.RequestSynchronizationFor("syncKey", new FakeSyncableEntity() { Content = "Content" });
    }

      

+3


source to share


1 answer


The reason for this behavior is very simple. Your test ends up being more complex than an asynchronous task. When the test ends, it throws a ThreadAbortException on the child threads.



You need to call task.Wait () to have the main thread wait for the task to complete.

0


source







All Articles