private async Task RunAsyncInner(AsyncSemaphore asyncSemaphore)
 {
     try
     {
         await this.doAsync();
     }
     finally
     {
         asyncSemaphore.Release();
     }          
 }
        public async Task RunAsync(int totalCalls)
        {
            AsyncSemaphore asyncSemaphore = new AsyncSemaphore(this.maxPendingCalls);
 
            List<Exception> exceptions = new List<Exception>();
            List<Task> tasks = new List<Task>();

            for (int i = 0; i < totalCalls; i++)
            {
                // need this to ensure the code below await only execute after entering critical section
                await asyncSemaphore.WaitAsync();
                Task newTask = this.RunAsyncInner(asyncSemaphore); 
                tasks.Add(newTask);
                this.DetectExceptionsFromPendingTasks(tasks, exceptions);
                if (exceptions.Count > 0)
                {
                    throw new AggregateException(exceptions).Flatten();
                }
            }

            await Task.WhenAll(tasks);
        }