aboutsummaryrefslogtreecommitdiff
path: root/libbutl
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2020-10-14 16:15:41 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2020-11-06 10:47:57 +0300
commit3ee9761b73aff34c7f30ee44b8aac276d413cc21 (patch)
tree8720a9282eb9cb9ea211c8fa0487724615b513c2 /libbutl
parente197ae6fae73719266fd4747f499cd6106fbff4e (diff)
Add builtin::timed_wait(), builtin::try_wait(), and pseudo_builtin()
Diffstat (limited to 'libbutl')
-rw-r--r--libbutl/builtin.cxx61
-rw-r--r--libbutl/builtin.ixx78
-rw-r--r--libbutl/builtin.mxx81
3 files changed, 191 insertions, 29 deletions
diff --git a/libbutl/builtin.cxx b/libbutl/builtin.cxx
index c6083b6..7a2c024 100644
--- a/libbutl/builtin.cxx
+++ b/libbutl/builtin.cxx
@@ -2164,17 +2164,22 @@ namespace butl
const dir_path& cwd,
const builtin_callbacks& cbs)
{
- return builtin (
- r,
- thread ([fn, &r, &args,
- in = move (in),
- out = move (out),
- err = move (err),
- &cwd,
- &cbs] () mutable noexcept
- {
- r = fn (args, move (in), move (out), move (err), cwd, cbs);
- }));
+ unique_ptr<builtin::async_state> s (
+ new builtin::async_state (
+ [fn,
+ &r,
+ &args,
+ in = move (in), out = move (out), err = move (err),
+ &cwd,
+ &cbs] () mutable noexcept
+ {
+ r = fn (args,
+ move (in), move (out), move (err),
+ cwd,
+ cbs);
+ }));
+
+ return builtin (r, move (s));
}
template <builtin_impl fn>
@@ -2200,7 +2205,7 @@ namespace butl
const builtin_callbacks& cbs)
{
r = fn (args, move (in), move (out), move (err), cwd, cbs);
- return builtin (r, thread ());
+ return builtin (r);
}
const builtin_map builtins
@@ -2222,4 +2227,36 @@ namespace butl
{"touch", {&sync_impl<&touch>, 2}},
{"true", {&true_, 0}}
};
+
+ // builtin
+ //
+ uint8_t builtin::
+ wait ()
+ {
+ if (state_ != nullptr)
+ {
+ unique_lock<mutex> l (state_->mutex);
+
+ if (!state_->finished)
+ state_->condv.wait (l, [this] {return state_->finished;});
+ }
+
+ return result_;
+ }
+
+ template <>
+ optional<uint8_t> builtin::
+ timed_wait (const chrono::milliseconds& tm)
+ {
+ if (state_ != nullptr)
+ {
+ unique_lock<mutex> l (state_->mutex);
+
+ if (!state_->finished &&
+ !state_->condv.wait_for (l, tm, [this] {return state_->finished;}))
+ return nullopt;
+ }
+
+ return result_;
+ }
}
diff --git a/libbutl/builtin.ixx b/libbutl/builtin.ixx
new file mode 100644
index 0000000..0356f8b
--- /dev/null
+++ b/libbutl/builtin.ixx
@@ -0,0 +1,78 @@
+// file : libbutl/builtin.ixx -*- C++ -*-
+// license : MIT; see accompanying LICENSE file
+
+namespace butl
+{
+ // builtin
+ //
+ // Implement timed_wait() function templates in terms of their milliseconds
+ // specialization.
+ //
+ template <>
+ LIBBUTL_SYMEXPORT optional<std::uint8_t> builtin::
+ timed_wait (const std::chrono::milliseconds&);
+
+ template <typename R, typename P>
+ inline optional<std::uint8_t> builtin::
+ timed_wait (const std::chrono::duration<R, P>& d)
+ {
+ using namespace std::chrono;
+ return timed_wait (duration_cast<milliseconds> (d));
+ }
+
+ inline optional<std::uint8_t> builtin::
+ try_wait ()
+ {
+ if (state_ != nullptr)
+ {
+ std::unique_lock<std::mutex> l (state_->mutex);
+
+ if (!state_->finished)
+ return nullopt;
+ }
+
+ return result_;
+ }
+
+ // builtin_map
+ //
+ inline const builtin_info* builtin_map::
+ find (const std::string& n) const
+ {
+ auto i (base::find (n));
+ return i != end () ? &i->second : nullptr;
+ }
+
+ // builtin::async_state
+ //
+ template <typename F>
+ inline builtin::async_state::
+ async_state (F f)
+ : thread ([f = std::move (f), this] () mutable noexcept
+ {
+ f ();
+
+ {
+ std::unique_lock<std::mutex> l (this->mutex);
+ finished = true;
+ }
+
+ condv.notify_all ();
+ })
+ {
+ }
+
+ template <typename F>
+ inline builtin
+ pseudo_builtin (std::uint8_t& r, F f)
+ {
+ std::unique_ptr<builtin::async_state> s (
+ new builtin::async_state (
+ [f = std::move (f), &r] () mutable noexcept
+ {
+ r = f ();
+ }));
+
+ return builtin (r, move (s));
+ }
+}
diff --git a/libbutl/builtin.mxx b/libbutl/builtin.mxx
index e4dd4f8..a99d6f4 100644
--- a/libbutl/builtin.mxx
+++ b/libbutl/builtin.mxx
@@ -9,13 +9,17 @@
#ifndef __cpp_lib_modules_ts
#include <map>
+#include <mutex>
#include <string>
#include <vector>
#include <thread>
-#include <cstddef> // size_t
-#include <utility> // move()
-#include <cstdint> // uint8_t
+#include <chrono>
+#include <memory> // unique_ptr
+#include <cstddef> // size_t
+#include <utility> // move()
+#include <cstdint> // uint8_t
#include <functional>
+#include <condition_variable>
#endif
// Other includes.
@@ -41,26 +45,61 @@ LIBBUTL_MODEXPORT namespace butl
{
// A process/thread-like object representing a running builtin.
//
- // For now, instead of allocating the result storage dynamically, we
- // expect it to be provided by the caller.
+ // For now, instead of allocating the result storage dynamically, we expect
+ // it to be provided by the caller (allocating it dynamically would be
+ // wasteful for synchronous builtins).
//
- class builtin
+ class LIBBUTL_SYMEXPORT builtin
{
public:
+ // Wait for the builtin to complete and return its exit code. This
+ // function can be called multiple times.
+ //
std::uint8_t
- wait () {if (t_.joinable ()) t_.join (); return r_;}
+ wait ();
+
+ // Return the same result as wait() if the builtin has already completed
+ // and nullopt otherwise.
+ //
+ optional<std::uint8_t>
+ try_wait ();
- ~builtin () {wait ();}
+ // Wait for the builtin to complete for up to the specified time duration.
+ // Return the same result as wait() if the builtin has completed in this
+ // timeframe and nullopt otherwise.
+ //
+ template <typename R, typename P>
+ optional<std::uint8_t>
+ timed_wait (const std::chrono::duration<R, P>&);
+
+ ~builtin () {if (state_ != nullptr) state_->thread.join ();}
public:
- builtin (std::uint8_t& r, std::thread&& t = std::thread ())
- : r_ (r), t_ (move (t)) {}
+ struct async_state
+ {
+ bool finished = false;
+ std::mutex mutex;
+ std::condition_variable condv;
+ std::thread thread;
+
+ // Note that we can't use std::function as an argument type to get rid
+ // of the template since std::function can only be instantiated with a
+ // copy-constructible function and that's too restrictive for us (won't
+ // be able to capture auto_fd by value in a lambda, etc).
+ //
+ template <typename F>
+ explicit
+ async_state (F);
+ };
+
+ builtin (std::uint8_t& r, std::unique_ptr<async_state>&& s = nullptr)
+ : result_ (r), state_ (move (s)) {}
builtin (builtin&&) = default;
private:
- std::uint8_t& r_;
- std::thread t_;
+ std::uint8_t& result_;
+ std::unique_ptr<async_state> state_;
};
// Builtin execution callbacks that can be used for checking/handling the
@@ -181,12 +220,20 @@ LIBBUTL_MODEXPORT namespace butl
// Return NULL if not a builtin.
//
const builtin_info*
- find (const std::string& n) const
- {
- auto i (base::find (n));
- return i != end () ? &i->second : nullptr;
- }
+ find (const std::string&) const;
};
+ // Asynchronously run a function as if it was a builtin. The function must
+ // have the std::uint8_t() signature and not throw exceptions.
+ //
+ // Note that using std::function as an argument type would be too
+ // restrictive (see above).
+ //
+ template <typename F>
+ builtin
+ pseudo_builtin (std::uint8_t&, F);
+
LIBBUTL_SYMEXPORT extern const builtin_map builtins;
}
+
+#include <libbutl/builtin.ixx>