boost::asio with boost::unique_future - c++

According to http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/overview/cpp2011/futures.html, we can use boost::asio with std::future. But I couldn't find any information about working with boost::unique_future, which has more functions, such as then(). How can I use?

Boost.Asio only provides first-class support for asynchronous operations to return a C++11 std::future or an actual value in stackful coroutines. Nevertheless, the requirements on asynchronous operations documents how to customize the return type for other types, such as Boost.Thread's boost::unique_future. It requires:
A specialization of the handler_type template. This template is used to determine the actual handler to use based on the asynchronous operation's signature.
A specialization of the async_result template. This template is used both to determine the return type and to extract the return value from the handler.
Below is a minimal complete example demonstrating deadline_timer::async_wait() returning boost:unique_future with a basic calculation being performed over a series of continuations composed with .then(). To keep the example simple, I have opted to only specialize handler_type for the asynchronous operation signatures used in the example. For a complete reference, I highly suggest reviewing use_future.hpp and impl/use_future.hpp.
#include <exception> // current_exception, make_exception_ptr
#include <memory> // make_shared, shared_ptr
#include <thread> // thread
#include <utility> // move
#define BOOST_RESULT_OF_USE_DECLTYPE
#define BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/thread/future.hpp>
/// #brief Class used to indicate an asynchronous operation should return
/// a boost::unique_future.
class use_unique_future_t {};
/// #brief A special value, similiar to std::nothrow.
constexpr use_unique_future_t use_unique_future;
namespace detail {
/// #brief Completion handler to adapt a boost::promise as a completion
/// handler.
template <typename T>
class unique_promise_handler;
/// #brief Completion handler to adapt a void boost::promise as a completion
/// handler.
template <>
class unique_promise_handler<void>
{
public:
/// #brief Construct from use_unique_future special value.
explicit unique_promise_handler(use_unique_future_t)
: promise_(std::make_shared<boost::promise<void> >())
{}
void operator()(const boost::system::error_code& error)
{
// On error, convert the error code into an exception and set it on
// the promise.
if (error)
promise_->set_exception(
std::make_exception_ptr(boost::system::system_error(error)));
// Otherwise, set the value.
else
promise_->set_value();
}
//private:
std::shared_ptr<boost::promise<void> > promise_;
};
// Ensure any exceptions thrown from the handler are propagated back to the
// caller via the future.
template <typename Function, typename T>
void asio_handler_invoke(
Function function,
unique_promise_handler<T>* handler)
{
// Guarantee the promise lives for the duration of the function call.
std::shared_ptr<boost::promise<T> > promise(handler->promise_);
try
{
function();
}
catch (...)
{
promise->set_exception(std::current_exception());
}
}
} // namespace detail
namespace boost {
namespace asio {
/// #brief Handler type specialization for use_unique_future.
template <typename ReturnType>
struct handler_type<
use_unique_future_t,
ReturnType(boost::system::error_code)>
{
typedef ::detail::unique_promise_handler<void> type;
};
/// #brief Handler traits specialization for unique_promise_handler.
template <typename T>
class async_result< ::detail::unique_promise_handler<T> >
{
public:
// The initiating function will return a boost::unique_future.
typedef boost::unique_future<T> type;
// Constructor creates a new promise for the async operation, and obtains the
// corresponding future.
explicit async_result(::detail::unique_promise_handler<T>& handler)
{
value_ = handler.promise_->get_future();
}
// Obtain the future to be returned from the initiating function.
type get() { return std::move(value_); }
private:
type value_;
};
} // namespace asio
} // namespace boost
int main()
{
boost::asio::io_service io_service;
boost::asio::io_service::work work(io_service);
// Run io_service in its own thread to demonstrate future usage.
std::thread thread([&io_service](){ io_service.run(); });
// Arm 3 second timer.
boost::asio::deadline_timer timer(
io_service, boost::posix_time::seconds(3));
// Asynchronously wait on the timer, then perform basic calculations
// within the future's continuations.
boost::unique_future<int> result =
timer.async_wait(use_unique_future)
.then([](boost::unique_future<void> future){
std::cout << "calculation 1" << std::endl;
return 21;
})
.then([](boost::unique_future<int> future){
std::cout << "calculation 2" << std::endl;
return 2 * future.get();
})
;
std::cout << "Waiting for result" << std::endl;
// Wait for the timer to trigger and for its continuations to calculate
// the result.
std::cout << result.get() << std::endl;
// Cleanup.
io_service.stop();
thread.join();
}
Output:
Waiting for result
calculation 1
calculation 2
42

Related

Use Boost strand in conjunction with std::future

I have come across a use case where I would like to use a Boost strand in conjunction with a std::future.
To reduce code duplication, I have written a generic function which will post a task to a boost strand and return the future.
// Some definitions first...
typedef boost::asio::io_service::strand cb_strand;
typedef std::shared_ptr< cb_strand > cb_strand_ptr;
The code looks something like:
//////////////////////////////////////////////////////////////////////////
template <class Task>
auto post_future_to_strand(cb_strand_ptr apStrand, Task task)
{
using return_type = decltype(task());
auto promise = std::make_shared<std::promise<return_type>>();
auto future = promise->get_future();
apStrand->wrap
(
[promise, task]()
{
try
{
promise->set_value(task());
}
catch (...)
{
// LOG ERROR ...
// NOTE: Exceptions can be thrown when setting the exception!
try
{
promise->set_exception(std::current_exception());
}
catch (...)
{
//LOG ERROR ...
}
}
}
);
return future;
};
I then hoped to post a future to a strand as presented in the following example:
std::future<int> f = post_future_to_strand(m_apStrand, std::bind(&foo::bar, this))
std::cout << "foo::bar() -> int is " << f.get() << std::endl;
Unfortunately, I get a runtime exception:
terminate called after throwing an instance of 'std::future_error'
what(): std::future_error: Broken promise
Signal: SIGABRT (Aborted)
Having read the docs, I think I understand what a broken promise is and how the situation arises; however, I feel like I am capturing the promise in the lambda so all should be well. I am a newcomer to this world of lambdas, so perhaps my understanding is amiss.
Ubuntu Zesty
GCC 6.3 (configured for C++14 with cmake)
You wrap the task, but you never post it. Therefore, the wrapped task is immediately destructed, and with that the promise.
There's another pitfall, things only work if you run the io_service on a different thread than the one blocking for the future... Otherwise you have created a deadlock:
Live On Coliru deadlock
Now that you have multiple threads, you need to avoid the race-condition where the service exits before the task is posted in the first place.
Bonus:
I'd suggest a far simpler take on the wrapper:
template <typename Task>
auto post_future_to_strand(cb_strand_ptr apStrand, Task task)
{
auto package = std::make_shared<std::packaged_task<decltype(task())()> >(task);
auto future = package->get_future();
apStrand->post([package] { (*package)(); });
return future;
}
Full Demo
Live On Coliru
#include <boost/asio.hpp>
#include <future>
#include <iostream>
using cb_strand_ptr = boost::asio::strand*;
//////////////////////////////////////////////////////////////////////////
template <typename Task>
auto post_future_to_strand(cb_strand_ptr apStrand, Task task)
{
auto package = std::make_shared<std::packaged_task<decltype(task())()> >(task);
auto future = package->get_future();
apStrand->post([package] { (*package)(); });
return future;
}
struct Foo {
boost::asio::strand s;
cb_strand_ptr m_apStrand = &s;
Foo(boost::asio::io_service& svc) : s{svc} {}
void do_it() {
std::future<int> f = post_future_to_strand(m_apStrand, std::bind(&Foo::bar, this));
std::cout << "foo::bar() -> int is " << f.get() << std::endl;
}
int bar() {
return 42;
}
};
int main() {
boost::asio::io_service svc;
auto lock = std::make_unique<boost::asio::io_service::work>(svc); // prevent premature exit
std::thread th([&]{ svc.run(); });
Foo foo(svc);
foo.do_it();
lock.reset(); // allow service to exit
th.join();
}
Prints
foo::bar() -> int is 42

How to use Boost.Coroutine in a fashion similar to Unity3D Coroutine in terms of sleep?

How to use Boost.Coroutine in a fashion similar to Unity3D Coroutine in terms of sleep for X milliseconds? Generally we want having an array of corutines allow them to forvard next execution futher in time using some alternative of WaitForSeconds object as yield return. Is there any utilety for such things in Boost.Coroutine or how to recreate effect from scratch?
Boost.Fiber has sleep function. see follow:
https://github.com/olk/boost-fiber
Boost.Fiber is wrapper of Boost.Coroutine for thread interface.
// <boost/fiber/operations.hpp>
namespace boost {
namespace this_fiber {
void sleep_until( fibers::clock_type::time_point const& sleep_time);
template< typename Rep, typename Period >
void sleep_for( chrono::duration< Rep, Period > const& timeout_duration)
}}
I have the coroutine class using C++ 11 and boost coroutine2.
Usage of CoroBehaviour is very similar with MonoBehaviour of Unity like below:
#include "CoroBehaviour.h"
#include <iostream>
class ExampleClass : public CoroBehaviour
{
void Start()
{
StartCoroutine(coroutineA());
}
Enumerator coroutineA()
{
return [=](CoroPush& yield_return)
{
// wait for 1 second
std::cout << "coroutineA created";
yield_return(new WaitForSeconds(1.0f));
yield_return(StartCoroutine(coroutineB()));
std::cout << "coroutineA running again";
};
}
Enumerator coroutineB()
{
return [=](CoroPush& yield_return)
{
std::cout << "coroutineB created";
yield_return(new WaitForSeconds(2.5f));
std::cout << "coroutineB enables coroutineA to run";
};
}
}
You can get the code from here: https://github.com/exawon/CoroBehaviour

Integrate boost::asio into file descriptor based eventloops (select/poll)

If I want to integrate stuff from boost::asio into an eventloop that is based on file descriptors (select/poll), how can I achieve it? Other libraries with asynchronous functions offer to hand out a file descriptor that becomes readable as soon as there is work to be done, so that you can integrate it into the select/poll of the eventloop and let it call a processing callback of the library (like a single shot event processing).
A great example would be an asynchronous name resolver in a thread pool, like discussed in this question.
Based on the example in this answer I came up with this solution that uses a generic handler, which writes into a wake-up pipe and then posts the handler call into another io_service. The read end of the pipe can be used in a file descriptor based event loop and the callback run_handler() is called from there, which clears the pipe and runs pending handlers in the main thread.
#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/optional.hpp>
#include <boost/thread.hpp>
/// #brief Type used to emulate asynchronous host resolution with a
/// dedicated thread pool.
class resolver {
public:
resolver(const std::size_t pool_size)
: work_(boost::ref(resolver_service_)) {
// Create wake-up pipe
pipe(pipe_);
fcntl(pipe_[0], F_SETFL, O_NONBLOCK);
// Create pool.
for (std::size_t i = 0; i < pool_size; ++i)
threads_.create_thread(boost::bind(&boost::asio::io_service::run,
&resolver_service_));
}
~resolver() {
work_ = boost::none;
threads_.join_all();
}
template <typename QueryOrEndpoint, typename Handler>
void async_resolve(QueryOrEndpoint query, Handler handler) {
resolver_service_.post(boost::bind(
&resolver::do_async_resolve<QueryOrEndpoint, Handler>, this,
query, handler));
}
// callback for eventloop in main thread
void run_handler() {
char c;
// clear wake-up pipe
while (read(pipe_[0], &c, 1) > 0);
// run handler posted from resolver threads
handler_service_.poll();
handler_service_.reset();
}
// get read end of wake up pipe for polling in eventloop
int fd() {
return pipe_[0];
}
private:
/// #brief Resolve address and invoke continuation handler.
template <typename QueryOrEndpoint, typename Handler>
void do_async_resolve(const QueryOrEndpoint& query, Handler handler) {
typedef typename QueryOrEndpoint::protocol_type protocol_type;
typedef typename protocol_type::resolver resolver_type;
// Resolve synchronously, as synchronous resolution will perform work
// in the calling thread. Thus, it will not use Boost.Asio's internal
// thread that is used for asynchronous resolution.
boost::system::error_code error;
resolver_type resolver(resolver_service_);
typename resolver_type::iterator result = resolver.resolve(query, error);
// post handler callback to service running in main thread
handler_service_.post(boost::bind(handler, error, result));
// wake up eventloop in main thread
write(pipe_[1], "*", 1);
}
private:
boost::asio::io_service resolver_service_;
boost::asio::io_service handler_service_;
boost::optional<boost::asio::io_service::work> work_;
boost::thread_group threads_;
int pipe_[2];
};
template <typename ProtocolType>
void handle_resolve(
const boost::system::error_code& error,
typename ProtocolType::resolver::iterator iterator) {
std::stringstream stream;
stream << "handle_resolve:\n"
" " << error.message() << "\n";
if (!error)
stream << " " << iterator->endpoint() << "\n";
std::cout << stream.str();
std::cout.flush();
}
int main() {
// Resolver will emulate asynchronous host resolution with a pool of 5
// threads.
resolver resolver(5);
namespace ip = boost::asio::ip;
resolver.async_resolve(
ip::udp::resolver::query("localhost", "12345"),
&handle_resolve<ip::udp>);
resolver.async_resolve(
ip::tcp::resolver::query("www.google.com", "80"),
&handle_resolve<ip::tcp>);
resolver.async_resolve(
ip::udp::resolver::query("www.stackoverflow.com", "80"),
&handle_resolve<ip::udp>);
resolver.async_resolve(
ip::icmp::resolver::query("some.other.address", "54321"),
&handle_resolve<ip::icmp>);
pollfd fds;
fds.fd = resolver.fd();
fds.events = POLLIN;
// simple eventloop
while (true) {
if (poll(&fds, 1, 2000)) // waiting for wakeup call
resolver.run_handler(); // call resolve handler
else
break;
}
}
Several of the objects in the Boost ASIO library expose a native_handle for scenarios like this.

std::function as sighandler_t

How to specify lambda, std::bind result or any other std::function as argument for unix signal function?
I'm trying the following
std::function<void(int)> handler1 = std::bind(&cancellation_token::cancel, &c);
std::function<void(int)> handler2 = [&c](int) { c.cancel(); };
but it doesn't work, because both
handler1.target<void(int)>()
and
handler2.target<void(int)>()
return null
It works if I initialize handler with free function pointer
void foo(int) { ... }
std::function<void(int)> handler = foo;
but this is absolutely useless. I need to capture some local variables, so I need either bind or lambda.
Actually I understand why it doesn't work. Documentation says that target function returns a pointer to the stored function if target_type() == typeid(T), otherwise a null pointer. I don't understand how to make it work.
Any suggestions?
Since it's constructed by bind, or lambda with captured-data, you cannot convert it to free function, since target function works by typeid, std::function saves it in runtime, not for type T, with which function is templated. For std::bind it will be some library-type and for lambda it will be some unnamed type.
You can use a dispatcher-like approach associating signal numbers to std::functions through a map.
You just need a map to hold the std::functions accesible from a free function:
std::unordered_map<int, std::function<void(int)>> signalHandlers;
And a generic handler (free function) to map the signal number to the function:
void dispatcher(int signal) {
// this will call a previously saved function
signalHandlers.at(signal)(signal);
}
Implementation example
main.cpp
#include <iostream>
#include <thread>
#include <csignal>
#include "cppsignal.hpp"
int main() {
bool stop = false;
// set a handler as easy as this
CppSignal::setHandler(SIGINT, [&stop] (int) { stop = true; });
while (!stop) {
std::this_thread::sleep_for(std::chrono::seconds(1));
}
std::cout << "Bye" << std::endl;
return 0;
}
cppsignal.cpp
#include <cstring> // strsignal
#include <csignal>
#include <string>
#include <stdexcept>
#include <unordered_map>
#include <mutex>
#include "signal.hpp"
namespace CppSignal {
std::timed_mutex signalHandlersMutex;
std::unordered_map<int, std::function<void(int)>> signalHandlers;
// generic handler (free function) to set as a handler for any signal
void dispatcher(int signal) {
std::unique_lock<std::timed_mutex> lock(signalHandlersMutex, std::defer_lock);
if (!lock.try_lock_for(std::chrono::seconds(1))) {
// unable to get the lock. should be a strange case
return;
}
auto it = signalHandlers.find(signal);
if (it != signalHandlers.end()) {
it->second(signal);
}
}
void registerHandler(int signal, const std::function<void(int)>& handler) {
std::lock_guard<std::timed_mutex> lock(signalHandlersMutex);
signalHandlers.emplace(signal, handler);
}
// this is the only method you will use
void setHandler(int signal, const std::function<void(int)>& handler, int flags) {
// configure sigaction structure
struct sigaction action;
if (sigfillset(&action.sa_mask) == -1) {
throw std::runtime_error("sigfillset failed");
}
action.sa_flags = flags;
action.sa_handler = dispatcher;
// set handler for the signal
if (sigaction(signal, &action, nullptr) == -1 && signal < __SIGRTMIN) {
throw std::runtime_error("Fail at configuring handler for signal: " + std::string(strsignal(signal)));
}
registerHandler(signal, handler);
}
}
cppsignal.hpp
#ifndef __CPPSIGNAL_HPP
#define __CPPSIGNAL_HPP
#include <functional>
namespace CppSignal {
void setHandler(int signal, const std::function<void(int)>& handler, int flags=0);
}
#endif
sighandler_t is defined to be a pointer to a function with the following definition:
void func(int);
Since std::bind and lambdas return functors, it is not possible to directly use them as signal handler. As a workaround you can use your own wrapper functions like
class SignalHandlerBase
{
public:
virtual void operator(int) = 0;
};
template <class T>
class SignalHandler : public SignalHandlerBase
{
T t;
public:
SignalHandler(T _t) : t(_t) { }
void operator(int i)
{
t(i);
}
};
class SignalManager
{
int sig;
SignalHandlerBase *shb;
static void handlerFunction(int i)
{
shb(i);
}
public:
SignalManager(int signal) : sig(signal), shb(nullptr) { signal(signal, &handlerFunction); }
template <class T>
void installHandler(T t)
{
delete shb;
shb = new SignalHandler<T>(t);
}
};
Use global instances of SignalManager to manage individual signals
C++11 1.9 [intro.execution]/6:
When the processing of the abstract machine is interrupted by receipt of a signal, the values of objects which
are neither
of type volatile std::sig_atomic_t nor
lock-free atomic objects (29.4)
are unspecified during the execution of the signal handler, and the value of any
object not in either of these
two categories that is modified by the handler becomes undefined.
The only action you can realistically take portably in a signal handler is to change the value of a flag whose type is volatile std::sig_atomic_t or a lock-free std::atomic (Note that not all std::atomic objects are lock-free). Non-signal handling code can then poll that flag to respond to the occurrence of the signal.
N3787 has some interesting discussion about how to fix C++11 basically breaking signal handlers as a concept.

Post callbacks to a task queue using boost::bind

Suppose I have a function called subscribe() that takes a callback handler, which will be called when the event is triggered.
Now, I have another version, called subscribe2(). Everything is the same except that, when triggered, it needs to post it to an event queue. It is implemented using the original subscribe(), with a helper funciton called helper(). All it does is to bind the original handler and whatever additional arguments into a functor, and call postToEventQueue().
Now, I wonder if there's a way to eliminate the helper function, so that in subsribe2(), I can somehow package the postToTaskQueue() function and the original callback handler directly, and pass it to subscribe(). The reason is that I have a lot of different handler types, and it is tedious and tiring to introduce helper function all over the place. Afterall, boost::bind is supposed to return a new function given the original function, right? I am trying to generate the helper function directly with boost::bind.
One attempt is to say
subscribe(boost::bind(boost::bind(postToTaskQueue, boost::bind(_1, _2)), cb, _1));
in subscribe2(), but it doesn't work. Is it possible at all?
Please see detailed example code below. Thanks!
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <iostream>
typedef boost::function<void(int)> SomeCallback;
typedef boost::function<void()> Task;
void handler(int i){
std::cout << "i=" << i <<std::endl;
}
void subscribe(SomeCallback cb)
{
cb(100); //just invoke the callback for simplicity
}
void postToTaskQueue(Task t)
{
t(); // just invoke the task for simplicity
}
void helper(SomeCallback cb, int i)
{
Task t = boost::bind(cb, i);
postToTaskQueue(t);
}
void subscribe2(SomeCallback cb)
{
subscribe(boost::bind(helper, cb, _1));
// this does not work..
// subscribe(boost::bind(boost::bind(postToTaskQueue, boost::bind(_1, _2)), cb, _1));
}
int main()
{
subscribe(boost::bind(handler, _1));
subscribe2(boost::bind(handler, _1));
}
I have no answer. However, I've played with this for over an hour:
boost::bind
boost::apply<>
boost::protect
Maybe, just maybe, a more experienced boost developer could take it from here:
void subscribe2(SomeCallback cb)
{
using boost::bind;
using boost::protect;
using boost::apply;
bind(cb, 41)(); // OK of course
postToTaskQueue(bind(cb, 46)); // also fine
bind(postToTaskQueue, protect(bind(cb, 146)))(); // boost::protect to the rescue
postToTaskQueue(bind(apply<void>(), cb, 47));
bind(postToTaskQueue, protect(bind(apply<void>(), cb, 147)))();
The above prints
i=41
i=46
i=146
i=47
i=147
But, sadly, I can't seem to make this this thing parameterizing (as suggested should work in the documentation on composition using Nested Binds):
// but sadly, this appears to not work ...
auto hmm = bind(postToTaskQueue, bind(apply<void>(), cb, _1));
hmm(997); // FAIL
}
Here's a fully compiled demo showing the state of affairs: Live on Coliru
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/bind/protect.hpp>
#include <boost/bind/apply.hpp>
#include <iostream>
typedef boost::function<void(int)> SomeCallback;
typedef boost::function<void()> Task;
void handler(int i){
std::cout << "i=" << i <<std::endl;
}
void subscribe(SomeCallback cb)
{
cb(100); //just invoke the callback for simplicity
}
void postToTaskQueue(Task t)
{
t(); // just invoke the task for simplicity
}
void helper(SomeCallback cb, int i)
{
postToTaskQueue(boost::bind(cb, i));
}
void subscribe2(SomeCallback cb)
{
using boost::bind;
using boost::protect;
using boost::apply;
bind(cb, 41)(); // OK of course
postToTaskQueue(bind(cb, 46)); // also find
bind(postToTaskQueue, protect(bind(cb, 146)))(); // boost::protect to the rescue
postToTaskQueue(bind(apply<void>(), cb, 47));
bind(postToTaskQueue, protect(bind(apply<void>(), cb, 147)))();
// but sadly, this appears to not work ...
auto hmm = bind(postToTaskQueue, bind(apply<void>(), cb, _1));
//hmm(997); // FAIL
}
int main()
{
subscribe (boost::bind(handler, _1));
subscribe2(boost::bind(handler, _1));
}
You are binding a function (helper) that itself does a bind. That mean you are (indirectly) binding bind itself. This is the key insight. The solution is to write a little bind function object wrapper that can itself be bound. Here's what my solution looks like:
#include <utility>
#include <iostream>
#include <boost/function.hpp>
#include <boost/phoenix/bind.hpp>
#include <boost/phoenix/core/argument.hpp>
using boost::phoenix::placeholders::_1;
typedef boost::function<void(int)> SomeCallback;
typedef boost::function<void()> Task;
struct bind_t
{
template<typename Sig>
struct result;
template<typename This, typename ...A>
struct result<This(A...)>
{
typedef decltype(boost::phoenix::bind(std::declval<A>()...)) type;
};
template<typename ...A>
auto operator()(A &&...a) const -> decltype(boost::phoenix::bind(std::forward<A>(a)...))
{
return boost::phoenix::bind(std::forward<A>(a)...);
}
};
bind_t const bind = {};
void handler(int i)
{
std::cout << "i=" << i <<std::endl;
}
void subscribe(SomeCallback cb)
{
cb(100); //just invoke the callback for simplicity
}
void postToTaskQueue(Task t)
{
t(); // just invoke the task for simplicity
}
void subscribe2(SomeCallback cb)
{
subscribe(bind(postToTaskQueue, bind(bind, cb, _1)));
}
int main()
{
subscribe(::bind(handler, _1));
subscribe2(::bind(handler, _1));
}
I switched to Phoenix's bind because it lets you bind polymorphic function objects (which bind above is).
This solution requires decltype. It also uses variadics, but that can be faked with overloads up to N arguments. Rvalue refs are also a convenience that can be done without with a little more work.