|
11 | 11 | //===----------------------------------------------------------------------===// |
12 | 12 |
|
13 | 13 | #include "llbuild/Basic/ExecutionQueue.h" |
| 14 | +#include "llbuild/Basic/PlatformUtility.h" |
14 | 15 |
|
15 | 16 | #include "llbuild/Basic/Tracing.h" |
16 | 17 |
|
@@ -195,22 +196,16 @@ class LaneBasedExecutionQueue : public ExecutionQueue { |
195 | 196 |
|
196 | 197 | public: |
197 | 198 | LaneBasedExecutionQueue(ExecutionQueueDelegate& delegate, |
198 | | - unsigned numLanes, SchedulerAlgorithm alg, |
| 199 | + unsigned numLanesSuggestion, SchedulerAlgorithm alg, |
199 | 200 | const char* const* environment) |
200 | | - : ExecutionQueue(delegate), buildID(std::random_device()()), numLanes(numLanes), |
| 201 | + : ExecutionQueue(delegate), buildID(std::random_device()()), |
201 | 202 | readyJobs(Scheduler::make(alg)), environment(environment) |
202 | 203 | { |
203 | | - // Configure the background task maximum. We currently support an |
204 | | - // environmental override for experimentation pursposes, but otherwise limit |
205 | | - // to a modest multiple of the core count, since we currently burn one thread |
206 | | - // per background task. |
207 | | - char *p = getenv("LLBUILD_BACKGROUND_TASK_MAX"); |
208 | | - if (p && !StringRef(p).getAsInteger(10, backgroundTaskMax)) { |
209 | | - // Parsed. |
210 | | - } else { |
211 | | - backgroundTaskMax = std::min(1024U, numLanes * 64U); |
212 | | - } |
213 | | - |
| 204 | + |
| 205 | + auto taskLimits = estimateTaskLimits(numLanesSuggestion); |
| 206 | + numLanes = taskLimits.first; |
| 207 | + backgroundTaskMax = taskLimits.second; |
| 208 | + |
214 | 209 | for (unsigned i = 0; i != numLanes; ++i) { |
215 | 210 | lanes.push_back(std::unique_ptr<std::thread>( |
216 | 211 | new std::thread( |
@@ -243,6 +238,52 @@ class LaneBasedExecutionQueue : public ExecutionQueue { |
243 | 238 | } |
244 | 239 | } |
245 | 240 |
|
| 241 | + /// Returns the number of allowed foreground and background tasks. |
| 242 | + static auto estimateTaskLimits(unsigned numLanes) -> std::pair<unsigned, unsigned> { |
| 243 | + llbuild_rlim_t curOpenFileLimit = llbuild::basic::sys::getOpenFileLimit(); |
| 244 | + const unsigned reservedFileCount = (STDERR_FILENO+1) + 2 /* Database */ |
| 245 | + + 1 /* Logging */ |
| 246 | + + 2 /* Additional fds during spawn */ |
| 247 | + + 2 /* Fudge factor */; |
| 248 | + if (curOpenFileLimit < reservedFileCount) { |
| 249 | + assert(curOpenFileLimit < reservedFileCount); |
| 250 | + // Certainly can't afford background tasks. |
| 251 | + // Maybe even can't afford building altogether, but let's risk it. |
| 252 | + return std::make_pair(1, 0); |
| 253 | + } |
| 254 | + |
| 255 | + unsigned allowedFilesForTasks = static_cast<unsigned>(std::min(curOpenFileLimit, static_cast<llbuild_rlim_t>(INT_MAX))) - reservedFileCount; |
| 256 | + unsigned filesPerTask = 2; // A task has output [and control] file descriptors. |
| 257 | + unsigned maxConcurrentTasks = allowedFilesForTasks / filesPerTask; |
| 258 | + |
| 259 | + if (numLanes > maxConcurrentTasks) { |
| 260 | + // Can't afford background tasks, and maybe won't even support |
| 261 | + // the full extent of requested concurrency. |
| 262 | + numLanes = std::max(1u, maxConcurrentTasks); |
| 263 | + return std::make_pair(numLanes, 0); |
| 264 | + } |
| 265 | + |
| 266 | + // Number of tasks that can be run, according to open file limits. |
| 267 | + unsigned extraTasksMax = maxConcurrentTasks - numLanes; |
| 268 | + |
| 269 | + // Configure the background task maximum. We currently support an |
| 270 | + // environmental override for experimentation purposes, but otherwise |
| 271 | + // limit to a modest multiple of the core count, since we currently burn |
| 272 | + // one thread per background task. |
| 273 | + unsigned backgroundTaskMax = 0; |
| 274 | + char *p = getenv("LLBUILD_BACKGROUND_TASK_MAX"); |
| 275 | + if (p && !StringRef(p).getAsInteger(10, backgroundTaskMax)) { |
| 276 | + // Parsed. |
| 277 | + } else { |
| 278 | + backgroundTaskMax = std::min(1024U, numLanes * 64U); |
| 279 | + } |
| 280 | + |
| 281 | + // The number of background can't exceed available concurrency. |
| 282 | + backgroundTaskMax = std::min(backgroundTaskMax, extraTasksMax); |
| 283 | + |
| 284 | + return std::make_pair(numLanes, backgroundTaskMax); |
| 285 | + } |
| 286 | + |
246 | 287 | virtual void addJob(QueueJob job) override { |
247 | 288 | uint64_t readyJobsCount; |
248 | 289 | { |
@@ -328,22 +369,15 @@ class LaneBasedExecutionQueue : public ExecutionQueue { |
328 | 369 | handle.id = context.jobID; |
329 | 370 |
|
330 | 371 | ProcessReleaseFn releaseFn = [this](std::function<void()>&& processWait) { |
331 | | - bool releaseAllowed = false; |
332 | | - // This check is not guaranteed to prevent more than backgroundTaskMax |
333 | | - // tasks from releasing. We could race between the check and increment and |
334 | | - // thus have a few extra. However, for our purposes, this should be fine. |
335 | | - // The cap is primarly intended to prevent runaway explosions of tasks. |
336 | | - if (backgroundTaskCount < backgroundTaskMax) { |
337 | | - backgroundTaskCount++; |
338 | | - releaseAllowed = true; |
339 | | - } |
340 | | - if (releaseAllowed) { |
| 372 | + auto previousTaskCount = backgroundTaskCount.fetch_add(1); |
| 373 | + if (previousTaskCount < backgroundTaskMax) { |
341 | 374 | // Launch the process wait on a detached thread |
342 | 375 | std::thread([this, processWait=std::move(processWait)]() mutable { |
343 | 376 | processWait(); |
344 | 377 | backgroundTaskCount--; |
345 | 378 | }).detach(); |
346 | 379 | } else { |
| 380 | + backgroundTaskCount--; |
347 | 381 | // not allowed to release, call wait directly |
348 | 382 | processWait(); |
349 | 383 | } |
|
0 commit comments