|
149 | 149 | let(:gem_two) { definition.specs.find {|spec| spec.name == "two" } } |
150 | 150 |
|
151 | 151 | it "takes all available slots" do |
152 | | - redefine_build_jobs do |
| 152 | + acquired = track_build_jobs(rendezvous: true) do |
153 | 153 | Bundler::ParallelInstaller.call(installer, definition.specs, 5, false, true) |
154 | 154 | end |
155 | 155 |
|
156 | | - # Take 3 slots out of the 5 available. |
157 | | - expect(File.read(File.join(gem_one.extension_dir, "gem_make.out"))).to include("make -j3") |
| 156 | + # Take 3 slots (capped per gem) out of the 5 available. |
| 157 | + expect(acquired["one"]).to eq(3) |
158 | 158 | # Take the remaining 2 slots. |
159 | | - expect(File.read(File.join(gem_two.extension_dir, "gem_make.out"))).to include("make -j2") |
| 159 | + expect(acquired["two"]).to eq(2) |
160 | 160 | end |
161 | 161 |
|
162 | 162 | it "fallback to non parallel when no slots are available" do |
163 | | - redefine_build_jobs do |
| 163 | + acquired = track_build_jobs(rendezvous: true) do |
164 | 164 | Bundler::ParallelInstaller.call(installer, definition.specs, 3, false, true) |
165 | 165 | end |
166 | 166 |
|
167 | 167 | # Take 3 slots out of the 3 available. |
168 | | - expect(File.read(File.join(gem_one.extension_dir, "gem_make.out"))).to include("make -j3") |
| 168 | + expect(acquired["one"]).to eq(3) |
169 | 169 | # Fallback to one slot (non parallel). |
170 | | - expect(File.read(File.join(gem_two.extension_dir, "gem_make.out"))).to_not include("make -j") |
| 170 | + expect(acquired["two"]).to eq(1) |
171 | 171 | end |
172 | 172 |
|
173 | 173 | it "uses one jobs when installing serially" do |
| 174 | + acquired = nil |
174 | 175 | Bundler.settings.temporary(jobs: 1) do |
175 | | - Bundler::ParallelInstaller.call(installer, definition.specs, 1, false, true) |
| 176 | + acquired = track_build_jobs do |
| 177 | + Bundler::ParallelInstaller.call(installer, definition.specs, 1, false, true) |
| 178 | + end |
176 | 179 | end |
177 | 180 |
|
178 | | - expect(File.read(File.join(gem_one.extension_dir, "gem_make.out"))).to_not include("make -j") |
179 | | - expect(File.read(File.join(gem_two.extension_dir, "gem_make.out"))).to_not include("make -j") |
| 181 | + expect(acquired["one"]).to eq(1) |
| 182 | + expect(acquired["two"]).to eq(1) |
180 | 183 | end |
181 | 184 |
|
182 | 185 | it "release the job slots" do |
|
188 | 191 | end |
189 | 192 | end |
190 | 193 |
|
191 | | - Bundler::ParallelInstaller.call(installer, definition.specs, 3, false, true) |
| 194 | + acquired = track_build_jobs do |
| 195 | + Bundler::ParallelInstaller.call(installer, definition.specs, 3, false, true) |
| 196 | + end |
192 | 197 |
|
193 | 198 | # Take 3 slots out of the 3 available. |
194 | | - expect(File.read(File.join(gem_one.extension_dir, "gem_make.out"))).to include("make -j3") |
195 | | - # Take 3 slots that were released. |
196 | | - expect(File.read(File.join(gem_two.extension_dir, "gem_make.out"))).to include("make -j3") |
| 199 | + expect(acquired["one"]).to eq(3) |
| 200 | + # Take 3 slots that were released by `one`. |
| 201 | + expect(acquired["two"]).to eq(3) |
197 | 202 | end |
198 | 203 |
|
199 | | - def redefine_build_jobs |
| 204 | + # Records how many jobserver slots each gem's build acquired. RubyGems turns |
| 205 | + # that count directly into `make -jN`, so asserting on it verifies slot |
| 206 | + # allocation and release without reading a build log, which a successful |
| 207 | + # build no longer writes. With +rendezvous+, "one" grabs its slots first and |
| 208 | + # holds them until "two" has grabbed the rest, making the split deterministic. |
| 209 | + def track_build_jobs(rendezvous: false) |
| 210 | + acquired = {} |
200 | 211 | old_method = Bundler::RubyGemsGemInstaller.instance_method(:build_jobs) |
201 | 212 | Bundler::RubyGemsGemInstaller.remove_method(:build_jobs) |
202 | 213 |
|
203 | | - # Rendezvous so that "one" grabs its slots first and keeps holding them |
204 | | - # until "two" has grabbed the rest. Blocking on a queue avoids the |
205 | | - # busy-wait and makes the ordering deterministic. |
206 | 214 | one_acquired = Thread::Queue.new |
207 | 215 | two_acquired = Thread::Queue.new |
208 | 216 |
|
209 | 217 | Bundler::RubyGemsGemInstaller.define_method(:build_jobs) do |
210 | | - if spec.name == "one" |
211 | | - value = old_method.bind(self).call |
212 | | - one_acquired << true |
213 | | - two_acquired.pop |
214 | | - elsif spec.name == "two" |
215 | | - one_acquired.pop |
216 | | - value = old_method.bind(self).call |
217 | | - two_acquired << true |
218 | | - end |
| 218 | + value = |
| 219 | + if rendezvous && spec.name == "one" |
| 220 | + v = old_method.bind(self).call |
| 221 | + one_acquired << true |
| 222 | + two_acquired.pop |
| 223 | + v |
| 224 | + elsif rendezvous && spec.name == "two" |
| 225 | + one_acquired.pop |
| 226 | + v = old_method.bind(self).call |
| 227 | + two_acquired << true |
| 228 | + v |
| 229 | + else |
| 230 | + old_method.bind(self).call |
| 231 | + end |
219 | 232 |
|
| 233 | + acquired[spec.name] = value |
220 | 234 | value |
221 | 235 | end |
222 | 236 |
|
223 | 237 | yield |
| 238 | + |
| 239 | + acquired |
224 | 240 | ensure |
225 | 241 | Bundler::RubyGemsGemInstaller.remove_method(:build_jobs) |
226 | 242 | Bundler::RubyGemsGemInstaller.define_method(:build_jobs, old_method) |
|
0 commit comments