So I need to flock (http://man7.org/linux/man-pages/man2/flock.2.html)
something (pre-existing flock in other piece of software to
interoperate with) and I though: "I think I'm actually starting to
understand ASIO, let's make a proper ASIO asynchronous operation with
flock". Long story short, I now know better: I will never get ASIO.
So my main issues are with the background thread.
According to https://www.boost.org/doc/libs/develop/doc/html/boost_asio/overview/core/thr...
"the threads ... must block all signals". OK, it makes sense, but...
- I can make pthread_sigmask() the first thing once the std::thread
starts executing. But, I can't really guarantee the thread will never
receive a signal, can I? There is a small amount of time between the
thread starting to be able to receive signals and pthread_sigmask()
doing its job. Modifying the signal mask before the background thread
is created (so also from the "not-background" thread) doesn't seem a
lot better.
- How do I cancel the asynchronous operation? flock() is blocking, not
a lot I can do to unblock it. I could send a signal to the background
thread, but... they must be blocked, so no. I can't join the thread
while it's blocked in flock(). So... should it be detached? I could
then maybe just destroy the std::thread, but I still need to call the
handler with boost::asio::error::operation_aborted...
So, my question: I'm making it more difficult than it is? There is
some pattern I should be following which makes all this simple?
See what I have below (sorry for the camel case)
-------------------------------------------------------------------------------
#include
#include
#include
#include
#include
#include <iostream>
#include
#include
#include
#include <thread>
namespace asio = boost::asio;
using error_code = boost::system::error_code;
using system_error = boost::system::system_error;
class FLockablePosixDescriptor : public asio::posix::descriptor {
public:
enum class FlockType : int { SHARED = LOCK_SH, EXCLUSIVE = LOCK_EX };
using asio::posix::descriptor::descriptor;
FLockablePosixDescriptor(FLockablePosixDescriptor &&) = default;
FLockablePosixDescriptor &operator=(FLockablePosixDescriptor &&) = default;
~FLockablePosixDescriptor();
template <typename FLockToken>
BOOST_ASIO_INITFN_RESULT_TYPE(FLockToken, void(error_code))
asyncFLock(FlockType type, FLockToken &&token);
void cancel();
void cancel(error_code &ec);
private:
template <typename FLockHandler>
void doFLock(int fd, FlockType type, FLockHandler &handler);
asio::io_context mBackgroundContext{1};
std::thread mThread;
};
FLockablePosixDescriptor::~FLockablePosixDescriptor() {
mBackgroundContext.stop();
mThread.join();
}
template <typename FLockToken>
BOOST_ASIO_INITFN_RESULT_TYPE(FLockToken, void(error_code))
FLockablePosixDescriptor::asyncFLock(FlockType type, FLockToken &&token) {
asio::async_completion init(token);
mBackgroundContext.post([this, fd = native_handle(), type,
handler = std::move(init.completion_handler),
work = asio::make_work_guard(get_executor())] {
doFLock(fd, type, handler);
});
mThread = std::thread([this] { mBackgroundContext.run(); });
return init.result.get();
}
template <typename FLockHandler>
void FLockablePosixDescriptor::doFLock(int fd, FlockType type,
FLockHandler &handler) {
error_code ec;
if (flock(fd, static_cast<int>(type)) == -1) {
ec = error_code(errno, boost::system::system_category());
}
asio::post(get_executor(), boost::beast::bind_handler(handler, ec));
}
void FLockablePosixDescriptor::cancel() {
error_code ec;
cancel(ec);
if (ec) {
throw system_error(ec, "cancel");
}
}
void FLockablePosixDescriptor::cancel(error_code &ec) {
// TODO: Cancel flock
asio::posix::descriptor::cancel(ec);
}
int main() {
boost::asio::io_context ioc;
int fd = open("l", O_RDONLY);
FLockablePosixDescriptor desc(ioc, fd);
desc.asyncFLock(FLockablePosixDescriptor::FlockType::SHARED,
[](const error_code &ec) {
std::clog << "Done " << ec << std::endl;
});
ioc.run();
}
-------------------------------------------------------------------------------