Skip to content

Commit aeec2e8

Browse files
committed
optimize task creation
1 parent 610a889 commit aeec2e8

File tree

1 file changed

+24
-12
lines changed

1 file changed

+24
-12
lines changed

sdk/src/Services/S3/Custom/Transfer/Internal/MultipartDownloadManager.cs

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -383,8 +383,27 @@ public async Task StartDownloadsAsync(DownloadDiscoveryResult discoveryResult, E
383383

384384
_logger.DebugFormat("MultipartDownloadManager: [Part {0}] Buffer space acquired", partNum);
385385

386-
var task = CreateDownloadTaskAsync(partNum, discoveryResult.ObjectSize, wrappedCallback, internalCts.Token);
387-
downloadTasks.Add(task);
386+
_logger.DebugFormat("MultipartDownloadManager: [Part {0}] Waiting for HTTP concurrency slot (Available: {1}/{2})",
387+
partNum, _httpConcurrencySlots.CurrentCount, _config.ConcurrentServiceRequests);
388+
389+
// Acquire HTTP slot in the loop before creating task
390+
// Loop will block here if all slots are in use
391+
await _httpConcurrencySlots.WaitAsync(cancellationToken).ConfigureAwait(false);
392+
393+
_logger.DebugFormat("MultipartDownloadManager: [Part {0}] HTTP concurrency slot acquired", partNum);
394+
395+
try
396+
{
397+
var task = CreateDownloadTaskAsync(partNum, discoveryResult.ObjectSize, wrappedCallback, internalCts.Token);
398+
downloadTasks.Add(task);
399+
}
400+
catch
401+
{
402+
// If task creation fails, release the HTTP slot we just acquired
403+
_httpConcurrencySlots.Release();
404+
_logger.DebugFormat("MultipartDownloadManager: [Part {0}] HTTP concurrency slot released due to task creation failure", partNum);
405+
throw;
406+
}
388407
}
389408

390409
var expectedTaskCount = downloadTasks.Count;
@@ -459,15 +478,8 @@ private async Task CreateDownloadTaskAsync(int partNumber, long objectSize, Even
459478

460479
try
461480
{
462-
_logger.DebugFormat("MultipartDownloadManager: [Part {0}] Waiting for HTTP concurrency slot (Available: {1}/{2})",
463-
partNumber, _httpConcurrencySlots.CurrentCount, _config.ConcurrentServiceRequests);
464-
465-
// Limit HTTP concurrency for both network download AND disk write
466-
// The semaphore is held until AFTER ProcessPartAsync completes to ensure
467-
// ConcurrentServiceRequests controls the entire I/O operation
468-
await _httpConcurrencySlots.WaitAsync(cancellationToken).ConfigureAwait(false);
469-
470-
_logger.DebugFormat("MultipartDownloadManager: [Part {0}] HTTP concurrency slot acquired", partNumber);
481+
// HTTP slot was already acquired in the for loop before this task was created
482+
// We just need to use it and release it when done
471483

472484
try
473485
{
@@ -544,7 +556,7 @@ private async Task CreateDownloadTaskAsync(int partNumber, long objectSize, Even
544556
finally
545557
{
546558
// Release semaphore after BOTH network download AND disk write complete
547-
// This ensures ConcurrentServiceRequests limits the entire I/O operation
559+
// Slot was acquired in the for loop before this task was created
548560
_httpConcurrencySlots.Release();
549561
_logger.DebugFormat("MultipartDownloadManager: [Part {0}] HTTP concurrency slot released (Available: {1}/{2})",
550562
partNumber, _httpConcurrencySlots.CurrentCount, _config.ConcurrentServiceRequests);

0 commit comments

Comments
 (0)