Skip to content

Commit 1ee720c

Browse files
bmehta001Copilot
andcommitted
fix(download): serialize the user progress callback across chunk workers
The per-chunk progress path (per_chunk_progress -> options.progress) could be entered concurrently by up to max_concurrency (default 64) chunk worker threads, but the public download progress API does not require the caller's callback to be thread-safe. A typical callback that updates a counter, UI handle, or logger would data-race. Guard the user callback invocation with a mutex so it is never re-entered concurrently; the atomics that compute the percentage are unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 42a7737 commit 1ee720c

1 file changed

Lines changed: 12 additions & 1 deletion

File tree

sdk_v2/cpp/src/download/blob_downloader.cc

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,13 @@ void DownloadBlobsToDirectory(IBlobDownloader& downloader,
504504
// overall percentage.
505505
std::atomic<int64_t> total_downloaded_bytes{skipped_bytes};
506506

507+
// The user progress callback can be reached from up to max_concurrency chunk
508+
// worker threads at once (per_chunk_progress below). Serialize it so a
509+
// caller's callback (UI handle, counter, logger, IPC) is never entered
510+
// concurrently — the public download progress API does not require callers to
511+
// be thread-safe.
512+
std::mutex progress_mutex;
513+
507514
for (const auto& [blob, local_path] : blobs_to_download) {
508515
// Check cancellation between blobs
509516
if (cancelled.load(std::memory_order_relaxed)) {
@@ -528,7 +535,11 @@ void DownloadBlobsToDirectory(IBlobDownloader& downloader,
528535
overall = std::min(overall, total_size);
529536

530537
float percent = static_cast<float>(overall) / static_cast<float>(total_size) * 100.0f;
531-
int result = options.progress(percent);
538+
int result;
539+
{
540+
std::lock_guard<std::mutex> lock(progress_mutex);
541+
result = options.progress(percent);
542+
}
532543
if (result != 0) {
533544
cancelled.store(true, std::memory_order_relaxed);
534545
FL_THROW(FOUNDRY_LOCAL_ERROR_OPERATION_CANCELLED, "download cancelled by user callback return value");

0 commit comments

Comments
 (0)