text:
// This is an independent project of an individual developer. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
#include "com_port_gw.h"

#include <boost/bind.hpp>
#include <boost/utility.hpp>
#include <boost/algorithm/string.hpp>

namespace ba = boost::asio;

namespace SerialOnBoost {
SerialPort::SerialPort()
    : m_serial_port(m_cont), m_timer(m_cont)
{

}

SerialPort::SerialPort(const std::string& port_name, std::size_t baud_rate, std::size_t timeout_ms)
    : SerialPort()
{
    open(port_name, baud_rate, timeout_ms);
}

void SerialPort::open(const std::string& port_name, std::size_t baud_rate, std::size_t timeout_ms)
{
    m_serial_port.open(port_name);
    // set serial port parameters
    m_serial_port.set_option(ba::serial_port_base::baud_rate(9600));
    m_serial_port.set_option( ba::serial_port_base::character_size(8));
    m_serial_port.set_option( ba::serial_port_base::flow_control(ba::serial_port_base::flow_control::none));
    m_serial_port.set_option( ba::serial_port_base::parity(ba::serial_port_base::parity::none));
    m_serial_port.set_option( ba::serial_port_base::stop_bits(ba::serial_port_base::stop_bits::one));
    // set timeout
    m_timeout = boost::posix_time::milliseconds(timeout_ms);
}

void SerialPort::write(const unsigned char* data, const std::size_t write_size)
{
    ba::write(m_serial_port, ba::buffer(data, write_size));
    //ba::write(m_serial_port, ba::buffer(data, write_size), ba::transfer_all());
}

int SerialPort::read(unsigned char* data, const std::size_t read_size)
{
    //ba::read(m_serial_port, ba::buffer(data, read_size), ba::transfer_all());
    if (m_timeout != boost::posix_time::seconds(0)) { //If timeout is set, start timer
        m_timer.expires_from_now(m_timeout);
        m_timer.async_wait(boost::bind(&SerialPort::timeout_expired,
                                       this,
                                       ba::placeholders::error
                                       )
                           );
    }

    ba::async_read(m_serial_port,
                   ba::buffer(data, read_size),
                   boost::bind(&SerialPort::read_completed,
                               this,
                               ba::placeholders::error,
                               ba::placeholders::bytes_transferred
                               )
                   );
    m_read_result = resultInProgress;
    m_bytes_transferred = 0;
    while(true)
    {
        m_cont.run_one(); //
        switch(m_read_result)
        {
        case resultSuccess:
            m_timer.cancel();
            return m_bytes_transferred;
        case resultTimeoutExpired:
            m_serial_port.cancel();
            throw SerialTimeoutException("Error in TimeutSerialBoost::read, timeout expired");
        case resultError:
            m_timer.cancel();
            m_serial_port.cancel();
            throw SerialException("Error in TimeutSerialBoost::read, read error");
        default: //if resultInProgress remain in the loop
            break;
        }
    }
}

void SerialPort::read_completed(const boost::system::error_code& error, const size_t transferred)
{
    if(error) {
        if(error != ba::error::operation_aborted)
            m_read_result = resultError;
    } else {
        if(m_read_result != resultInProgress)
            return;
        m_read_result = resultSuccess;
        this->m_bytes_transferred = transferred;
    }
}

void SerialPort::timeout_expired(const boost::system::error_code& error)
{
    if(m_read_result != resultInProgress)
        return;
    if(error != ba::error::operation_aborted)
        m_read_result = resultTimeoutExpired;
}
}

max_stars_repo_name: gwlsrm/Units

max_issues_repo_name: gwlsrm/Units

max_forks_repo_name: gwlsrm/Units


text:
/**
 * @file fsOperations.cc
 * @author Krzysztof Trzepla
 * @copyright (C) 2015 ACK CYFRONET AGH
 * @copyright This software is released under the MIT license cited in
 * 'LICENSE.txt'
 */

#include "fsOperations.h"

#include "communication/exception.h"
#include "fslogic/composite.h"
#include "fuseOperations.h"
#include "helpers/logging.h"
#include "monitoring/monitoring.h"
#include "oneException.h"
#include "util/xattrHelper.h"

#include <boost/algorithm/string/predicate.hpp>
#include <folly/FBString.h>
#include <folly/Range.h>
#include <folly/container/Enumerate.h>
#include <folly/futures/Future.h>
#include <folly/io/IOBufQueue.h>
#if FUSE_USE_VERSION > 30
#include <fuse3/fuse.h>
#else
#include <fuse/fuse.h>
#endif

#include <array>
#include <exception>
#include <execinfo.h>
#include <memory>
#include <sys/xattr.h>
#include <system_error>

namespace fslogic = one::client::fslogic;
namespace xattr = one::client::util::xattr;

namespace {

// Fuse readdir requires that a response (i.e. a list of file
// names or list of file attribute structs) fits in a single page (4K). If we
// only request from op-worker chunks in the size of PAGE_SIZE/MAXIMUM_FILE_NAME
// most of the time we'll be making very small requests. Since typically most
// file names are short, with this constant we can make bigger requests, even if
// sometimes we will have to request entries already received in the previous
// request
constexpr auto AVERAGE_FILE_NAME_LENGTH = 20;

// Print Fuse request context, including uid, gid, pid and umask
#define LOG_FUSE_CTX(ARG)                                                      \
    " " #ARG " = {uid: " << (ARG)->uid << ", gid: " << (ARG)->gid              \
                         << ", pid: " << (ARG)->pid                            \
                         << ", umask: " << (ARG)->umask << "}"

template <typename Fun, typename... Args>
auto callFslogic(Fun &&fun, void *userData, Args &&...args)
{
    auto &fsLogic =
        *static_cast<std::unique_ptr<fslogic::Composite> *>(userData);

    return ((*fsLogic).*std::forward<Fun>(fun))(std::forward<Args>(args)...);
}

template <typename Fun, typename... Args, typename Cb>
void wrap(Fun &&fun, Cb &&callback, fuse_req_t req, Args &&...args)
{
    one::helpers::activateFuseSession();

    callFslogic(std::forward<Fun>(fun), fuse_req_userdata(req),
        std::forward<Args>(args)...)
        .thenValue(std::forward<Cb>(callback))
        .thenError(folly::tag_t<std::errc>{},
            [req](auto &&errc) {
                LOG(ERROR) << "Fuse error exception: "
                           << std::make_error_code(errc).message();
                fuse_reply_err(req, std::make_error_code(errc).value());
            })
        .thenError(folly::tag_t<std::system_error>{},
            [req](auto &&e) {
                LOG_DBG(1) << "System error exception: " << e.what() << " ("
                           << e.code().value() << ")";
                fuse_reply_err(req, e.code().value());
            })
        .thenError(folly::tag_t<one::communication::TimeoutExceeded>{},
            [req](auto && /*unused*/) {
                LOG(ERROR) << "Provider connection timed out";
                fuse_reply_err(
                    req, std::make_error_code(std::errc::timed_out).value());
            })
        .thenError(folly::tag_t<one::communication::Exception>{},
            [req](auto &&t) {
                LOG(ERROR) << "Communication exception: " << t.what();
                fuse_reply_err(
                    req, std::make_error_code(std::errc::io_error).value());
            })
        .thenError(folly::tag_t<one::client::OneException>{},
            [req](auto &&e) {
                LOG(ERROR) << "OneException: " << e.what();
                fuse_reply_err(
                    req, std::make_error_code(std::errc::io_error).value());
            })
        .thenError(folly::tag_t<folly::FutureTimeout>{},
            [req](auto && /*unused*/) {
                LOG(ERROR) << "Fuse operation timed out";
                fuse_reply_err(
                    req, std::make_error_code(std::errc::timed_out).value());
            })
        .thenError(folly::tag_t<folly::exception_wrapper>{}, [req](auto &&ew) {
            try {
                try {
                    ew.throw_exception();
                }
                catch (const std::exception &e) {
                    LOG(ERROR) << "Exception: " << e.what();
                    throw;
                }
            }
            catch (...) {
                LOG_STACKTRACE(1, "Unknown exception caught at the top level: ")
            }

            fuse_reply_err(req, EIO);
        });
}

extern "C" {

void wrap_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
{
    LOG_FCALL() << LOG_FUSE_CTX(fuse_req_ctx(req)) << LOG_FARG(parent)
                << LOG_FARG(name);

    auto timer = ONE_METRIC_TIMERCTX_CREATE("comp.oneclient.mod.fuse.lookup");
    // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDelete)
    wrap(
        &fslogic::Composite::lookup,
        [req, timer = std::move(timer)](const struct fuse_entry_param &entry) {
            auto *const userdata = fuse_req_userdata(req);
            // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDelete)
            if (fuse_reply_entry(req, &entry) != 0)
                // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDelete)
                callFslogic(
                    &fslogic::Composite::forget, userdata, entry.ino, 1);
        },
        req, parent, name);
}

void wrap_getattr(
    fuse_req_t req, fuse_ino_t ino, struct fuse_file_info * /*fi*/)
{
    LOG_FCALL() << LOG_FUSE_CTX(fuse_req_ctx(req)) << LOG_FARG(ino);

    auto timer = ONE_METRIC_TIMERCTX_CREATE("comp.oneclient.mod.fuse.getattr");
    wrap(
        &fslogic::Composite::getattr,
        [req, timer = std::move(timer)](
            const struct stat &attrs) { fuse_reply_attr(req, &attrs, 0); },
        req, ino);
}

void wrap_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
    LOG_FCALL() << LOG_FUSE_CTX(fuse_req_ctx(req)) << LOG_FARG(ino);

    auto timer = ONE_METRIC_TIMERCTX_CREATE("comp.oneclient.mod.fuse.opendir");
    wrap(
        &fslogic::Composite::opendir,
        [req, timer = std::move(timer), fi = *fi](
            const std::uint64_t fh) mutable {
            fi.fh = fh;
            fuse_reply_open(req, &fi);
        },
        req, ino);
}

void wrap_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
    LOG_FCALL() << LOG_FUSE_CTX(fuse_req_ctx(req)) << LOG_FARG(ino);

    auto timer =
        ONE_METRIC_TIMERCTX_CREATE("comp.oneclient.mod.fuse.releasedir");
    wrap(
        &fslogic::Composite::releasedir,
        [req, timer = std::move(timer)](
            auto && /*unit*/) { fuse_reply_err(req, 0); },
        req, ino, fi->fh);
}

void wrap_readdir(fuse_req_t req, fuse_ino_t ino, size_t maxSize, off_t off,
    struct fuse_file_info * /*fi*/)
{
    LOG_FCALL() << LOG_FUSE_CTX(fuse_req_ctx(req)) << LOG_FARG(ino)
                << LOG_FARG(maxSize) << LOG_FARG(off);

    auto timer = ONE_METRIC_TIMERCTX_CREATE("comp.oneclient.mod.fuse.readdir");
    wrap(
        &fslogic::Composite::readdir,
        [req, maxSize, off, timer = std::move(timer)](
            const folly::fbvector<folly::fbstring> &names) {
            LOG_DBG(2) << "Received " << names.size() << " directory entries.";

            if (names.empty()) {
                fuse_reply_buf(req, nullptr, 0);
                ONE_METRIC_TIMERCTX_STOP(timer, 0);
                return;
            }

            std::size_t bufSize = 0;
            const auto *begin = names.begin();
            const auto *end = begin;
            for (; end < names.end(); ++end) {
                const auto nextSize = fuse_add_direntry(
                    req, nullptr, 0, end->c_str(), nullptr, 0);

                if (bufSize + nextSize > maxSize)
                    break;

                LOG_DBG(2) << "Returning directory entry: " << end->c_str();

                bufSize += nextSize;
            }

            folly::fbvector<char> buf(bufSize);
            struct stat stbuf = {0};
            stbuf.st_ino = static_cast<fuse_ino_t>(-1);

            auto *bufPoint = buf.data();

            for (const auto it : folly::enumerate(folly::range(begin, end))) {
                const auto remaining = buf.size() - (bufPoint - buf.data());
                const auto nextSize = fuse_add_direntry(req, bufPoint,
                    remaining, it->c_str(), &stbuf, off + it.index + 1);

                bufPoint += nextSize;
            }

            fuse_reply_buf(req, buf.data(), buf.size());

            ONE_METRIC_TIMERCTX_STOP(timer, names.size());
        },
        req, ino, maxSize / AVERAGE_FILE_NAME_LENGTH, off);
}

void wrap_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
    LOG_FCALL() << LOG_FUSE_CTX(fuse_req_ctx(req)) << LOG_FARG(ino)
                << LOG_FARG(fi->fh);

    auto timer = ONE_METRIC_TIMERCTX_CREATE("comp.oneclient.mod.fuse.open");
    wrap(
        &fslogic::Composite::open,
        [req, ino, fi = *fi, timer = std::move(timer)](
            const std::uint64_t fh) mutable {
            auto *const userdata = fuse_req_userdata(req);
            fi.fh = fh;
            fi.direct_io = 1;
            if (fuse_reply_open(req, &fi) != 0)
                callFslogic(&fslogic::Composite::release, userdata, ino, fh);
        },
        req, ino, fi->flags);
}

void wrap_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
    LOG_FCALL() << LOG_FUSE_CTX(fuse_req_ctx(req)) << LOG_FARG(ino)
                << LOG_FARG(fi->fh);

    auto timer = ONE_METRIC_TIMERCTX_CREATE("comp.oneclient.mod.fuse.release");
    wrap(
        &fslogic::Composite::release,
        [&, req, timer = std::move(timer)](
            auto && /*unit*/) { fuse_reply_err(req, 0); },
        req, ino, fi->fh);
}

void wrap_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
    struct fuse_file_info *fi)
{
    LOG_FCALL() << LOG_FUSE_CTX(fuse_req_ctx(req)) << LOG_FARG(ino)
                << LOG_FARG(size) << LOG_FARG(off) << LOG_FARG(fi->fh);

    auto timer = ONE_METRIC_TIMERCTX_CREATE("comp.oneclient.mod.fuse.read");

    wrap(
        &fslogic::Composite::read,
        [req, timer = std::move(timer), ino, fh = fi->fh, size, off](
            folly::IOBufQueue &&buf) {
            if (!buf.empty()) {
                auto &fsLogic =
                    (*static_cast<std::unique_ptr<fslogic::Composite> *>(
                         fuse_req_userdata(req)))
                        ->fsLogic();

                // When full block read mode is forced, read until exactly
                // 'size' bytes are read from storage or any of the 'read' calls
                // returns 0 or error.
                while (fsLogic.isFullBlockReadForced() &&
                    (buf.chainLength() < size)) {
                    auto remainderBuf = fsLogic.read(ino, fh,
                        off + buf.chainLength(), size - buf.chainLength());
                    if (remainderBuf.chainLength() > 0)
                        buf.append(std::move(remainderBuf));
                    else
                        break;
                }

                LOG_DBG(2) << "Received  " << buf.chainLength()
                           << " bytes when reading inode " << ino
                           << " at offset " << off;

                auto iov = buf.front()->getIov();
                fuse_reply_iov(req, iov.data(), iov.size());
                ONE_METRIC_TIMERCTX_STOP(timer, buf.chainLength());
            }
            else {
                LOG_DBG(2) << "Received empty buffer when reading inode " << ino
                           << " at offset " << off;
                fuse_reply_buf(req, nullptr, 0);
                ONE_METRIC_TIMERCTX_STOP(timer, 0);
            }
        },
        req, ino, fi->fh, off, size);
}

void wrap_write(fuse_req_t req, fuse_ino_t ino, const char *buf, size_t size,
    off_t off, struct fuse_file_info *fi)
{
    LOG_FCALL() << LOG_FUSE_CTX(fuse_req_ctx(req)) << LOG_FARG(ino)
                << LOG_FARG(size) << LOG_FARG(off) << LOG_FARG(fi->fh);

    auto timer = ONE_METRIC_TIMERCTX_CREATE("comp.oneclient.mod.fuse.write");
    std::shared_ptr<folly::IOBuf> iobuf{folly::IOBuf::copyBuffer(buf, size)};

    wrap(
        &fslogic::Composite::write,
        [req, timer = std::move(timer), ino, off](const std::size_t wrote) {
            LOG_DBG(2) << "Written " << wrote << " bytes to inode " << ino
                       << " at offset " << off;
            fuse_reply_write(req, wrote);
            ONE_METRIC_TIMERCTX_STOP(timer, wrote);
        },
        req, ino, fi->fh, off, std::move(iobuf));
}

void wrap_mkdir(
    fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode)
{
    LOG_FCALL() << LOG_FUSE_CTX(fuse_req_ctx(req)) << LOG_FARG(parent)
                << LOG_FARG(name) << LOG_FARG(mode);

    auto timer = ONE_METRIC_TIMERCTX_CREATE("comp.oneclient.mod.fuse.mkdir");
    wrap(
        &fslogic::Composite::mkdir,
        [req, timer = std::move(timer), sname = std::string(name), mode](
            const struct fuse_entry_param &entry) {
            LOG_DBG(2) << "Created directory " << sname << " with mode "
                       << LOG_OCT(mode);
            fuse_reply_entry(req, &entry);
        },
        req, parent, name, mode);
}

void wrap_mknod(fuse_req_t req, fuse_ino_t parent, const char *name,
    mode_t mode, dev_t /*rdev*/)
{
    LOG_FCALL() << LOG_FUSE_CTX(fuse_req_ctx(req)) << LOG_FARG(parent)
                << LOG_FARG(name) << LOG_FARG(mode);

    auto timer = ONE_METRIC_TIMERCTX_CREATE("comp.oneclient.mod.fuse.mknod");
    wrap(
        &fslogic::Composite::mknod,
        [req, timer = std::move(timer), sname = std::string(name), mode](
            const struct fuse_entry_param &entry) {
            LOG_DBG(2) << "Created node " << sname << " with mode "
                       << LOG_OCT(mode);
            fuse_reply_entry(req, &entry);
        },
        req, parent, name, mode);
}

void wrap_link(
    fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, const char *newname)
{
    LOG_FCALL() << LOG_FUSE_CTX(fuse_req_ctx(req)) << LOG_FARG(ino)
                << LOG_FARG(newparent) << LOG_FARG(newname);

    auto timer = ONE_METRIC_TIMERCTX_CREATE("comp.oneclient.mod.fuse.link");
    wrap(
        &fslogic::Composite::link,
        [req, timer = std::move(timer), newname = folly::fbstring(newname)](
            const struct fuse_entry_param &entry) {
            LOG_DBG(2) << "Created hard link " << newname;
            fuse_reply_entry(req, &entry);
        },
        req, ino, newparent, newname);
}

void wrap_symlink(
    fuse_req_t req, const char *link, fuse_ino_t parent, const char *name)
{
    LOG_FCALL() << LOG_FUSE_CTX(fuse_req_ctx(req)) << LOG_FARG(link)
                << LOG_FARG(parent) << LOG_FARG(name);

    auto timer = ONE_METRIC_TIMERCTX_CREATE("comp.oneclient.mod.fuse.symlink");
    wrap(
        &fslogic::Composite::symlink,
        [req, timer = std::move(timer), name = folly::fbstring(name)](
            const struct fuse_entry_param &entry) {
            LOG_DBG(2) << "Created symbolic link " << name;
            fuse_reply_entry(req, &entry);
        },
        req, parent, name, link);
}

void wrap_readlink(fuse_req_t req, fuse_ino_t ino)
{
    LOG_FCALL() << LOG_FUSE_CTX(fuse_req_ctx(req)) << LOG_FARG(ino);

    auto timer = ONE_METRIC_TIMERCTX_CREATE("comp.oneclient.mod.fuse.readlink");
    wrap(
        &fslogic::Composite::readlink,
        [req, timer = std::move(timer)](const folly::fbstring &link) {
            LOG_DBG(2) << "Read symbolic link: " << link;
            fuse_reply_readlink(req, link.c_str());
        },
        req, ino);
}

void wrap_unlink(fuse_req_t req, fuse_ino_t parent, const char *name)
{
    LOG_FCALL() << LOG_FUSE_CTX(fuse_req_ctx(req)) << LOG_FARG(parent)
                << LOG_FARG(name);

    auto timer = ONE_METRIC_TIMERCTX_CREATE("comp.oneclient.mod.fuse.unlink");
    wrap(
        &fslogic::Composite::unlink,
        [req, timer = std::move(timer), sname = std::string(name)](
            auto && /*unit*/) {
            LOG_DBG(2) << "Unlinked file " << sname;
            fuse_reply_err(req, 0);
        },
        req, parent, name);
}

void wrap_rename(fuse_req_t req, fuse_ino_t parent, const char *name,
    fuse_ino_t newparent, const char *newname
#if FUSE_USE_VERSION > 30
    ,
    const unsigned int /*flags*/
#endif
)
{
    LOG_FCALL() << LOG_FUSE_CTX(fuse_req_ctx(req)) << LOG_FARG(parent)
                << LOG_FARG(name) << LOG_FARG(newparent) << LOG_FARG(newname);

    auto timer = ONE_METRIC_TIMERCTX_CREATE("comp.oneclient.mod.fuse.rename");
    wrap(
        &fslogic::Composite::rename,
        [req, timer = std::move(timer), parent, newparent,
            sname = std::string(name),
            snewname = std::string(newname)](auto && /*unit*/) {
            LOG_DBG(2) << "Renamed " << parent << ":" << sname << " to "
                       << newparent << ":" << snewname;
            fuse_reply_err(req, 0);
        },
        req, parent, name, newparent, newname);
}

void wrap_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
{
    LOG_FCALL() << LOG_FUSE_CTX(fuse_req_ctx(req)) << LOG_FARG(ino)
                << LOG_FARG(nlookup);

    auto timer = ONE_METRIC_TIMERCTX_CREATE("comp.oneclient.mod.fuse.forget");
    wrap(
        &fslogic::Composite::forget,
        [req, timer = std::move(timer), ino](auto && /*unit*/) {
            LOG_DBG(2) << "Forgotten inode " << ino;
            fuse_reply_none(req);
        },
        req, ino, nlookup);
}

void wrap_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, int to_set,
    struct fuse_file_info * /*fi*/)
{
    LOG_FCALL() << LOG_FUSE_CTX(fuse_req_ctx(req)) << LOG_FARG(ino)
                << LOG_FARG(attr->st_ino) << LOG_FARG(to_set);

    auto timer = ONE_METRIC_TIMERCTX_CREATE("comp.oneclient.mod.fuse.setattr");
    wrap(
        &fslogic::Composite::setattr,
        [req, timer = std::move(timer), ino](const struct stat &attrs) {
            LOG_DBG(2) << "Changed attributes on inode " << ino;
            fuse_reply_attr(req, &attrs, 0);
        },
        req, ino, *attr, to_set);
}

void wrap_create(fuse_req_t req, fuse_ino_t parent, const char *name,
    mode_t mode, struct fuse_file_info *fi)
{
    LOG_FCALL() << LOG_FUSE_CTX(fuse_req_ctx(req)) << LOG_FARG(parent)
                << LOG_FARG(name) << LOG_FARG(mode) << LOG_FARG(fi->fh);

    auto timer = ONE_METRIC_TIMERCTX_CREATE("comp.oneclient.mod.fuse.create");
    wrap(
        &fslogic::Composite::create,
        [req, fi = *fi, timer = std::move(timer), mode,
            sname = std::string(name)](
            const std::pair<const struct fuse_entry_param, std::uint64_t>
                &res) mutable {
            fi.fh = res.second;
            LOG_DBG(2) << "Created file " << sname << " with mode "
                       << LOG_OCT(mode);
            fuse_reply_create(req, &res.first, &fi);
        },
        req, parent, name, mode, fi->flags);
}

void wrap_statfs(fuse_req_t req, fuse_ino_t ino)
{
    LOG_FCALL() << LOG_FUSE_CTX(fuse_req_ctx(req)) << LOG_FARG(ino);

    auto timer = ONE_METRIC_TIMERCTX_CREATE("comp.oneclient.mod.fuse.statfs");
    wrap(
        &fslogic::Composite::statfs,
        [req, timer = std::move(timer)](const struct statvfs &statinfo) {
            fuse_reply_statfs(req, &statinfo);
        },
        req, ino);
}

void wrap_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
    LOG_FCALL() << LOG_FUSE_CTX(fuse_req_ctx(req)) << LOG_FARG(ino)
                << LOG_FARG(fi->fh);

    auto timer = ONE_METRIC_TIMERCTX_CREATE("comp.oneclient.mod.fuse.flush");
    wrap(
        &fslogic::Composite::flush,
        [req, timer = std::move(timer)](
            auto && /*unit*/) { fuse_reply_err(req, 0); },
        req, ino, fi->fh);
}

void wrap_fsync(
    fuse_req_t req, fuse_ino_t ino, int dataSync, struct fuse_file_info *fi)
{
    LOG_FCALL() << LOG_FUSE_CTX(fuse_req_ctx(req)) << LOG_FARG(ino)
                << LOG_FARG(dataSync) << LOG_FARG(fi->fh);

    auto timer = ONE_METRIC_TIMERCTX_CREATE("comp.oneclient.mod.fuse.fsync");
    wrap(
        &fslogic::Composite::fsync,
        [req, timer = std::move(timer)](
            auto && /*unit*/) { fuse_reply_err(req, 0); },
        req, ino, fi->fh, dataSync);
}

void wrap_getxattr(fuse_req_t req, fuse_ino_t ino, const char *attr, size_t size
#if defined(__APPLE__)
    ,
    uint32_t position // This attribute is used only on macOS resource forks
#endif
)
{
    LOG_FCALL() << LOG_FUSE_CTX(fuse_req_ctx(req)) << LOG_FARG(ino)
                << LOG_FARG(attr);

    if (attr == nullptr) {
        fuse_reply_err(req, EINVAL);
        return;
    }

    auto timer = ONE_METRIC_TIMERCTX_CREATE("comp.oneclient.mod.fuse.getxattr");

    //
    // Ignore selected system extended attributes which can degrade performance
    // on some linux distributions, and do not make sense for network filesystem
    // anyway
    //
    if (boost::starts_with(attr, "system.") ||
        boost::starts_with(attr, "security.") ||
        boost::starts_with(attr, "capability.")) {
        fuse_reply_err(req, ENODATA);
        return;
    }

    std::string xattrJsonName;
    if (!xattr::encodeJsonXAttrName(attr, xattrJsonName)) {
        LOG(WARNING) << "Getting extended attribute with invalid name: "
                     << attr;
        fuse_reply_err(req, EINVAL);
        return;
    }

    LOG_DBG(2) << "Getting extended attribute '" << xattrJsonName
               << "' for file " << ino;

    wrap(
        &fslogic::Composite::getxattr,
        [req, size, attr, timer = std::move(timer)](
            const folly::fbstring &value) {
            // If the value is a JSON string, strip the enclosing
            // double qoutes
            std::string stringValue;
            if (!xattr::decodeJsonXAttrValue(
                    value.toStdString(), stringValue)) {
                fuse_reply_err(req, ERANGE);
                return;
            }

            size_t buflen = stringValue.size();

            if (size == 0) {
                // return the size of the buffer needed to allocate the
                // xattr value.
                // For empty strings it is not possible to determine whether
                // this call is meant to return fuse_reply_xattr
                // or fuse_reply_buf, thus in such case we return 1 to
                // check it again on second call when the size is 0.
                if (buflen == 0) {
                    fuse_reply_xattr(req, 1);
                }
                else {
                    fuse_reply_xattr(req, buflen);
                }
            }
            else if (buflen == 0 && size == 1) {
                // Handle special case when the attribute has empty
                // value
                const char *buf = stringValue.data();
                LOG_DBG(2) << "Returning extended attribute " << attr
                           << " with empty value";
                fuse_reply_buf(req, buf, 0);
            }
            else if (buflen <= size) {
                // return the value
                const char *buf = stringValue.data();
                LOG_DBG(2) << "Returning extended attribute " << attr
                           << " with value " << buf;
                fuse_reply_buf(req, buf, buflen);
            }
            else {
                // return error
                LOG_DBG(2) << "Extended attribute " << attr << " doesn't exist";
                fuse_reply_err(req, ERANGE);
            }
        },
        req, ino, attr);
}

void wrap_setxattr(fuse_req_t req, fuse_ino_t ino, const char *attr,
    const char *val, size_t size, int flags
#if defined(__APPLE__)
    ,
    uint32_t position // This attribute is used only on macOS resource forks
#endif
)
{
    LOG_FCALL() << LOG_FUSE_CTX(fuse_req_ctx(req)) << LOG_FARG(ino)
                << LOG_FARG(attr) << LOG_FARG(val) << LOG_FARG(size)
                << LOG_FARGO(flags);

    auto timer = ONE_METRIC_TIMERCTX_CREATE("comp.oneclient.mod.fuse.setxattr");

    //
    // Creating system extended attributes should be disabled
    //
    if (boost::starts_with(attr, ONE_XATTR_PREFIX) ||
        boost::starts_with(attr, "system.") ||
        boost::starts_with(attr, "security.") ||
        boost::starts_with(attr, "capability.")) {
        fuse_reply_err(req, EPERM);
        return;
    }

    std::string xattrJsonName;
    if (!xattr::encodeJsonXAttrName(attr, xattrJsonName)) {
        LOG(WARNING) << "Setting extended attribute with invalid name: "
                     << attr;
        fuse_reply_err(req, EINVAL);
        return;
    }

    // Since Oneprovider stores extended attributes as JSON values
    // we have to check the type of the extended attribute based on its
    // contents. The possible values are:
    // - number - e.g. 1 or 3.1415 --> send without qoutes
    // - boolean - e.g. true or false --> send without qoutes
    // - null - i.e. null --> send without qoutes
    // - string - e.g. 'ABCDEFGSSDEEDSA' --> send in qoutes
    // - binary data - e.g. '\0x12\0x00\0x21\0x22\0x00\0x00\0x00' -
    // convert to base64 value
    std::string xattrRawValue;
    xattrRawValue.assign(val, size);

    std::string xattrJsonValue;
    if (!xattr::encodeJsonXAttrValue(xattrRawValue, xattrJsonValue)) {
        LOG(WARNING) << "Setting extended attribute with invalid value";
        fuse_reply_err(req, EINVAL);
        return;
    }
    LOG_DBG(1) << "Setting extended attribute '" << attr << "' to value '"
               << xattrJsonValue << "' for file " << ino << " with flags "
               << "XATTR_CREATE=" << std::to_string(flags & XATTR_CREATE)
               << " XATTR_REPLACE=" << std::to_string(flags & XATTR_REPLACE);

    wrap(
        &fslogic::Composite::setxattr,
        [req, timer = std::move(timer)](
            auto && /*unit*/) { fuse_reply_err(req, 0); },
        req, ino, xattrJsonName, xattrJsonValue, flags & XATTR_CREATE,
        flags & XATTR_REPLACE);
}

void wrap_removexattr(fuse_req_t req, fuse_ino_t ino, const char *attr)
{
    LOG_FCALL() << LOG_FUSE_CTX(fuse_req_ctx(req)) << LOG_FARG(ino)
                << LOG_FARG(attr);

    auto timer =
        ONE_METRIC_TIMERCTX_CREATE("comp.oneclient.mod.fuse.removexattr");

    //
    // Removing system extended attributes should be disabled
    //
    if (boost::starts_with(attr, ONE_XATTR_PREFIX) ||
        boost::starts_with(attr, "system.") ||
        boost::starts_with(attr, "security.") ||
        boost::starts_with(attr, "capability.")) {
        fuse_reply_err(req, EPERM);
        return;
    }

    std::string xattrJsonName;
    if (!xattr::encodeJsonXAttrName(attr, xattrJsonName)) {
        LOG(WARNING) << "Removing extended attribute with invalid name: "
                     << attr;
        fuse_reply_err(req, EINVAL);
        return;
    }

    LOG_DBG(2) << "Removing extended attribute '" << xattrJsonName
               << "' for file " << ino;

    wrap(
        &fslogic::Composite::removexattr,
        [req, timer = std::move(timer)](
            auto && /*unit*/) { fuse_reply_err(req, 0); },
        req, ino, xattrJsonName);
}

void wrap_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
{
    LOG_FCALL() << LOG_FUSE_CTX(fuse_req_ctx(req)) << LOG_FARG(ino)
                << LOG_FARG(size);

    auto timer =
        ONE_METRIC_TIMERCTX_CREATE("comp.oneclient.mod.fuse.listxattr");

    LOG_DBG(2) << "Listing extended attributes for file " << ino;
    // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDelete)
    wrap(
        &fslogic::Composite::listxattr,
        [req, size, timer = std::move(timer), ino](
            const folly::fbvector<folly::fbstring> &names) {
            LOG_DBG(3) << "Received extended attributes for inode " << ino
                       << ": " << LOG_VEC(names);

            // Calculate the length of all xattr names in the list
            // including the end of string characters needed to separate
            // the xattr names in the buffer
            size_t buflen = std::accumulate(names.cbegin(), names.cend(), 0,
                [](int sum, const folly::fbstring &elem) {
                    return sum + elem.size() + 1;
                });

            if (size == 0) {
                // return the size of the buffer needed to allocate the
                // xattr names list
                fuse_reply_xattr(req, buflen);
            }
            else if (buflen <= size) {
                // return the list of extended attribute names
                auto buf = std::unique_ptr<char[]>(new char[buflen]);
                auto offset = 0UL;

                for (const auto &name : names) {
                    // NOLINTNEXTLINE(hicpp-vararg,cppcoreguidelines-pro-type-vararg)
                    sprintf(buf.get() + offset, name.data(), name.length());
                    offset += name.length() + 1;
                }
                fuse_reply_buf(req, buf.get(), buflen);
            }
            else {
                fuse_reply_err(req, ERANGE);
            }
        },
        req, ino);
}

} // extern "C"
} // namespace

struct fuse_lowlevel_ops fuseOperations()
{
    struct fuse_lowlevel_ops operations = {nullptr};

    operations.create = wrap_create;
    operations.flush = wrap_flush;
    operations.forget = wrap_forget;
    operations.fsync = wrap_fsync;
    operations.getattr = wrap_getattr;
    operations.lookup = wrap_lookup;
    operations.mkdir = wrap_mkdir;
    operations.mknod = wrap_mknod;
    operations.link = wrap_link;
    operations.symlink = wrap_symlink;
    operations.readlink = wrap_readlink;
    operations.open = wrap_open;
    operations.read = wrap_read;
    operations.readdir = wrap_readdir;
    operations.opendir = wrap_opendir;
    operations.releasedir = wrap_releasedir;
    operations.release = wrap_release;
    operations.rename = wrap_rename;
    operations.rmdir = wrap_unlink;
    operations.setattr = wrap_setattr;
    operations.statfs = wrap_statfs;
    operations.unlink = wrap_unlink;
    operations.write = wrap_write;
    operations.getxattr = wrap_getxattr;
    operations.setxattr = wrap_setxattr;
    operations.removexattr = wrap_removexattr;
    operations.listxattr = wrap_listxattr;

    return operations;
}

ext: cc

max_stars_repo_name: onedata/oneclient

max_issues_repo_name: onedata/oneclient

max_forks_repo_name: onedata/oneclient


text:
// Boost.Geometry (aka GGL, Generic Geometry Library)

// Copyright (c) 2020 Tinko Bartels, Berlin, Germany.

// Contributed and/or modified by Tinko Bartels,
//   as part of Google Summer of Code 2020 program.

// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_GEOMETRY_EXTENSIONS_GENERIC_ROBUST_PREDICATES_STRATEGIES_CARTESIAN_DETAIL_EXPRESSION_TREE_HPP
#define BOOST_GEOMETRY_EXTENSIONS_GENERIC_ROBUST_PREDICATES_STRATEGIES_CARTESIAN_DETAIL_EXPRESSION_TREE_HPP

#include <cstddef>
#include <type_traits>

#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/integral.hpp>
#include <boost/mp11/list.hpp>

namespace boost { namespace geometry
{

namespace detail { namespace generic_robust_predicates
{

enum class operator_types {
    sum, difference, product, abs, no_op, max, min
};

enum class operator_arities { nullary, unary, binary };

constexpr int sign_uncertain = -2;

struct sum_error_type {};
struct product_error_type {};
struct no_error_type {};

template <typename... Children>
struct internal_node
{
    static constexpr bool is_leaf = false;
    static constexpr bool non_negative = false;
    using all_children = boost::mp11::mp_list<Children...>; //for convenience
};

template <typename Left, typename Right>
struct internal_binary_node : internal_node<Left, Right>
{
    using left  = Left;
    using right = Right;
    static constexpr operator_arities operator_arity = operator_arities::binary;
};

template <typename Child>
struct internal_unary_node : internal_node<Child>
{
    using child = Child;
    static constexpr operator_arities operator_arity = operator_arities::unary;
};

template <typename Left, typename Right>
struct sum : public internal_binary_node<Left, Right>
{
    static constexpr bool sign_exact = Left::is_leaf && Right::is_leaf;
    static constexpr bool non_negative = Left::non_negative && Right::non_negative;
    static constexpr operator_types operator_type = operator_types::sum;
    using error_type = sum_error_type;
};

template <typename Left, typename Right>
struct difference : public internal_binary_node<Left, Right>
{
    static constexpr bool sign_exact = Left::is_leaf && Right::is_leaf;
    static constexpr bool non_negative = false;
    static constexpr operator_types operator_type = operator_types::difference;
    using error_type = sum_error_type;
};

template <typename Left, typename Right>
struct product : public internal_binary_node<Left, Right>
{
    static constexpr bool sign_exact = Left::sign_exact && Right::sign_exact;
    static constexpr bool non_negative =
           (Left::non_negative && Right::non_negative)
        || std::is_same<Left, Right>::value;
    static constexpr operator_types operator_type = operator_types::product;
    using error_type = product_error_type;
};

template <typename Left, typename Right>
struct max : public internal_binary_node<Left, Right>
{
    static constexpr bool sign_exact = Left::sign_exact && Right::sign_exact;
    static constexpr bool non_negative =
        Left::non_negative || Right::non_negative;
    static constexpr operator_types operator_type = operator_types::max;
    using error_type = no_error_type;
};

template <typename Left, typename Right>
struct min : public internal_binary_node<Left, Right>
{
    static constexpr bool sign_exact = Left::sign_exact && Right::sign_exact;
    static constexpr bool non_negative =
        Left::non_negative && Right::non_negative;
    static constexpr operator_types operator_type = operator_types::min;
    using error_type = no_error_type;
};

template <typename Child>
struct abs : public internal_unary_node<Child>
{
    using error_type = no_error_type;
    static constexpr operator_types operator_type = operator_types::abs;
    static constexpr bool sign_exact = Child::sign_exact;
    static constexpr bool non_negative = true;
};

struct leaf
{
    static constexpr bool is_leaf = true;
    static constexpr bool sign_exact = true;
    static constexpr bool non_negative = false;
    static constexpr operator_types operator_type = operator_types::no_op;
    static constexpr operator_arities operator_arity = operator_arities::nullary;
};

template <std::size_t Argn>
struct argument : public leaf
{
    static constexpr std::size_t argn = Argn;
};

template <typename NumberType>
struct static_constant_interface : public leaf
{
    using value_type = NumberType;
    static constexpr NumberType value = 0; //override
    static constexpr std::size_t argn = 0;
};

template <typename Node>
using is_leaf = boost::mp11::mp_bool<Node::is_leaf>;

template
<
    typename In,
    typename Out,
    template <typename> class Anchor = is_leaf,
    bool IsBinary = In::operator_arity == operator_arities::binary,
    bool AtAnchor = Anchor<In>::value
>
struct post_order_impl;

template
<
    typename In,
    typename Out,
    template <typename> class Anchor,
    bool IsBinary
>
struct post_order_impl<In, Out, Anchor, IsBinary, true>
{
    using type = Out;
};

template <typename In, typename Out, template <typename> class Anchor>
struct post_order_impl<In, Out, Anchor, true, false>
{
    using leftl = typename post_order_impl
            <
                typename In::left,
                boost::mp11::mp_list<>,
                Anchor
            >::type;
    using rightl = typename post_order_impl
            <
                typename In::right,
                boost::mp11::mp_list<>,
                Anchor
            >::type;
    using merged = boost::mp11::mp_append<Out, leftl, rightl>;
    using type   =
        boost::mp11::mp_unique<boost::mp11::mp_push_back<merged, In>>;
};

template <typename In, typename Out, template <typename> class Anchor>
struct post_order_impl<In, Out, Anchor, false, false>
{
    using childl = typename post_order_impl
            <
                typename In::child,
                boost::mp11::mp_list<>,
                Anchor
            >::type;
    using merged = boost::mp11::mp_append<Out, childl>;
    using type   =
        boost::mp11::mp_unique<boost::mp11::mp_push_back<merged, In>>;
};

template <typename In, template <typename> class Anchor = is_leaf>
using post_order =
    typename post_order_impl<In, boost::mp11::mp_list<>, Anchor>::type;

template <typename Node, typename IsLeaf = is_leaf<Node>>
struct max_argn_impl;

template <typename Node> using max_argn = typename max_argn_impl<Node>::type;

template <typename Node>
struct max_argn_impl<Node, boost::mp11::mp_false>
{
private:
    using children_list = boost::mp11::mp_rename<Node, boost::mp11::mp_list>;
    using children_max_argn =
        boost::mp11::mp_transform<max_argn, children_list>;
public:
    using type = boost::mp11::mp_max_element
        <
            children_max_argn,
            boost::mp11::mp_less
        >;
};

template <typename Node>
struct max_argn_impl<Node, boost::mp11::mp_true>
{
    using type = boost::mp11::mp_size_t<Node::argn>;
};

using  _1 = argument<1>;
using  _2 = argument<2>;
using  _3 = argument<3>;
using  _4 = argument<4>;
using  _5 = argument<5>;
using  _6 = argument<6>;
using  _7 = argument<7>;
using  _8 = argument<8>;
using  _9 = argument<9>;
using _10 = argument<10>;
using _11 = argument<11>;
using _12 = argument<12>;

}} // namespace detail::generic_robust_predicates

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_EXTENSIONS_GENERIC_ROBUST_PREDICATES_STRATEGIES_CARTESIAN_DETAIL_EXPRESSION_TREE_HPP

ext: hpp


text:
// Boost.Function library

//  Copyright Douglas Gregor 2008. Use, modification and
//  distribution is subject to the Boost Software License, Version
//  1.0. (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)

// For more information, see http://www.boost.org
#include <boost/function/function_typeof.hpp>
#include <boost/function.hpp>
#include <boost/typeof/typeof.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/static_assert.hpp>

void f(boost::function0<void> f, boost::function0<void> g)
{
  BOOST_STATIC_ASSERT((boost::is_same<boost::function0<void>, BOOST_TYPEOF(f = g)>::value));
}

ext: cpp

max_stars_repo_name: randolphwong/mcsema

max_issues_repo_name: randolphwong/mcsema

max_forks_repo_name: randolphwong/mcsema


text:
#include <common.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>

void export_vector_save()
{
	class_<std::vector<parser_t::save_t> >("VectorSave")
		.def(vector_indexing_suite<std::vector<parser_t::save_t> >())
	;
}

max_stars_repo_name: academicRobot/mmstructlib

max_issues_repo_name: academicRobot/mmstructlib

max_forks_repo_name: academicRobot/mmstructlib


text:
#ifndef MPLLIBS_METAPARSE_V1_TRANSFORM_HPP
#define MPLLIBS_METAPARSE_V1_TRANSFORM_HPP

// Copyright Abel Sinkovics (abel@sinkovics.hu)  2009 - 2010.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)

#include <mpllibs/metaparse/v1/return_.hpp>
#include <mpllibs/metaparse/v1/get_result.hpp>
#include <mpllibs/metaparse/v1/get_remaining.hpp>
#include <mpllibs/metaparse/v1/get_position.hpp>
#include <mpllibs/metaparse/v1/unless_error.hpp>

#include <boost/mpl/eval_if.hpp>
#include <boost/mpl/apply.hpp>
#include <boost/mpl/apply_wrap.hpp>

namespace mpllibs
{
  namespace metaparse
  {
    namespace v1
    {
      template <class P, class T>
      struct transform
      {
      private:
        template <class R>
        struct apply_transformation_function :
          boost::mpl::apply<T, typename R::type>
        {};

      public:
        typedef transform type;

        template <class S, class Pos>
        struct apply :
          unless_error<
            boost::mpl::apply<P, S, Pos>,
            boost::mpl::apply_wrap2<
              return_<
                apply_transformation_function<
                  get_result<boost::mpl::apply<P, S, Pos> >
                >
              >,
              get_remaining<boost::mpl::apply<P, S, Pos> >,
              get_position<boost::mpl::apply<P, S, Pos> >
            >
          >
        {};
      };
    }
  }
}

#endif


text:
/*
 * SerializationTest.cpp
 *
 *  Created on: May 23, 2012
 *      Author: joe
 */

#define BOOST_TEST_MODULE SerializationTests
#include "../src/config.h"
#define BOOST_TEST_MAIN
#ifdef USE_DYNAMIC_UNIT_TEST
#define BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>
#else
#include <boost/test/included/unit_test.hpp>
#endif
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <iostream>
#include <sstream>
#include "../src/inference/MCSat.h"
#include "../src/AllSerializationExports.h"
#include "TestUtilities.h"

template <class T>
void checkSerializationPtr(T s) {
    std::stringstream stream;
    {
        boost::archive::text_oarchive oarch(stream);
        registerAllPELTypes(oarch);
        oarch << s;
    }
    T t;
    {
        boost::archive::text_iarchive iarch(stream);
        registerAllPELTypes(iarch);
        iarch >> t;
    }

    BOOST_CHECK(*s == *t);
}

template <class T>
void checkSerialization(T s) {
    std::stringstream stream;
    {
        boost::archive::text_oarchive oarch(stream);
        registerAllPELTypes(oarch);
        oarch << s;
    }
    T t;
    {
        boost::archive::text_iarchive iarch(stream);
        registerAllPELTypes(iarch);
        iarch >> t;
    }

    BOOST_CHECK(s == t);
}

BOOST_AUTO_TEST_CASE(generalSentenceSerialization) {
    checkSerializationPtr(getAsSentence("P(a)"));
    checkSerializationPtr(getAsSentence("true"));
    checkSerializationPtr(getAsSentence("false"));
    checkSerializationPtr(getAsSentence("!P(a)"));
    checkSerializationPtr(getAsSentence("P(a) v Q(a)"));
    checkSerializationPtr(getAsSentence("P(a) ^{m} R(a)"));
    checkSerializationPtr(getAsSentence("[P(a)]"));
    checkSerializationPtr(getAsSentence("<>{m} P(a)"));
    checkSerializationPtr(getAsSentence("P(a) v [!R(a) ^ S(a)] v <>{s} T(a)"));
}

BOOST_AUTO_TEST_CASE(domainSerialization) {
    std::string facts("P(a) @ [1:10]\n"
            "Q(A) @ [5:6]");
    std::string formulas("1: [P(a) -> Q(a)]\n"
            "[Q(a) -> P(a)]");

    Domain d = loadDomainWithStreams(facts, formulas);
    checkSerialization(d);
}

BOOST_AUTO_TEST_CASE(mcSatSerialization) {
    std::string facts("P(a) @ [1:10]\n"
            "Q(A) @ [5:6]");
    std::string formulas("1: [P(a) -> Q(a)]\n"
            "[Q(a) -> P(a)]");

    Domain d = loadDomainWithStreams(facts, formulas);
    MCSat sat(&d);
    // don't actually run it, it takes too long. just test it
    checkSerialization(sat);
}

max_stars_repo_name: JunLi-Galios/repel

max_issues_repo_name: JunLi-Galios/repel

max_forks_repo_name: JunLi-Galios/repel


text:
// Copyright (c) 2015, Wyatt Technology Corporation
// All rights reserved.

// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:

// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.

// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.

// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.

// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "stm.h"

#include <boost/format.hpp>
using boost::format;
using boost::str;

#ifdef NO_THREAD_LOCAL
#include <boost/thread/tss.hpp>
#endif

#include <condition_variable>
#include <unordered_map>
#include <atomic>
#include <mutex>
#include <list>
#include <thread>

//Define this to turn on stm profiling
//#define STM_PROFILING

namespace  WSTM
{
   WLibraryVersion GetVersion ()
   {
      return {WSTM_VERSION_MAJOR, WSTM_VERSION_MINOR, WSTM_VERSION_PATCH};
   }

   namespace Internal
   {
      WLocalValueBase::~WLocalValueBase ()
      {}

      uint64_t GetTransactionLocalKey ()
      {
         static std::atomic<uint64_t> nextKey (0);
         return nextKey.fetch_add (1);
      }
   }

   namespace
   {
#ifdef STM_PROFILING
      std::mutex s_profileMutex;
      std::chrono::high_resolution_clock::time_point s_profileStart;
      std::atomic<unsigned int> s_numConflicts;
      std::atomic<unsigned int> s_numReadCommits;
      std::atomic<unsigned int> s_numWriteCommits;
#endif //STM_PROFILING

      void IncrementNumConflicts ()
      {
#ifdef STM_PROFILING
         ++s_numConflicts;
#endif
      }

      void IncrementNumReadCommits ()
      {
#ifdef STM_PROFILING
         ++s_numReadCommits;
#endif
      }

      void IncrementNumWriteCommits ()
      {
#ifdef STM_PROFILING
         ++s_numWriteCommits;
#endif
      }
   }

   void StartProfiling ()
   {
#ifdef STM_PROFILING
      std::unique_lock<std::mutex> lock (s_profileMutex);
      s_profileStart = std::chrono::high_resolution_clock::now ();
      s_numConflicts = 0;
      s_numReadCommits = 0;
      s_numWriteCommits = 0;
#endif
   }

   std::string WProfileData::FormatData () const
   {
#ifdef STM_PROFILING
      const auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(m_end - m_start).count ();
      return str (format ("\ttime = %1%secs\n"
                          "\tconflicts = %2%/sec (%3% total)\n"
                          "\treads = %4%/sec (%5% total)\n"
                          "\twrites = %6%/sec (%7% total)")
                  % elapsed
                  % (static_cast<double>(m_numConflicts)/elapsed)
                  % m_numConflicts
                  % (static_cast<double>(m_numReadCommits)/elapsed)
                  % m_numReadCommits
                  % (static_cast<double>(m_numWriteCommits)/elapsed)
                  % m_numWriteCommits);
#else
      return "\tProfiling not active";
#endif
   }

   WProfileData Checkpoint ()
   {
      WProfileData data;
#ifdef STM_PROFILING
      data.m_numConflicts = s_numConflicts;
      data.m_numReadCommits = s_numReadCommits;
      data.m_numWriteCommits = s_numWriteCommits;
      data.m_end = std::chrono::high_resolution_clock::now ();
      std::unique_lock<std::mutex> lock (s_profileMutex);
      data.m_start = s_profileStart;
#endif
      return data;
   }

   const unsigned int UNLIMITED = std::numeric_limits<unsigned int>::max();

   namespace
   {
      //This mutex locks out commits while a STM::Var is reading its own
      //value, while commiting this mutex is write locked so that all
      //var reads are held until the commit finishes. It is also
      //upgrade locked when a transaction is running with other
      //commits locked out.
      boost::upgrade_mutex s_readMutex;

#ifdef NO_THREAD_LOCAL

      //Some compilers don't support thread_local (e.g. apple clang), on those platforms we resort
      //to using boost::thread_specific_pointer
      template <typename Type_t>
      class WThreadLocalValueDefaultCtor
      {
      public:
         Type_t* operator->()
         {
            auto value_p = m_value_p.get ();
            if (!value_p)
            {
               value_p = new Type_t;
               m_value_p.reset (value_p);
            }
            return value_p;
         }

      protected:
         boost::thread_specific_ptr<Type_t> m_value_p;
      };

#define THREAD_LOCAL(type, name) WThreadLocalValueDefaultCtor<type> name;//

      template <typename Type_t>
      class WThreadLocalValueWithInitValue
      {
      public:
         explicit WThreadLocalValueWithInitValue (const Type_t& value):
            m_initValue (value)
         {}

         operator Type_t ()
         {
            return *GetValuePtr ();
         }

         Type_t& operator= (const Type_t value)
         {
            auto value_p = GetValuePtr ();
            *value_p = value;
            return *value_p;
         }

      private:
         Type_t* GetValuePtr ()
         {
            auto value_p = m_value_p.get ();
            if (!value_p)
            {
               value_p = new Type_t (m_initValue);
               m_value_p.reset (value_p);
            }
            return value_p;
         }

         Type_t m_initValue;
         boost::thread_specific_ptr<Type_t> m_value_p;
      };

#define THREAD_LOCAL_WITH_INIT_VALUE(type, name, value) WThreadLocalValueWithInitValue<type> name (value);//

#else

#define THREAD_LOCAL(type, name) thread_local type name##__Obj; thread_local type* const name = &name##__Obj;//
#define THREAD_LOCAL_WITH_INIT_VALUE(type, name, value) thread_local type name = value;//

#endif //#else NO_THREAD_LOCAL

#ifdef _DEBUG
THREAD_LOCAL_WITH_INIT_VALUE (bool, s_readMutexReadLocked, false);
THREAD_LOCAL_WITH_INIT_VALUE (bool, s_readMutexUpgradeLocked, false);
THREAD_LOCAL_WITH_INIT_VALUE (bool, s_readMutexWriteLocked, false);
THREAD_LOCAL_WITH_INIT_VALUE (bool, s_committing, false);
#endif //_DEBUG

      //This signal is notified when a commit succeeds. s_readMutex
      //must be used with this signal. A read lock lock should be
      //used when waiting. A write lock shoudl be held when
      //notifiying.
      std::condition_variable_any s_commitSignal;

      //exception thrown by Retry() to signal AtomicallyImpl that it should
      //"retry" the current operation.
      struct WRetryException
      {
         WRetryException(const WTimeArg& timeout):
            m_timeout(timeout)
         {}

         const WTimeArg m_timeout;
      };
   }

   WCantContinueException::WCantContinueException(const std::string& msg):
      WException(msg)
   {}

   WMaxConflicts::WMaxConflicts ():
      m_max (UNLIMITED),
      m_resolution (WConflictResolution::THROW)
   {}

   WMaxConflicts::WMaxConflicts (const unsigned int m, const WConflictResolution res) :
      m_max(m),
      m_resolution (res)
   {}

   WMaxRetries::WMaxRetries ():
      m_value (UNLIMITED)
   {}

   WMaxRetries::WMaxRetries (const unsigned int max):
      m_value (max)
   {}

   WMaxRetryWait::WMaxRetryWait ()
   {}

   WMaxRetryWait::WMaxRetryWait (const WTimeArg wait):
      m_value (wait)
   {}

   namespace
   {

      struct WReadLockTraits
      {
         using LockType = boost::shared_lock<boost::upgrade_mutex>;

         static void DoLock ()
         {
#ifdef _DEBUG
            s_readMutexReadLocked = true;
#endif //_DEBUG
         }

         static void DoUnlock ()
         {
#ifdef _DEBUG
            s_readMutexReadLocked = false;
#endif //_DEBUG
         }
      };

      struct WUpgradeableLockTraits
      {
         using LockType = boost::upgrade_lock<boost::upgrade_mutex>;

         static void DoLock ()
         {
#ifdef _DEBUG
            s_readMutexUpgradeLocked = true;
#endif //_DEBUG
         }

         static void DoUnlock ()
         {
#ifdef _DEBUG
            s_readMutexUpgradeLocked = false;
#endif //_DEBUG
         }
      };

      template <typename LockTraits_t>
      struct WLockImpl
      {
         explicit WLockImpl(bool doLock = true);

         //these methods must match the boost locking convention
         void lock();
         bool try_lock();
         bool locked() const {return m_lock.owns_lock ();}
         void unlock(const int i = 1);

         void UnlockAll ();
         bool WaitForCommit(const WTimeArg& timeout);

         int m_count;
         using Lock = typename LockTraits_t::LockType;
         Lock m_lock;

#ifdef _DEBUG
         std::thread::id m_threadId;
         bool m_writeLocked;
#endif //_DEBUG
      };

      template <typename LockTraits_t>
      WLockImpl<LockTraits_t>::WLockImpl (bool doLock):
         m_count(0),
         m_lock(s_readMutex, boost::defer_lock_t())
#ifdef _DEBUG
         ,m_threadId (std::this_thread::get_id ())
         ,m_writeLocked(false)
#endif //_DEBUG
      {
         if(doLock)
         {
            lock();
         }
      }

      template <typename LockTraits_t>
      void WLockImpl<LockTraits_t>::lock()
      {
         assert (m_threadId == std::this_thread::get_id ());
         if(m_count == 0)
         {
            if(m_lock.owns_lock())
            {
               throw boost::lock_error();
            }
            m_lock.lock();
            LockTraits_t::DoLock ();
         }
         ++m_count;
      }

      template <typename LockTraits_t>
      bool WLockImpl<LockTraits_t>::try_lock()
      {
         assert (m_threadId == std::this_thread::get_id ());
         if(m_count == 0)
         {
            if(m_lock.owns_lock())
            {
               throw boost::lock_error();
            }
            if(!m_lock.try_lock())
            {
               return false;
            }
            LockTraits_t::DoLock ();
         }
         ++m_count;
         return true;
      }

      template <typename LockTraits_t>
      void WLockImpl<LockTraits_t>::unlock(const int i)
      {
         assert (m_threadId == std::this_thread::get_id ());
         if (m_count == 0)
         {
            return;
         }

         if(!m_lock.owns_lock())
         {
            throw boost::lock_error();
         }

         assert(m_count >= i);
         m_count -= i;
         assert(m_count >= 0);
         if(m_count == 0)
         {
            m_lock.unlock();
            LockTraits_t::DoUnlock ();
         }
      }

      template <typename LockTraits_t>
      void WLockImpl<LockTraits_t>::UnlockAll ()
      {
         assert (m_threadId == std::this_thread::get_id ());
         unlock (m_count);
      }

      template <typename LockTraits_t>
      bool WLockImpl<LockTraits_t>::WaitForCommit(const WTimeArg& timeout)
      {
         assert (m_threadId == std::this_thread::get_id ());
         if(!m_lock.owns_lock())
         {
            throw boost::lock_error();
         }

         if(timeout.IsUnlimited ())
         {
            s_commitSignal.wait(m_lock);
            return true;
         }
         else
         {
            return (s_commitSignal.wait_until (m_lock, *timeout.m_time_o) != std::cv_status::timeout);
         }
      }

      using WReadLock = WLockImpl<WReadLockTraits>;
      using WUpgradeableLock = WLockImpl<WUpgradeableLockTraits>;

      class WWriteLock
      {
      public:
         WWriteLock(WUpgradeableLock& readLock);
         ~WWriteLock();

      private:
#ifdef _DEBUG
         WUpgradeableLock& m_readLock;
#endif //_DEBUG
         boost::upgrade_to_unique_lock<boost::upgrade_mutex> m_lock;
      };

      WWriteLock::WWriteLock(WUpgradeableLock& readLock):
#ifdef _DEBUG
         m_readLock (readLock),
#endif //_DEBUG
         m_lock (readLock.m_lock)
      {
#ifdef _DEBUG
         assert(!m_readLock.m_writeLocked);
         m_readLock.m_writeLocked = true;
         s_readMutexWriteLocked = true;
#endif //_DEBUG
      }

      WWriteLock::~WWriteLock()
      {
#ifdef _DEBUG
         assert(m_readLock.m_writeLocked);
         m_readLock.m_writeLocked = false;
         s_readMutexWriteLocked = false;
#endif //_DEBUG
      }

      struct WValueCoreBaseHash
      {
         size_t operator()(const std::shared_ptr<Internal::WVarCoreBase>& p) const
         {
            return m_hash (p.get ());
         }

         std::hash<Internal::WVarCoreBase*> m_hash;
      };

      using VarMap =  std::unordered_map<std::shared_ptr<Internal::WVarCoreBase>,
                                         std::shared_ptr<Internal::WValueBase>,
                                         WValueCoreBaseHash>;
   }

   namespace Internal
   {
      //Data used for each transaction
      struct WTransactionData
      {
         explicit WTransactionData (WUpgradeableLock& lock);

         WTransactionData* CreateChild ();

         //Don't use this directly, call CreateChild instead
         explicit WTransactionData (WTransactionData* parent);

         WTransactionData (const WTransactionData&) = delete;
         WTransactionData& operator=(const WTransactionData&) = delete;

         void Activate ();
         bool IsActive () const;

         WTransactionData* GetParent () const;
         WTransactionData* GetChild () const;

         int GetLevel () const;

         WReadLock& GetReadLock ();
         WUpgradeableLock& GetUpgradeLock ();

         VarMap& GetGot ();
         VarMap& GetSet ();

         Internal::WLocalValueBase* GetLocalValue (uint64_t key);
         void SetLocalValue (uint64_t key, std::unique_ptr<Internal::WLocalValueBase>&& value_p);

         void AddBeforeCommit (WAtomic::WBeforeCommitFunc& after);
         using WBeforeCommitList = std::list<WAtomic::WBeforeCommitFunc> ;
         void GetBeforeCommits (WBeforeCommitList& beforeCommit);

         void AddAfter (WAtomic::WAfterFunc& after);
         using WAfterList = std::list<WAtomic::WAfterFunc>;
         void GetAfters (WAfterList& afters);

         void AddOnFail (WAtomic::WOnFailFunc& after);
         using WOnFailList = std::list<WAtomic::WOnFailFunc>;
         void RunOnFails ();

         void MergeToParent ();
         void MergeGetsToRoot ();
         void Clear ();
         void ClearWrites ();

#ifdef _DEBUG
         static void* const MARKER_VALUE;
         void* GetMarker () const;
#endif //_DEBUG

      private:
#ifdef _DEBUG
         void* const m_marker;
#endif //_DEBUG

         bool m_active;

         //The transaction's level (1 = root transaction)
         int m_level;

         WTransactionData* m_parent_p;
         std::unique_ptr<WTransactionData> m_child_p;

         //locks for this thread.
         WReadLock m_readLock;
         WUpgradeableLock& m_upgradeLock;

         //The WVar's that have been read.
         VarMap m_got;
         //The WVar's that have been set.
         VarMap m_set;

         //The "transaction local" values
         std::unordered_map<uint64_t, std::unique_ptr<Internal::WLocalValueBase>> m_locals;

         //list of functions to run just before the top-level
         //transaction commits.
         WBeforeCommitList m_beforeCommits;

         //list of functions to run after the top-level transaction
         //commits.
         WAfterList m_afters;

         //list of functions to run if transaction fails
         WOnFailList m_onFails;
      };

#ifdef _DEBUG
      void* const WTransactionData::MARKER_VALUE = (void*)0xdeadbeefdeadbeef;
#endif //_DEBUG

      WTransactionData::WTransactionData (WUpgradeableLock& lock):
#ifdef _DEBUG
         m_marker (MARKER_VALUE),
#endif //_DEBUG
         m_active (false),
         m_level (1),
         m_parent_p (nullptr),
         m_readLock (false),
         m_upgradeLock (lock)
      {}

      WTransactionData* WTransactionData::CreateChild ()
      {
         if (!m_child_p)
         {
            m_child_p = std::make_unique<WTransactionData>(this);
         }

         return m_child_p.get ();
      }

      WTransactionData::WTransactionData (WTransactionData* parent_p):
#ifdef _DEBUG
         m_marker (MARKER_VALUE),
#endif //_DEBUG
         m_active (false),
         m_level (parent_p->m_level + 1),
         m_parent_p (parent_p),
         m_readLock (false),
         m_upgradeLock (parent_p->m_upgradeLock)
      {}

      void WTransactionData::Activate ()
      {
         m_active = true;
      }

      bool WTransactionData::IsActive () const
      {
         return m_active;
      }

      WTransactionData* WTransactionData::GetParent () const
      {
         return m_parent_p;
      }

      WTransactionData* WTransactionData::GetChild () const
      {
         return m_child_p.get ();
      }

      int WTransactionData::GetLevel () const
      {
         return m_level;
      }

      WReadLock& WTransactionData::GetReadLock ()
      {
         assert (m_active);
         return m_readLock;
      }

      WUpgradeableLock& WTransactionData::GetUpgradeLock ()
      {
         assert (m_active);
         return m_upgradeLock;
      }

      VarMap& WTransactionData::GetGot ()
      {
         assert (m_active);
         return m_got;
      }

      VarMap& WTransactionData::GetSet ()
      {
         assert (m_active);
         return m_set;
      }

      Internal::WLocalValueBase* WTransactionData::GetLocalValue (uint64_t key)
      {
         assert (m_active);

         WTransactionData* data_p = this;
         while (data_p)
         {
            const auto it = data_p->m_locals.find (key);
            if (it != data_p->m_locals.end ())
            {
               return it->second.get ();
            }
            data_p = data_p->m_parent_p;
         }

         return nullptr;
      }

      void WTransactionData::SetLocalValue (uint64_t key, std::unique_ptr<Internal::WLocalValueBase>&& value_p)
      {
         assert (m_active);
         m_locals[key] = std::move (value_p);
      }

      void WTransactionData::AddBeforeCommit (WAtomic::WBeforeCommitFunc& beforeCommit)
      {
         assert (m_active);
         m_beforeCommits.push_back (beforeCommit);
      }

      void WTransactionData::GetBeforeCommits (WBeforeCommitList& beforeCommits)
      {
         assert (m_active);
         beforeCommits.swap (m_beforeCommits);
      }

      void WTransactionData::AddAfter (WAtomic::WAfterFunc& after)
      {
         assert (m_active);
         m_afters.push_back (after);
      }

      void WTransactionData::GetAfters (WAfterList& afters)
      {
         assert (m_active);
         afters.swap (m_afters);
      }

      void WTransactionData::AddOnFail (WAtomic::WOnFailFunc& onFail)
      {
         assert (m_active);
         m_onFails.push_back (onFail);
      }

      void WTransactionData::RunOnFails ()
      {
         if (m_active)
         {
            for (const WAtomic::WOnFailFunc& func: m_onFails)
            {
               func ();
            }
         }
         m_onFails.clear ();
      }

      void WTransactionData::MergeToParent ()
      {
         assert (m_active);
         assert (m_parent_p);

         for (VarMap::value_type& value: m_got)
         {
            m_parent_p->m_got[std::get<0>(value)] = std::move (std::get<1>(value));
         }
         for (VarMap::value_type& value: m_set)
         {
            m_parent_p->m_set[std::get<0>(value)] = std::move (std::get<1>(value));
         }
         for (auto& val: m_locals)
         {
            m_parent_p->m_locals[val.first] = std::move (val.second);
         }

         m_parent_p->m_beforeCommits.splice (m_parent_p->m_beforeCommits.end (), m_beforeCommits);
         m_parent_p->m_afters.splice (m_parent_p->m_afters.end (), m_afters);
         m_parent_p->m_onFails.splice (m_parent_p->m_onFails.end (), m_onFails);

         Clear ();
      }

      void WTransactionData::MergeGetsToRoot ()
      {
         assert (m_active);
         assert (m_parent_p);

         WTransactionData* root_p = m_parent_p;
         while (root_p->m_parent_p)
         {
            root_p = root_p->m_parent_p;
         }

         for (VarMap::value_type& value: m_got)
         {
            m_parent_p->m_got[std::get<0>(value)] = std::move (std::get<1>(value));
         }

         Clear ();
      }

      void WTransactionData::Clear ()
      {
         if (!m_got.empty ())
         {
            m_got.clear ();
         }
         ClearWrites ();
         if (!m_onFails.empty ())
         {
            m_onFails.clear ();
         }
         m_active = false;
      }

      void WTransactionData::ClearWrites()
      {
         if (!m_set.empty ())
         {
            m_set.clear ();
         }

         //For some reason we are getting random crashes when clearing
         //the after list when the list is empty so only clear it when it
         //is non-empty.
         if (!m_beforeCommits.empty ())
         {
            m_beforeCommits.clear();
         }

         if (!m_afters.empty ())
         {
            m_afters.clear();
         }

         if (!m_locals.empty ())
         {
            m_locals.clear ();
         }

         if (!m_upgradeLock.locked ())
         {
            m_readLock.UnlockAll ();
         }
         else if (m_level == 1)
         {
            m_upgradeLock.UnlockAll ();
         }
      }

#ifdef _DEBUG
      void* WTransactionData::GetMarker () const
      {
         return m_marker;
      }
#endif //_DEBUG
   }

   namespace
   {
      //Stores the transaction data for each thread
      class WTransactionDataList
      {
      public:
         WTransactionDataList ();

         Internal::WTransactionData* Get ();
         Internal::WTransactionData* GetNew ();

         class WPushGuard
         {
         public:
            WPushGuard (std::unique_ptr<Internal::WTransactionData>&& root_p, Internal::WTransactionData* cur_p);
            WPushGuard (WPushGuard&& g);
            ~WPushGuard ();

         private:
            std::unique_ptr<Internal::WTransactionData> m_root_p;
            Internal::WTransactionData* m_cur_p;
         };
         friend WPushGuard;
         WPushGuard Push ();

         void MergeToParent ();
         void Abandon ();

         void CheckIntegrity () const;

      private:
         Internal::WTransactionData* GetNewNoActivate ();

         std::unique_ptr<Internal::WTransactionData> m_root_p;
         Internal::WTransactionData* m_cur_p;
         WUpgradeableLock m_lock;
      };

      THREAD_LOCAL (WTransactionDataList, s_transData_p);

      WTransactionDataList::WTransactionDataList ():
         m_cur_p (nullptr),
         m_lock (false)
      {}

      Internal::WTransactionData* WTransactionDataList::Get ()
      {
         if (m_cur_p)
         {
            return m_cur_p;
         }
         else
         {
            return GetNewNoActivate ();
         }
      }

      Internal::WTransactionData* WTransactionDataList::GetNew ()
      {
         Internal::WTransactionData* data_p = GetNewNoActivate ();
         data_p->Activate();
         return data_p;
      }

      Internal::WTransactionData* WTransactionDataList::GetNewNoActivate ()
      {
         CheckIntegrity ();
         if (!m_cur_p)
         {
            assert (!m_root_p);
            m_root_p = std::make_unique<Internal::WTransactionData>(m_lock);
            m_cur_p = m_root_p.get ();
         }
         else if (m_cur_p->IsActive ())
         {
            m_cur_p = m_cur_p->CreateChild ();
            assert (!m_cur_p->IsActive ());
         }
         //Note: if m_cur_p wasn't active above then we just use it
         CheckIntegrity ();

         return m_cur_p;
      }

      WTransactionDataList::WPushGuard::WPushGuard (std::unique_ptr<Internal::WTransactionData>&& root_p, Internal::WTransactionData* cur_p):
         m_root_p (std::move (root_p)),
         m_cur_p (cur_p)
      {}

      WTransactionDataList::WPushGuard::WPushGuard (WPushGuard&& g):
         m_root_p (std::move (g.m_root_p)),
         m_cur_p (g.m_cur_p)
      {}

      WTransactionDataList::WPushGuard::~WPushGuard ()
      {
         s_transData_p->m_root_p = std::move (m_root_p);
         s_transData_p->m_cur_p = m_cur_p;
      }

      WTransactionDataList::WPushGuard WTransactionDataList::Push ()
      {
         CheckIntegrity ();
         auto oldRoot_p = std::move (m_root_p);
         auto oldCur_p = m_cur_p;
         m_root_p.reset ();
         m_cur_p = nullptr;
         return WPushGuard (std::move (oldRoot_p), oldCur_p);
      }

      void WTransactionDataList::MergeToParent ()
      {
         CheckIntegrity ();
         assert (m_cur_p->IsActive ());
         assert (m_cur_p->GetParent ());
         if (m_cur_p->GetParent ())
         {
            m_cur_p->MergeToParent ();
            m_cur_p = m_cur_p->GetParent ();
            CheckIntegrity ();
         }
      }

      void WTransactionDataList::Abandon ()
      {
         assert (m_cur_p);
         if (!m_cur_p)
         {
            return;
         }
         m_cur_p->Clear ();

         if (m_cur_p->GetParent ())
         {
            m_cur_p = m_cur_p->GetParent ();
            CheckIntegrity ();
         }
      }

//#define CHECK_TLS_INTEGRITY
#ifdef CHECK_TLS_INTEGRITY

      void WTransactionDataList::CheckIntegrity () const
      {
         if (m_cur_p)
         {
            Internal::WTransactionData* cur_p = m_cur_p;
            Internal::WTransactionData* parent_p = cur_p->GetParent ();
            assert (cur_p->GetMarker () == Internal::WTransactionData::MARKER_VALUE);

            while (parent_p)
            {
               assert (parent_p->GetMarker () == Internal::WTransactionData::MARKER_VALUE);
               assert (parent_p->GetChild () == cur_p);
               assert (parent_p->GetLevel () == (cur_p->GetLevel () - 1));
               cur_p = parent_p;
               parent_p = cur_p->GetParent ();
            }

            cur_p = m_cur_p;
            Internal::WTransactionData* child_p = cur_p->GetChild ();
            while (child_p)
            {
               assert (child_p->GetMarker () == Internal::WTransactionData::MARKER_VALUE);
               assert (child_p->GetParent () == cur_p);
               assert (child_p->GetLevel () == (cur_p->GetLevel () + 1));
               cur_p = child_p;
               child_p = cur_p->GetChild ();
            }
         }
      }

#else
      void WTransactionDataList::CheckIntegrity () const
      {}
#endif //CHECK_TLS_INTEGRITY

   }

   WTimeArg::WTimeArg ()
   {}

   WTimeArg::WTimeArg (const time_point& t):
      m_time_o (t)
   {}

   WTimeArg WTimeArg::Unlimited ()
   {
      return WTimeArg ();
   }

   bool WTimeArg::IsUnlimited () const
   {
      return !m_time_o;
   }

   bool WTimeArg::operator<(const WTimeArg& t) const
   {
      if (IsUnlimited ())
      {
         //nothing is less than unlimited
         return false;
      }
      else if (!t.IsUnlimited ())
      {
         return (*m_time_o < *t.m_time_o);
      }
      else
      {
         //something limited is always less than an unlimited value
         return true;
      }
   }

   namespace Internal
   {

#ifdef _DEBUG

      bool ReadLocked()
      {
         return s_readMutexReadLocked;
      }

      bool UpgradeLocked()
      {
         return s_readMutexUpgradeLocked;
      }

      bool WriteLocked()
      {
         return s_readMutexWriteLocked;
      }
#endif //_DEBUG

      WValueBase::WValueBase (const size_t version):
         m_version (version)
      {}

      WValueBase::~WValueBase ()
      {
      }

      WVarCoreBase::~WVarCoreBase ()
      {
      }

   }

   WAtomic::WAtomic ():
      m_data_p (s_transData_p->GetNew ()),
      m_committed (false)
   {
#ifdef _DEBUG
      Internal::WTransactionData* data_p = m_data_p;
      while (data_p)
      {
         assert (data_p->IsActive ());
         data_p = data_p->GetParent ();
      }
#endif //DEBUG
   }

   void WAtomic::Validate() const
   {
      boost::unique_lock<WReadLock> lock(m_data_p->GetReadLock (), boost::defer_lock_t ());
      if (!m_data_p->GetUpgradeLock ().locked ())
      {
         lock.lock ();
      }
      if(!DoValidation())
      {
         throw Internal::WFailedValidationException();
      }
   }

   bool WAtomic::DoValidation() const
   {
      assert(Internal::ReadLocked() || Internal::UpgradeLocked ());
      for (const VarMap::value_type& val: m_data_p->GetGot ())
      {
         if (!val.first->Validate (*val.second))
         {
            return false;
         }
      }

      return true;
   }

   void WAtomic::ReadLock()
   {
      if (!m_data_p->GetUpgradeLock ().locked ())
      {
         m_data_p->GetReadLock ().lock();
      }
   }

   bool WAtomic::IsReadLocked() const
   {
      return m_data_p->GetReadLock ().locked ();
   }

   void WAtomic::ReadUnlock()
   {
      if (!m_data_p->GetUpgradeLock ().locked ())
      {
         m_data_p->GetReadLock ().unlock();
      }
   }

   void WAtomic::BeforeCommit (WBeforeCommitFunc func)
   {
      m_data_p->AddBeforeCommit (func);
   }

   void WAtomic::After(WAfterFunc func)
   {
      m_data_p->AddAfter (func);
   }

   void WAtomic::OnFail (WOnFailFunc func)
   {
      m_data_p->AddOnFail (func);
   }

   void WAtomic::ClearWrites()
   {
      m_data_p->ClearWrites ();
   }

   void WAtomic::Abort ()
   {
      m_data_p->Clear ();
   }

   void WAtomic::Restart ()
   {
      //We need to push our transaction data aside here, if we don't
      //and a destructor triggered by the clearing of data below
      //starts a new transaction it will end up being a child of the
      //transaction that is being restarted. If this happens at best
      //the actions of the detstructor will never be comitted, at
      //worst memory corruption will result.
      WTransactionDataList::WPushGuard guard = s_transData_p->Push ();
      m_data_p->Clear ();
      m_data_p->Activate ();
   }

   void WAtomic::RunOnFails ()
   {
      //We need to push our transaction data aside here so that
      //transactions in the "on fail" handlers will run properly
      //(properly here means: no memory corruption)
      WTransactionDataList::WPushGuard guard = s_transData_p->Push ();
      m_data_p->RunOnFails ();
   }

   void WAtomic::CommitLock()
   {
      if (!m_data_p->GetUpgradeLock ().locked ())
      {
         m_data_p->GetUpgradeLock ().lock ();
         //unlock all the old read-locks here else we won't be able to
         //promote our upgrade lock later
         m_data_p->GetReadLock ().UnlockAll ();
      }
   }

   bool WAtomic::Commit()
   {
      assert (m_data_p->GetLevel () == 1);
      if(m_data_p->GetLevel () == 1)
      {
         Internal::WTransactionData::WBeforeCommitList beforeCommits;
         m_data_p->GetBeforeCommits (beforeCommits);
         for (WAtomic::WBeforeCommitFunc& beforeCommit: beforeCommits)
         {
            beforeCommit (*this);
         }

#ifdef _DEBUG
         s_committing = true;
         struct WClearFlag
         {
            ~WClearFlag()
            {
               s_committing = false;
            }
         };
         WClearFlag clearFlag;
         (void)clearFlag; //avoid a compiler warning
#endif //_DEBUG

         std::list<std::shared_ptr<Internal::WValueBase>> dead;
         if (!m_data_p->GetSet ().empty ())
         {
            CommitLock ();

            if(!DoValidation())
            {
               m_data_p->GetUpgradeLock ().UnlockAll ();
               return false;
            }

            {
               //scope introduced so that wlock goes away at end of block
               WWriteLock wlock(m_data_p->GetUpgradeLock ());
               for (const VarMap::value_type& val: m_data_p->GetSet ())
               {
                  //save old values until after we're done committing
                  //in case they run transactions in their destructors
                  dead.push_back (val.first->Commit (val.second));
               }
               s_commitSignal.notify_all();
            }

            m_data_p->GetUpgradeLock ().UnlockAll ();
            IncrementNumWriteCommits ();
         }
         else
         {
            if (m_data_p->GetUpgradeLock ().locked ())
            {
               if(!DoValidation())
               {
                  m_data_p->GetUpgradeLock ().UnlockAll ();
                  return false;
               }
               m_data_p->GetUpgradeLock ().UnlockAll ();
            }
            else
            {
               m_data_p->GetReadLock ().lock ();
               if(!DoValidation())
               {
                  m_data_p->GetReadLock ().UnlockAll ();
                  return false;
               }
               m_data_p->GetReadLock ().UnlockAll ();
            }
            IncrementNumReadCommits ();
         }

         //reset transaction data here so that after funcs will see no
         //transaction in progress
         Internal::WTransactionData::WAfterList afters;
         m_data_p->GetAfters (afters);
         m_data_p->Clear ();
#ifdef _DEBUG
         s_committing = false;
#endif //_DEBUG
         m_committed = true;

         //The dead must be cleared before the afters run, else we coudl be holding on to things
         //that the afters are relying on being gone already (specifically
         //WChannelReader::Wdata::Release assumes that the after function that it attaches won't be
         //fighting with the transaction to release the channel nodes in order to avoid a stack overflow).
         dead.clear ();

         for (WAtomic::WAfterFunc& after: afters)
         {
            after ();
         }

      }

      return true;
   }

   bool WAtomic::WaitForChanges(const WTimeArg& timeout)
   {
      if (m_data_p->GetUpgradeLock ().locked ())
      {
         m_data_p->GetUpgradeLock ().unlock ();
      }

      WReadLockGuard<WAtomic> rlock(*this);
      if(!timeout.IsUnlimited ())
      {
         do
         {
            if(!DoValidation())
            {
               return true;
            }
            m_data_p->GetReadLock ().WaitForCommit(timeout);
         }while(std::chrono::steady_clock::now () < *timeout.m_time_o);
      }
      else
      {
         for (;;)
         {
            if(!DoValidation())
            {
               return true;
            }
            m_data_p->GetReadLock ().WaitForCommit(timeout);
         }
      }

      return false;
   }

   const Internal::WValueBase* WAtomic::GetVarValue (const std::shared_ptr<Internal::WVarCoreBase>& core_p)
   {
      //Look in the values of this transaction and its parents
      Internal::WTransactionData* data_p = m_data_p;
      while (data_p)
      {
         //first try the set value
         VarMap::iterator it = data_p->GetSet ().find (core_p);
         if (it != data_p->GetSet ().end ())
         {
            return it->second.get ();
         }

         //next the got value
         it = data_p->GetGot ().find (core_p);
         if (it != data_p->GetGot ().end ())
         {
            return it->second.get ();
         }

         //try the parent transaction
         data_p = data_p->GetParent ();
      }

      //We haven't seen this var yet
      return nullptr;
   }

   const Internal::WValueBase* WAtomic::GetVarGotValue (const std::shared_ptr<Internal::WVarCoreBase>& core_p)
   {
      //Look in the values of this transaction and its parents
      Internal::WTransactionData* data_p = m_data_p;
      while (data_p)
      {
         //next the got value
         auto it = data_p->GetGot ().find (core_p);
         if (it != data_p->GetGot ().end ())
         {
            return it->second.get ();
         }

         //try the parent transaction
         data_p = data_p->GetParent ();
      }

      //We haven't seen this var yet
      return nullptr;
   }

   void WAtomic::SetVarGetValue (const std::shared_ptr<Internal::WVarCoreBase>& core_p, std::shared_ptr<Internal::WValueBase>&& value_p)
   {
      m_data_p->GetGot ()[core_p] = std::move (value_p);
   }

   Internal::WValueBase* WAtomic::GetVarSetValue (const std::shared_ptr<Internal::WVarCoreBase>& core_p)
   {
      //Note that we only check this transaction's set values not the
      //parent's. Values need to be set in the current transaction,
      //even if they have already been set in the parent. When this
      //transaction commits the set values will be merged into the
      //parent transaction.
      VarMap::iterator it = m_data_p->GetSet ().find (core_p);
      if (it != m_data_p->GetSet ().end ())
      {
         return it->second.get ();
      }
      else
      {
         return 0;
      }
   }

   void WAtomic::SetVarValue (const std::shared_ptr<Internal::WVarCoreBase>& core_p, std::shared_ptr<Internal::WValueBase>&& value_p)
   {
      m_data_p->GetSet ()[core_p] = std::move (value_p);
   }

   Internal::WLocalValueBase* WAtomic::GetLocalValue (uint64_t key)
   {
      return m_data_p->GetLocalValue (key);
   }

   void WAtomic::SetLocalValue (uint64_t key, std::unique_ptr<Internal::WLocalValueBase>&& value_p)
   {
      m_data_p->SetLocalValue (key, std::move (value_p));
   }

   WAtomic::~WAtomic()
   {
      if (!m_committed)
      {
         s_transData_p->Abandon ();
      }
   }

   namespace
   {
//#define TRACK_LAST_TRANS_CONFLICTS
#ifdef TRACK_LAST_TRANS_CONFLICTS

      THREAD_LOCAL_WITH_INIT_VALUE (unsigned int, s_lastTransConflicts, 0);

      struct WSetLastTransConflicts
      {
         const unsigned int& m_badCommits;

         WSetLastTransConflicts (const unsigned int& badCommits);

         ~WSetLastTransConflicts ();
      };

      WSetLastTransConflicts::WSetLastTransConflicts (const unsigned int& badCommits):
         m_badCommits (badCommits)
      {}

      WSetLastTransConflicts::~WSetLastTransConflicts ()
      {
         s_lastTransConflicts = m_badCommits;
      }
#endif //TRACK_LAST_TRANS_CONFLICTS

   }

   void WAtomic::AtomicallyImpl(Internal::WAtomicOp& op,
                                const WMaxConflicts& maxConflicts,
                                const WMaxRetries& maxRetries,
                                const WMaxRetryWait& maxRetryWait)
   {
#ifdef _DEBUG
      //if this assertion fails we got a new transaction starting
      //while the current transaction is committing
      assert(!s_committing);
#endif //_DEBUG

      WAtomic at;
      assert (!at.m_committed);
      struct WRunOnFailHandlers
      {
         WAtomic& m_at;

         WRunOnFailHandlers (WAtomic& at): m_at (at) {}

         ~WRunOnFailHandlers ()
         {
            if (!m_at.m_committed)
            {
               m_at.RunOnFails ();
            }
         }
      };
      WRunOnFailHandlers runOnFailHandlers (at);
      if (at.m_data_p->GetLevel () > 1)
      {
         try
         {
            //this is a child transaction so just run the op and
            //merge to parent, exceptions and committing will be handled by the
            //root transaction
            op.Run (at);
            s_transData_p->MergeToParent ();
            at.m_committed = true;
            return;
         }
         catch(WRetryException&)
         {
            at.RunOnFails ();
            //we need to merge our gets into the root transaction so
            //that those vars get checked in the root transaction
            //retry handler
            at.m_data_p->MergeGetsToRoot ();
            throw;
         }
      }

      unsigned int badCommits = 0;
#ifdef TRACK_LAST_TRANS_CONFLICTS
      const size_t numConflictsLastTime = s_lastTransConflicts;
      (void)numConflictsLastTime;
      WSetLastTransConflicts setLastTransConflicts (badCommits);
#endif //TRACK_LAST_TRANS_CONFLICTS
      unsigned int retries = 0;
      for (;;)
      {
         if(maxConflicts.m_max != UNLIMITED && badCommits >= maxConflicts.m_max)
         {
            if(WConflictResolution::THROW == maxConflicts.m_resolution)
            {
               throw WMaxConflictsException(badCommits);
            }
            else
            {
               at.CommitLock();
            }
         }

         try
         {
            op.Run (at);

            //have to commit in this try block in case a "before action" does something that throws
            //an exception
            if(at.Commit())
            {
               break;
            }
         }
         catch(Internal::WFailedValidationException&)
         {
            ++badCommits;
            IncrementNumConflicts ();
            at.RunOnFails ();
            at.Restart();
            continue;
         }
         catch(WRetryException& exc)
         {
            ++retries;
            if(maxRetries.m_value != UNLIMITED && retries >= maxRetries.m_value)
            {
               throw WMaxRetriesException(retries);
            }
            at.RunOnFails ();

            const auto timeout = std::min (exc.m_timeout, maxRetryWait.m_value);
            if(!at.WaitForChanges(timeout))
            {
               throw WRetryTimeoutException();
            }
            at.Restart();
            continue;
         }
         catch (...)
         {
            at.RunOnFails ();
            at.Restart();
            throw;
         }

         at.RunOnFails ();
         at.Restart();
         ++badCommits;
         IncrementNumConflicts ();
      }
   }

   void WInconsistent::InconsistentlyImpl(Internal::WInconsistentOp& op)
   {
      WInconsistent ins;
      op.Run (ins);
   }

   WInconsistent::WInconsistent() :
      m_lock (s_readMutex, boost::defer_lock_t ()),
      m_lockCount (0)
   {}

   void WInconsistent::ReadLock()
   {
      if (m_lockCount == 0)
      {
         m_lock.lock();
      }
      ++m_lockCount;
   }

   bool WInconsistent::IsReadLocked() const
   {
      return m_lock.owns_lock();
   }

   void WInconsistent::ReadUnlock()
   {
      if(m_lockCount > 0)
      {
         --m_lockCount;
         if (m_lockCount == 0)
         {
            m_lock.unlock();
         }
      }
   }

   bool InAtomic()
   {
      return (s_transData_p->Get()->IsActive ());
   }

   WNoAtomic::WNoAtomic ()
   {
      if (InAtomic ())
      {
         throw WInAtomicError ();
      }
   }

   WInAtomicError::WInAtomicError ():
      WException ("Attempt to use function marked NO ATOMIC from within a transaction")
   {}

   void Retry(WAtomic&, const WTimeArg& timeout)
   {
      throw WRetryException(timeout);
   }

   bool WTransactionLocalFlag::TestAndSet (WAtomic& at)
   {
      const auto isSet = m_flag.Get (at) && *m_flag.Get (at);
      m_flag.Set (true, at);
      return isSet;
   }

}

max_stars_repo_name: bretthall/Wyatt-STM

max_issues_repo_name: bretthall/Wyatt-STM

max_forks_repo_name: bretthall/Wyatt-STM


text:
#include <goblin-engineer/components/http/http_server/http_session.hpp>

#include <goblin-engineer/components/http/detail/network.hpp>
#include <goblin-engineer/components/http/http_server/websocket_session.hpp>

#include <boost/beast/core/bind_handler.hpp>
#include <iostream>

namespace goblin_engineer { namespace components { namespace http_server {

inline void fail(boost::beast::error_code ec, char const *what) {
  std::cerr << what << ": " << ec.message() << "\n";
}

http_session::http_session(detail::tcp::socket &&socket,helper_write_f_t context_)
    : stream_(std::move(socket))
    , queue_(*this)
    , handle_processing(context_)
    , id(reinterpret_cast<std::uintptr_t>(this)) {}

void http_session::run() {
  boost::asio::dispatch(
      stream_.get_executor(),
      detail::beast::bind_front_handler(&http_session::do_read, this->shared_from_this())
  );
}

void http_session::on_read(boost::beast::error_code ec,std::size_t bytes_transferred) {
  boost::ignore_unused(bytes_transferred);

  // This means they closed the connection
  if (ec == detail::http::error::end_of_stream)
    return do_close();

  if (ec)
    return fail(ec, "read");

  // See if it is a WebSocket Upgrade
  if (detail::websocket::is_upgrade(parser_->get())) {
    // Create a websocket session, transferring ownership
    // of both the socket and the HTTP request.
    std::make_shared<websocket_session>(stream_.release_socket(),handle_processing)->do_accept(parser_->release());
    return;
  }

  // Send the response
  handle_processing(parser_->release(), id);

  // If we aren't at the queue limit, try to pipeline another request
  if (!queue_.is_full())
    do_read();
}

void http_session::on_write(
    bool close
    , boost::beast::error_code ec
    , std::size_t bytes_transferred ) {
  boost::ignore_unused(bytes_transferred);

  if (ec)
    return fail(ec, "write");

  if (close) {
    // This means we should close the connection, usually because
    // the response indicated the "Connection: close" semantic.
    return do_close();
  }

  // Inform the queue that a write completed
  if (queue_.on_write()) {
    // Read another request
    do_read();
  }
}

void http_session::do_close() {
  boost::beast::error_code ec;
  stream_.socket().shutdown(detail::tcp::socket::shutdown_send, ec);
}

void http_session::do_read() {
  // Construct a new parser for each message
  parser_.emplace();

  // Apply a reasonable limit to the allowed size
  // of the body in bytes to prevent abuse.
  parser_->body_limit(10000);

  // Set the timeout.
  stream_.expires_after(std::chrono::seconds(30));

  // Read a request using the parser-oriented interface
  detail::http::async_read(
      stream_
      , buffer_
      , *parser_
      ,boost::beast::bind_front_handler(
          &http_session::on_read
          , shared_from_this()
      )
  );
}

void http_session::write(detail::response_type &&body) {
  boost::asio::post(
      stream_.get_executor(),
      [this, body = std::move(body)]() mutable {
        queue_(std::move(body));
      }
  );
}

http_session::queue::queue(http_session &self) : self_(self) {
  static_assert(limit > 0, "queue limit must be positive");
  items_.reserve(limit);
}

bool http_session::queue::on_write() {
  assert(!items_.empty());
  auto const was_full = is_full();
  items_.erase(items_.begin());
  if (!items_.empty())
    (*items_.front())();
  return was_full;
}
}}}