<commit_before>/*************************************************************************
 *
 * REALM CONFIDENTIAL
 * __________________
 *
 *  [2011] - [2015] Realm Inc
 *  All Rights Reserved.
 *
 * NOTICE:  All information contained herein is, and remains
 * the property of Realm Incorporated and its suppliers,
 * if any.  The intellectual and technical concepts contained
 * herein are proprietary to Realm Incorporated
 * and its suppliers and may be covered by U.S. and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained
 * from Realm Incorporated.
 *
 **************************************************************************/
#ifndef REALM_UTIL_LOGGER_HPP
#define REALM_UTIL_LOGGER_HPP

#include <string.h>
#include <utility>
#include <string>
#include <locale>
#include <sstream>
#include <iostream>

#include <realm/util/features.h>
#include <realm/util/tuple.hpp>
#include <realm/util/thread.hpp>
#include <realm/util/file.hpp>

namespace realm {
namespace util {

/// All Logger objects store a reference to a LevelThreshold object which it
/// uses to efficiently query about the current log level threshold
/// (`level_threshold.get()`). All messages logged with a level that is lower
/// than the current threshold will be dropped. For the sake of efficiency, this
/// test happens before the message is formatted.
///
/// A logger is not inherently thread-safe, but specific implementations can be
/// (see ThreadSafeLogger). For a logger to be thread-safe, the implementation
/// of do_log() must be thread-safe and the referenced LevelThreshold object
/// must have a thread-safe get() method.
///
/// Examples:
///
///    logger.error("Overlong message from master coordinator");
///    logger.info("Listening for peers on %1:%2", listen_address, listen_port);
class Logger {
public:
    template<class... Params> void trace(const char* message, Params...);
    template<class... Params> void debug(const char* message, Params...);
    template<class... Params> void detail(const char* message, Params...);
    template<class... Params> void info(const char* message, Params...);
    template<class... Params> void warn(const char* message, Params...);
    template<class... Params> void error(const char* message, Params...);
    template<class... Params> void fatal(const char* message, Params...);

    /// Specifies criticality when passed to log(). Functions as a criticality
    /// threshold when returned from LevelThreshold::get().
    enum class Level { all, trace, debug, detail, info, warn, error, fatal, off };

    template<class... Params> void log(Level, const char* message, Params...);

    /// Shorthand for `int(level) >= int(level_threshold.get())`.
    bool would_log(Level level) const noexcept;

    class LevelThreshold;

    const LevelThreshold& level_threshold;

    virtual ~Logger() noexcept;

protected:
    Logger(const LevelThreshold&) noexcept;

    static void do_log(Logger&, std::string message);

    virtual void do_log(std::string message) = 0;

private:
    struct State;
    template<class> struct Subst;

    template<class... Params> REALM_NOINLINE void do_log(Level, const char* message, Params...);
    void log_impl(State&);
    template<class Param, class... Params> void log_impl(State&, const Param&, Params...);
};

template<class C, class T>
std::basic_ostream<C,T>& operator<<(std::basic_ostream<C,T>&, Logger::Level);

template<class C, class T>
std::basic_istream<C,T>& operator>>(std::basic_istream<C,T>&, Logger::Level&);

class Logger::LevelThreshold {
public:
    virtual Level get() const noexcept = 0;
};

/// A root logger that is not thread-safe and allows for the log level threshold
/// to be changed over time. The initial log level threshold is
/// Logger::Level::info.
class RootLogger: private Logger::LevelThreshold, public Logger  {
public:
    void set_level_threshold(Level) noexcept;

protected:
    RootLogger();

private:
    Level m_level_threshold = Level::info;
    Level get() const noexcept override final;
};

/// A logger that writes to STDERR. This logger is not thread-safe.
class StderrLogger: public RootLogger {
protected:
    void do_log(std::string) override final;
};

/// A logger that writes to a stream. This logger is not thread-safe.
class StreamLogger: public RootLogger {
public:
    explicit StreamLogger(std::ostream&) noexcept;

protected:
    void do_log(std::string) override final;

private:
    std::ostream& m_out;
};

/// A logger that writes to a file. This logger is not thread-safe.
class FileLogger: public StreamLogger {
public:
    explicit FileLogger(std::string path);
    explicit FileLogger(util::File);

private:
    util::File m_file;
    util::File::Streambuf m_streambuf;
    std::ostream m_out;
};

/// A thread-safe logger. This logger ignores the level threshold of the base
/// logger. Instead, it introduces new a LevelThreshold object with a fixed
/// value to achieve thread safety.
class ThreadSafeLogger: private Logger::LevelThreshold, public Logger {
public:
    explicit ThreadSafeLogger(Logger& base_logger, Level = Level::info);

protected:
    void do_log(std::string) override final;

private:
    const Level m_level_threshold; // Immutable for thread safety
    Logger& m_base_logger;
    Mutex m_mutex;
    Level get() const noexcept override final;
};

/// A logger that adds a fixed prefix to each message. This logger inherits the
/// LevelThreshold object of the specified base logger. This logger is
/// thread-safe if, and only if the base logger is thread-safe.
class PrefixLogger: public Logger {
public:
    PrefixLogger(std::string prefix, Logger& base_logger) noexcept;

protected:
    void do_log(std::string) override final;

private:
    const std::string m_prefix;
    Logger& m_base_logger;
};

// Implementation

struct Logger::State {
    std::string m_message;
    std::string m_search;
    int m_param_num = 1;
    std::ostringstream m_formatter;
    State(const char* s):
        m_message(s),
        m_search(m_message)
    {
        m_formatter.imbue(std::locale::classic());
    }
};

template<class T> struct Logger::Subst {
    void operator()(const T& param, State* state)
    {
        state->m_formatter << "%" << state->m_param_num;
        std::string key = state->m_formatter.str();
        state->m_formatter.str(std::string());
        std::string::size_type j = state->m_search.find(key);
        if (j != std::string::npos) {
            state->m_formatter << param;
            std::string str = state->m_formatter.str();
            state->m_formatter.str(std::string());
            state->m_message.replace(j, key.size(), str);
            state->m_search.replace(j, key.size(), std::string(str.size(), '\0'));
        }
        ++state->m_param_num;
    }
};

template<class... Params> inline void Logger::trace(const char* message, Params... params)
{
    log(Level::trace, message, params...); // Throws
}

template<class... Params> inline void Logger::debug(const char* message, Params... params)
{
    log(Level::debug, message, params...); // Throws
}

template<class... Params> inline void Logger::detail(const char* message, Params... params)
{
    log(Level::detail, message, params...); // Throws
}

template<class... Params> inline void Logger::info(const char* message, Params... params)
{
    log(Level::info, message, params...); // Throws
}

template<class... Params> inline void Logger::warn(const char* message, Params... params)
{
    log(Level::warn, message, params...); // Throws
}

template<class... Params> inline void Logger::error(const char* message, Params... params)
{
    log(Level::error, message, params...); // Throws
}

template<class... Params> inline void Logger::fatal(const char* message, Params... params)
{
    log(Level::fatal, message, params...); // Throws
}

template<class... Params>
inline void Logger::log(Level level, const char* message, Params... params)
{
    if (would_log(level))
        do_log(level, message, params...); // Throws
}

inline bool Logger::would_log(Level level) const noexcept
{
    return int(level) >= int(level_threshold.get());
}

inline Logger::~Logger() noexcept
{
}

inline Logger::Logger(const LevelThreshold& lt) noexcept:
    level_threshold(lt)
{
}

inline void Logger::do_log(Logger& logger, std::string message)
{
    logger.do_log(std::move(message));
}

template<class... Params> void Logger::do_log(Level, const char* message, Params... params)
{
    State state(message);
    log_impl(state, params...);
}

inline void Logger::log_impl(State& state)
{
    do_log(std::move(state.m_message));
}

template<class Param, class... Params>
inline void Logger::log_impl(State& state, const Param& param, Params... params)
{
    Subst<Param>()(param, &state);
    log_impl(state, params...);
}

template<class C, class T>
std::basic_ostream<C,T>& operator<<(std::basic_ostream<C,T>& out, Logger::Level level)
{
    switch(level) {
        case Logger::Level::all:
            out << "all";
            return out;
        case Logger::Level::trace:
            out << "trace";
            return out;
        case Logger::Level::debug:
            out << "debug";
            return out;
        case Logger::Level::detail:
            out << "detail";
            return out;
        case Logger::Level::info:
            out << "info";
            return out;
        case Logger::Level::warn:
            out << "warn";
            return out;
        case Logger::Level::error:
            out << "error";
            return out;
        case Logger::Level::fatal:
            out << "fatal";
            return out;
        case Logger::Level::off:
            out << "off";
            return out;
    }
    REALM_ASSERT(false);
    return out;
}

template<class C, class T>
std::basic_istream<C,T>& operator>>(std::basic_istream<C,T>& in, Logger::Level& level)
{
    std::basic_string<C,T> str;
    auto check = [&](const char* name) {
        size_t n = strlen(name);
        if (n != str.size())
            return false;
        for (size_t i = 0; i < n; ++i) {
            if (in.widen(name[i]) != str[i])
                return false;
        }
        return true;
    };
    if (in >> str) {
        if (check("all")) {
            level = Logger::Level::all;
        }
        else if (check("trace")) {
            level = Logger::Level::trace;
        }
        else if (check("debug")) {
            level = Logger::Level::debug;
        }
        else if (check("detail")) {
            level = Logger::Level::detail;
        }
        else if (check("info")) {
            level = Logger::Level::info;
        }
        else if (check("warn")) {
            level = Logger::Level::warn;
        }
        else if (check("error")) {
            level = Logger::Level::error;
        }
        else if (check("fatal")) {
            level = Logger::Level::fatal;
        }
        else if (check("off")) {
            level = Logger::Level::off;
        }
        else {
            in.setstate(std::ios_base::failbit);
        }
    }
    return in;
}

inline void RootLogger::set_level_threshold(Level level_threshold) noexcept
{
    m_level_threshold = level_threshold;
}

inline RootLogger::RootLogger():
    Logger::LevelThreshold(),
    Logger(static_cast<Logger::LevelThreshold&>(*this))
{
}

inline Logger::Level RootLogger::get() const noexcept
{
    return m_level_threshold;
}

inline void StderrLogger::do_log(std::string message)
{
    std::cerr << message << '\n'; // Throws
    std::cerr.flush(); // Throws
}

inline StreamLogger::StreamLogger(std::ostream& out) noexcept:
    m_out(out)
{
}

inline void StreamLogger::do_log(std::string message)
{
    m_out << message << '\n'; // Throws
    m_out.flush(); // Throws
}

inline FileLogger::FileLogger(std::string path):
    StreamLogger(m_out),
    m_file(path, util::File::mode_Write), // Throws
    m_streambuf(&m_file), // Throws
    m_out(&m_streambuf) // Throws
{
}

inline FileLogger::FileLogger(util::File file):
    StreamLogger(m_out),
    m_file(std::move(file)),
    m_streambuf(&m_file), // Throws
    m_out(&m_streambuf) // Throws
{
}

inline ThreadSafeLogger::ThreadSafeLogger(Logger& base_logger, Level level_threshold):
    Logger::LevelThreshold(),
    Logger(static_cast<Logger::LevelThreshold&>(*this)),
    m_level_threshold(level_threshold),
    m_base_logger(base_logger)
{
}

inline void ThreadSafeLogger::do_log(std::string message)
{
    LockGuard l(m_mutex);
    Logger::do_log(m_base_logger, message); // Throws
}

inline Logger::Level ThreadSafeLogger::get() const noexcept
{
    return m_level_threshold;
}

inline PrefixLogger::PrefixLogger(std::string prefix, Logger& base_logger) noexcept:
    Logger(base_logger.level_threshold),
    m_prefix(std::move(prefix)),
    m_base_logger(base_logger)
{
}

inline void PrefixLogger::do_log(std::string message)
{
    Logger::do_log(m_base_logger, m_prefix + message); // Throws
}

} // namespace util
} // namespace realm

#endif // REALM_UTIL_LOGGER_HPP
<commit_msg>Improvement of util::Logger documentation<commit_after>/*************************************************************************
 *
 * REALM CONFIDENTIAL
 * __________________
 *
 *  [2011] - [2015] Realm Inc
 *  All Rights Reserved.
 *
 * NOTICE:  All information contained herein is, and remains
 * the property of Realm Incorporated and its suppliers,
 * if any.  The intellectual and technical concepts contained
 * herein are proprietary to Realm Incorporated
 * and its suppliers and may be covered by U.S. and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained
 * from Realm Incorporated.
 *
 **************************************************************************/
#ifndef REALM_UTIL_LOGGER_HPP
#define REALM_UTIL_LOGGER_HPP

#include <string.h>
#include <utility>
#include <string>
#include <locale>
#include <sstream>
#include <iostream>

#include <realm/util/features.h>
#include <realm/util/tuple.hpp>
#include <realm/util/thread.hpp>
#include <realm/util/file.hpp>

namespace realm {
namespace util {

/// All Logger objects store a reference to a LevelThreshold object which it
/// uses to efficiently query about the current log level threshold
/// (`level_threshold.get()`). All messages logged with a level that is lower
/// than the current threshold will be dropped. For the sake of efficiency, this
/// test happens before the message is formatted.
///
/// A logger is not inherently thread-safe, but specific implementations can be
/// (see ThreadSafeLogger). For a logger to be thread-safe, the implementation
/// of do_log() must be thread-safe and the referenced LevelThreshold object
/// must have a thread-safe get() method.
///
/// Examples:
///
///    logger.error("Overlong message from master coordinator");
///    logger.info("Listening for peers on %1:%2", listen_address, listen_port);
class Logger {
public:
    template<class... Params> void trace(const char* message, Params...);
    template<class... Params> void debug(const char* message, Params...);
    template<class... Params> void detail(const char* message, Params...);
    template<class... Params> void info(const char* message, Params...);
    template<class... Params> void warn(const char* message, Params...);
    template<class... Params> void error(const char* message, Params...);
    template<class... Params> void fatal(const char* message, Params...);

    /// Specifies criticality when passed to log(). Functions as a criticality
    /// threshold when returned from LevelThreshold::get().
    enum class Level { all, trace, debug, detail, info, warn, error, fatal, off };

    template<class... Params> void log(Level, const char* message, Params...);

    /// Shorthand for `int(level) >= int(level_threshold.get())`.
    bool would_log(Level level) const noexcept;

    class LevelThreshold;

    const LevelThreshold& level_threshold;

    virtual ~Logger() noexcept;

protected:
    Logger(const LevelThreshold&) noexcept;

    static void do_log(Logger&, std::string message);

    virtual void do_log(std::string message) = 0;

private:
    struct State;
    template<class> struct Subst;

    template<class... Params> REALM_NOINLINE void do_log(Level, const char* message, Params...);
    void log_impl(State&);
    template<class Param, class... Params> void log_impl(State&, const Param&, Params...);
};

template<class C, class T>
std::basic_ostream<C,T>& operator<<(std::basic_ostream<C,T>&, Logger::Level);

template<class C, class T>
std::basic_istream<C,T>& operator>>(std::basic_istream<C,T>&, Logger::Level&);

class Logger::LevelThreshold {
public:
    virtual Level get() const noexcept = 0;
};

/// A root logger that is not thread-safe and allows for the log level threshold
/// to be changed over time. The initial log level threshold is
/// Logger::Level::info.
class RootLogger: private Logger::LevelThreshold, public Logger  {
public:
    void set_level_threshold(Level) noexcept;

protected:
    RootLogger();

private:
    Level m_level_threshold = Level::info;
    Level get() const noexcept override final;
};

/// A logger that writes to STDERR. This logger is not thread-safe.
///
/// Since this class is a RootLogger, it contains modifiable a log level
/// threshold.
class StderrLogger: public RootLogger {
protected:
    void do_log(std::string) override final;
};

/// A logger that writes to a stream. This logger is not thread-safe.
///
/// Since this class is a RootLogger, it contains modifiable a log level
/// threshold.
class StreamLogger: public RootLogger {
public:
    explicit StreamLogger(std::ostream&) noexcept;

protected:
    void do_log(std::string) override final;

private:
    std::ostream& m_out;
};

/// A logger that writes to a file. This logger is not thread-safe.
///
/// Since this class is a RootLogger, it contains modifiable a log level
/// threshold.
class FileLogger: public StreamLogger {
public:
    explicit FileLogger(std::string path);
    explicit FileLogger(util::File);

private:
    util::File m_file;
    util::File::Streambuf m_streambuf;
    std::ostream m_out;
};

/// A thread-safe logger. This logger ignores the level threshold of the base
/// logger. Instead, it introduces new a LevelThreshold object with a fixed
/// value to achieve thread safety.
class ThreadSafeLogger: private Logger::LevelThreshold, public Logger {
public:
    explicit ThreadSafeLogger(Logger& base_logger, Level = Level::info);

protected:
    void do_log(std::string) override final;

private:
    const Level m_level_threshold; // Immutable for thread safety
    Logger& m_base_logger;
    Mutex m_mutex;
    Level get() const noexcept override final;
};

/// A logger that adds a fixed prefix to each message. This logger inherits the
/// LevelThreshold object of the specified base logger. This logger is
/// thread-safe if, and only if the base logger is thread-safe.
class PrefixLogger: public Logger {
public:
    PrefixLogger(std::string prefix, Logger& base_logger) noexcept;

protected:
    void do_log(std::string) override final;

private:
    const std::string m_prefix;
    Logger& m_base_logger;
};

// Implementation

struct Logger::State {
    std::string m_message;
    std::string m_search;
    int m_param_num = 1;
    std::ostringstream m_formatter;
    State(const char* s):
        m_message(s),
        m_search(m_message)
    {
        m_formatter.imbue(std::locale::classic());
    }
};

template<class T> struct Logger::Subst {
    void operator()(const T& param, State* state)
    {
        state->m_formatter << "%" << state->m_param_num;
        std::string key = state->m_formatter.str();
        state->m_formatter.str(std::string());
        std::string::size_type j = state->m_search.find(key);
        if (j != std::string::npos) {
            state->m_formatter << param;
            std::string str = state->m_formatter.str();
            state->m_formatter.str(std::string());
            state->m_message.replace(j, key.size(), str);
            state->m_search.replace(j, key.size(), std::string(str.size(), '\0'));
        }
        ++state->m_param_num;
    }
};

template<class... Params> inline void Logger::trace(const char* message, Params... params)
{
    log(Level::trace, message, params...); // Throws
}

template<class... Params> inline void Logger::debug(const char* message, Params... params)
{
    log(Level::debug, message, params...); // Throws
}

template<class... Params> inline void Logger::detail(const char* message, Params... params)
{
    log(Level::detail, message, params...); // Throws
}

template<class... Params> inline void Logger::info(const char* message, Params... params)
{
    log(Level::info, message, params...); // Throws
}

template<class... Params> inline void Logger::warn(const char* message, Params... params)
{
    log(Level::warn, message, params...); // Throws
}

template<class... Params> inline void Logger::error(const char* message, Params... params)
{
    log(Level::error, message, params...); // Throws
}

template<class... Params> inline void Logger::fatal(const char* message, Params... params)
{
    log(Level::fatal, message, params...); // Throws
}

template<class... Params>
inline void Logger::log(Level level, const char* message, Params... params)
{
    if (would_log(level))
        do_log(level, message, params...); // Throws
}

inline bool Logger::would_log(Level level) const noexcept
{
    return int(level) >= int(level_threshold.get());
}

inline Logger::~Logger() noexcept
{
}

inline Logger::Logger(const LevelThreshold& lt) noexcept:
    level_threshold(lt)
{
}

inline void Logger::do_log(Logger& logger, std::string message)
{
    logger.do_log(std::move(message));
}

template<class... Params> void Logger::do_log(Level, const char* message, Params... params)
{
    State state(message);
    log_impl(state, params...);
}

inline void Logger::log_impl(State& state)
{
    do_log(std::move(state.m_message));
}

template<class Param, class... Params>
inline void Logger::log_impl(State& state, const Param& param, Params... params)
{
    Subst<Param>()(param, &state);
    log_impl(state, params...);
}

template<class C, class T>
std::basic_ostream<C,T>& operator<<(std::basic_ostream<C,T>& out, Logger::Level level)
{
    switch(level) {
        case Logger::Level::all:
            out << "all";
            return out;
        case Logger::Level::trace:
            out << "trace";
            return out;
        case Logger::Level::debug:
            out << "debug";
            return out;
        case Logger::Level::detail:
            out << "detail";
            return out;
        case Logger::Level::info:
            out << "info";
            return out;
        case Logger::Level::warn:
            out << "warn";
            return out;
        case Logger::Level::error:
            out << "error";
            return out;
        case Logger::Level::fatal:
            out << "fatal";
            return out;
        case Logger::Level::off:
            out << "off";
            return out;
    }
    REALM_ASSERT(false);
    return out;
}

template<class C, class T>
std::basic_istream<C,T>& operator>>(std::basic_istream<C,T>& in, Logger::Level& level)
{
    std::basic_string<C,T> str;
    auto check = [&](const char* name) {
        size_t n = strlen(name);
        if (n != str.size())
            return false;
        for (size_t i = 0; i < n; ++i) {
            if (in.widen(name[i]) != str[i])
                return false;
        }
        return true;
    };
    if (in >> str) {
        if (check("all")) {
            level = Logger::Level::all;
        }
        else if (check("trace")) {
            level = Logger::Level::trace;
        }
        else if (check("debug")) {
            level = Logger::Level::debug;
        }
        else if (check("detail")) {
            level = Logger::Level::detail;
        }
        else if (check("info")) {
            level = Logger::Level::info;
        }
        else if (check("warn")) {
            level = Logger::Level::warn;
        }
        else if (check("error")) {
            level = Logger::Level::error;
        }
        else if (check("fatal")) {
            level = Logger::Level::fatal;
        }
        else if (check("off")) {
            level = Logger::Level::off;
        }
        else {
            in.setstate(std::ios_base::failbit);
        }
    }
    return in;
}

inline void RootLogger::set_level_threshold(Level level_threshold) noexcept
{
    m_level_threshold = level_threshold;
}

inline RootLogger::RootLogger():
    Logger::LevelThreshold(),
    Logger(static_cast<Logger::LevelThreshold&>(*this))
{
}

inline Logger::Level RootLogger::get() const noexcept
{
    return m_level_threshold;
}

inline void StderrLogger::do_log(std::string message)
{
    std::cerr << message << '\n'; // Throws
    std::cerr.flush(); // Throws
}

inline StreamLogger::StreamLogger(std::ostream& out) noexcept:
    m_out(out)
{
}

inline void StreamLogger::do_log(std::string message)
{
    m_out << message << '\n'; // Throws
    m_out.flush(); // Throws
}

inline FileLogger::FileLogger(std::string path):
    StreamLogger(m_out),
    m_file(path, util::File::mode_Write), // Throws
    m_streambuf(&m_file), // Throws
    m_out(&m_streambuf) // Throws
{
}

inline FileLogger::FileLogger(util::File file):
    StreamLogger(m_out),
    m_file(std::move(file)),
    m_streambuf(&m_file), // Throws
    m_out(&m_streambuf) // Throws
{
}

inline ThreadSafeLogger::ThreadSafeLogger(Logger& base_logger, Level level_threshold):
    Logger::LevelThreshold(),
    Logger(static_cast<Logger::LevelThreshold&>(*this)),
    m_level_threshold(level_threshold),
    m_base_logger(base_logger)
{
}

inline void ThreadSafeLogger::do_log(std::string message)
{
    LockGuard l(m_mutex);
    Logger::do_log(m_base_logger, message); // Throws
}

inline Logger::Level ThreadSafeLogger::get() const noexcept
{
    return m_level_threshold;
}

inline PrefixLogger::PrefixLogger(std::string prefix, Logger& base_logger) noexcept:
    Logger(base_logger.level_threshold),
    m_prefix(std::move(prefix)),
    m_base_logger(base_logger)
{
}

inline void PrefixLogger::do_log(std::string message)
{
    Logger::do_log(m_base_logger, m_prefix + message); // Throws
}

} // namespace util
} // namespace realm

#endif // REALM_UTIL_LOGGER_HPP
<|endoftext|>


<commit_before>/** Implementation of the pqxx::robusttransaction class.
 *
 * pqxx::robusttransaction is a slower but safer transaction class.
 *
 * Copyright (c) 2000-2020, Jeroen T. Vermeulen.
 *
 * See COPYING for copyright license.  If you did not receive a file called
 * COPYING with this source code, please notify the distributor of this
 * mistake, or contact the author.
 */
#include "pqxx-source.hxx"

#include <chrono>
#include <cstdint>
#include <stdexcept>
#include <thread>
#include <unordered_map>

#include "pqxx/connection"
#include "pqxx/nontransaction"
#include "pqxx/result"
#include "pqxx/robusttransaction"

namespace
{
/// Statuses in which we may find our transaction.
/** There's also "in the future," but it manifests as an error, not as an
 * actual status.
 */
enum tx_stat
{
  tx_unknown,
  tx_committed,
  tx_aborted,
  tx_in_progress,
};

/* Super-simple string hash function: just use the initial byte.
 *
 * Happens to be a perfect hash for this case, and should be cheap to compute.
 */
struct initial_hash
{
  size_t operator()(const std::string &x) const noexcept
  {
    return static_cast<uint8_t>(x[0]);
  }
};

// TODO: Is there a simple, lightweight, constexpr alternative?
const std::unordered_map<std::string, tx_stat, initial_hash> statuses{
  {"committed", tx_committed},
  {"aborted", tx_aborted},
  {"in progress", tx_in_progress},
};

tx_stat query_status(const std::string &xid, const std::string conn_str)
{
  const std::string name{"robustx-check-status"};
  const std::string query{"SELECT txid_status(" + xid + ")"};
  pqxx::connection c{conn_str};
  pqxx::nontransaction w{c, name};
  const auto row{w.exec1(query, name)};
  const auto status_text{row[0].as<std::string>()};
  if (status_text.empty())
    throw pqxx::internal_error{"Transaction status string is empty."};
  const auto here{statuses.find(status_text)};
  if (here == statuses.end())
    throw pqxx::internal_error{"Unknown transaction status: " + status_text};
  return here->second;
}
} // namespace

pqxx::internal::basic_robusttransaction::basic_robusttransaction(
  connection &C, const char begin_command[]) :
        namedclass{"robusttransaction"},
        dbtransaction(C),
        m_conn_string{C.connection_string()}
{
  m_backendpid = C.backendpid();
  direct_exec(begin_command);
  direct_exec("SELECT txid_current()")[0][0].to(m_xid);
}

pqxx::internal::basic_robusttransaction::~basic_robusttransaction() = default;

void pqxx::internal::basic_robusttransaction::do_commit()
{
  // Check constraints before sending the COMMIT to the database, so as to
  // minimise our in-doubt window.
  try
  {
    direct_exec("SET CONSTRAINTS ALL IMMEDIATE");
  }
  catch (const std::exception &)
  {
    do_abort();
    throw;
  }

  // Here comes the in-doubt window.  If we lose our connection here, we'll be
  // left clueless as to what happened on the backend.  It may have received
  // the commit command and completed the transaction, and ended up with a
  // success it could not report back to us.  Or it may have noticed the broken
  // connection and aborted the transaction.  It may even still be executing
  // the commit, only to fail later.
  //
  // All this uncertainty requires some special handling, and that s what makes
  // robusttransaction what it is.
  try
  {
    direct_exec("COMMIT");

    // If we make it here, great.  Normal, successful commit.
    return;
  }
  catch (const broken_connection &)
  {
    // Oops, lost connection at the crucial moment.  Fall through to in-doubt
    // handling below.
  }
  catch (const std::exception &)
  {
    if (conn().is_open())
    {
      // Commit failed, for some other reason.
      do_abort();
      throw;
    }
    // Otherwise, fall through to in-doubt handling.
  }

  // If we get here, we're in doubt.  Figure out what happened.

  // TODO: Make delay and attempts configurable.
  const auto delay{std::chrono::milliseconds(300)};
  const int max_attempts{500};
  static_assert(max_attempts > 0);

  tx_stat stat;
  for (int attempts{0}; attempts < max_attempts;
       ++attempts, std::this_thread::sleep_for(delay))
  {
    stat = tx_unknown;
    try
    {
      stat = query_status(m_xid, m_conn_string);
    }
    catch (const pqxx::broken_connection &)
    {
      // Swallow the error.  Pause and retry.
    }
    switch (stat)
    {
    case tx_unknown:
      // We were unable to reconnect and query transaction status.
      // Stay in it for another attempt.
      return;
    case tx_committed:
      // Success!  We're done.
      return;
    case tx_aborted:
      // Aborted.  We're done.
      do_abort();
      return;
    case tx_in_progress:
      // The transaction is still running.  Stick around until we know what
      // transpires.
      break;
    }
  }

  // Okay, this has taken too long.  Give up, report in-doubt state.
  throw in_doubt_error{
    "Transaction " + name() + " (with transaction ID " + m_xid +
    ") "
    "lost connection while committing.  It's impossible to tell whether "
    "it committed, or aborted, or is still running.  "
    "Attempts to find out its outcome have failed.  "
    "The backend process on the server had process ID " +
    to_string(m_backendpid) +
    ".  "
    "You may be able to check what happened to that process."};
}

void pqxx::internal::basic_robusttransaction::do_abort()
{
  direct_exec("ROLLBACK");
}
<commit_msg>Micro-optimisation.<commit_after>/** Implementation of the pqxx::robusttransaction class.
 *
 * pqxx::robusttransaction is a slower but safer transaction class.
 *
 * Copyright (c) 2000-2020, Jeroen T. Vermeulen.
 *
 * See COPYING for copyright license.  If you did not receive a file called
 * COPYING with this source code, please notify the distributor of this
 * mistake, or contact the author.
 */
#include "pqxx-source.hxx"

#include <chrono>
#include <cstdint>
#include <stdexcept>
#include <thread>
#include <unordered_map>

#include "pqxx/connection"
#include "pqxx/nontransaction"
#include "pqxx/result"
#include "pqxx/robusttransaction"

namespace
{
/// Statuses in which we may find our transaction.
/** There's also "in the future," but it manifests as an error, not as an
 * actual status.
 */
enum tx_stat
{
  tx_unknown,
  tx_committed,
  tx_aborted,
  tx_in_progress,
};

/* Super-simple string hash function: just use the initial byte.
 *
 * Happens to be a perfect hash for this case, and should be cheap to compute.
 */
struct initial_hash
{
  size_t operator()(const std::string &x) const noexcept
  {
    return static_cast<uint8_t>(x[0]);
  }
};

// TODO: Is there a simple, lightweight, constexpr alternative?
const std::unordered_map<std::string, tx_stat, initial_hash> statuses{
  {"committed", tx_committed},
  {"aborted", tx_aborted},
  {"in progress", tx_in_progress},
};

tx_stat query_status(const std::string &xid, const std::string &conn_str)
{
  static const std::string name{"robusttxck"};
  const std::string query{"SELECT txid_status(" + xid + ")"};
  pqxx::connection c{conn_str};
  pqxx::nontransaction w{c, name};
  const auto row{w.exec1(query, name)};
  const auto status_text{row[0].as<std::string>()};
  if (status_text.empty())
    throw pqxx::internal_error{"Transaction status string is empty."};
  const auto here{statuses.find(status_text)};
  if (here == statuses.end())
    throw pqxx::internal_error{"Unknown transaction status: " + status_text};
  return here->second;
}
} // namespace

pqxx::internal::basic_robusttransaction::basic_robusttransaction(
  connection &C, const char begin_command[]) :
        namedclass{"robusttransaction"},
        dbtransaction(C),
        m_conn_string{C.connection_string()}
{
  m_backendpid = C.backendpid();
  direct_exec(begin_command);
  direct_exec("SELECT txid_current()")[0][0].to(m_xid);
}

pqxx::internal::basic_robusttransaction::~basic_robusttransaction() = default;

void pqxx::internal::basic_robusttransaction::do_commit()
{
  // Check constraints before sending the COMMIT to the database, so as to
  // minimise our in-doubt window.
  try
  {
    direct_exec("SET CONSTRAINTS ALL IMMEDIATE");
  }
  catch (const std::exception &)
  {
    do_abort();
    throw;
  }

  // Here comes the in-doubt window.  If we lose our connection here, we'll be
  // left clueless as to what happened on the backend.  It may have received
  // the commit command and completed the transaction, and ended up with a
  // success it could not report back to us.  Or it may have noticed the broken
  // connection and aborted the transaction.  It may even still be executing
  // the commit, only to fail later.
  //
  // All this uncertainty requires some special handling, and that s what makes
  // robusttransaction what it is.
  try
  {
    direct_exec("COMMIT");

    // If we make it here, great.  Normal, successful commit.
    return;
  }
  catch (const broken_connection &)
  {
    // Oops, lost connection at the crucial moment.  Fall through to in-doubt
    // handling below.
  }
  catch (const std::exception &)
  {
    if (conn().is_open())
    {
      // Commit failed, for some other reason.
      do_abort();
      throw;
    }
    // Otherwise, fall through to in-doubt handling.
  }

  // If we get here, we're in doubt.  Figure out what happened.

  // TODO: Make delay and attempts configurable.
  const auto delay{std::chrono::milliseconds(300)};
  const int max_attempts{500};
  static_assert(max_attempts > 0);

  tx_stat stat;
  for (int attempts{0}; attempts < max_attempts;
       ++attempts, std::this_thread::sleep_for(delay))
  {
    stat = tx_unknown;
    try
    {
      stat = query_status(m_xid, m_conn_string);
    }
    catch (const pqxx::broken_connection &)
    {
      // Swallow the error.  Pause and retry.
    }
    switch (stat)
    {
    case tx_unknown:
      // We were unable to reconnect and query transaction status.
      // Stay in it for another attempt.
      return;
    case tx_committed:
      // Success!  We're done.
      return;
    case tx_aborted:
      // Aborted.  We're done.
      do_abort();
      return;
    case tx_in_progress:
      // The transaction is still running.  Stick around until we know what
      // transpires.
      break;
    }
  }

  // Okay, this has taken too long.  Give up, report in-doubt state.
  throw in_doubt_error{
    "Transaction " + name() + " (with transaction ID " + m_xid +
    ") "
    "lost connection while committing.  It's impossible to tell whether "
    "it committed, or aborted, or is still running.  "
    "Attempts to find out its outcome have failed.  "
    "The backend process on the server had process ID " +
    to_string(m_backendpid) +
    ".  "
    "You may be able to check what happened to that process."};
}

void pqxx::internal::basic_robusttransaction::do_abort()
{
  direct_exec("ROLLBACK");
}
<|endoftext|>


<commit_before>// Copyright (c) 2014 Steinwurf ApS
// All Rights Reserved
//
// Distributed under the "BSD License". See the accompanying LICENSE.rst file.

#pragma once

#include "easy_bind.hpp"

namespace sak
{
    // Do not expose implementation details to users of this header file

    // namespace detail
    // {
    //     template<typename T>
    //     struct get_signature_impl;

    //     template<typename C, typename R, typename... A>
    //     struct get_signature_impl<R(C::*)(A...)>
    //     {
    //         typedef R type(A...);
    //     };

    //     template<typename C, typename R, typename... A>
    //     struct get_signature_impl<R(C::*)(A...) const>
    //     {
    //         typedef R type(A...);
    //     };

    //     template<typename C, typename R, typename... A>
    //     struct get_signature_impl<R(C::*)(A...) volatile>
    //     {
    //         typedef R type(A...);
    //     };

    //     template<typename C, typename R, typename... A>
    //     struct get_signature_impl<R(C::*)(A...) const volatile>
    //     {
    //         typedef R type(A...);
    //     };

    //     template<typename R, typename... A>
    //     struct get_signature_impl<R(A...)>
    //     {
    //         typedef R type(A...);
    //     };

    //     template<typename R, typename... A>
    //     struct get_signature_impl<R(&)(A...)>
    //     {
    //         typedef R type(A...);
    //     };

    //     template<typename R, typename... A>
    //     struct get_signature_impl<R(*)(A...)>
    //     {
    //         typedef R type(A...);
    //     };

    //     template<typename T> using get_signature =
    //         typename get_signature_impl<T>::type;

    //     template<typename F> using make_function_type =
    //         std::function<get_signature<F>>;

    //     template<typename F> make_function_type<F> make_function(F f) {
    //         return make_function_type<F>(f); }
    // }

    namespace detail
    {
        /// Helper struct which serves a return value if we were not able
        /// to bind to the specified function
        struct not_valid_bind
        { };

        /// Overload of the internal optional_bind. This overload is
        /// chosen if the B::bind(f) expression cannot be
        /// instantiated. Thanks to SFINAE this means that this
        /// overload will be the only valid expression and therefore
        /// it will be chosen. If however the bind expression is valid
        /// then both overloads will be available, however since the
        /// int version does not require an implict conversion of the
        /// numeric constant form int to char it will be preferred.
        template<class B, class F>
        not_valid_bind optional_bind(F&, char)
        {
            return not_valid_bind();
        }

        template<class B, class F>
        auto optional_bind(F& f, int) -> decltype(B::bind(f))
        {
            return B::bind(f);
        }
    }

    /// The optional_bind utility was designed for cases where we want
    /// to bind to a specific member function if that function is
    /// available otherwise we want to skip the bind.
    ///
    /// optional_bind is designed with a quite narrow scope, but it
    /// can probably be extended to serve more purposes if needed.
    ///
    /// The following example shows optional_bind in action:
    ///
    ///     struct foo
    ///     {
    ///         void make_coffee()
    ///         {
    ///             std::cout << "coffee on its way" << std::endl;
    ///         }
    ///     };
    ///
    /// Now somewhere in your code you want to bind to two different
    /// member functions, but if they are not there it is ok.
    ///
    /// To do this we need to define two helpers which implement the
    /// actual bind (we will utilize the easy_bind helper here to make
    /// the code simple but you can also use std::bind):
    ///
    ///     struct bind_make_coffee
    ///     {
    ///         template<class T>
    ///         static auto bind(T& t) ->
    ///             decltype(sak::easy_bind(&T::make_coffee, &t))
    ///         {
    ///             return sak::easy_bind(&T::make_coffee, &t);
    ///         }
    ///     };
    ///
    ///     struct bind_grind_beans
    ///     {
    ///         template<class T>
    ///         static auto bind(T& t) ->
    ///             decltype(sak::easy_bind(&T::grind_beans, &t))
    ///         {
    ///             return sak::easy_bind(&T::grind_beans, &t);
    ///         }
    ///     };
    ///
    /// With these helpers we can now write the following:
    ///
    ///    foo f;
    ///    auto make_coffee = sak::optinal_bind<bind_make_coffee>(&f);
    ///    auto grind_beans = sak::optional_bind<bind_grind_beans>(&f);
    ///
    ///    assert(sak::is_bind_expression(make_coffee) == true);
    ///    assert(sak::is_bind_expression(grind_beans) == false);
    ///
    /// The nice thing about this is that is can be done without
    /// having to directly utilize SFINAE tricks directly in the code.
    ///
    /// The return value of optional_bind can only be used if
    /// sak::is_bind_expression returns true. If that is the case then the
    /// return value will be the equal to the return value of the bind
    /// expression used in the helper. In the example given above we
    /// are using sak::easy_bind which internally uses std::bind so
    /// the return value is that of std::bind.
    ///
    /// The optional_bind helper takes the following arguments:
    ///
    ///  - B which is a type that should provide a static function
    ///    called bind which should take F as a parameter. Usually we
    ///    will make bind a template function to allow late binding as
    ///    per our example above.
    ///  - F is the object to which we wish to bind
    ///
    template<class B, class F>
    auto optional_bind(F& f) -> decltype(detail::optional_bind<B, F>(f, 0))
    {
        return detail::optional_bind<B, F>(f, 0);
    }

    /// Checks whether the optional_bind return a valid bind expression
    /// @param The return value of optional_bind
    /// @return True if the type T is a valid bind expression otherwise false
    template<class T>
    bool is_bind_expression(const T&)
    {
        return std::is_bind_expression<T>::value;
    }
}
<commit_msg>Removing obsolete code<commit_after>// Copyright (c) 2014 Steinwurf ApS
// All Rights Reserved
//
// Distributed under the "BSD License". See the accompanying LICENSE.rst file.

#pragma once

#include <functional>

namespace sak
{
    // Do not expose implementation details to users of this header file
    namespace detail
    {
        /// Helper struct which serves a return value if we were not able
        /// to bind to the specified function
        struct not_valid_bind
        { };

        /// Overload of the internal optional_bind. This overload is
        /// chosen if the B::bind(f) expression cannot be
        /// instantiated. Thanks to SFINAE this means that this
        /// overload will be the only valid expression and therefore
        /// it will be chosen. If however the bind expression is valid
        /// then both overloads will be available, however since the
        /// int version does not require an implict conversion of the
        /// numeric constant form int to char it will be preferred.
        template<class B, class F>
        not_valid_bind optional_bind(F&, char)
        {
            return not_valid_bind();
        }

        template<class B, class F>
        auto optional_bind(F& f, int) -> decltype(B::bind(f))
        {
            return B::bind(f);
        }
    }

    /// The optional_bind utility was designed for cases where we want
    /// to bind to a specific member function if that function is
    /// available otherwise we want to skip the bind.
    ///
    /// optional_bind is designed with a quite narrow scope, but it
    /// can probably be extended to serve more purposes if needed.
    ///
    /// The following example shows optional_bind in action:
    ///
    ///     struct foo
    ///     {
    ///         void make_coffee()
    ///         {
    ///             std::cout << "coffee on its way" << std::endl;
    ///         }
    ///     };
    ///
    /// Now somewhere in your code you want to bind to two different
    /// member functions, but if they are not there it is ok.
    ///
    /// To do this we need to define two helpers which implement the
    /// actual bind (we will utilize the easy_bind helper here to make
    /// the code simple but you can also use std::bind):
    ///
    ///     struct bind_make_coffee
    ///     {
    ///         template<class T>
    ///         static auto bind(T& t) ->
    ///             decltype(sak::easy_bind(&T::make_coffee, &t))
    ///         {
    ///             return sak::easy_bind(&T::make_coffee, &t);
    ///         }
    ///     };
    ///
    ///     struct bind_grind_beans
    ///     {
    ///         template<class T>
    ///         static auto bind(T& t) ->
    ///             decltype(sak::easy_bind(&T::grind_beans, &t))
    ///         {
    ///             return sak::easy_bind(&T::grind_beans, &t);
    ///         }
    ///     };
    ///
    /// With these helpers we can now write the following:
    ///
    ///    foo f;
    ///    auto make_coffee = sak::optinal_bind<bind_make_coffee>(&f);
    ///    auto grind_beans = sak::optional_bind<bind_grind_beans>(&f);
    ///
    ///    assert(sak::is_bind_expression(make_coffee) == true);
    ///    assert(sak::is_bind_expression(grind_beans) == false);
    ///
    /// The nice thing about this is that is can be done without
    /// having to directly utilize SFINAE tricks directly in the code.
    ///
    /// The return value of optional_bind can only be used if
    /// sak::is_bind_expression returns true. If that is the case then the
    /// return value will be the equal to the return value of the bind
    /// expression used in the helper. In the example given above we
    /// are using sak::easy_bind which internally uses std::bind so
    /// the return value is that of std::bind.
    ///
    /// The optional_bind helper takes the following arguments:
    ///
    ///  - B which is a type that should provide a static function
    ///    called bind which should take F as a parameter. Usually we
    ///    will make bind a template function to allow late binding as
    ///    per our example above.
    ///  - F is the object to which we wish to bind
    ///
    template<class B, class F>
    auto optional_bind(F& f) -> decltype(detail::optional_bind<B, F>(f, 0))
    {
        return detail::optional_bind<B, F>(f, 0);
    }

    /// Checks whether the optional_bind return a valid bind expression
    /// @param The return value of optional_bind
    /// @return True if the type T is a valid bind expression otherwise false
    template<class T>
    bool is_bind_expression(const T&)
    {
        return std::is_bind_expression<T>::value;
    }
}
<|endoftext|>


<commit_before>/*
	This file is part of cpp-ethereum.

	cpp-ethereum is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	Foobar is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
*/
/** @file peer.cpp
 * @author Gav Wood <i@gavwood.com>
 * @date 2014
 * Peer Network test functions.
 */

#include <chrono>
#include <thread>
#include <BlockChain.h>
#include <PeerNetwork.h>
using namespace std;
using namespace eth;
using boost::asio::ip::tcp;

int peerTest(int argc, char** argv)
{
	short listenPort = 30303;
	string remoteHost;
	short remotePort = 30303;

	for (int i = 1; i < argc; ++i)
	{
		string arg = argv[i];
		if (arg == "-l" && i + 1 < argc)
			listenPort = atoi(argv[++i]);
		else if (arg == "-r" && i + 1 < argc)
			remoteHost = argv[++i];
		else if (arg == "-p" && i + 1 < argc)
			remotePort = atoi(argv[++i]);
		else
			remoteHost = argv[i];
	}

	BlockChain ch("/tmp");
	PeerServer pn("Test", ch, 0, listenPort);

	if (!remoteHost.empty())
		pn.connect(remoteHost, remotePort);

	for (int i = 0; ; ++i)
	{
		this_thread::sleep_for(chrono::milliseconds(100));
		pn.process(ch);
		if (!(i % 10))
			pn.pingAll();
	}

	return 0;
}

<commit_msg>Less hangy.<commit_after>/*
	This file is part of cpp-ethereum.

	cpp-ethereum is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	Foobar is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
*/
/** @file peer.cpp
 * @author Gav Wood <i@gavwood.com>
 * @date 2014
 * Peer Network test functions.
 */

#include <chrono>
#include <thread>
#include <BlockChain.h>
#include <PeerNetwork.h>
using namespace std;
using namespace eth;
using boost::asio::ip::tcp;

int peerTest(int argc, char** argv)
{
	short listenPort = 30303;
	string remoteHost;
	short remotePort = 30303;

	for (int i = 1; i < argc; ++i)
	{
		string arg = argv[i];
		if (arg == "-l" && i + 1 < argc)
			listenPort = atoi(argv[++i]);
		else if (arg == "-r" && i + 1 < argc)
			remoteHost = argv[++i];
		else if (arg == "-p" && i + 1 < argc)
			remotePort = atoi(argv[++i]);
		else
			remoteHost = argv[i];
	}

	BlockChain ch("/tmp");
	PeerServer pn("Test", ch, 0, listenPort);

	if (!remoteHost.empty())
		pn.connect(remoteHost, remotePort);

	for (int i = 0; ; ++i)
	{
		this_thread::sleep_for(chrono::milliseconds(100));
		pn.sync();
		if (!(i % 10))
			pn.pingAll();
	}

	return 0;
}

<|endoftext|>


<commit_before>#pragma once

// `krbn::hid_manager` can be used safely in a multi-threaded environment.

#include "boost_utility.hpp"
#include "cf_utility.hpp"
#include "device_detail.hpp"
#include "dispatcher.hpp"
#include "human_interface_device.hpp"
#include "logger.hpp"
#include "types.hpp"
#include <IOKit/hid/IOHIDManager.h>
#include <unordered_map>

namespace krbn {

class hid_manager final : public pqrs::dispatcher::extra::dispatcher_client {
public:
  // Signals (invoked from the shared dispatcher thread)

  // Return false to ignore device.
  boost::signals2::signal<bool(IOHIDDeviceRef _Nonnull), boost_utility::signals2_combiner_call_while_true> device_detecting;
  boost::signals2::signal<void(std::weak_ptr<human_interface_device>)> device_detected;
  boost::signals2::signal<void(std::weak_ptr<human_interface_device>)> device_removed;

  // Methods

  hid_manager(const hid_manager&) = delete;

  hid_manager(const std::vector<std::pair<hid_usage_page, hid_usage>>& usage_pairs) : dispatcher_client(),
                                                                                usage_pairs_(usage_pairs),
                                                                                manager_(nullptr),
                                                                                refresh_timer_(*this) {
    run_loop_thread_ = std::make_unique<cf_utility::run_loop_thread>();
  }

  virtual ~hid_manager(void) {
    detach_from_dispatcher([this] {
      stop();
    });

    run_loop_thread_->terminate();
    run_loop_thread_ = nullptr;
  }

  void async_start(void) {
    enqueue_to_dispatcher([this] {
      start();
    });
  }

  void async_stop(void) {
    enqueue_to_dispatcher([this] {
      stop();
    });
  }

  std::vector<std::shared_ptr<human_interface_device>> copy_hids(void) const {
    std::lock_guard<std::mutex> lock(hids_mutex_);

    return hids_;
  }

private:
  // This method is executed in the dispatcher thread.
  void start(void) {
    if (manager_) {
      logger::get_logger().warn("hid_manager is already started.");
      return;
    }

    manager_ = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
    if (!manager_) {
      logger::get_logger().error("IOHIDManagerCreate is failed.");
      return;
    }

    if (auto device_matching_dictionaries = iokit_utility::create_device_matching_dictionaries(usage_pairs_)) {
      IOHIDManagerSetDeviceMatchingMultiple(manager_, device_matching_dictionaries);
      CFRelease(device_matching_dictionaries);
    }

    IOHIDManagerRegisterDeviceMatchingCallback(manager_, static_device_matching_callback, this);
    IOHIDManagerRegisterDeviceRemovalCallback(manager_, static_device_removal_callback, this);

    IOHIDManagerScheduleWithRunLoop(manager_,
                                    run_loop_thread_->get_run_loop(),
                                    kCFRunLoopCommonModes);

    // We need to call `wake` after `IOHIDManagerScheduleWithRunLoop`.

    run_loop_thread_->wake();

    refresh_timer_.start(
        [this] {
          refresh_if_needed();
        },
        std::chrono::milliseconds(5000));

    logger::get_logger().info("hid_manager is started.");
  }

  // This method is executed in the dispatcher thread.
  void stop(void) {
    if (!manager_) {
      return;
    }

    // refresh_timer_

    refresh_timer_.stop();

    // manager_

    run_loop_thread_->enqueue_and_wait(^{
      // Release manager_ in run_loop_thread_ to avoid accessing released manager_ in run_loop_thread_.

      IOHIDManagerUnscheduleFromRunLoop(manager_,
                                        run_loop_thread_->get_run_loop(),
                                        kCFRunLoopCommonModes);

      CFRelease(manager_);
      manager_ = nullptr;
    });

    // Other variables

    registry_entry_ids_.clear();

    {
      std::lock_guard<std::mutex> lock(hids_mutex_);

      hids_.clear();
    }

    logger::get_logger().info("hid_manager is stopped.");
  }

  // This method is executed in the dispatcher thread.
  void refresh_if_needed(void) {
    // `device_removal_callback` is sometimes missed
    // and invalid human_interface_devices are remained into hids_.
    // (The problem is likely occur just after wake up from sleep.)
    //
    // We validate the human_interface_device,
    // and then reload devices if there is an invalid human_interface_device.

    bool needs_refresh = false;

    {
      std::lock_guard<std::mutex> lock(hids_mutex_);

      for (const auto& hid : hids_) {
        if (!hid->validate()) {
          logger::get_logger().warn("Refreshing hid_manager since a dangling human_interface_device is found. ({0})",
                                    hid->get_name_for_log());
          needs_refresh = true;
          break;
        }
      }
    }

    if (needs_refresh) {
      stop();
      start();
    }
  }

  boost::optional<registry_entry_id> find_registry_entry_id(IOHIDDeviceRef _Nonnull device) {
    auto it = registry_entry_ids_.find(device);
    if (it != std::end(registry_entry_ids_)) {
      return it->second;
    }
    return boost::none;
  }

  static void static_device_matching_callback(void* _Nullable context,
                                              IOReturn result,
                                              void* _Nullable sender,
                                              IOHIDDeviceRef _Nonnull device) {
    if (result != kIOReturnSuccess) {
      return;
    }

    auto self = static_cast<hid_manager*>(context);
    if (!self) {
      return;
    }

    self->device_matching_callback(device);
  }

  void device_matching_callback(IOHIDDeviceRef _Nonnull device) {
    // `static_device_matching_callback` is called by multiple threads.
    // (It is not called from `run_loop_thread_`.)
    // Thus, we have to synchronize by using `dispatcher`.

    if (!device) {
      return;
    }

    CFRetain(device);

    enqueue_to_dispatcher([this, device] {
      if (!device_detecting(device)) {
        goto finish;
      }

      if (auto registry_entry_id = iokit_utility::find_registry_entry_id(device)) {
        // iokit_utility::find_registry_entry_id will be failed in device_removal_callback at least on macOS 10.13.
        // Thus, we have to memory device and registry_entry_id correspondence by myself.
        registry_entry_ids_[device] = *registry_entry_id;

        // Skip if same device is already matched.
        // (Multiple usage device (e.g. usage::pointer and usage::mouse) will be matched twice.)

        std::shared_ptr<human_interface_device> hid;

        {
          std::lock_guard<std::mutex> lock(hids_mutex_);

          if (std::any_of(std::begin(hids_),
                          std::end(hids_),
                          [&](auto&& h) {
                            return *registry_entry_id == h->get_registry_entry_id();
                          })) {
            // logger::get_logger().info("registry_entry_id:{0} already exists", static_cast<uint64_t>(*registry_entry_id));
            goto finish;
          }

          hid = std::make_shared<human_interface_device>(device, *registry_entry_id);
          hids_.push_back(hid);
        }

        enqueue_to_dispatcher([this, hid] {
          device_detected(hid);
        });
      }

    finish:
      CFRelease(device);
    });
  }

  static void static_device_removal_callback(void* _Nullable context,
                                             IOReturn result,
                                             void* _Nullable sender,
                                             IOHIDDeviceRef _Nonnull device) {
    if (result != kIOReturnSuccess) {
      return;
    }

    auto self = static_cast<hid_manager*>(context);
    if (!self) {
      return;
    }

    self->device_removal_callback(device);
  }

  void device_removal_callback(IOHIDDeviceRef _Nonnull device) {
    // `static_device_removal_callback` is called by multiple threads.
    // (It is not called from `run_loop_thread_`.)
    // Thus, we have to synchronize by using `dispatcher`.

    if (!device) {
      return;
    }

    CFRetain(device);

    enqueue_to_dispatcher([this, device] {
      if (auto registry_entry_id = find_registry_entry_id(device)) {
        std::shared_ptr<human_interface_device> hid;

        {
          std::lock_guard<std::mutex> lock(hids_mutex_);

          auto it = std::find_if(std::begin(hids_),
                                 std::end(hids_),
                                 [&](auto&& h) {
                                   return *registry_entry_id == h->get_registry_entry_id();
                                 });
          if (it != hids_.end()) {
            hid = *it;
            hids_.erase(it);
          }
        }

        if (hid) {
          hid->set_removed();

          enqueue_to_dispatcher([this, hid] {
            device_removed(hid);
          });
        }

        // There might be multiple devices for one registry_entry_id.
        // (For example, keyboard and mouse combined device.)
        // And there is possibility that removal callback is not called with some device.
        //
        // For example, there are 3 devices for 1 registry_entry_id (4294974284).
        //   - device1 { device:0x7fb9d64078a0 => registry_entry_id:4294974284 }
        //   - device2 { device:0x7fb9d8301390 => registry_entry_id:4294974284 }
        //   - device3 { device:0x7fb9d830a630 => registry_entry_id:4294974284 }
        // And device_removal_callback are called only with device1 and device2.
        //
        // We should remove device1, device2 and device3 at the same time in order to deal with this case.

        for (auto it = std::begin(registry_entry_ids_); it != std::end(registry_entry_ids_);) {
          if (it->second == *registry_entry_id) {
            it = registry_entry_ids_.erase(it);
          } else {
            std::advance(it, 1);
          }
        }
      }

      CFRelease(device);
    });
  }

  std::unique_ptr<cf_utility::run_loop_thread> run_loop_thread_;

  std::vector<std::pair<hid_usage_page, hid_usage>> usage_pairs_;

  IOHIDManagerRef _Nullable manager_;
  pqrs::dispatcher::extra::timer refresh_timer_;

  std::unordered_map<IOHIDDeviceRef, registry_entry_id> registry_entry_ids_;

  std::vector<std::shared_ptr<human_interface_device>> hids_;
  mutable std::mutex hids_mutex_;
};
} // namespace krbn
<commit_msg>improve hid_manager<commit_after>#pragma once

// `krbn::hid_manager` can be used safely in a multi-threaded environment.

#include "boost_utility.hpp"
#include "cf_utility.hpp"
#include "device_detail.hpp"
#include "dispatcher.hpp"
#include "human_interface_device.hpp"
#include "logger.hpp"
#include "monitor/service_monitor.hpp"
#include "types.hpp"
#include <IOKit/hid/IOHIDManager.h>
#include <unordered_map>

namespace krbn {

// We implement hid_manager without IOHIDManager.
//
// IOHIDManager opens connected device automatically, and we cannot stop it.
// We want to prevent opening device depending the `device_detecting` result.
// Thus, we cannot use IOHIDManager to this purpose.

class hid_manager final : public pqrs::dispatcher::extra::dispatcher_client {
public:
  // Signals (invoked from the shared dispatcher thread)

  // Return false to ignore device.
  boost::signals2::signal<bool(IOHIDDeviceRef _Nonnull), boost_utility::signals2_combiner_call_while_true> device_detecting;
  boost::signals2::signal<void(std::weak_ptr<human_interface_device>)> device_detected;
  boost::signals2::signal<void(std::weak_ptr<human_interface_device>)> device_removed;

  // Methods

  hid_manager(const hid_manager&) = delete;

  hid_manager(const std::vector<std::pair<hid_usage_page, hid_usage>>& usage_pairs) : dispatcher_client(),
                                                                                usage_pairs_(usage_pairs),
                                                                                refresh_timer_(*this) {
  }

  virtual ~hid_manager(void) {
    detach_from_dispatcher([this] {
      stop();
    });
  }

  void async_start(void) {
    enqueue_to_dispatcher([this] {
      start();
    });
  }

  std::vector<std::shared_ptr<human_interface_device>> copy_hids(void) const {
    std::lock_guard<std::mutex> lock(hids_mutex_);

    return hids_;
  }

private:
  // This method is executed in the dispatcher thread.
  void start(void) {
    for (const auto& pair : usage_pairs_) {
      if (auto dictionary = IOServiceMatching(kIOHIDDeviceKey)) {
        cf_utility::set_cfmutabledictionary_value(dictionary,
                                                  CFSTR(kIOHIDDeviceUsagePageKey),
                                                  static_cast<int64_t>(pair.first));
        cf_utility::set_cfmutabledictionary_value(dictionary,
                                                  CFSTR(kIOHIDDeviceUsageKey),
                                                  static_cast<int64_t>(pair.second));

        auto monitor = std::make_shared<monitor::service_monitor::monitor>(dictionary);

        monitor->service_detected.connect([this](auto&& services) {
          for (const auto& service : services->get_services()) {
            if (devices_.find(service) == std::end(devices_)) {
              if (auto device = IOHIDDeviceCreate(kCFAllocatorDefault, service)) {
                CFRetain(device);
                devices_[service] = device;

                device_matching_callback(device);

                CFRelease(device);
              }
            }
          }
        });

        monitor->service_removed.connect([this](auto&& services) {
          for (const auto& service : services->get_services()) {
            auto it = devices_.find(service);
            if (it != std::end(devices_)) {
              device_removal_callback(it->second);

              devices_.erase(it);
              CFRelease(it->second);
            }
          }
        });

        monitor->async_start();

        service_monitors_.push_back(monitor);

        CFRelease(dictionary);
      }
    }

    refresh_timer_.start(
        [this] {
          refresh_if_needed();
        },
        std::chrono::milliseconds(5000));

    logger::get_logger().info("hid_manager is started.");
  }

  // This method is executed in the dispatcher thread.
  void stop(void) {
    refresh_timer_.stop();
    service_monitors_.clear();
    hids_.clear();
    registry_entry_ids_.clear();

    for (const auto& pair : devices_) {
      CFRelease(pair.second);
    }
    devices_.clear();
  }

  // This method is executed in the dispatcher thread.
  void refresh_if_needed(void) {
    // `device_removal_callback` is sometimes missed
    // and invalid human_interface_devices are remained into hids_.
    // (The problem is likely occur just after wake up from sleep.)
    //
    // We validate the human_interface_device,
    // and then reload devices if there is an invalid human_interface_device.

    bool needs_refresh = false;

    {
      std::lock_guard<std::mutex> lock(hids_mutex_);

      for (const auto& hid : hids_) {
        if (!hid->validate()) {
          logger::get_logger().warn("Refreshing hid_manager since a dangling human_interface_device is found. ({0})",
                                    hid->get_name_for_log());
          needs_refresh = true;
          break;
        }
      }
    }

    if (needs_refresh) {
      stop();
      start();
    }
  }

  boost::optional<registry_entry_id> find_registry_entry_id(IOHIDDeviceRef _Nonnull device) {
    auto it = registry_entry_ids_.find(device);
    if (it != std::end(registry_entry_ids_)) {
      return it->second;
    }
    return boost::none;
  }

  void device_matching_callback(IOHIDDeviceRef _Nonnull device) {
    if (!device) {
      return;
    }

    CFRetain(device);

    enqueue_to_dispatcher([this, device] {
      if (!device_detecting(device)) {
        goto finish;
      }

      if (auto registry_entry_id = iokit_utility::find_registry_entry_id(device)) {
        // iokit_utility::find_registry_entry_id will be failed in device_removal_callback at least on macOS 10.13.
        // Thus, we have to memory device and registry_entry_id correspondence by myself.
        registry_entry_ids_[device] = *registry_entry_id;

        // Skip if same device is already matched.
        // (Multiple usage device (e.g. usage::pointer and usage::mouse) will be matched twice.)

        std::shared_ptr<human_interface_device> hid;

        {
          std::lock_guard<std::mutex> lock(hids_mutex_);

          if (std::any_of(std::begin(hids_),
                          std::end(hids_),
                          [&](auto&& h) {
                            return *registry_entry_id == h->get_registry_entry_id();
                          })) {
            // logger::get_logger().info("registry_entry_id:{0} already exists", static_cast<uint64_t>(*registry_entry_id));
            goto finish;
          }

          hid = std::make_shared<human_interface_device>(device, *registry_entry_id);
          hids_.push_back(hid);
        }

        enqueue_to_dispatcher([this, hid] {
          device_detected(hid);
        });
      }

    finish:
      CFRelease(device);
    });
  }

  static void static_device_removal_callback(void* _Nullable context,
                                             IOReturn result,
                                             void* _Nullable sender,
                                             IOHIDDeviceRef _Nonnull device) {
    if (result != kIOReturnSuccess) {
      return;
    }

    auto self = static_cast<hid_manager*>(context);
    if (!self) {
      return;
    }

    self->device_removal_callback(device);
  }

  void device_removal_callback(IOHIDDeviceRef _Nonnull device) {
    if (!device) {
      return;
    }

    CFRetain(device);

    enqueue_to_dispatcher([this, device] {
      if (auto registry_entry_id = find_registry_entry_id(device)) {
        std::shared_ptr<human_interface_device> hid;

        {
          std::lock_guard<std::mutex> lock(hids_mutex_);

          auto it = std::find_if(std::begin(hids_),
                                 std::end(hids_),
                                 [&](auto&& h) {
                                   return *registry_entry_id == h->get_registry_entry_id();
                                 });
          if (it != hids_.end()) {
            hid = *it;
            hids_.erase(it);
          }
        }

        if (hid) {
          hid->set_removed();

          enqueue_to_dispatcher([this, hid] {
            device_removed(hid);
          });
        }

        // There might be multiple devices for one registry_entry_id.
        // (For example, keyboard and mouse combined device.)
        // And there is possibility that removal callback is not called with some device.
        //
        // For example, there are 3 devices for 1 registry_entry_id (4294974284).
        //   - device1 { device:0x7fb9d64078a0 => registry_entry_id:4294974284 }
        //   - device2 { device:0x7fb9d8301390 => registry_entry_id:4294974284 }
        //   - device3 { device:0x7fb9d830a630 => registry_entry_id:4294974284 }
        // And device_removal_callback are called only with device1 and device2.
        //
        // We should remove device1, device2 and device3 at the same time in order to deal with this case.

        for (auto it = std::begin(registry_entry_ids_); it != std::end(registry_entry_ids_);) {
          if (it->second == *registry_entry_id) {
            it = registry_entry_ids_.erase(it);
          } else {
            std::advance(it, 1);
          }
        }
      }

      CFRelease(device);
    });
  }

  std::vector<std::pair<hid_usage_page, hid_usage>> usage_pairs_;

  std::vector<std::shared_ptr<monitor::service_monitor::monitor>> service_monitors_;
  pqrs::dispatcher::extra::timer refresh_timer_;

  std::unordered_map<io_service_t, IOHIDDeviceRef> devices_;
  std::unordered_map<IOHIDDeviceRef, registry_entry_id> registry_entry_ids_;

  std::vector<std::shared_ptr<human_interface_device>> hids_;
  mutable std::mutex hids_mutex_;
}; // namespace krbn
} // namespace krbn
<|endoftext|>


<commit_before>/*
 * interpreter_interface.cpp
 *
 * Wrapper to allow routines to be called from fortran and/or provide a result
 * without requiring the caller to include interpreter.h or deal with namespaces
 * or classes.
 *
 *  Created on: Aug 10, 2013
 *      Author: basbas
 */

#include "config.h"
#include "sip_interface.h"
#include "interpreter.h"
#include "index_table.h"
#include "block.h"
#include <stdexcept>

#ifdef HAVE_MPI
#include "sip_mpi_attr.h"
#include "sip_server.h"
#endif // HAVE_MPI

#ifdef __cplusplus
extern "C" {
#endif

/**
 * Gets a currently active scalar value
 * @param name
 * @return
 */
double scalar_value(const char * name) {
	return sip::Interpreter::global_interpreter->scalar_value(std::string(name));
}

/**
 * Sets the value of a currently active scalar
 * @param name
 * @param value
 */
void set_scalar_value(const char * name, double value) {
	sip::Interpreter::global_interpreter->set_scalar_value(std::string(name),
			value);
}

/**
 * query existance of double constant from initialization data
 * @param cname [in]  Name of the constant
 * @return 0 for exists
 */
int query_scalar_constant(const char*cname) {
    try {
	sip::Interpreter::global_interpreter->sip_tables().setup_reader().predefined_scalar(
			std::string(cname));
	return 1;
    } catch(const std::out_of_range& oor) {
	return 0;
    }
}

/**
 * Get a double constant from initialization data
 * @param cname [in]  Name of the constant
 * @return Value of the constant
 */
double scalar_constant(const char*cname) {
	return sip::Interpreter::global_interpreter->sip_tables().setup_reader().predefined_scalar(
			std::string(cname));
}

/**
 * query integer constant from initialization data
 * @param cname [in]  Name of the constant
 * @return 0 for exists
 */
int query_int_constant(const char*cname) {
    try {
	sip::Interpreter::global_interpreter->sip_tables().setup_reader().predefined_int(
			std::string(cname));
	return 1;
    } catch(const std::out_of_range& oor) {
	return 0;
    }
}

/**
 * Get an integer constant from initialization data
 * @param cname [in]  Name of the constant
 * @return Value of the constant
 */
int int_constant(const char*cname) {
	return sip::Interpreter::global_interpreter->sip_tables().setup_reader().predefined_int(
			std::string(cname));
}

/**
 * Get predefined scalar array from initialization data
 * @param aname [in]  Name of the array
 * @param num_dims [out]
 * @param dims [out] array of extents
 * @param values [out] the data contained in the array
 */

void predefined_scalar_array(const char*aname, int& num_dims, int **dims,
		double **values) {
	try {
//		std::pair<int, std::pair<int *, double *> > a =
//				sip::Interpreter::global_interpreter->sip_tables().setup_reader().predef_arr_.at(
//						std::string(aname));
//		num_dims = a.first;
//		*dims = a.second.first;
//		*values = a.second.second;
		setup::PredefContigArray rankblock = sip::Interpreter::global_interpreter->sip_tables().setup_reader().predefined_contiguous_array(std::string(aname));
		num_dims = rankblock.first;
		sip::Block::BlockPtr block = rankblock.second;
		*dims = const_cast<sip::segment_size_array_t&>(block->shape().segment_sizes_);
		*values = block->get_data();
		return;
	} catch (const std::out_of_range& oor) {
		sip::check(false,
				"predefined array " + std::string(aname)
						+ " not in predefined array map\n");
		return;
	}
}

void scratch_array(int& num_elements, double **array){
    try{
        double * scratch = new double[num_elements]();  //initialize to zero
        *array = scratch;
    } catch (const std::bad_alloc& ba) {
        std::cerr << "Got bad alloc ! Memory being requestd : " << num_elements << std::endl;
        throw ba;
    }
}

void delete_scratch_array(double **array){
	delete [] *array;
}

void integer_scratch_array(int& num_elements, int **array){
	int * scratch = new int[num_elements]();  //initialize to zero
	*array = scratch;
}

void delete_integer_scratch_array(int **array){
	delete [] *array;
}

/**
 * Get predefined integer array from initialization data
 *
 * @param aname [in] Name of the array
 * @param [out] num_dims
 * @param [out] dims array of extents
 * @param [out] values 1-d array containing the data contained in the array
 */
void predefined_int_array(const char*aname, int& num_dims, int **dims,
		int **values) {
	try {
		setup::PredefIntArray a =
				sip::Interpreter::global_interpreter->sip_tables().setup_reader().predefined_integer_array(std::string(aname));
		//std::cout << "aname= " << aname << ", a=" << a.first << std::endl;
		num_dims = a.rank;
		*dims = a.dims;
		*values = a.data;
        /*
		std::cout << num_dims << "," << *dims << "," << *values << std::endl;
        std::cout<<"Dims :"<<std::endl;
        for(int i=0; i<num_dims; i++){
            std::cout<<(*dims)[i]<< " ";
        }
        std::cout<<std::endl<<"Values :"<<std::endl;
        for (int i=0; i< 10; i++){
            std::cout<<(*values)[i]<<" ";
        }
        */
		return;
	} catch (const std::out_of_range& oor) {
		sip::check(false,
				"predefined array " + std::string(aname)
						+ " not in predefined array map\n");
		return;
	}
}

/**
 * Gets the current SIALX line number begin executed
 * @return
 */
int current_line() {
	return sip::get_line_number();
}

//NOT YET IMPLEMENTED
//void get_config_info(const char* sialfile, const char* key, const char* value);

#ifdef __cplusplus
}
#endif

namespace sip {
std::string index_name_value(int slot) {
	return sip::Interpreter::global_interpreter->index_value_to_string(slot);
}
std::string array_name_value(int array_table_slot) {
	return sip::Interpreter::global_interpreter->array_name(array_table_slot);
}
int get_line_number() {
#ifdef HAVE_MPI
	sip::Interpreter *interpreter = sip::Interpreter::global_interpreter;
	sip::SIPServer * server = sip::SIPServer::global_sipserver;
	sip::SIPMPIAttr &mpiattr = sip::SIPMPIAttr::get_instance();
	if (mpiattr.is_worker()){
		if (interpreter != NULL)
			return interpreter->line_number();
		else
			return 0;
	} else {
		if (server != NULL)
			return server->last_seen_line();
		else
			return 0;
	}

#else	// HAVE_MPI
	if (sip::Interpreter::global_interpreter != NULL) {
		return sip::Interpreter::global_interpreter->line_number();
	} else
		return 0;
#endif	// HAVE_MPI
}

}
<commit_msg>updated comments to reflect correct return value<commit_after>/*
 * interpreter_interface.cpp
 *
 * Wrapper to allow routines to be called from fortran and/or provide a result
 * without requiring the caller to include interpreter.h or deal with namespaces
 * or classes.
 *
 *  Created on: Aug 10, 2013
 *      Author: basbas
 */

#include "config.h"
#include "sip_interface.h"
#include "interpreter.h"
#include "index_table.h"
#include "block.h"
#include <stdexcept>

#ifdef HAVE_MPI
#include "sip_mpi_attr.h"
#include "sip_server.h"
#endif // HAVE_MPI

#ifdef __cplusplus
extern "C" {
#endif

/**
 * Gets a currently active scalar value
 * @param name
 * @return
 */
double scalar_value(const char * name) {
	return sip::Interpreter::global_interpreter->scalar_value(std::string(name));
}

/**
 * Sets the value of a currently active scalar
 * @param name
 * @param value
 */
void set_scalar_value(const char * name, double value) {
	sip::Interpreter::global_interpreter->set_scalar_value(std::string(name),
			value);
}

/**
 * query existance of double constant from initialization data
 * @param cname [in]  Name of the constant
 * @return 1 for exists
 */
int query_scalar_constant(const char*cname) {
    try {
	sip::Interpreter::global_interpreter->sip_tables().setup_reader().predefined_scalar(
			std::string(cname));
	return 1;
    } catch(const std::out_of_range& oor) {
	return 0;
    }
}

/**
 * Get a double constant from initialization data
 * @param cname [in]  Name of the constant
 * @return Value of the constant
 */
double scalar_constant(const char*cname) {
	return sip::Interpreter::global_interpreter->sip_tables().setup_reader().predefined_scalar(
			std::string(cname));
}

/**
 * query integer constant from initialization data
 * @param cname [in]  Name of the constant
 * @return 1 for exists
 */
int query_int_constant(const char*cname) {
    try {
	sip::Interpreter::global_interpreter->sip_tables().setup_reader().predefined_int(
			std::string(cname));
	return 1;
    } catch(const std::out_of_range& oor) {
	return 0;
    }
}

/**
 * Get an integer constant from initialization data
 * @param cname [in]  Name of the constant
 * @return Value of the constant
 */
int int_constant(const char*cname) {
	return sip::Interpreter::global_interpreter->sip_tables().setup_reader().predefined_int(
			std::string(cname));
}

/**
 * Get predefined scalar array from initialization data
 * @param aname [in]  Name of the array
 * @param num_dims [out]
 * @param dims [out] array of extents
 * @param values [out] the data contained in the array
 */

void predefined_scalar_array(const char*aname, int& num_dims, int **dims,
		double **values) {
	try {
//		std::pair<int, std::pair<int *, double *> > a =
//				sip::Interpreter::global_interpreter->sip_tables().setup_reader().predef_arr_.at(
//						std::string(aname));
//		num_dims = a.first;
//		*dims = a.second.first;
//		*values = a.second.second;
		setup::PredefContigArray rankblock = sip::Interpreter::global_interpreter->sip_tables().setup_reader().predefined_contiguous_array(std::string(aname));
		num_dims = rankblock.first;
		sip::Block::BlockPtr block = rankblock.second;
		*dims = const_cast<sip::segment_size_array_t&>(block->shape().segment_sizes_);
		*values = block->get_data();
		return;
	} catch (const std::out_of_range& oor) {
		sip::check(false,
				"predefined array " + std::string(aname)
						+ " not in predefined array map\n");
		return;
	}
}

void scratch_array(int& num_elements, double **array){
    try{
        double * scratch = new double[num_elements]();  //initialize to zero
        *array = scratch;
    } catch (const std::bad_alloc& ba) {
        std::cerr << "Got bad alloc ! Memory being requestd : " << num_elements << std::endl;
        throw ba;
    }
}

void delete_scratch_array(double **array){
	delete [] *array;
}

void integer_scratch_array(int& num_elements, int **array){
	int * scratch = new int[num_elements]();  //initialize to zero
	*array = scratch;
}

void delete_integer_scratch_array(int **array){
	delete [] *array;
}

/**
 * Get predefined integer array from initialization data
 *
 * @param aname [in] Name of the array
 * @param [out] num_dims
 * @param [out] dims array of extents
 * @param [out] values 1-d array containing the data contained in the array
 */
void predefined_int_array(const char*aname, int& num_dims, int **dims,
		int **values) {
	try {
		setup::PredefIntArray a =
				sip::Interpreter::global_interpreter->sip_tables().setup_reader().predefined_integer_array(std::string(aname));
		//std::cout << "aname= " << aname << ", a=" << a.first << std::endl;
		num_dims = a.rank;
		*dims = a.dims;
		*values = a.data;
        /*
		std::cout << num_dims << "," << *dims << "," << *values << std::endl;
        std::cout<<"Dims :"<<std::endl;
        for(int i=0; i<num_dims; i++){
            std::cout<<(*dims)[i]<< " ";
        }
        std::cout<<std::endl<<"Values :"<<std::endl;
        for (int i=0; i< 10; i++){
            std::cout<<(*values)[i]<<" ";
        }
        */
		return;
	} catch (const std::out_of_range& oor) {
		sip::check(false,
				"predefined array " + std::string(aname)
						+ " not in predefined array map\n");
		return;
	}
}

/**
 * Gets the current SIALX line number begin executed
 * @return
 */
int current_line() {
	return sip::get_line_number();
}

//NOT YET IMPLEMENTED
//void get_config_info(const char* sialfile, const char* key, const char* value);

#ifdef __cplusplus
}
#endif

namespace sip {
std::string index_name_value(int slot) {
	return sip::Interpreter::global_interpreter->index_value_to_string(slot);
}
std::string array_name_value(int array_table_slot) {
	return sip::Interpreter::global_interpreter->array_name(array_table_slot);
}
int get_line_number() {
#ifdef HAVE_MPI
	sip::Interpreter *interpreter = sip::Interpreter::global_interpreter;
	sip::SIPServer * server = sip::SIPServer::global_sipserver;
	sip::SIPMPIAttr &mpiattr = sip::SIPMPIAttr::get_instance();
	if (mpiattr.is_worker()){
		if (interpreter != NULL)
			return interpreter->line_number();
		else
			return 0;
	} else {
		if (server != NULL)
			return server->last_seen_line();
		else
			return 0;
	}

#else	// HAVE_MPI
	if (sip::Interpreter::global_interpreter != NULL) {
		return sip::Interpreter::global_interpreter->line_number();
	} else
		return 0;
#endif	// HAVE_MPI
}

}
<|endoftext|>