Boost logo

Boost Users :

Subject: Re: [Boost-users] Making an ASIO asynchronous operation from flock()
From: Stian Zeljko Vrba (vrba_at_[hidden])
Date: 2018-11-19 19:10:13


I'd suggest to use fcntl http://man7.org/linux/man-pages/man2/fcntl.2.html which immediately returns an error value if a conflicting lock is attempted to be taken (see under Advisory record locking; F_SETLK). Then you can use a timer to implement retry with exponential back-off or similar.

Alternately, I'd take "block all signals" with a grain of salt. You can use signals from the real-time signal range that isn't used by the system, and install a no-op signal handler. It will interrupt blocking operations (cause them to return with EINTR), but I strongly doubt that it'll affect non-blocking operations. (Signals are delivered on return from kernel space to user space and checking for them "too often" would be a performance penalty.)

-- Stian

________________________________
From: Boost-users <boost-users-bounces_at_[hidden]> on behalf of Cristian Morales Vega via Boost-users <boost-users_at_[hidden]>
Sent: Monday, November 19, 2018 6:04:01 PM
To: boost-users_at_[hidden]
Cc: Cristian Morales Vega
Subject: [Boost-users] Making an ASIO asynchronous operation from flock()

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/threads.html
"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 <boost/asio.hpp>
#include <boost/beast/core/bind_handler.hpp>
#include <boost/system/error_code.hpp>
#include <boost/system/system_error.hpp>
#include <fcntl.h>
#include <iostream>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/types.h>
#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<FLockToken, void(error_code)> 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();
}
-------------------------------------------------------------------------------
_______________________________________________
Boost-users mailing list
Boost-users_at_[hidden]
https://lists.boost.org/mailman/listinfo.cgi/boost-users



Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net