From 600da2b97e937b9c96791c291cb5e08cd8526bdd Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 7 Aug 2020 08:57:01 +0200 Subject: Add ability to allocate additional active threads to current thread --- libbuild2/scheduler.cxx | 34 +++++++++++++++++++++-- libbuild2/scheduler.hxx | 71 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 102 insertions(+), 3 deletions(-) diff --git a/libbuild2/scheduler.cxx b/libbuild2/scheduler.cxx index c660bba..03842ec 100644 --- a/libbuild2/scheduler.cxx +++ b/libbuild2/scheduler.cxx @@ -144,8 +144,8 @@ namespace build2 if (collision) stat_wait_collisions_++; - // If we have spare active threads, then become active. Otherwise it - // enters the ready queue. + // If we have spare active threads, then become active. Otherwise we enter + // the ready queue. // if (external) external_--; @@ -186,6 +186,36 @@ namespace build2 } size_t scheduler:: + allocate (size_t n) + { + if (max_active_ == 1) // Serial execution. + return 0; + + lock l (mutex_); + + if (active_ < max_active_) + { + size_t d (max_active_ - active_); + if (n == 0 || d < n) + n = d; + active_ -= n; + return n; + } + else + return 0; + } + + void scheduler:: + deallocate (size_t n) + { + if (max_active_ == 1) // Serial execution. + return; + + lock l (mutex_); + active_ += n; + } + + size_t scheduler:: suspend (size_t start_count, const atomic_count& task_count) { wait_slot& s ( diff --git a/libbuild2/scheduler.hxx b/libbuild2/scheduler.hxx index b7bc3c5..7e052cd 100644 --- a/libbuild2/scheduler.hxx +++ b/libbuild2/scheduler.hxx @@ -159,6 +159,72 @@ namespace build2 static void active_sleep (const duration&); + // Allocate additional active thread count to the current active thread, + // for example, to be "passed" to an external program: + // + // scheduler::alloc_guard ag (ctx.sched, ctx.sched.max_active () / 2); + // args.push_back ("-flto=" + to_string (1 + ag.n)); + // run (args); + // ag.deallocate (); + // + // The allocate() function reserves up to the specified number of + // additional threads returning the number actually allocated (which can + // be less than requested, including 0). If 0 is specified, then it + // allocates all the currently available threads. + // + // The deallocate() function returns the specified number of previously + // allocated threads back to the active thread pool. + // + // Note that when the thread is deactivated (directly or indirectly via + // wait, phase switching, etc), the additionally allocated threads are + // considered to be still active (this semantics could be changed if we + // have a plausible scenario for where waiting, etc., with allocated + // threads is useful). + // + size_t + allocate (size_t); + + void + deallocate (size_t); + + struct alloc_guard + { + size_t n; + + alloc_guard (): n (0), s_ (nullptr) {} + alloc_guard (scheduler& s, size_t m): n (s.allocate (m)), s_ (&s) {} + alloc_guard (alloc_guard&& x): n (x.n), s_ (x.s_) {x.s_ = nullptr;} + alloc_guard& operator= (alloc_guard&& x) + { + if (&x != this) + { + n = x.n; + s_ = x.s_; + x.s_ = nullptr; + } + return *this; + } + + ~alloc_guard () + { + if (s_ != nullptr && n != 0) + s_->deallocate (n); + } + + void + deallocate () + { + if (n != 0) + { + s_->deallocate (n); + n = 0; + } + } + + private: + scheduler* s_; + }; + // Startup and shutdown. // public: @@ -248,13 +314,16 @@ namespace build2 size_t o_; }; - // Return true if the scheduler is configured to run tasks serially. + // Return scheduler configuration. // // Note: can only be called from threads that have observed startup. // bool serial () const {return max_active_ == 1;} + size_t + max_active () const {return max_active_;} + // Wait for all the helper threads to terminate. Throw system_error on // failure. Note that the initially active threads are not waited for. // Return scheduling statistics. -- cgit v1.1