11# frozen_string_literal: true
22
3- require 'thread'
43require 'set'
54require 'tempfile'
65require 'stringio'
@@ -9,7 +8,6 @@ module Aws
98 module S3
109 # @api private
1110 class MultipartStreamUploader
12-
1311 DEFAULT_PART_SIZE = 5 * 1024 * 1024 # 5MB
1412 CREATE_OPTIONS = Set . new ( Client . api . operation ( :create_multipart_upload ) . input . shape . member_names )
1513 UPLOAD_PART_OPTIONS = Set . new ( Client . api . operation ( :upload_part ) . input . shape . member_names )
@@ -133,10 +131,22 @@ def upload_with_executor(read_pipe, completed, errors, options)
133131 queued_parts = 0
134132 part_number = 0
135133 mutex = Mutex . new
134+ concurent_readed_parts = 0 # slots available for reading
135+
136136 loop do
137137 part_body , current_part_num = mutex . synchronize do
138- [ read_to_part_body ( read_pipe ) , part_number += 1 ]
138+ # Prevent reading ahead of the executor if no slots available
139+ if concurent_readed_parts >= @executor . max_threads
140+ [ :skip , -1 ]
141+ else
142+ concurent_readed_parts += 1
143+ [ read_to_part_body ( read_pipe ) , part_number += 1 ]
144+ end
139145 end
146+
147+ next if part_body == :skip # Wait for the executor free a read slot
148+
149+ # No more parts to read or we need at least one part for empty content
140150 break unless part_body || current_part_num == 1
141151
142152 queued_parts += 1
@@ -145,8 +155,10 @@ def upload_with_executor(read_pipe, completed, errors, options)
145155 resp = @client . upload_part ( part )
146156 completed_part = create_completed_part ( resp , part )
147157 completed . push ( completed_part )
158+ mutex . synchronize { concurent_readed_parts -= 1 } # free a read slot
148159 rescue StandardError => e
149160 mutex . synchronize do
161+ concurent_readed_parts -= 1 # free a read slot
150162 errors . push ( e )
151163 read_pipe . close_read unless read_pipe . closed?
152164 end
0 commit comments