mirror of https://github.com/oxen-io/lokinet
Merge remote-tracking branch 'origin/staging' into fix-libabyss-352
commit
71302ee48b
@ -0,0 +1,182 @@
|
||||
#include <util/printer.hpp>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
namespace
|
||||
{
|
||||
static void
|
||||
putSpaces(std::ostream& stream, size_t count)
|
||||
{
|
||||
// chunk n write
|
||||
static const char spaces[] = " ";
|
||||
static constexpr size_t size = sizeof(spaces) - 1;
|
||||
|
||||
while(size < count)
|
||||
{
|
||||
stream.write(spaces, size);
|
||||
count -= size;
|
||||
}
|
||||
|
||||
if(count > 0)
|
||||
{
|
||||
stream.write(spaces, count);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Printer::Printer(std::ostream& stream, int level, int spacesPerLevel)
|
||||
: m_stream(stream)
|
||||
, m_level(level < 0 ? -level : level)
|
||||
, m_levelPlusOne(m_level + 1)
|
||||
, m_suppressIndent(level < 0)
|
||||
, m_spaces(spacesPerLevel)
|
||||
{
|
||||
if(!m_suppressIndent)
|
||||
{
|
||||
const int absSpaces = m_spaces < 0 ? -m_spaces : m_spaces;
|
||||
putSpaces(m_stream, absSpaces * m_level);
|
||||
}
|
||||
|
||||
m_stream << '[';
|
||||
if(m_spaces >= 0)
|
||||
{
|
||||
m_stream << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
Printer::~Printer()
|
||||
{
|
||||
putSpaces(m_stream, m_spaces < 0 ? 1 : m_spaces * m_level);
|
||||
m_stream << ']';
|
||||
}
|
||||
|
||||
void
|
||||
Printer::printIndent() const
|
||||
{
|
||||
putSpaces(m_stream, m_spaces < 0 ? 1 : m_spaces * m_levelPlusOne);
|
||||
}
|
||||
|
||||
void
|
||||
Printer::printHexAddr(string_view name, const void* address) const
|
||||
{
|
||||
printIndent();
|
||||
m_stream << name << " = ";
|
||||
|
||||
PrintHelper::print(m_stream, address, -m_levelPlusOne, m_spaces);
|
||||
}
|
||||
void
|
||||
Printer::printHexAddr(const void* address) const
|
||||
{
|
||||
printIndent();
|
||||
PrintHelper::print(m_stream, address, -m_levelPlusOne, m_spaces);
|
||||
}
|
||||
|
||||
void
|
||||
PrintHelper::printType(std::ostream& stream, char value, int,
|
||||
int spacesPerLevel,
|
||||
traits::select::Case< std::is_fundamental >)
|
||||
{
|
||||
if(std::isprint(static_cast< unsigned char >(value)))
|
||||
{
|
||||
stream << "'" << value << "'";
|
||||
}
|
||||
else
|
||||
{
|
||||
#define PRINT_CONTROL_CHAR(x) \
|
||||
case x: \
|
||||
stream << #x; \
|
||||
break;
|
||||
|
||||
switch(value)
|
||||
{
|
||||
PRINT_CONTROL_CHAR('\n');
|
||||
PRINT_CONTROL_CHAR('\t');
|
||||
PRINT_CONTROL_CHAR('\0');
|
||||
default:
|
||||
{
|
||||
// Print as hex
|
||||
FormatFlagsGuard guard(stream);
|
||||
stream << std::hex << std::showbase
|
||||
<< static_cast< std::uintptr_t >(
|
||||
static_cast< unsigned char >(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(spacesPerLevel >= 0)
|
||||
{
|
||||
stream << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PrintHelper::printType(std::ostream& stream, bool value, int,
|
||||
int spacesPerLevel,
|
||||
traits::select::Case< std::is_fundamental >)
|
||||
{
|
||||
{
|
||||
FormatFlagsGuard guard(stream);
|
||||
stream << std::boolalpha << value;
|
||||
}
|
||||
|
||||
if(spacesPerLevel >= 0)
|
||||
{
|
||||
stream << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PrintHelper::printType(std::ostream& stream, const char* value, int,
|
||||
int spacesPerLevel,
|
||||
traits::select::Case< std::is_pointer >)
|
||||
{
|
||||
if(value == nullptr)
|
||||
{
|
||||
stream << "null";
|
||||
}
|
||||
else
|
||||
{
|
||||
stream << '"' << value << '"';
|
||||
}
|
||||
|
||||
if(spacesPerLevel >= 0)
|
||||
{
|
||||
stream << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PrintHelper::printType(std::ostream& stream, const void* value, int,
|
||||
int spacesPerLevel,
|
||||
traits::select::Case< std::is_pointer >)
|
||||
{
|
||||
if(value == nullptr)
|
||||
{
|
||||
stream << "null";
|
||||
}
|
||||
else
|
||||
{
|
||||
FormatFlagsGuard guard(stream);
|
||||
stream << std::hex << std::showbase
|
||||
<< reinterpret_cast< std::uintptr_t >(value);
|
||||
}
|
||||
|
||||
if(spacesPerLevel >= 0)
|
||||
{
|
||||
stream << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PrintHelper::printType(std::ostream& stream, const string_view& value, int,
|
||||
int spacesPerLevel,
|
||||
traits::select::Case< traits::is_container >)
|
||||
{
|
||||
stream << '"' << value << '"';
|
||||
if(spacesPerLevel >= 0)
|
||||
{
|
||||
stream << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace llarp
|
@ -0,0 +1,511 @@
|
||||
#ifndef LLARP_PRINTER_HPP
|
||||
#define LLARP_PRINTER_HPP
|
||||
|
||||
#include <util/string_view.hpp>
|
||||
#include <util/traits.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
/// simple guard class to restore stream flags.
|
||||
struct FormatFlagsGuard
|
||||
{
|
||||
std::ios_base& m_base;
|
||||
std::ios_base::fmtflags m_flags;
|
||||
|
||||
FormatFlagsGuard(std::ios_base& base) : m_base(base), m_flags(base.flags())
|
||||
{
|
||||
}
|
||||
|
||||
~FormatFlagsGuard()
|
||||
{
|
||||
m_base.flags(m_flags);
|
||||
}
|
||||
};
|
||||
|
||||
/// A general-purpose, stateful printer class.
|
||||
class Printer
|
||||
{
|
||||
private:
|
||||
std::ostream& m_stream;
|
||||
const int m_level;
|
||||
const int m_levelPlusOne;
|
||||
const bool m_suppressIndent;
|
||||
const int m_spaces;
|
||||
|
||||
public:
|
||||
template < typename Type >
|
||||
using PrintFunction =
|
||||
std::function< std::ostream&(std::ostream&, const Type&, int, int) >;
|
||||
|
||||
/// Create a printer.
|
||||
/// - level: the indentation level to use. If negative, suppress indentation
|
||||
/// on the first line.
|
||||
/// - spaces: the number of spaces to indent. If negative, put all output on
|
||||
/// a single line
|
||||
Printer(std::ostream& stream, int level, int spaces);
|
||||
|
||||
~Printer();
|
||||
|
||||
/// Print the given `data` to the stream, using the following strategies:
|
||||
/// - If `Type` is fundamental, print to stream
|
||||
/// - If `Type` is a C-style array (and not a char array), print each
|
||||
/// element to the stream
|
||||
/// - If `Type` is a `void *`, `const void *` or function pointer, and not
|
||||
/// null, print in hex format or print "null".
|
||||
/// - If `Type` is a `char *`, a `const char *`, a C-style char array, a
|
||||
/// `std::string` or `llarp::string_view` print the string wrapped in `"`.
|
||||
/// - If `Type` is a pointer type, print the pointer, followed by the value
|
||||
/// if not-null.
|
||||
/// - If `Type` is a pair/tuple type, print the elements of the tuple.
|
||||
/// - If `Type` has STL-style iterators, print all elements in the
|
||||
/// container.
|
||||
/// - If `Type` is any other type, call the `print` method on that type.
|
||||
template < typename Type >
|
||||
void
|
||||
printAttribute(string_view name, const Type& value) const;
|
||||
|
||||
template < typename Type >
|
||||
void
|
||||
printAttributeAsHex(string_view name, const Type& value) const;
|
||||
|
||||
template < typename InputIt >
|
||||
void
|
||||
printAttribute(string_view name, const InputIt& begin,
|
||||
const InputIt& end) const;
|
||||
|
||||
template < typename Type >
|
||||
void
|
||||
printValue(const Type& value) const;
|
||||
|
||||
template < typename InputIt >
|
||||
void
|
||||
printValue(const InputIt& begin, const InputIt& end) const;
|
||||
|
||||
template < typename Type >
|
||||
void
|
||||
printForeignAttribute(string_view name, const Type& value,
|
||||
const PrintFunction< Type >& printFunction) const;
|
||||
|
||||
template < typename Type >
|
||||
void
|
||||
printForeignValue(const Type& value,
|
||||
const PrintFunction< Type >& printFunction) const;
|
||||
|
||||
void
|
||||
printHexAddr(string_view name, const void* address) const;
|
||||
void
|
||||
printHexAddr(const void* address) const;
|
||||
|
||||
template < class Type >
|
||||
void
|
||||
printOrNull(string_view name, const Type& address) const;
|
||||
template < class Type >
|
||||
void
|
||||
printOrNull(const Type& address) const;
|
||||
|
||||
private:
|
||||
void
|
||||
printIndent() const;
|
||||
};
|
||||
|
||||
/// helper struct
|
||||
struct PrintHelper
|
||||
{
|
||||
template < typename Type >
|
||||
static void
|
||||
print(std::ostream& stream, const Type& value, int level, int spaces);
|
||||
|
||||
template < typename InputIt >
|
||||
static void
|
||||
print(std::ostream& stream, const InputIt& begin, const InputIt& end,
|
||||
int level, int spaces);
|
||||
|
||||
// Specialisations
|
||||
|
||||
// Fundamental types
|
||||
static void
|
||||
printType(std::ostream& stream, char value, int level, int spaces,
|
||||
traits::select::Case< std::is_fundamental >);
|
||||
|
||||
static void
|
||||
printType(std::ostream& stream, bool value, int level, int spaces,
|
||||
traits::select::Case< std::is_fundamental >);
|
||||
|
||||
template < typename Type >
|
||||
static void
|
||||
printType(std::ostream& stream, Type value, int level, int spaces,
|
||||
traits::select::Case< std::is_fundamental >);
|
||||
|
||||
template < typename Type >
|
||||
static void
|
||||
printType(std::ostream& stream, Type value, int level, int spaces,
|
||||
traits::select::Case< std::is_enum >);
|
||||
|
||||
// Function types
|
||||
template < typename Type >
|
||||
static void
|
||||
printType(std::ostream& stream, Type value, int level, int spaces,
|
||||
traits::select::Case< std::is_function >);
|
||||
|
||||
// Pointer types
|
||||
static void
|
||||
printType(std::ostream& stream, const char* value, int level, int spaces,
|
||||
traits::select::Case< std::is_pointer >);
|
||||
|
||||
static void
|
||||
printType(std::ostream& stream, const void* value, int level, int spaces,
|
||||
traits::select::Case< std::is_pointer >);
|
||||
|
||||
template < typename Type >
|
||||
static void
|
||||
printType(std::ostream& stream, const Type* value, int level, int spaces,
|
||||
traits::select::Case< std::is_pointer >);
|
||||
|
||||
template < typename Type >
|
||||
static void
|
||||
printType(std::ostream& stream, const Type* value, int level, int spaces,
|
||||
traits::select::Case< std::is_array >);
|
||||
|
||||
// Container types
|
||||
static void
|
||||
printType(std::ostream& stream, const std::string& value, int level,
|
||||
int spaces, traits::select::Case< traits::is_container >);
|
||||
|
||||
static void
|
||||
printType(std::ostream& stream, const string_view& value, int level,
|
||||
int spaces, traits::select::Case< traits::is_container >);
|
||||
|
||||
template < typename Type >
|
||||
static void
|
||||
printType(std::ostream& stream, const Type& value, int level, int spaces,
|
||||
traits::select::Case< traits::is_container >);
|
||||
|
||||
// Utility types
|
||||
template < typename Type1, typename Type2 >
|
||||
static void
|
||||
printType(std::ostream& stream, const std::pair< Type1, Type2 >& value,
|
||||
int level, int spaces, traits::select::Case<>);
|
||||
|
||||
template < typename... Types >
|
||||
static void
|
||||
printType(std::ostream& stream, const std::tuple< Types... >& value,
|
||||
int level, int spaces, traits::select::Case<>);
|
||||
|
||||
// Default type
|
||||
template < typename Type >
|
||||
static void
|
||||
printType(std::ostream& stream, const Type& value, int level, int spaces,
|
||||
traits::select::Case<>);
|
||||
};
|
||||
|
||||
template < typename Type >
|
||||
inline void
|
||||
Printer::printAttribute(string_view name, const Type& value) const
|
||||
{
|
||||
assert(!name.empty());
|
||||
printIndent();
|
||||
|
||||
m_stream << name << " = ";
|
||||
|
||||
PrintHelper::print(m_stream, value, -m_levelPlusOne, m_spaces);
|
||||
}
|
||||
|
||||
template < typename Type >
|
||||
inline void
|
||||
Printer::printAttributeAsHex(string_view name, const Type& value) const
|
||||
{
|
||||
static_assert(std::is_integral< Type >::value, "type should be integral");
|
||||
assert(!name.empty());
|
||||
printIndent();
|
||||
|
||||
m_stream << name << " = ";
|
||||
{
|
||||
FormatFlagsGuard guard(m_stream);
|
||||
m_stream << std::hex << value;
|
||||
}
|
||||
|
||||
if(m_spaces >= 0)
|
||||
{
|
||||
m_stream << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
template < typename InputIt >
|
||||
inline void
|
||||
Printer::printAttribute(string_view name, const InputIt& begin,
|
||||
const InputIt& end) const
|
||||
{
|
||||
assert(!name.empty());
|
||||
printIndent();
|
||||
|
||||
m_stream << name << " = ";
|
||||
|
||||
PrintHelper::print(m_stream, begin, end, -m_levelPlusOne, m_spaces);
|
||||
}
|
||||
|
||||
template < typename Type >
|
||||
inline void
|
||||
Printer::printValue(const Type& value) const
|
||||
{
|
||||
printIndent();
|
||||
|
||||
PrintHelper::print(m_stream, value, -m_levelPlusOne, m_spaces);
|
||||
}
|
||||
|
||||
template < typename InputIt >
|
||||
inline void
|
||||
Printer::printValue(const InputIt& begin, const InputIt& end) const
|
||||
{
|
||||
printIndent();
|
||||
|
||||
PrintHelper::print(m_stream, begin, end, -m_levelPlusOne, m_spaces);
|
||||
}
|
||||
|
||||
template < typename Type >
|
||||
inline void
|
||||
Printer::printForeignAttribute(
|
||||
string_view name, const Type& value,
|
||||
const PrintFunction< Type >& printFunction) const
|
||||
{
|
||||
assert(!name.empty());
|
||||
printIndent();
|
||||
|
||||
m_stream << name << " = ";
|
||||
|
||||
printFunction(m_stream, value, -m_levelPlusOne, m_spaces);
|
||||
}
|
||||
|
||||
template < typename Type >
|
||||
inline void
|
||||
Printer::printForeignValue(const Type& value,
|
||||
const PrintFunction< Type >& printFunction) const
|
||||
{
|
||||
printIndent();
|
||||
|
||||
printFunction(m_stream, value, -m_levelPlusOne, m_spaces);
|
||||
}
|
||||
|
||||
template < typename Type >
|
||||
inline void
|
||||
Printer::printOrNull(string_view name, const Type& address) const
|
||||
{
|
||||
assert(!name.empty());
|
||||
printIndent();
|
||||
|
||||
m_stream << name << " = ";
|
||||
|
||||
if(address == nullptr)
|
||||
{
|
||||
m_stream << "null";
|
||||
|
||||
if(m_spaces >= 0)
|
||||
{
|
||||
m_stream << '\n';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintHelper::print(m_stream, *address, -m_levelPlusOne, m_spaces);
|
||||
}
|
||||
}
|
||||
template < typename Type >
|
||||
inline void
|
||||
Printer::printOrNull(const Type& address) const
|
||||
{
|
||||
printIndent();
|
||||
|
||||
if(address == nullptr)
|
||||
{
|
||||
m_stream << "null";
|
||||
|
||||
if(m_spaces >= 0)
|
||||
{
|
||||
m_stream << '\n';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintHelper::print(m_stream, *address, -m_levelPlusOne, m_spaces);
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void
|
||||
Printer::printOrNull< const void* >(string_view name,
|
||||
const void* const& address) const
|
||||
{
|
||||
assert(!name.empty());
|
||||
printIndent();
|
||||
|
||||
m_stream << name << " = ";
|
||||
const void* temp = address;
|
||||
|
||||
PrintHelper::print(m_stream, temp, -m_levelPlusOne, m_spaces);
|
||||
}
|
||||
template <>
|
||||
inline void
|
||||
Printer::printOrNull< void* >(string_view name, void* const& address) const
|
||||
{
|
||||
const void* const& temp = address;
|
||||
|
||||
printOrNull(name, temp);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void
|
||||
Printer::printOrNull< const void* >(const void* const& address) const
|
||||
{
|
||||
printIndent();
|
||||
|
||||
const void* temp = address;
|
||||
|
||||
PrintHelper::print(m_stream, temp, -m_levelPlusOne, m_spaces);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void
|
||||
Printer::printOrNull< void* >(void* const& address) const
|
||||
{
|
||||
const void* const& temp = address;
|
||||
|
||||
printOrNull(temp);
|
||||
}
|
||||
|
||||
// Print Helper methods
|
||||
|
||||
template < typename InputIt >
|
||||
inline void
|
||||
PrintHelper::print(std::ostream& stream, const InputIt& begin,
|
||||
const InputIt& end, int level, int spaces)
|
||||
{
|
||||
Printer printer(stream, level, spaces);
|
||||
std::for_each(begin, end, [&](const auto& x) { printer.printValue(x); });
|
||||
}
|
||||
|
||||
template < typename Type >
|
||||
inline void
|
||||
PrintHelper::printType(std::ostream& stream, Type value, int, int spaces,
|
||||
traits::select::Case< std::is_fundamental >)
|
||||
{
|
||||
stream << value;
|
||||
if(spaces >= 0)
|
||||
{
|
||||
stream << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
template < typename Type >
|
||||
inline void
|
||||
PrintHelper::printType(std::ostream& stream, Type value, int, int spaces,
|
||||
traits::select::Case< std::is_enum >)
|
||||
{
|
||||
printType(stream, value, 0, spaces,
|
||||
traits::select::Case< std::is_fundamental >());
|
||||
}
|
||||
|
||||
template < typename Type >
|
||||
inline void
|
||||
PrintHelper::printType(std::ostream& stream, Type value, int level,
|
||||
int spaces, traits::select::Case< std::is_function >)
|
||||
{
|
||||
PrintHelper::print(stream, reinterpret_cast< const void* >(value), level,
|
||||
spaces);
|
||||
}
|
||||
|
||||
template < typename Type >
|
||||
inline void
|
||||
PrintHelper::printType(std::ostream& stream, const Type* value, int level,
|
||||
int spaces, traits::select::Case< std::is_pointer >)
|
||||
{
|
||||
printType(stream, static_cast< const void* >(value), level, -1,
|
||||
traits::select::Case< std::is_pointer >());
|
||||
if(value == nullptr)
|
||||
{
|
||||
if(spaces >= 0)
|
||||
{
|
||||
stream << '\n';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
stream << ' ';
|
||||
PrintHelper::print(stream, *value, level, spaces);
|
||||
}
|
||||
}
|
||||
|
||||
template < typename Type >
|
||||
inline void
|
||||
PrintHelper::printType(std::ostream& stream, const Type* value, int level,
|
||||
int spaces, traits::select::Case< std::is_array >)
|
||||
{
|
||||
printType(stream, value, level, spaces,
|
||||
traits::select::Case< std::is_pointer >());
|
||||
}
|
||||
|
||||
inline void
|
||||
PrintHelper::printType(std::ostream& stream, const std::string& value,
|
||||
int level, int spaces,
|
||||
traits::select::Case< traits::is_container >)
|
||||
{
|
||||
printType(stream, value.c_str(), level, spaces,
|
||||
traits::select::Case< std::is_pointer >());
|
||||
}
|
||||
|
||||
template < typename Type >
|
||||
inline void
|
||||
PrintHelper::printType(std::ostream& stream, const Type& value, int level,
|
||||
int spaces,
|
||||
traits::select::Case< traits::is_container >)
|
||||
{
|
||||
print(stream, value.begin(), value.end(), level, spaces);
|
||||
}
|
||||
|
||||
template < typename Type1, typename Type2 >
|
||||
inline void
|
||||
PrintHelper::printType(std::ostream& stream,
|
||||
const std::pair< Type1, Type2 >& value, int level,
|
||||
int spaces, traits::select::Case<>)
|
||||
{
|
||||
Printer print(stream, level, spaces);
|
||||
print.printValue(value.first);
|
||||
print.printValue(value.second);
|
||||
}
|
||||
|
||||
template < typename... Types >
|
||||
inline void
|
||||
PrintHelper::printType(std::ostream& stream,
|
||||
const std::tuple< Types... >& value, int level,
|
||||
int spaces, traits::select::Case<>)
|
||||
{
|
||||
Printer print(stream, level, spaces);
|
||||
traits::for_each_in_tuple(value,
|
||||
[&](const auto& x) { print.printValue(x); });
|
||||
}
|
||||
|
||||
template < typename Type >
|
||||
inline void
|
||||
PrintHelper::printType(std::ostream& stream, const Type& value, int level,
|
||||
int spaces, traits::select::Case<>)
|
||||
{
|
||||
value.print(stream, level, spaces);
|
||||
}
|
||||
|
||||
template < typename Type >
|
||||
inline void
|
||||
PrintHelper::print(std::ostream& stream, const Type& value, int level,
|
||||
int spaces)
|
||||
{
|
||||
using Selection =
|
||||
traits::select::Select< Type, std::is_fundamental, std::is_enum,
|
||||
std::is_function, std::is_pointer,
|
||||
std::is_array, traits::is_container >;
|
||||
|
||||
PrintHelper::printType(stream, value, level, spaces, Selection());
|
||||
}
|
||||
|
||||
} // namespace llarp
|
||||
|
||||
#endif
|
@ -0,0 +1 @@
|
||||
#include <util/traits.hpp>
|
@ -0,0 +1,198 @@
|
||||
#ifndef LLARP_TRAITS_HPP
|
||||
#define LLARP_TRAITS_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
namespace traits
|
||||
{
|
||||
/// Represents the empty type
|
||||
struct Bottom
|
||||
{
|
||||
};
|
||||
|
||||
/// C++17 compatibility. template pack
|
||||
template < class... >
|
||||
using void_t = void;
|
||||
|
||||
/// Type trait representing whether a type is an STL-style container
|
||||
template < typename T, typename _ = void >
|
||||
struct is_container : public std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
// We take that the container has begin, end and size methods to be a
|
||||
// container.
|
||||
// clang-format off
|
||||
template < typename T >
|
||||
struct is_container<
|
||||
T,
|
||||
std::conditional_t<
|
||||
false,
|
||||
void_t< typename T::value_type,
|
||||
typename T::size_type,
|
||||
typename T::iterator,
|
||||
typename T::const_iterator,
|
||||
decltype(std::declval<T>().size()),
|
||||
decltype(std::declval<T>().begin()),
|
||||
decltype(std::declval<T>().end()),
|
||||
decltype(std::declval<T>().cbegin()),
|
||||
decltype(std::declval<T>().cend()) >,
|
||||
void > > : public std::true_type
|
||||
{
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
namespace Switch
|
||||
{
|
||||
template < size_t Selector, typename... Types >
|
||||
struct Switch
|
||||
{
|
||||
using Type = Bottom;
|
||||
};
|
||||
|
||||
template < typename T, typename... Types >
|
||||
struct Switch< 0u, T, Types... >
|
||||
{
|
||||
using Type = T;
|
||||
};
|
||||
|
||||
template < size_t Selector, typename Tn, typename... Types >
|
||||
struct Switch< Selector, Tn, Types... >
|
||||
{
|
||||
using Type = typename Switch< Selector - 1, Types... >::Type;
|
||||
};
|
||||
|
||||
} // namespace Switch
|
||||
|
||||
namespace select
|
||||
{
|
||||
/// This provides a way to do a compile-type dispatch based on type traits
|
||||
|
||||
/// meta function which always returns false
|
||||
template < typename >
|
||||
class False : public std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
/// a case in the selection
|
||||
template < template < typename... > class Trait = False >
|
||||
class Case
|
||||
{
|
||||
public:
|
||||
template < typename Type >
|
||||
struct Selector : public Trait< Type >::type
|
||||
{
|
||||
};
|
||||
|
||||
using Type = Case;
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
|
||||
/// implementation helper
|
||||
template < typename T,
|
||||
template < typename... > class Trait1,
|
||||
template < typename... > class Trait2,
|
||||
template < typename... > class Trait3,
|
||||
template < typename... > class Trait4,
|
||||
template < typename... > class Trait5,
|
||||
template < typename... > class Trait6,
|
||||
template < typename... > class Trait7,
|
||||
template < typename... > class Trait8,
|
||||
template < typename... > class Trait9 >
|
||||
struct SelectHelper {
|
||||
enum {
|
||||
Selector = (
|
||||
Trait1<T>::value ? 1 :
|
||||
Trait2<T>::value ? 2 :
|
||||
Trait3<T>::value ? 3 :
|
||||
Trait4<T>::value ? 4 :
|
||||
Trait5<T>::value ? 5 :
|
||||
Trait6<T>::value ? 6 :
|
||||
Trait7<T>::value ? 7 :
|
||||
Trait8<T>::value ? 8 :
|
||||
Trait9<T>::value ? 9 : 0
|
||||
)
|
||||
};
|
||||
|
||||
using Type = typename Switch::Switch<
|
||||
Selector,
|
||||
Case<>,
|
||||
Case<Trait1>,
|
||||
Case<Trait2>,
|
||||
Case<Trait3>,
|
||||
Case<Trait4>,
|
||||
Case<Trait5>,
|
||||
Case<Trait6>,
|
||||
Case<Trait7>,
|
||||
Case<Trait8>,
|
||||
Case<Trait9>
|
||||
>::Type;
|
||||
};
|
||||
|
||||
template< typename Type,
|
||||
template < typename... > class Trait1,
|
||||
template < typename... > class Trait2 = False,
|
||||
template < typename... > class Trait3 = False,
|
||||
template < typename... > class Trait4 = False,
|
||||
template < typename... > class Trait5 = False,
|
||||
template < typename... > class Trait6 = False,
|
||||
template < typename... > class Trait7 = False,
|
||||
template < typename... > class Trait8 = False,
|
||||
template < typename... > class Trait9 = False >
|
||||
struct Select : public SelectHelper< Type,
|
||||
Trait1,
|
||||
Trait2,
|
||||
Trait3,
|
||||
Trait4,
|
||||
Trait5,
|
||||
Trait6,
|
||||
Trait7,
|
||||
Trait8,
|
||||
Trait9 >::Type
|
||||
{
|
||||
enum {
|
||||
Selector = SelectHelper< Type,
|
||||
Trait1,
|
||||
Trait2,
|
||||
Trait3,
|
||||
Trait4,
|
||||
Trait5,
|
||||
Trait6,
|
||||
Trait7,
|
||||
Trait8,
|
||||
Trait9 >::Selector
|
||||
};
|
||||
|
||||
using SelectorType = std::integral_constant<int, Selector>;
|
||||
};
|
||||
|
||||
// clang-format on
|
||||
} // namespace select
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template < typename T, typename F, size_t... Is >
|
||||
void
|
||||
for_each(T&& t, F f, std::index_sequence< Is... >)
|
||||
{
|
||||
auto l = {(f(std::get< Is >(t)), 0)...};
|
||||
(void)l;
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
template < typename... Ts, typename F >
|
||||
void
|
||||
for_each_in_tuple(std::tuple< Ts... > const& t, F f)
|
||||
{
|
||||
detail::for_each(t, f, std::make_index_sequence< sizeof...(Ts) >());
|
||||
}
|
||||
|
||||
} // namespace traits
|
||||
} // namespace llarp
|
||||
|
||||
#endif
|
@ -1,603 +0,0 @@
|
||||
/**
|
||||
* @file condition_variable.h
|
||||
* @brief std::condition_variable implementation for MinGW
|
||||
*
|
||||
* (c) 2013-2016 by Mega Limited, Auckland, New Zealand
|
||||
* @author Alexander Vassilev
|
||||
*
|
||||
* @copyright Simplified (2-clause) BSD License.
|
||||
* You should have received a copy of the license along with this
|
||||
* program.
|
||||
*
|
||||
* This code 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.
|
||||
* @note
|
||||
* This file may become part of the mingw-w64 runtime package. If/when this
|
||||
* happens, the appropriate license will be added, i.e. this code will become
|
||||
* dual-licensed, and the current BSD 2-clause license will stay.
|
||||
*/
|
||||
|
||||
#ifndef MINGW_CONDITIONAL_VARIABLE_H
|
||||
#define MINGW_CONDITIONAL_VARIABLE_H
|
||||
|
||||
#if !defined(__cplusplus) || (__cplusplus < 201103L)
|
||||
#error A C++11 compiler is required!
|
||||
#endif
|
||||
// Use the standard classes for std::, if available.
|
||||
#include <condition_variable>
|
||||
|
||||
#include <windows.h>
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <system_error>
|
||||
#include "mingw.mutex.h"
|
||||
#include "mingw.shared_mutex.h"
|
||||
|
||||
namespace mingw_stdthread
|
||||
{
|
||||
#if defined(__MINGW32__) && !defined(_GLIBCXX_HAS_GTHREADS)
|
||||
enum class cv_status
|
||||
{
|
||||
no_timeout,
|
||||
timeout
|
||||
};
|
||||
#else
|
||||
using std::cv_status;
|
||||
#endif
|
||||
namespace xp
|
||||
{
|
||||
// Include the XP-compatible condition_variable classes only if actually
|
||||
// compiling for XP. The XP-compatible classes are slower than the newer
|
||||
// versions, and depend on features not compatible with Windows Phone 8.
|
||||
#if(WINVER < _WIN32_WINNT_VISTA)
|
||||
class condition_variable_any
|
||||
{
|
||||
protected:
|
||||
recursive_mutex mMutex;
|
||||
std::atomic< int > mNumWaiters;
|
||||
HANDLE mSemaphore;
|
||||
HANDLE mWakeEvent;
|
||||
|
||||
public:
|
||||
typedef HANDLE native_handle_type;
|
||||
native_handle_type
|
||||
native_handle()
|
||||
{
|
||||
return mSemaphore;
|
||||
}
|
||||
condition_variable_any(const condition_variable_any&) = delete;
|
||||
condition_variable_any&
|
||||
operator=(const condition_variable_any&) = delete;
|
||||
condition_variable_any()
|
||||
: mMutex()
|
||||
, mNumWaiters(0)
|
||||
, mSemaphore(CreateSemaphore(NULL, 0, 0xFFFF, NULL))
|
||||
, mWakeEvent(CreateEvent(NULL, FALSE, FALSE, NULL))
|
||||
{
|
||||
}
|
||||
~condition_variable_any()
|
||||
{
|
||||
CloseHandle(mWakeEvent);
|
||||
CloseHandle(mSemaphore);
|
||||
}
|
||||
|
||||
protected:
|
||||
template < class M >
|
||||
bool
|
||||
wait_impl(M& lock, DWORD timeout)
|
||||
{
|
||||
{
|
||||
lock_guard< recursive_mutex > guard(mMutex);
|
||||
mNumWaiters++;
|
||||
}
|
||||
lock.unlock();
|
||||
DWORD ret = WaitForSingleObject(mSemaphore, timeout);
|
||||
|
||||
mNumWaiters--;
|
||||
SetEvent(mWakeEvent);
|
||||
lock.lock();
|
||||
if(ret == WAIT_OBJECT_0)
|
||||
return true;
|
||||
else if(ret == WAIT_TIMEOUT)
|
||||
return false;
|
||||
// 2 possible cases:
|
||||
// 1)The point in notify_all() where we determine the count to
|
||||
// increment the semaphore with has not been reached yet:
|
||||
// we just need to decrement mNumWaiters, but setting the event does not
|
||||
// hurt
|
||||
//
|
||||
// 2)Semaphore has just been released with mNumWaiters just before
|
||||
// we decremented it. This means that the semaphore count
|
||||
// after all waiters finish won't be 0 - because not all waiters
|
||||
// woke up by acquiring the semaphore - we woke up by a timeout.
|
||||
// The notify_all() must handle this grafecully
|
||||
//
|
||||
else
|
||||
{
|
||||
using namespace std;
|
||||
throw system_error(make_error_code(errc::protocol_error));
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
template < class M >
|
||||
void
|
||||
wait(M& lock)
|
||||
{
|
||||
wait_impl(lock, INFINITE);
|
||||
}
|
||||
template < class M, class Predicate >
|
||||
void
|
||||
wait(M& lock, Predicate pred)
|
||||
{
|
||||
while(!pred())
|
||||
{
|
||||
wait(lock);
|
||||
};
|
||||
}
|
||||
|
||||
void
|
||||
notify_all() noexcept
|
||||
{
|
||||
lock_guard< recursive_mutex > lock(
|
||||
mMutex); // block any further wait requests until all current
|
||||
// waiters are unblocked
|
||||
if(mNumWaiters.load() <= 0)
|
||||
return;
|
||||
|
||||
ReleaseSemaphore(mSemaphore, mNumWaiters, NULL);
|
||||
while(mNumWaiters > 0)
|
||||
{
|
||||
auto ret = WaitForSingleObject(mWakeEvent, 1000);
|
||||
if(ret == WAIT_FAILED || ret == WAIT_ABANDONED)
|
||||
std::terminate();
|
||||
}
|
||||
assert(mNumWaiters == 0);
|
||||
// in case some of the waiters timed out just after we released the
|
||||
// semaphore by mNumWaiters, it won't be zero now, because not all
|
||||
// waiters woke up by acquiring the semaphore. So we must zero the
|
||||
// semaphore before we accept waiters for the next event See _wait_impl
|
||||
// for details
|
||||
while(WaitForSingleObject(mSemaphore, 0) == WAIT_OBJECT_0)
|
||||
;
|
||||
}
|
||||
void
|
||||
notify_one() noexcept
|
||||
{
|
||||
lock_guard< recursive_mutex > lock(mMutex);
|
||||
int targetWaiters = mNumWaiters.load() - 1;
|
||||
if(targetWaiters <= -1)
|
||||
return;
|
||||
ReleaseSemaphore(mSemaphore, 1, NULL);
|
||||
while(mNumWaiters > targetWaiters)
|
||||
{
|
||||
auto ret = WaitForSingleObject(mWakeEvent, 1000);
|
||||
if(ret == WAIT_FAILED || ret == WAIT_ABANDONED)
|
||||
std::terminate();
|
||||
}
|
||||
assert(mNumWaiters == targetWaiters);
|
||||
}
|
||||
template < class M, class Rep, class Period >
|
||||
cv_status
|
||||
wait_for(M& lock, const std::chrono::duration< Rep, Period >& rel_time)
|
||||
{
|
||||
using namespace std::chrono;
|
||||
long long timeout = duration_cast< milliseconds >(rel_time).count();
|
||||
if(timeout < 0)
|
||||
timeout = 0;
|
||||
bool ret = wait_impl(lock, (DWORD)timeout);
|
||||
return ret ? cv_status::no_timeout : cv_status::timeout;
|
||||
}
|
||||
|
||||
template < class M, class Rep, class Period, class Predicate >
|
||||
bool
|
||||
wait_for(M& lock, const std::chrono::duration< Rep, Period >& rel_time,
|
||||
Predicate pred)
|
||||
{
|
||||
return wait_until(lock, std::chrono::steady_clock::now() + rel_time,
|
||||
pred);
|
||||
}
|
||||
template < class M, class Clock, class Duration >
|
||||
cv_status
|
||||
wait_until(M& lock,
|
||||
const std::chrono::time_point< Clock, Duration >& abs_time)
|
||||
{
|
||||
return wait_for(lock, abs_time - Clock::now());
|
||||
}
|
||||
template < class M, class Clock, class Duration, class Predicate >
|
||||
bool
|
||||
wait_until(M& lock,
|
||||
const std::chrono::time_point< Clock, Duration >& abs_time,
|
||||
Predicate pred)
|
||||
{
|
||||
while(!pred())
|
||||
{
|
||||
if(wait_until(lock, abs_time) == cv_status::timeout)
|
||||
{
|
||||
return pred();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
class condition_variable : protected condition_variable_any
|
||||
{
|
||||
protected:
|
||||
typedef condition_variable_any base;
|
||||
|
||||
public:
|
||||
using base::base;
|
||||
using base::native_handle;
|
||||
using base::native_handle_type;
|
||||
using base::notify_all;
|
||||
using base::notify_one;
|
||||
void
|
||||
wait(unique_lock< mutex >& lock)
|
||||
{
|
||||
base::wait(lock);
|
||||
}
|
||||
template < class Predicate >
|
||||
void
|
||||
wait(unique_lock< mutex >& lock, Predicate pred)
|
||||
{
|
||||
base::wait(lock, pred);
|
||||
}
|
||||
template < class Rep, class Period >
|
||||
cv_status
|
||||
wait_for(unique_lock< mutex >& lock,
|
||||
const std::chrono::duration< Rep, Period >& rel_time)
|
||||
{
|
||||
return base::wait_for(lock, rel_time);
|
||||
}
|
||||
template < class Rep, class Period, class Predicate >
|
||||
bool
|
||||
wait_for(unique_lock< mutex >& lock,
|
||||
const std::chrono::duration< Rep, Period >& rel_time,
|
||||
Predicate pred)
|
||||
{
|
||||
return base::wait_for(lock, rel_time, pred);
|
||||
}
|
||||
template < class Clock, class Duration >
|
||||
cv_status
|
||||
wait_until(unique_lock< mutex >& lock,
|
||||
const std::chrono::time_point< Clock, Duration >& abs_time)
|
||||
{
|
||||
return base::wait_until(lock, abs_time);
|
||||
}
|
||||
template < class Clock, class Duration, class Predicate >
|
||||
bool
|
||||
wait_until(unique_lock< mutex >& lock,
|
||||
const std::chrono::time_point< Clock, Duration >& abs_time,
|
||||
Predicate pred)
|
||||
{
|
||||
return base::wait_until(lock, abs_time, pred);
|
||||
}
|
||||
};
|
||||
#endif // Compiling for XP
|
||||
} // namespace xp
|
||||
|
||||
#if(WINVER >= _WIN32_WINNT_VISTA)
|
||||
namespace vista
|
||||
{
|
||||
// If compiling for Vista or higher, use the native condition variable.
|
||||
class condition_variable
|
||||
{
|
||||
protected:
|
||||
CONDITION_VARIABLE cvariable_;
|
||||
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
template < typename MTX >
|
||||
inline static void
|
||||
before_wait(MTX* pmutex)
|
||||
{
|
||||
pmutex->mOwnerThread.checkSetOwnerBeforeUnlock();
|
||||
}
|
||||
template < typename MTX >
|
||||
inline static void
|
||||
after_wait(MTX* pmutex)
|
||||
{
|
||||
pmutex->mOwnerThread.setOwnerAfterLock(GetCurrentThreadId());
|
||||
}
|
||||
#else
|
||||
inline static void
|
||||
before_wait(void*)
|
||||
{
|
||||
}
|
||||
inline static void
|
||||
after_wait(void*)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
wait_impl(unique_lock< xp::mutex >& lock, DWORD time)
|
||||
{
|
||||
static_assert(std::is_same< typename xp::mutex::native_handle_type,
|
||||
PCRITICAL_SECTION >::value,
|
||||
"Native Win32 condition variable requires std::mutex to \
|
||||
use native Win32 critical section objects.");
|
||||
xp::mutex* pmutex = lock.release();
|
||||
before_wait(pmutex);
|
||||
BOOL success = SleepConditionVariableCS(&cvariable_,
|
||||
pmutex->native_handle(), time);
|
||||
after_wait(pmutex);
|
||||
lock = unique_lock< xp::mutex >(*pmutex, adopt_lock);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool
|
||||
wait_unique(windows7::mutex* pmutex, DWORD time)
|
||||
{
|
||||
before_wait(pmutex);
|
||||
BOOL success = SleepConditionVariableSRW(
|
||||
native_handle(), pmutex->native_handle(), time, 0);
|
||||
after_wait(pmutex);
|
||||
return success;
|
||||
}
|
||||
bool
|
||||
wait_impl(unique_lock< windows7::mutex >& lock, DWORD time)
|
||||
{
|
||||
windows7::mutex* pmutex = lock.release();
|
||||
bool success = wait_unique(pmutex, time);
|
||||
lock = unique_lock< windows7::mutex >(*pmutex, adopt_lock);
|
||||
return success;
|
||||
}
|
||||
|
||||
public:
|
||||
typedef PCONDITION_VARIABLE native_handle_type;
|
||||
native_handle_type
|
||||
native_handle(void)
|
||||
{
|
||||
return &cvariable_;
|
||||
}
|
||||
|
||||
condition_variable(void) : cvariable_()
|
||||
{
|
||||
InitializeConditionVariable(&cvariable_);
|
||||
}
|
||||
|
||||
~condition_variable(void) = default;
|
||||
|
||||
condition_variable(const condition_variable&) = delete;
|
||||
condition_variable&
|
||||
operator=(const condition_variable&) = delete;
|
||||
|
||||
void
|
||||
notify_one(void) noexcept
|
||||
{
|
||||
WakeConditionVariable(&cvariable_);
|
||||
}
|
||||
|
||||
void
|
||||
notify_all(void) noexcept
|
||||
{
|
||||
WakeAllConditionVariable(&cvariable_);
|
||||
}
|
||||
|
||||
void
|
||||
wait(unique_lock< mutex >& lock)
|
||||
{
|
||||
wait_impl(lock, INFINITE);
|
||||
}
|
||||
|
||||
template < class Predicate >
|
||||
void
|
||||
wait(unique_lock< mutex >& lock, Predicate pred)
|
||||
{
|
||||
while(!pred())
|
||||
wait(lock);
|
||||
}
|
||||
|
||||
template < class Rep, class Period >
|
||||
cv_status
|
||||
wait_for(unique_lock< mutex >& lock,
|
||||
const std::chrono::duration< Rep, Period >& rel_time)
|
||||
{
|
||||
using namespace std::chrono;
|
||||
auto time = duration_cast< milliseconds >(rel_time).count();
|
||||
if(time < 0)
|
||||
time = 0;
|
||||
bool result = wait_impl(lock, static_cast< DWORD >(time));
|
||||
return result ? cv_status::no_timeout : cv_status::timeout;
|
||||
}
|
||||
|
||||
template < class Rep, class Period, class Predicate >
|
||||
bool
|
||||
wait_for(unique_lock< mutex >& lock,
|
||||
const std::chrono::duration< Rep, Period >& rel_time,
|
||||
Predicate pred)
|
||||
{
|
||||
return wait_until(lock, std::chrono::steady_clock::now() + rel_time,
|
||||
std::move(pred));
|
||||
}
|
||||
template < class Clock, class Duration >
|
||||
cv_status
|
||||
wait_until(unique_lock< mutex >& lock,
|
||||
const std::chrono::time_point< Clock, Duration >& abs_time)
|
||||
{
|
||||
return wait_for(lock, abs_time - Clock::now());
|
||||
}
|
||||
template < class Clock, class Duration, class Predicate >
|
||||
bool
|
||||
wait_until(unique_lock< mutex >& lock,
|
||||
const std::chrono::time_point< Clock, Duration >& abs_time,
|
||||
Predicate pred)
|
||||
{
|
||||
while(!pred())
|
||||
{
|
||||
if(wait_until(lock, abs_time) == cv_status::timeout)
|
||||
{
|
||||
return pred();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class condition_variable_any : protected condition_variable
|
||||
{
|
||||
protected:
|
||||
typedef condition_variable base;
|
||||
typedef windows7::shared_mutex native_shared_mutex;
|
||||
|
||||
// When available, the SRW-based mutexes should be faster than the
|
||||
// CriticalSection-based mutexes. Only try_lock will be unavailable in
|
||||
// Vista, and try_lock is not used by condition_variable_any.
|
||||
windows7::mutex internal_mutex_;
|
||||
|
||||
template < class L >
|
||||
bool
|
||||
wait_impl(L& lock, DWORD time)
|
||||
{
|
||||
unique_lock< decltype(internal_mutex_) > internal_lock(internal_mutex_);
|
||||
lock.unlock();
|
||||
bool success = base::wait_impl(internal_lock, time);
|
||||
lock.lock();
|
||||
return success;
|
||||
}
|
||||
// If the lock happens to be called on a native Windows mutex, skip any
|
||||
// extra
|
||||
// contention.
|
||||
inline bool
|
||||
wait_impl(unique_lock< mutex >& lock, DWORD time)
|
||||
{
|
||||
return base::wait_impl(lock, time);
|
||||
}
|
||||
// Some shared_mutex functionality is available even in Vista, but it's
|
||||
// not
|
||||
// until Windows 7 that a full implementation is natively possible. The
|
||||
// class itself is defined, with missing features, at the Vista feature
|
||||
// level.
|
||||
static_assert(CONDITION_VARIABLE_LOCKMODE_SHARED != 0,
|
||||
"The flag \
|
||||
CONDITION_VARIABLE_LOCKMODE_SHARED is not defined as expected. The value for \
|
||||
exclusive mode is unknown (not specified by Microsoft Dev Center), but assumed \
|
||||
to be 0. There is a conflict with CONDITION_VARIABLE_LOCKMODE_SHARED.");
|
||||
//#if (WINVER >= _WIN32_WINNT_VISTA)
|
||||
bool
|
||||
wait_impl(unique_lock< native_shared_mutex >& lock, DWORD time)
|
||||
{
|
||||
native_shared_mutex* pmutex = lock.release();
|
||||
bool success = wait_unique(pmutex, time);
|
||||
lock = unique_lock< native_shared_mutex >(*pmutex, adopt_lock);
|
||||
return success;
|
||||
}
|
||||
bool
|
||||
wait_impl(shared_lock< native_shared_mutex >& lock, DWORD time)
|
||||
{
|
||||
native_shared_mutex* pmutex = lock.release();
|
||||
BOOL success = SleepConditionVariableSRW(
|
||||
base::native_handle(), pmutex->native_handle(), time,
|
||||
CONDITION_VARIABLE_LOCKMODE_SHARED);
|
||||
lock = shared_lock< native_shared_mutex >(*pmutex, adopt_lock);
|
||||
return success;
|
||||
}
|
||||
//#endif
|
||||
public:
|
||||
typedef typename base::native_handle_type native_handle_type;
|
||||
using base::native_handle;
|
||||
|
||||
condition_variable_any(void) : base(), internal_mutex_()
|
||||
{
|
||||
}
|
||||
|
||||
~condition_variable_any(void) = default;
|
||||
|
||||
using base::notify_all;
|
||||
using base::notify_one;
|
||||
|
||||
template < class L >
|
||||
void
|
||||
wait(L& lock)
|
||||
{
|
||||
wait_impl(lock, INFINITE);
|
||||
}
|
||||
|
||||
template < class L, class Predicate >
|
||||
void
|
||||
wait(L& lock, Predicate pred)
|
||||
{
|
||||
while(!pred())
|
||||
wait(lock);
|
||||
}
|
||||
|
||||
template < class L, class Rep, class Period >
|
||||
cv_status
|
||||
wait_for(L& lock, const std::chrono::duration< Rep, Period >& period)
|
||||
{
|
||||
using namespace std::chrono;
|
||||
auto time = duration_cast< milliseconds >(period).count();
|
||||
if(time < 0)
|
||||
time = 0;
|
||||
bool result = wait_impl(lock, static_cast< DWORD >(time));
|
||||
return result ? cv_status::no_timeout : cv_status::timeout;
|
||||
}
|
||||
|
||||
template < class L, class Rep, class Period, class Predicate >
|
||||
bool
|
||||
wait_for(L& lock, const std::chrono::duration< Rep, Period >& period,
|
||||
Predicate pred)
|
||||
{
|
||||
return wait_until(lock, std::chrono::steady_clock::now() + period,
|
||||
std::move(pred));
|
||||
}
|
||||
template < class L, class Clock, class Duration >
|
||||
cv_status
|
||||
wait_until(L& lock,
|
||||
const std::chrono::time_point< Clock, Duration >& abs_time)
|
||||
{
|
||||
return wait_for(lock, abs_time - Clock::now());
|
||||
}
|
||||
template < class L, class Clock, class Duration, class Predicate >
|
||||
bool
|
||||
wait_until(L& lock,
|
||||
const std::chrono::time_point< Clock, Duration >& abs_time,
|
||||
Predicate pred)
|
||||
{
|
||||
while(!pred())
|
||||
{
|
||||
if(wait_until(lock, abs_time) == cv_status::timeout)
|
||||
{
|
||||
return pred();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
} // Namespace vista
|
||||
#endif
|
||||
#if WINVER < 0x0600
|
||||
using xp::condition_variable;
|
||||
using xp::condition_variable_any;
|
||||
#else
|
||||
using vista::condition_variable;
|
||||
using vista::condition_variable_any;
|
||||
#endif
|
||||
} // Namespace mingw_stdthread
|
||||
|
||||
// Push objects into std, but only if they are not already there.
|
||||
namespace std
|
||||
{
|
||||
// Because of quirks of the compiler, the common "using namespace std;"
|
||||
// directive would flatten the namespaces and introduce ambiguity where there
|
||||
// was none. Direct specification (std::), however, would be unaffected.
|
||||
// Take the safe option, and include only in the presence of MinGW's win32
|
||||
// implementation.
|
||||
#if defined(__MINGW32__) && !defined(_GLIBCXX_HAS_GTHREADS)
|
||||
using mingw_stdthread::condition_variable;
|
||||
using mingw_stdthread::condition_variable_any;
|
||||
using mingw_stdthread::cv_status;
|
||||
#elif !defined(MINGW_STDTHREAD_REDUNDANCY_WARNING) // Skip repetition
|
||||
#define MINGW_STDTHREAD_REDUNDANCY_WARNING
|
||||
#pragma message \
|
||||
"This version of MinGW seems to include a win32 port of\
|
||||
pthreads, and probably already has C++11 std threading classes implemented,\
|
||||
based on pthreads. These classes, found in namespace std, are not overridden\
|
||||
by the mingw-std-thread library. If you would still like to use this\
|
||||
implementation (as it is more lightweight), use the classes provided in\
|
||||
namespace mingw_stdthread."
|
||||
#endif
|
||||
} // namespace std
|
||||
#endif // MINGW_CONDITIONAL_VARIABLE_H
|
@ -1,525 +0,0 @@
|
||||
/**
|
||||
* @file mingw.mutex.h
|
||||
* @brief std::mutex et al implementation for MinGW
|
||||
** (c) 2013-2016 by Mega Limited, Auckland, New Zealand
|
||||
* @author Alexander Vassilev
|
||||
*
|
||||
* @copyright Simplified (2-clause) BSD License.
|
||||
* You should have received a copy of the license along with this
|
||||
* program.
|
||||
*
|
||||
* This code 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.
|
||||
* @note
|
||||
* This file may become part of the mingw-w64 runtime package. If/when this
|
||||
*happens, the appropriate license will be added, i.e. this code will become
|
||||
*dual-licensed, and the current BSD 2-clause license will stay.
|
||||
*/
|
||||
|
||||
#ifndef WIN32STDMUTEX_H
|
||||
#define WIN32STDMUTEX_H
|
||||
#if defined(RPI)
|
||||
#error this should not be set
|
||||
#endif
|
||||
|
||||
#if !defined(__cplusplus) || (__cplusplus < 201103L)
|
||||
#error A C++11 compiler is required!
|
||||
#endif
|
||||
// Recursion checks on non-recursive locks have some performance penalty, and
|
||||
// the C++ standard does not mandate them. The user might want to explicitly
|
||||
// enable or disable such checks. If the user has no preference, enable such
|
||||
// checks in debug builds, but not in release builds.
|
||||
#ifdef STDMUTEX_RECURSION_CHECKS
|
||||
#elif defined(NDEBUG)
|
||||
#define STDMUTEX_RECURSION_CHECKS 0
|
||||
#else
|
||||
#define STDMUTEX_RECURSION_CHECKS 1
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <mutex> //need for call_once()
|
||||
#include <system_error>
|
||||
|
||||
// Need for yield in spinlock and the implementation of invoke
|
||||
#include "mingw.thread.h"
|
||||
|
||||
namespace mingw_stdthread
|
||||
{
|
||||
// The _NonRecursive class has mechanisms that do not play nice with direct
|
||||
// manipulation of the native handle. This forward declaration is part of
|
||||
// a friend class declaration.
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
namespace vista
|
||||
{
|
||||
class condition_variable;
|
||||
}
|
||||
#endif
|
||||
// To make this namespace equivalent to the thread-related subset of std,
|
||||
// pull in the classes and class templates supplied by std but not by this
|
||||
// implementation.
|
||||
using std::adopt_lock;
|
||||
using std::adopt_lock_t;
|
||||
using std::defer_lock;
|
||||
using std::defer_lock_t;
|
||||
using std::lock_guard;
|
||||
using std::try_to_lock;
|
||||
using std::try_to_lock_t;
|
||||
using std::unique_lock;
|
||||
|
||||
class recursive_mutex
|
||||
{
|
||||
CRITICAL_SECTION mHandle;
|
||||
|
||||
public:
|
||||
typedef LPCRITICAL_SECTION native_handle_type;
|
||||
native_handle_type
|
||||
native_handle()
|
||||
{
|
||||
return &mHandle;
|
||||
}
|
||||
recursive_mutex() noexcept : mHandle()
|
||||
{
|
||||
InitializeCriticalSection(&mHandle);
|
||||
}
|
||||
recursive_mutex(const recursive_mutex&) = delete;
|
||||
recursive_mutex&
|
||||
operator=(const recursive_mutex&) = delete;
|
||||
~recursive_mutex() noexcept
|
||||
{
|
||||
DeleteCriticalSection(&mHandle);
|
||||
}
|
||||
void
|
||||
lock()
|
||||
{
|
||||
EnterCriticalSection(&mHandle);
|
||||
}
|
||||
void
|
||||
unlock()
|
||||
{
|
||||
LeaveCriticalSection(&mHandle);
|
||||
}
|
||||
bool
|
||||
try_lock()
|
||||
{
|
||||
return (TryEnterCriticalSection(&mHandle) != 0);
|
||||
}
|
||||
};
|
||||
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
struct _OwnerThread
|
||||
{
|
||||
// If this is to be read before locking, then the owner-thread variable
|
||||
// must
|
||||
// be atomic to prevent a torn read from spuriously causing errors.
|
||||
std::atomic< DWORD > mOwnerThread;
|
||||
constexpr _OwnerThread() noexcept : mOwnerThread(0)
|
||||
{
|
||||
}
|
||||
static void
|
||||
on_deadlock(void)
|
||||
{
|
||||
using namespace std;
|
||||
fprintf(stderr,
|
||||
"FATAL: Recursive locking of non-recursive mutex\
|
||||
detected. Throwing system exception\n");
|
||||
fflush(stderr);
|
||||
throw system_error(make_error_code(errc::resource_deadlock_would_occur));
|
||||
}
|
||||
DWORD
|
||||
checkOwnerBeforeLock() const
|
||||
{
|
||||
DWORD self = GetCurrentThreadId();
|
||||
if(mOwnerThread.load(std::memory_order_relaxed) == self)
|
||||
on_deadlock();
|
||||
return self;
|
||||
}
|
||||
void
|
||||
setOwnerAfterLock(DWORD id)
|
||||
{
|
||||
mOwnerThread.store(id, std::memory_order_relaxed);
|
||||
}
|
||||
void
|
||||
checkSetOwnerBeforeUnlock()
|
||||
{
|
||||
DWORD self = GetCurrentThreadId();
|
||||
if(mOwnerThread.load(std::memory_order_relaxed) != self)
|
||||
on_deadlock();
|
||||
mOwnerThread.store(0, std::memory_order_relaxed);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
// Though the Slim Reader-Writer (SRW) locks used here are not complete until
|
||||
// Windows 7, implementing partial functionality in Vista will simplify the
|
||||
// interaction with condition variables.
|
||||
#if defined(_WIN32) && (WINVER >= _WIN32_WINNT_VISTA)
|
||||
namespace windows7
|
||||
{
|
||||
class mutex
|
||||
{
|
||||
SRWLOCK mHandle;
|
||||
// Track locking thread for error checking.
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
friend class vista::condition_variable;
|
||||
_OwnerThread mOwnerThread;
|
||||
#endif
|
||||
public:
|
||||
typedef PSRWLOCK native_handle_type;
|
||||
constexpr mutex() noexcept
|
||||
: mHandle(SRWLOCK_INIT)
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
, mOwnerThread()
|
||||
#endif
|
||||
{
|
||||
}
|
||||
mutex(const mutex&) = delete;
|
||||
mutex&
|
||||
operator=(const mutex&) = delete;
|
||||
void
|
||||
lock(void)
|
||||
{
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
DWORD self = mOwnerThread.checkOwnerBeforeLock();
|
||||
#endif
|
||||
AcquireSRWLockExclusive(&mHandle);
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
mOwnerThread.setOwnerAfterLock(self);
|
||||
#endif
|
||||
}
|
||||
void
|
||||
unlock(void)
|
||||
{
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
mOwnerThread.checkSetOwnerBeforeUnlock();
|
||||
#endif
|
||||
ReleaseSRWLockExclusive(&mHandle);
|
||||
}
|
||||
// TryAcquireSRW functions are a Windows 7 feature.
|
||||
#if(WINVER >= _WIN32_WINNT_WIN7)
|
||||
bool
|
||||
try_lock(void)
|
||||
{
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
DWORD self = mOwnerThread.checkOwnerBeforeLock();
|
||||
#endif
|
||||
BOOL ret = TryAcquireSRWLockExclusive(&mHandle);
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
if(ret)
|
||||
mOwnerThread.setOwnerAfterLock(self);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
native_handle_type
|
||||
native_handle(void)
|
||||
{
|
||||
return &mHandle;
|
||||
}
|
||||
};
|
||||
} // Namespace windows7
|
||||
#endif // Compiling for Vista
|
||||
namespace xp
|
||||
{
|
||||
class mutex
|
||||
{
|
||||
CRITICAL_SECTION mHandle;
|
||||
std::atomic_uchar mState;
|
||||
// Track locking thread for error checking.
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
friend class vista::condition_variable;
|
||||
_OwnerThread mOwnerThread;
|
||||
#endif
|
||||
public:
|
||||
typedef PCRITICAL_SECTION native_handle_type;
|
||||
constexpr mutex() noexcept
|
||||
: mHandle()
|
||||
, mState(2)
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
, mOwnerThread()
|
||||
#endif
|
||||
{
|
||||
}
|
||||
mutex(const mutex&) = delete;
|
||||
mutex&
|
||||
operator=(const mutex&) = delete;
|
||||
~mutex() noexcept
|
||||
{
|
||||
DeleteCriticalSection(&mHandle);
|
||||
}
|
||||
void
|
||||
lock(void)
|
||||
{
|
||||
unsigned char state = mState.load(std::memory_order_acquire);
|
||||
while(state)
|
||||
{
|
||||
if((state == 2)
|
||||
&& mState.compare_exchange_weak(state, 1,
|
||||
std::memory_order_acquire))
|
||||
{
|
||||
InitializeCriticalSection(&mHandle);
|
||||
mState.store(0, std::memory_order_release);
|
||||
break;
|
||||
}
|
||||
if(state == 1)
|
||||
{
|
||||
this_thread::yield();
|
||||
state = mState.load(std::memory_order_acquire);
|
||||
}
|
||||
}
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
DWORD self = mOwnerThread.checkOwnerBeforeLock();
|
||||
#endif
|
||||
EnterCriticalSection(&mHandle);
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
mOwnerThread.setOwnerAfterLock(self);
|
||||
#endif
|
||||
}
|
||||
void
|
||||
unlock(void)
|
||||
{
|
||||
assert(mState.load(std::memory_order_relaxed) == 0);
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
mOwnerThread.checkSetOwnerBeforeUnlock();
|
||||
#endif
|
||||
LeaveCriticalSection(&mHandle);
|
||||
}
|
||||
bool
|
||||
try_lock(void)
|
||||
{
|
||||
unsigned char state = mState.load(std::memory_order_acquire);
|
||||
if((state == 2)
|
||||
&& mState.compare_exchange_strong(state, 1,
|
||||
std::memory_order_acquire))
|
||||
{
|
||||
InitializeCriticalSection(&mHandle);
|
||||
mState.store(0, std::memory_order_release);
|
||||
}
|
||||
if(state == 1)
|
||||
return false;
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
DWORD self = mOwnerThread.checkOwnerBeforeLock();
|
||||
#endif
|
||||
BOOL ret = TryEnterCriticalSection(&mHandle);
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
if(ret)
|
||||
mOwnerThread.setOwnerAfterLock(self);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
native_handle_type
|
||||
native_handle(void)
|
||||
{
|
||||
return &mHandle;
|
||||
}
|
||||
};
|
||||
} // namespace xp
|
||||
#if(WINVER >= _WIN32_WINNT_WIN7)
|
||||
using windows7::mutex;
|
||||
#else
|
||||
using xp::mutex;
|
||||
#endif
|
||||
|
||||
class recursive_timed_mutex
|
||||
{
|
||||
bool
|
||||
try_lock_internal(DWORD ms)
|
||||
{
|
||||
DWORD ret = WaitForSingleObject(mHandle, ms);
|
||||
using namespace std;
|
||||
switch(ret)
|
||||
{
|
||||
case WAIT_TIMEOUT:
|
||||
return false;
|
||||
case WAIT_OBJECT_0:
|
||||
return true;
|
||||
case WAIT_ABANDONED:
|
||||
throw system_error(make_error_code(errc::owner_dead));
|
||||
default:
|
||||
throw system_error(make_error_code(errc::protocol_error));
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
HANDLE mHandle;
|
||||
// Track locking thread for error checking of non-recursive timed_mutex. For
|
||||
// standard compliance, this must be defined in same class and at the same
|
||||
// access-control level as every other variable in the timed_mutex.
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
friend class vista::condition_variable;
|
||||
_OwnerThread mOwnerThread;
|
||||
#endif
|
||||
public:
|
||||
typedef HANDLE native_handle_type;
|
||||
native_handle_type
|
||||
native_handle() const
|
||||
{
|
||||
return mHandle;
|
||||
}
|
||||
recursive_timed_mutex(const recursive_timed_mutex&) = delete;
|
||||
recursive_timed_mutex&
|
||||
operator=(const recursive_timed_mutex&) = delete;
|
||||
recursive_timed_mutex()
|
||||
: mHandle(CreateMutex(NULL, FALSE, NULL))
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
, mOwnerThread()
|
||||
#endif
|
||||
{
|
||||
}
|
||||
~recursive_timed_mutex()
|
||||
{
|
||||
CloseHandle(mHandle);
|
||||
}
|
||||
void
|
||||
lock()
|
||||
{
|
||||
try_lock_internal(INFINITE);
|
||||
}
|
||||
void
|
||||
unlock()
|
||||
{
|
||||
using namespace std;
|
||||
if(!ReleaseMutex(mHandle))
|
||||
throw system_error(
|
||||
make_error_code(errc::resource_deadlock_would_occur));
|
||||
}
|
||||
bool
|
||||
try_lock()
|
||||
{
|
||||
return try_lock_internal(0);
|
||||
}
|
||||
template < class Rep, class Period >
|
||||
bool
|
||||
try_lock_for(const std::chrono::duration< Rep, Period >& dur)
|
||||
{
|
||||
using namespace std::chrono;
|
||||
DWORD timeout = (DWORD)duration_cast< milliseconds >(dur).count();
|
||||
return try_lock_internal(timeout);
|
||||
}
|
||||
template < class Clock, class Duration >
|
||||
bool
|
||||
try_lock_until(
|
||||
const std::chrono::time_point< Clock, Duration >& timeout_time)
|
||||
{
|
||||
return try_lock_for(timeout_time - Clock::now());
|
||||
}
|
||||
};
|
||||
|
||||
// Override if, and only if, it is necessary for error-checking.
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
class timed_mutex : recursive_timed_mutex
|
||||
{
|
||||
public:
|
||||
timed_mutex(const timed_mutex&) = delete;
|
||||
timed_mutex&
|
||||
operator=(const timed_mutex&) = delete;
|
||||
void
|
||||
lock()
|
||||
{
|
||||
DWORD self = mOwnerThread.checkOwnerBeforeLock();
|
||||
recursive_timed_mutex::lock();
|
||||
mOwnerThread.setOwnerAfterLock(self);
|
||||
}
|
||||
void
|
||||
unlock()
|
||||
{
|
||||
mOwnerThread.checkSetOwnerBeforeUnlock();
|
||||
recursive_timed_mutex::unlock();
|
||||
}
|
||||
template < class Rep, class Period >
|
||||
bool
|
||||
try_lock_for(const std::chrono::duration< Rep, Period >& dur)
|
||||
{
|
||||
DWORD self = mOwnerThread.checkOwnerBeforeLock();
|
||||
bool ret = recursive_timed_mutex::try_lock_for(dur);
|
||||
if(ret)
|
||||
mOwnerThread.setOwnerAfterLock(self);
|
||||
return ret;
|
||||
}
|
||||
template < class Clock, class Duration >
|
||||
bool
|
||||
try_lock_until(
|
||||
const std::chrono::time_point< Clock, Duration >& timeout_time)
|
||||
{
|
||||
return try_lock_for(timeout_time - Clock::now());
|
||||
}
|
||||
bool
|
||||
try_lock()
|
||||
{
|
||||
return try_lock_for(std::chrono::milliseconds(0));
|
||||
}
|
||||
};
|
||||
#else
|
||||
typedef recursive_timed_mutex timed_mutex;
|
||||
#endif
|
||||
|
||||
class once_flag
|
||||
{
|
||||
// When available, the SRW-based mutexes should be faster than the
|
||||
// CriticalSection-based mutexes. Only try_lock will be unavailable in Vista,
|
||||
// and try_lock is not used by once_flag.
|
||||
#if(_WIN32_WINNT == _WIN32_WINNT_VISTA)
|
||||
windows7::mutex mMutex;
|
||||
#else
|
||||
mutex mMutex;
|
||||
#endif
|
||||
std::atomic_bool mHasRun;
|
||||
once_flag(const once_flag&) = delete;
|
||||
once_flag&
|
||||
operator=(const once_flag&) = delete;
|
||||
template < class Callable, class... Args >
|
||||
friend void
|
||||
call_once(once_flag& once, Callable&& f, Args&&... args);
|
||||
|
||||
public:
|
||||
constexpr once_flag() noexcept : mMutex(), mHasRun(false)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template < class Callable, class... Args >
|
||||
void
|
||||
call_once(once_flag& flag, Callable&& func, Args&&... args)
|
||||
{
|
||||
if(flag.mHasRun.load(std::memory_order_acquire))
|
||||
return;
|
||||
lock_guard< decltype(flag.mMutex) > lock(flag.mMutex);
|
||||
if(flag.mHasRun.load(std::memory_order_acquire))
|
||||
return;
|
||||
detail::invoke(std::forward< Callable >(func),
|
||||
std::forward< Args >(args)...);
|
||||
flag.mHasRun.store(true, std::memory_order_release);
|
||||
}
|
||||
} // Namespace mingw_stdthread
|
||||
|
||||
// Push objects into std, but only if they are not already there.
|
||||
namespace std
|
||||
{
|
||||
// Because of quirks of the compiler, the common "using namespace std;"
|
||||
// directive would flatten the namespaces and introduce ambiguity where there
|
||||
// was none. Direct specification (std::), however, would be unaffected.
|
||||
// Take the safe option, and include only in the presence of MinGW's win32
|
||||
// implementation.
|
||||
#if defined(__MINGW32__) && !defined(_GLIBCXX_HAS_GTHREADS)
|
||||
using mingw_stdthread::call_once;
|
||||
using mingw_stdthread::mutex;
|
||||
using mingw_stdthread::once_flag;
|
||||
using mingw_stdthread::recursive_mutex;
|
||||
using mingw_stdthread::recursive_timed_mutex;
|
||||
using mingw_stdthread::timed_mutex;
|
||||
#elif !defined(MINGW_STDTHREAD_REDUNDANCY_WARNING) // Skip repetition
|
||||
#define MINGW_STDTHREAD_REDUNDANCY_WARNING
|
||||
#pragma message \
|
||||
"This version of MinGW seems to include a win32 port of\
|
||||
pthreads, and probably already has C++11 std threading classes implemented,\
|
||||
based on pthreads. These classes, found in namespace std, are not overridden\
|
||||
by the mingw-std-thread library. If you would still like to use this\
|
||||
implementation (as it is more lightweight), use the classes provided in\
|
||||
namespace mingw_stdthread."
|
||||
#endif
|
||||
} // namespace std
|
||||
#endif // WIN32STDMUTEX_H
|
@ -1,536 +0,0 @@
|
||||
/// \file mingw.shared_mutex.h
|
||||
/// \brief Standard-compliant shared_mutex for MinGW
|
||||
///
|
||||
/// (c) 2017 by Nathaniel J. McClatchey, Athens OH, United States
|
||||
/// \author Nathaniel J. McClatchey
|
||||
///
|
||||
/// \copyright Simplified (2-clause) BSD License.
|
||||
///
|
||||
/// \note This file may become part of the mingw-w64 runtime package. If/when
|
||||
/// this happens, the appropriate license will be added, i.e. this code will
|
||||
/// become dual-licensed, and the current BSD 2-clause license will stay.
|
||||
/// \note Target Windows version is determined by WINVER, which is determined in
|
||||
/// <windows.h> from _WIN32_WINNT, which can itself be set by the user.
|
||||
|
||||
// Notes on the namespaces:
|
||||
// - The implementation can be accessed directly in the namespace
|
||||
// mingw_stdthread.
|
||||
// - Objects will be brought into namespace std by a using directive. This
|
||||
// will cause objects declared in std (such as MinGW's implementation) to
|
||||
// hide this implementation's definitions.
|
||||
// - To avoid poluting the namespace with implementation details, all objects
|
||||
// to be pushed into std will be placed in mingw_stdthread::visible.
|
||||
// The end result is that if MinGW supplies an object, it is automatically
|
||||
// used. If MinGW does not supply an object, this implementation's version will
|
||||
// instead be used.
|
||||
|
||||
#ifndef MINGW_SHARED_MUTEX_H_
|
||||
#define MINGW_SHARED_MUTEX_H_
|
||||
|
||||
#if !defined(__cplusplus) || (__cplusplus < 201103L)
|
||||
#error A C++11 compiler is required!
|
||||
#endif
|
||||
|
||||
#include <cassert>
|
||||
|
||||
// Use MinGW's shared_lock class template, if it's available. Requires C++14.
|
||||
// If unavailable (eg. because this library is being used in C++11), then an
|
||||
// implementation of shared_lock is provided by this header.
|
||||
#if(__cplusplus >= 201402L)
|
||||
#include <shared_mutex>
|
||||
#endif
|
||||
// For defer_lock_t, adopt_lock_t, and try_to_lock_t
|
||||
#include "mingw.mutex.h"
|
||||
|
||||
// For descriptive errors.
|
||||
#include <system_error>
|
||||
// Implementing a shared_mutex without OS support will require atomic read-
|
||||
// modify-write capacity.
|
||||
#include <atomic>
|
||||
// For timing in shared_lock and shared_timed_mutex.
|
||||
#include <chrono>
|
||||
|
||||
// For this_thread::yield.
|
||||
#include "mingw.thread.h"
|
||||
|
||||
// Might be able to use native Slim Reader-Writer (SRW) locks.
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
namespace mingw_stdthread
|
||||
{
|
||||
// Define a portable atomics-based shared_mutex
|
||||
namespace portable
|
||||
{
|
||||
class shared_mutex
|
||||
{
|
||||
typedef uint_fast16_t counter_type;
|
||||
std::atomic< counter_type > mCounter;
|
||||
static constexpr counter_type kWriteBit = 1
|
||||
<< (sizeof(counter_type) * CHAR_BIT - 1);
|
||||
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
// Runtime checker for verifying owner threads. Note: Exclusive mode
|
||||
// only.
|
||||
_OwnerThread mOwnerThread;
|
||||
#endif
|
||||
public:
|
||||
typedef shared_mutex* native_handle_type;
|
||||
|
||||
shared_mutex()
|
||||
: mCounter(0)
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
, mOwnerThread()
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
// No form of copying or moving should be allowed.
|
||||
shared_mutex(const shared_mutex&) = delete;
|
||||
shared_mutex&
|
||||
operator=(const shared_mutex&) = delete;
|
||||
|
||||
~shared_mutex()
|
||||
{
|
||||
// Terminate if someone tries to destroy an owned mutex.
|
||||
assert(mCounter.load(std::memory_order_relaxed) == 0);
|
||||
}
|
||||
|
||||
void
|
||||
lock_shared(void)
|
||||
{
|
||||
counter_type expected = mCounter.load(std::memory_order_relaxed);
|
||||
do
|
||||
{
|
||||
// Delay if writing or if too many readers are attempting to read.
|
||||
if(expected >= kWriteBit - 1)
|
||||
{
|
||||
using namespace std;
|
||||
using namespace this_thread;
|
||||
yield();
|
||||
expected = mCounter.load(std::memory_order_relaxed);
|
||||
continue;
|
||||
}
|
||||
if(mCounter.compare_exchange_weak(expected, expected + 1,
|
||||
std::memory_order_acquire,
|
||||
std::memory_order_relaxed))
|
||||
break;
|
||||
} while(true);
|
||||
}
|
||||
|
||||
bool
|
||||
try_lock_shared(void)
|
||||
{
|
||||
counter_type expected =
|
||||
mCounter.load(std::memory_order_relaxed) & (~kWriteBit);
|
||||
if(expected + 1 == kWriteBit)
|
||||
return false;
|
||||
else
|
||||
return mCounter.compare_exchange_strong(expected, expected + 1,
|
||||
std::memory_order_acquire,
|
||||
std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void
|
||||
unlock_shared(void)
|
||||
{
|
||||
using namespace std;
|
||||
#ifndef NDEBUG
|
||||
if(!(mCounter.fetch_sub(1, memory_order_release) & (~kWriteBit)))
|
||||
throw system_error(make_error_code(errc::operation_not_permitted));
|
||||
#else
|
||||
mCounter.fetch_sub(1, memory_order_release);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Behavior is undefined if a lock was previously acquired.
|
||||
void
|
||||
lock(void)
|
||||
{
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
DWORD self = mOwnerThread.checkOwnerBeforeLock();
|
||||
#endif
|
||||
using namespace std;
|
||||
// Might be able to use relaxed memory order...
|
||||
// Wait for the write-lock to be unlocked, then claim the write slot.
|
||||
counter_type current;
|
||||
while(
|
||||
(current = mCounter.fetch_or(kWriteBit, std::memory_order_acquire))
|
||||
& kWriteBit)
|
||||
this_thread::yield();
|
||||
// Wait for readers to finish up.
|
||||
while(current != kWriteBit)
|
||||
{
|
||||
this_thread::yield();
|
||||
current = mCounter.load(std::memory_order_acquire);
|
||||
}
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
mOwnerThread.setOwnerAfterLock(self);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
try_lock(void)
|
||||
{
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
DWORD self = mOwnerThread.checkOwnerBeforeLock();
|
||||
#endif
|
||||
counter_type expected = 0;
|
||||
bool ret = mCounter.compare_exchange_strong(expected, kWriteBit,
|
||||
std::memory_order_acquire,
|
||||
std::memory_order_relaxed);
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
if(ret)
|
||||
mOwnerThread.setOwnerAfterLock(self);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
unlock(void)
|
||||
{
|
||||
#if STDMUTEX_RECURSION_CHECKS
|
||||
mOwnerThread.checkSetOwnerBeforeUnlock();
|
||||
#endif
|
||||
using namespace std;
|
||||
#ifndef NDEBUG
|
||||
if(mCounter.load(memory_order_relaxed) != kWriteBit)
|
||||
throw system_error(make_error_code(errc::operation_not_permitted));
|
||||
#endif
|
||||
mCounter.store(0, memory_order_release);
|
||||
}
|
||||
|
||||
native_handle_type
|
||||
native_handle(void)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
} // Namespace portable
|
||||
|
||||
// The native shared_mutex implementation primarily uses features of Windows
|
||||
// Vista, but the features used for try_lock and try_lock_shared were not
|
||||
// introduced until Windows 7. To allow limited use while compiling for Vista,
|
||||
// I define the class without try_* functions in that case.
|
||||
// Only fully-featured implementations will be placed into namespace std.
|
||||
#if defined(_WIN32) && (WINVER >= _WIN32_WINNT_VISTA)
|
||||
namespace vista
|
||||
{
|
||||
class condition_variable_any;
|
||||
}
|
||||
|
||||
namespace windows7
|
||||
{
|
||||
// We already #include "mingw.mutex.h". May as well reduce redundancy.
|
||||
class shared_mutex : windows7::mutex
|
||||
{
|
||||
// Allow condition_variable_any (and only condition_variable_any) to
|
||||
// treat a
|
||||
// shared_mutex as its base class.
|
||||
friend class vista::condition_variable_any;
|
||||
|
||||
public:
|
||||
using windows7::mutex::lock;
|
||||
using windows7::mutex::native_handle;
|
||||
using windows7::mutex::native_handle_type;
|
||||
using windows7::mutex::unlock;
|
||||
|
||||
void
|
||||
lock_shared(void)
|
||||
{
|
||||
AcquireSRWLockShared(native_handle());
|
||||
}
|
||||
|
||||
void
|
||||
unlock_shared(void)
|
||||
{
|
||||
ReleaseSRWLockShared(native_handle());
|
||||
}
|
||||
|
||||
// TryAcquireSRW functions are a Windows 7 feature.
|
||||
#if(WINVER >= _WIN32_WINNT_WIN7)
|
||||
bool
|
||||
try_lock_shared(void)
|
||||
{
|
||||
return TryAcquireSRWLockShared(native_handle()) != 0;
|
||||
}
|
||||
|
||||
using windows7::mutex::try_lock;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // Namespace windows7
|
||||
#endif // Compiling for Vista
|
||||
#if(defined(_WIN32) && (WINVER >= _WIN32_WINNT_WIN7))
|
||||
using windows7::shared_mutex;
|
||||
#else
|
||||
using portable::shared_mutex;
|
||||
#endif
|
||||
|
||||
class shared_timed_mutex : shared_mutex
|
||||
{
|
||||
typedef shared_mutex Base;
|
||||
|
||||
public:
|
||||
using Base::lock;
|
||||
using Base::lock_shared;
|
||||
using Base::try_lock;
|
||||
using Base::try_lock_shared;
|
||||
using Base::unlock;
|
||||
using Base::unlock_shared;
|
||||
|
||||
template < class Clock, class Duration >
|
||||
bool
|
||||
try_lock_until(const std::chrono::time_point< Clock, Duration >& cutoff)
|
||||
{
|
||||
do
|
||||
{
|
||||
if(try_lock())
|
||||
return true;
|
||||
} while(std::chrono::steady_clock::now() < cutoff);
|
||||
return false;
|
||||
}
|
||||
|
||||
template < class Rep, class Period >
|
||||
bool
|
||||
try_lock_for(const std::chrono::duration< Rep, Period >& rel_time)
|
||||
{
|
||||
return try_lock_until(std::chrono::steady_clock::now() + rel_time);
|
||||
}
|
||||
|
||||
template < class Clock, class Duration >
|
||||
bool
|
||||
try_lock_shared_until(
|
||||
const std::chrono::time_point< Clock, Duration >& cutoff)
|
||||
{
|
||||
do
|
||||
{
|
||||
if(try_lock_shared())
|
||||
return true;
|
||||
} while(std::chrono::steady_clock::now() < cutoff);
|
||||
return false;
|
||||
}
|
||||
|
||||
template < class Rep, class Period >
|
||||
bool
|
||||
try_lock_shared_for(const std::chrono::duration< Rep, Period >& rel_time)
|
||||
{
|
||||
return try_lock_shared_until(std::chrono::steady_clock::now() + rel_time);
|
||||
}
|
||||
};
|
||||
|
||||
#if __cplusplus >= 201402L
|
||||
using std::shared_lock;
|
||||
#else
|
||||
// If not supplied by shared_mutex (eg. because C++14 is not supported), I
|
||||
// supply the various helper classes that the header should have defined.
|
||||
template < class Mutex >
|
||||
class shared_lock
|
||||
{
|
||||
Mutex* mMutex;
|
||||
bool mOwns;
|
||||
// Reduce code redundancy
|
||||
void
|
||||
verify_lockable(void)
|
||||
{
|
||||
using namespace std;
|
||||
if(mMutex == nullptr)
|
||||
throw system_error(make_error_code(errc::operation_not_permitted));
|
||||
if(mOwns)
|
||||
throw system_error(
|
||||
make_error_code(errc::resource_deadlock_would_occur));
|
||||
}
|
||||
|
||||
public:
|
||||
typedef Mutex mutex_type;
|
||||
|
||||
shared_lock(void) noexcept : mMutex(nullptr), mOwns(false)
|
||||
{
|
||||
}
|
||||
|
||||
shared_lock(shared_lock< Mutex >&& other) noexcept
|
||||
: mMutex(other.mutex_), mOwns(other.owns_)
|
||||
{
|
||||
other.mMutex = nullptr;
|
||||
other.mOwns = false;
|
||||
}
|
||||
|
||||
explicit shared_lock(mutex_type& m) : mMutex(&m), mOwns(true)
|
||||
{
|
||||
mMutex->lock_shared();
|
||||
}
|
||||
|
||||
shared_lock(mutex_type& m, defer_lock_t) noexcept : mMutex(&m), mOwns(false)
|
||||
{
|
||||
}
|
||||
|
||||
shared_lock(mutex_type& m, adopt_lock_t) : mMutex(&m), mOwns(true)
|
||||
{
|
||||
}
|
||||
|
||||
shared_lock(mutex_type& m, try_to_lock_t)
|
||||
: mMutex(&m), mOwns(m.try_lock_shared())
|
||||
{
|
||||
}
|
||||
|
||||
template < class Rep, class Period >
|
||||
shared_lock(mutex_type& m,
|
||||
const std::chrono::duration< Rep, Period >& timeout_duration)
|
||||
: mMutex(&m), mOwns(m.try_lock_shared_for(timeout_duration))
|
||||
{
|
||||
}
|
||||
|
||||
template < class Clock, class Duration >
|
||||
shared_lock(mutex_type& m,
|
||||
const std::chrono::time_point< Clock, Duration >& timeout_time)
|
||||
: mMutex(&m), mOwns(m.try_lock_shared_until(timeout_time))
|
||||
{
|
||||
}
|
||||
|
||||
shared_lock&
|
||||
operator=(shared_lock< Mutex >&& other) noexcept
|
||||
{
|
||||
if(&other != this)
|
||||
{
|
||||
if(mOwns)
|
||||
mMutex->unlock_shared();
|
||||
mMutex = other.mMutex;
|
||||
mOwns = other.mOwns;
|
||||
other.mMutex = nullptr;
|
||||
other.mOwns = false;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~shared_lock(void)
|
||||
{
|
||||
if(mOwns)
|
||||
mMutex->unlock_shared();
|
||||
}
|
||||
|
||||
shared_lock(const shared_lock< Mutex >&) = delete;
|
||||
shared_lock&
|
||||
operator=(const shared_lock< Mutex >&) = delete;
|
||||
|
||||
// Shared locking
|
||||
void
|
||||
lock(void)
|
||||
{
|
||||
verify_lockable();
|
||||
mMutex->lock_shared();
|
||||
mOwns = true;
|
||||
}
|
||||
|
||||
bool
|
||||
try_lock(void)
|
||||
{
|
||||
verify_lockable();
|
||||
mOwns = mMutex->try_lock_shared();
|
||||
return mOwns;
|
||||
}
|
||||
|
||||
template < class Clock, class Duration >
|
||||
bool
|
||||
try_lock_until(const std::chrono::time_point< Clock, Duration >& cutoff)
|
||||
{
|
||||
verify_lockable();
|
||||
do
|
||||
{
|
||||
mOwns = mMutex->try_lock_shared();
|
||||
if(mOwns)
|
||||
return mOwns;
|
||||
} while(std::chrono::steady_clock::now() < cutoff);
|
||||
return false;
|
||||
}
|
||||
|
||||
template < class Rep, class Period >
|
||||
bool
|
||||
try_lock_for(const std::chrono::duration< Rep, Period >& rel_time)
|
||||
{
|
||||
return try_lock_until(std::chrono::steady_clock::now() + rel_time);
|
||||
}
|
||||
|
||||
void
|
||||
unlock(void)
|
||||
{
|
||||
using namespace std;
|
||||
if(!mOwns)
|
||||
throw system_error(make_error_code(errc::operation_not_permitted));
|
||||
mMutex->unlock_shared();
|
||||
mOwns = false;
|
||||
}
|
||||
|
||||
// Modifiers
|
||||
void
|
||||
swap(shared_lock< Mutex >& other) noexcept
|
||||
{
|
||||
using namespace std;
|
||||
swap(mMutex, other.mMutex);
|
||||
swap(mOwns, other.mOwns);
|
||||
}
|
||||
|
||||
mutex_type*
|
||||
release(void) noexcept
|
||||
{
|
||||
mutex_type* ptr = mMutex;
|
||||
mMutex = nullptr;
|
||||
mOwns = false;
|
||||
return ptr;
|
||||
}
|
||||
// Observers
|
||||
mutex_type*
|
||||
mutex(void) const noexcept
|
||||
{
|
||||
return mMutex;
|
||||
}
|
||||
|
||||
bool
|
||||
owns_lock(void) const noexcept
|
||||
{
|
||||
return mOwns;
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept
|
||||
{
|
||||
return owns_lock();
|
||||
}
|
||||
};
|
||||
|
||||
template < class Mutex >
|
||||
void
|
||||
swap(shared_lock< Mutex >& lhs, shared_lock< Mutex >& rhs) noexcept
|
||||
{
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
#endif // C++11
|
||||
} // Namespace mingw_stdthread
|
||||
|
||||
namespace std
|
||||
{
|
||||
// Because of quirks of the compiler, the common "using namespace std;"
|
||||
// directive would flatten the namespaces and introduce ambiguity where there
|
||||
// was none. Direct specification (std::), however, would be unaffected.
|
||||
// Take the safe option, and include only in the presence of MinGW's win32
|
||||
// implementation.
|
||||
#if(__cplusplus < 201703L) \
|
||||
|| (defined(__MINGW32__) && !defined(_GLIBCXX_HAS_GTHREADS))
|
||||
using mingw_stdthread::shared_mutex;
|
||||
#endif
|
||||
#if(__cplusplus < 201402L) \
|
||||
|| (defined(__MINGW32__) && !defined(_GLIBCXX_HAS_GTHREADS))
|
||||
using mingw_stdthread::shared_lock;
|
||||
using mingw_stdthread::shared_timed_mutex;
|
||||
#elif !defined(MINGW_STDTHREAD_REDUNDANCY_WARNING) // Skip repetition
|
||||
#define MINGW_STDTHREAD_REDUNDANCY_WARNING
|
||||
#pragma message \
|
||||
"This version of MinGW seems to include a win32 port of\
|
||||
pthreads, and probably already has C++ std threading classes implemented,\
|
||||
based on pthreads. These classes, found in namespace std, are not overridden\
|
||||
by the mingw-std-thread library. If you would still like to use this\
|
||||
implementation (as it is more lightweight), use the classes provided in\
|
||||
namespace mingw_stdthread."
|
||||
#endif
|
||||
} // Namespace std
|
||||
#endif // MINGW_SHARED_MUTEX_H_
|
@ -1,475 +0,0 @@
|
||||
/**
|
||||
* @file mingw.thread.h
|
||||
* @brief std::thread implementation for MinGW
|
||||
* (c) 2013-2016 by Mega Limited, Auckland, New Zealand
|
||||
* @author Alexander Vassilev
|
||||
*
|
||||
* @copyright Simplified (2-clause) BSD License.
|
||||
* You should have received a copy of the license along with this
|
||||
* program.
|
||||
*
|
||||
* This code 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.
|
||||
* @note
|
||||
* This file may become part of the mingw-w64 runtime package. If/when this
|
||||
* happens, the appropriate license will be added, i.e. this code will become
|
||||
* dual-licensed, and the current BSD 2-clause license will stay.
|
||||
*/
|
||||
|
||||
#ifndef WIN32STDTHREAD_H
|
||||
#define WIN32STDTHREAD_H
|
||||
|
||||
#if !defined(__cplusplus) || (__cplusplus < 201103L)
|
||||
#error A C++11 compiler is required!
|
||||
#endif
|
||||
|
||||
// Use the standard classes for std::, if available.
|
||||
#include <thread>
|
||||
|
||||
#include <process.h>
|
||||
#include <windows.h>
|
||||
#include <cerrno>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <system_error>
|
||||
#include <type_traits>
|
||||
|
||||
#ifndef NDEBUG
|
||||
#include <cstdio>
|
||||
#endif
|
||||
|
||||
// instead of INVALID_HANDLE_VALUE _beginthreadex returns 0
|
||||
#define _STD_THREAD_INVALID_HANDLE 0
|
||||
namespace mingw_stdthread
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
// For compatibility, implement std::invoke for C++11 and C++14
|
||||
#if __cplusplus < 201703L
|
||||
template < bool PMemFunc, bool PMemData >
|
||||
struct Invoker
|
||||
{
|
||||
template < class F, class... Args >
|
||||
inline static typename std::result_of< F(Args...) >::type
|
||||
invoke(F&& f, Args&&... args)
|
||||
{
|
||||
return std::forward< F >(f)(std::forward< Args >(args)...);
|
||||
}
|
||||
};
|
||||
template < bool >
|
||||
struct InvokerHelper;
|
||||
|
||||
template <>
|
||||
struct InvokerHelper< false >
|
||||
{
|
||||
template < class T1 >
|
||||
inline static auto
|
||||
get(T1&& t1) -> decltype(*std::forward< T1 >(t1))
|
||||
{
|
||||
return *std::forward< T1 >(t1);
|
||||
}
|
||||
|
||||
template < class T1 >
|
||||
inline static auto
|
||||
get(const std::reference_wrapper< T1 >& t1) -> decltype(t1.get())
|
||||
{
|
||||
return t1.get();
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct InvokerHelper< true >
|
||||
{
|
||||
template < class T1 >
|
||||
inline static auto
|
||||
get(T1&& t1) -> decltype(std::forward< T1 >(t1))
|
||||
{
|
||||
return std::forward< T1 >(t1);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Invoker< true, false >
|
||||
{
|
||||
template < class T, class F, class T1, class... Args >
|
||||
inline static auto
|
||||
invoke(F T::*f, T1&& t1, Args&&... args) -> decltype((
|
||||
InvokerHelper< std::is_base_of< T, typename std::decay< T1 >::type >::
|
||||
value >::get(std::forward< T1 >(t1))
|
||||
.*f)(std::forward< Args >(args)...))
|
||||
{
|
||||
return (
|
||||
InvokerHelper<
|
||||
std::is_base_of< T, typename std::decay< T1 >::type >::value >::
|
||||
get(std::forward< T1 >(t1))
|
||||
.*f)(std::forward< Args >(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Invoker< false, true >
|
||||
{
|
||||
template < class T, class F, class T1, class... Args >
|
||||
inline static auto
|
||||
invoke(F T::*f, T1&& t1, Args&&... args)
|
||||
-> decltype(InvokerHelper< std::is_base_of<
|
||||
T, typename std::decay< T1 >::type >::value >::get(t1)
|
||||
.*f)
|
||||
{
|
||||
return InvokerHelper< std::is_base_of<
|
||||
T, typename std::decay< T1 >::type >::value >::get(t1)
|
||||
.*f;
|
||||
}
|
||||
};
|
||||
|
||||
template < class F, class... Args >
|
||||
struct InvokeResult
|
||||
{
|
||||
typedef Invoker< std::is_member_function_pointer<
|
||||
typename std::remove_reference< F >::type >::value,
|
||||
std::is_member_object_pointer<
|
||||
typename std::remove_reference< F >::type >::value
|
||||
&& (sizeof...(Args) == 1) >
|
||||
invoker;
|
||||
inline static auto
|
||||
invoke(F&& f, Args&&... args)
|
||||
-> decltype(invoker::invoke(std::forward< F >(f),
|
||||
std::forward< Args >(args)...))
|
||||
{
|
||||
return invoker::invoke(std::forward< F >(f),
|
||||
std::forward< Args >(args)...);
|
||||
};
|
||||
};
|
||||
|
||||
template < class F, class... Args >
|
||||
auto
|
||||
invoke(F&& f, Args&&... args)
|
||||
-> decltype(InvokeResult< F, Args... >::invoke(
|
||||
std::forward< F >(f), std::forward< Args >(args)...))
|
||||
{
|
||||
return InvokeResult< F, Args... >::invoke(std::forward< F >(f),
|
||||
std::forward< Args >(args)...);
|
||||
}
|
||||
#else
|
||||
using std::invoke;
|
||||
#endif
|
||||
|
||||
template < int... >
|
||||
struct IntSeq
|
||||
{
|
||||
};
|
||||
|
||||
template < int N, int... S >
|
||||
struct GenIntSeq : GenIntSeq< N - 1, N - 1, S... >
|
||||
{
|
||||
};
|
||||
|
||||
template < int... S >
|
||||
struct GenIntSeq< 0, S... >
|
||||
{
|
||||
typedef IntSeq< S... > type;
|
||||
};
|
||||
|
||||
// We can't define the Call struct in the function - the standard forbids
|
||||
// template methods in that case
|
||||
template < class Func, typename... Args >
|
||||
struct ThreadFuncCall
|
||||
{
|
||||
typedef std::tuple< Args... > Tuple;
|
||||
Func mFunc;
|
||||
Tuple mArgs;
|
||||
ThreadFuncCall(Func&& aFunc, Args&&... aArgs)
|
||||
: mFunc(std::forward< Func >(aFunc))
|
||||
, mArgs(std::forward< Args >(aArgs)...)
|
||||
{
|
||||
}
|
||||
template < int... S >
|
||||
void
|
||||
callFunc(detail::IntSeq< S... >)
|
||||
{
|
||||
detail::invoke(std::forward< Func >(mFunc),
|
||||
std::get< S >(std::forward< Tuple >(mArgs))...);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
class thread
|
||||
{
|
||||
public:
|
||||
class id
|
||||
{
|
||||
DWORD mId;
|
||||
void
|
||||
clear()
|
||||
{
|
||||
mId = 0;
|
||||
}
|
||||
friend class thread;
|
||||
friend class std::hash< id >;
|
||||
|
||||
public:
|
||||
explicit id(DWORD aId = 0) noexcept : mId(aId)
|
||||
{
|
||||
}
|
||||
friend bool
|
||||
operator==(id x, id y) noexcept
|
||||
{
|
||||
return x.mId == y.mId;
|
||||
}
|
||||
friend bool
|
||||
operator!=(id x, id y) noexcept
|
||||
{
|
||||
return x.mId != y.mId;
|
||||
}
|
||||
friend bool
|
||||
operator<(id x, id y) noexcept
|
||||
{
|
||||
return x.mId < y.mId;
|
||||
}
|
||||
friend bool
|
||||
operator<=(id x, id y) noexcept
|
||||
{
|
||||
return x.mId <= y.mId;
|
||||
}
|
||||
friend bool
|
||||
operator>(id x, id y) noexcept
|
||||
{
|
||||
return x.mId > y.mId;
|
||||
}
|
||||
friend bool
|
||||
operator>=(id x, id y) noexcept
|
||||
{
|
||||
return x.mId >= y.mId;
|
||||
}
|
||||
|
||||
template < class _CharT, class _Traits >
|
||||
friend std::basic_ostream< _CharT, _Traits >&
|
||||
operator<<(std::basic_ostream< _CharT, _Traits >& __out, id __id)
|
||||
{
|
||||
if(__id.mId == 0)
|
||||
{
|
||||
return __out << "(invalid std::thread::id)";
|
||||
}
|
||||
else
|
||||
{
|
||||
return __out << __id.mId;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
protected:
|
||||
HANDLE mHandle;
|
||||
id mThreadId;
|
||||
|
||||
public:
|
||||
typedef HANDLE native_handle_type;
|
||||
id
|
||||
get_id() const noexcept
|
||||
{
|
||||
return mThreadId;
|
||||
}
|
||||
native_handle_type
|
||||
native_handle() const
|
||||
{
|
||||
return mHandle;
|
||||
}
|
||||
thread() : mHandle(_STD_THREAD_INVALID_HANDLE), mThreadId()
|
||||
{
|
||||
}
|
||||
|
||||
thread(thread&& other) : mHandle(other.mHandle), mThreadId(other.mThreadId)
|
||||
{
|
||||
other.mHandle = _STD_THREAD_INVALID_HANDLE;
|
||||
other.mThreadId.clear();
|
||||
}
|
||||
|
||||
thread(const thread& other) = delete;
|
||||
|
||||
template < class Func, typename... Args >
|
||||
explicit thread(Func&& func, Args&&... args) : mHandle(), mThreadId()
|
||||
{
|
||||
typedef detail::ThreadFuncCall< Func, Args... > Call;
|
||||
auto call =
|
||||
new Call(std::forward< Func >(func), std::forward< Args >(args)...);
|
||||
mHandle =
|
||||
(HANDLE)_beginthreadex(NULL, 0, threadfunc< Call, Args... >,
|
||||
(LPVOID)call, 0, (unsigned*)&(mThreadId.mId));
|
||||
if(mHandle == _STD_THREAD_INVALID_HANDLE)
|
||||
{
|
||||
int errnum = errno;
|
||||
delete call;
|
||||
// Note: Should only throw EINVAL, EAGAIN, EACCES
|
||||
throw std::system_error(errnum, std::generic_category());
|
||||
}
|
||||
}
|
||||
template < class Call, typename... Args >
|
||||
static unsigned __stdcall threadfunc(void* arg)
|
||||
{
|
||||
std::unique_ptr< Call > call(static_cast< Call* >(arg));
|
||||
call->callFunc(typename detail::GenIntSeq< sizeof...(Args) >::type());
|
||||
return 0;
|
||||
}
|
||||
bool
|
||||
joinable() const
|
||||
{
|
||||
return mHandle != _STD_THREAD_INVALID_HANDLE;
|
||||
}
|
||||
void
|
||||
join()
|
||||
{
|
||||
using namespace std;
|
||||
if(get_id() == id(GetCurrentThreadId()))
|
||||
throw system_error(
|
||||
make_error_code(errc::resource_deadlock_would_occur));
|
||||
if(mHandle == _STD_THREAD_INVALID_HANDLE)
|
||||
throw system_error(make_error_code(errc::no_such_process));
|
||||
if(!joinable())
|
||||
throw system_error(make_error_code(errc::invalid_argument));
|
||||
WaitForSingleObject(mHandle, INFINITE);
|
||||
CloseHandle(mHandle);
|
||||
mHandle = _STD_THREAD_INVALID_HANDLE;
|
||||
mThreadId.clear();
|
||||
}
|
||||
|
||||
~thread()
|
||||
{
|
||||
if(joinable())
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
std::printf(
|
||||
"Error: Must join() or detach() a thread before \
|
||||
destroying it.\n");
|
||||
#endif
|
||||
std::terminate();
|
||||
}
|
||||
}
|
||||
thread&
|
||||
operator=(const thread&) = delete;
|
||||
thread&
|
||||
operator=(thread&& other) noexcept
|
||||
{
|
||||
if(joinable())
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
std::printf(
|
||||
"Error: Must join() or detach() a thread before \
|
||||
moving another thread to it.\n");
|
||||
#endif
|
||||
std::terminate();
|
||||
}
|
||||
swap(std::forward< thread >(other));
|
||||
return *this;
|
||||
}
|
||||
void
|
||||
swap(thread&& other) noexcept
|
||||
{
|
||||
std::swap(mHandle, other.mHandle);
|
||||
std::swap(mThreadId.mId, other.mThreadId.mId);
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
_hardware_concurrency_helper() noexcept
|
||||
{
|
||||
SYSTEM_INFO sysinfo;
|
||||
::GetNativeSystemInfo(&sysinfo);
|
||||
return sysinfo.dwNumberOfProcessors;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
hardware_concurrency() noexcept
|
||||
{
|
||||
static unsigned int cached = _hardware_concurrency_helper();
|
||||
return cached;
|
||||
}
|
||||
|
||||
void
|
||||
detach()
|
||||
{
|
||||
if(!joinable())
|
||||
{
|
||||
using namespace std;
|
||||
throw system_error(make_error_code(errc::invalid_argument));
|
||||
}
|
||||
if(mHandle != _STD_THREAD_INVALID_HANDLE)
|
||||
{
|
||||
CloseHandle(mHandle);
|
||||
mHandle = _STD_THREAD_INVALID_HANDLE;
|
||||
}
|
||||
mThreadId.clear();
|
||||
}
|
||||
};
|
||||
|
||||
namespace this_thread
|
||||
{
|
||||
inline thread::id
|
||||
get_id() noexcept
|
||||
{
|
||||
return thread::id(GetCurrentThreadId());
|
||||
}
|
||||
inline void
|
||||
yield() noexcept
|
||||
{
|
||||
Sleep(0);
|
||||
}
|
||||
template < class Rep, class Period >
|
||||
void
|
||||
sleep_for(const std::chrono::duration< Rep, Period >& sleep_duration)
|
||||
{
|
||||
Sleep(std::chrono::duration_cast< std::chrono::milliseconds >(
|
||||
sleep_duration)
|
||||
.count());
|
||||
}
|
||||
template < class Clock, class Duration >
|
||||
void
|
||||
sleep_until(const std::chrono::time_point< Clock, Duration >& sleep_time)
|
||||
{
|
||||
sleep_for(sleep_time - Clock::now());
|
||||
}
|
||||
} // namespace this_thread
|
||||
} // Namespace mingw_stdthread
|
||||
|
||||
namespace std
|
||||
{
|
||||
// Because of quirks of the compiler, the common "using namespace std;"
|
||||
// directive would flatten the namespaces and introduce ambiguity where there
|
||||
// was none. Direct specification (std::), however, would be unaffected.
|
||||
// Take the safe option, and include only in the presence of MinGW's win32
|
||||
// implementation.
|
||||
#if defined(__MINGW32__) && !defined(_GLIBCXX_HAS_GTHREADS)
|
||||
using mingw_stdthread::thread;
|
||||
// Remove ambiguity immediately, to avoid problems arising from the above.
|
||||
// using std::thread;
|
||||
namespace this_thread
|
||||
{
|
||||
using namespace mingw_stdthread::this_thread;
|
||||
}
|
||||
#elif !defined(MINGW_STDTHREAD_REDUNDANCY_WARNING) // Skip repetition
|
||||
#define MINGW_STDTHREAD_REDUNDANCY_WARNING
|
||||
#pragma message \
|
||||
"This version of MinGW seems to include a win32 port of\
|
||||
pthreads, and probably already has C++11 std threading classes implemented,\
|
||||
based on pthreads. These classes, found in namespace std, are not overridden\
|
||||
by the mingw-std-thread library. If you would still like to use this\
|
||||
implementation (as it is more lightweight), use the classes provided in\
|
||||
namespace mingw_stdthread."
|
||||
#endif
|
||||
|
||||
// Specialize hash for this implementation's thread::id, even if the
|
||||
// std::thread::id already has a hash.
|
||||
template <>
|
||||
struct hash< mingw_stdthread::thread::id >
|
||||
{
|
||||
typedef mingw_stdthread::thread::id argument_type;
|
||||
typedef size_t result_type;
|
||||
size_t
|
||||
operator()(const argument_type& i) const noexcept
|
||||
{
|
||||
return i.mId;
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
#endif // WIN32STDTHREAD_H
|
@ -0,0 +1,145 @@
|
||||
#include <util/printer.hpp>
|
||||
|
||||
#include <absl/types/variant.h>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
using namespace llarp;
|
||||
using namespace ::testing;
|
||||
|
||||
struct PrintableType
|
||||
{
|
||||
std::ostream &
|
||||
print(std::ostream &stream, int level, int spaces) const
|
||||
{
|
||||
stream << "PrintableType " << level << " " << spaces;
|
||||
return stream;
|
||||
}
|
||||
};
|
||||
|
||||
using SingleVariant =
|
||||
absl::variant< char, bool, short, int, unsigned int, const void *,
|
||||
const char *, std::string, const int *,
|
||||
std::pair< int, std::string >,
|
||||
std::tuple< int, std::string, int >,
|
||||
std::map< std::string, char >, PrintableType >;
|
||||
|
||||
using SingleType = std::pair< SingleVariant, Matcher< std::string > >;
|
||||
|
||||
class SingleValueTest : public ::testing::TestWithParam< SingleType >
|
||||
{
|
||||
};
|
||||
|
||||
TEST_P(SingleValueTest, value)
|
||||
{
|
||||
SingleType d = GetParam();
|
||||
std::ostringstream stream;
|
||||
{
|
||||
Printer printer(stream, -1, -1);
|
||||
absl::visit([&](const auto &x) { printer.printValue(x); }, d.first);
|
||||
}
|
||||
ASSERT_THAT(stream.str(), d.second);
|
||||
}
|
||||
|
||||
static const char PTR_TYPE[] = "abacus";
|
||||
static const int INT_VAL = 100;
|
||||
|
||||
// clang-format off
|
||||
static const SingleType singleType[] = {
|
||||
{char('a'), StrEq("[ 'a' ]")},
|
||||
{bool(true), StrEq("[ true ]")},
|
||||
{bool(false), StrEq("[ false ]")},
|
||||
{short(123), StrEq("[ 123 ]")},
|
||||
{int(INT_MAX - 1), StrEq("[ 2147483646 ]")},
|
||||
{static_cast< unsigned int >(std::numeric_limits< int >::max()) + 1, StrEq("[ 2147483648 ]")},
|
||||
{static_cast< const void * >(PTR_TYPE), AllOf(StartsWith("[ 0x"), EndsWith(" ]"))},
|
||||
{static_cast< const char * >(PTR_TYPE), StrEq("[ \"abacus\" ]")},
|
||||
{std::string("abacus"), StrEq("[ \"abacus\" ]")},
|
||||
{static_cast< const int * >(&INT_VAL), AllOf(StartsWith("[ 0x"), EndsWith(" ]"))},
|
||||
{std::pair< int, std::string >(100, "abacus"), StrEq("[ [ 100 \"abacus\" ] ]")},
|
||||
{std::tuple< int, std::string, int >(100, "abacus", 123), StrEq("[ [ 100 \"abacus\" 123 ] ]")},
|
||||
{std::map< std::string, char >{{"one", 'a'}, {"two", 'b'}, {"three", 'c'}}, StrEq("[ [ [ \"one\" \'a\' ] [ \"three\" \'c\' ] [ \"two\" 'b' ] ] ]")},
|
||||
{PrintableType(), StrEq("[ PrintableType -2 -1 ]")},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(Printer, SingleValueTest,
|
||||
::testing::ValuesIn(singleType), );
|
||||
|
||||
using SingleAttributeType =
|
||||
std::tuple< std::string, SingleVariant, Matcher< std::string > >;
|
||||
|
||||
class SingleAttributeTest
|
||||
: public ::testing::TestWithParam< SingleAttributeType >
|
||||
{
|
||||
};
|
||||
|
||||
TEST_P(SingleAttributeTest, value)
|
||||
{
|
||||
SingleAttributeType d = GetParam();
|
||||
std::ostringstream stream;
|
||||
{
|
||||
Printer printer(stream, -1, -1);
|
||||
absl::visit(
|
||||
[&](const auto &x) { printer.printAttribute(std::get< 0 >(d), x); },
|
||||
std::get< 1 >(d));
|
||||
}
|
||||
ASSERT_THAT(stream.str(), std::get< 2 >(d));
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
static const SingleAttributeType singleAttributeType[] = {
|
||||
{"our_value", char('a'), StrEq("[ our_value = 'a' ]")},
|
||||
{"our_value", bool(true), StrEq("[ our_value = true ]")},
|
||||
{"our_value", bool(false), StrEq("[ our_value = false ]")},
|
||||
{"our_value", short(123), StrEq("[ our_value = 123 ]")},
|
||||
{"our_value", int(INT_MAX - 1), StrEq("[ our_value = 2147483646 ]")},
|
||||
{"our_value", static_cast< unsigned int >(std::numeric_limits< int >::max()) + 1, StrEq("[ our_value = 2147483648 ]")},
|
||||
{"our_value", static_cast< const void * >(PTR_TYPE), AllOf(StartsWith("[ our_value = 0x"), EndsWith(" ]"))},
|
||||
{"our_value", static_cast< const char * >(PTR_TYPE), StrEq("[ our_value = \"abacus\" ]")},
|
||||
{"our_value", std::string("abacus"), StrEq("[ our_value = \"abacus\" ]")},
|
||||
{"our_value", static_cast< const int * >(&INT_VAL), AllOf(StartsWith("[ our_value = 0x"), EndsWith(" ]"))},
|
||||
{"our_value", std::pair< int, std::string >(100, "abacus"), StrEq("[ our_value = [ 100 \"abacus\" ] ]")},
|
||||
{"our_value", std::tuple< int, std::string, int >(100, "abacus", 123), StrEq("[ our_value = [ 100 \"abacus\" 123 ] ]")},
|
||||
{"our_value", std::map< std::string, char >{{"one", 'a'}, {"two", 'b'}, {"three", 'c'}}, StrEq("[ our_value = [ [ \"one\" \'a\' ] [ \"three\" \'c\' ] [ \"two\" 'b' ] ] ]")},
|
||||
{"our_value", PrintableType(), StrEq("[ our_value = PrintableType -2 -1 ]")},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(Printer, SingleAttributeTest,
|
||||
::testing::ValuesIn(singleAttributeType), );
|
||||
|
||||
using ManyAttributes =
|
||||
std::pair< std::vector< std::pair< std::string, SingleVariant > >,
|
||||
Matcher< std::string > >;
|
||||
|
||||
class ManyAttributesTest : public ::testing::TestWithParam< ManyAttributes >
|
||||
{
|
||||
};
|
||||
|
||||
TEST_P(ManyAttributesTest, value)
|
||||
{
|
||||
ManyAttributes d = GetParam();
|
||||
std::ostringstream stream;
|
||||
{
|
||||
Printer printer(stream, -1, -1);
|
||||
std::for_each(d.first.begin(), d.first.end(), [&](const auto &y) {
|
||||
std::string n = y.first;
|
||||
const auto &v = y.second;
|
||||
absl::visit([&](const auto &x) { printer.printAttribute(n, x); }, v);
|
||||
});
|
||||
}
|
||||
ASSERT_THAT(stream.str(), d.second);
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
static const ManyAttributes manyAttributes[] = {
|
||||
{{{"val", 1}, {"v2", 2}, {"v3", 3}, {"str", std::string("xxx")}}, StrEq("[ val = 1 v2 = 2 v3 = 3 str = \"xxx\" ]")},
|
||||
{{{"str", std::string("xxx")}}, StrEq("[ str = \"xxx\" ]")}
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(Printer, ManyAttributesTest,
|
||||
::testing::ValuesIn(manyAttributes), );
|
@ -0,0 +1,154 @@
|
||||
#include <util/traits.hpp>
|
||||
|
||||
#include <list>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
using namespace llarp;
|
||||
|
||||
TEST(traits_bottom, Smoke)
|
||||
{
|
||||
traits::Bottom bottom;
|
||||
(void)bottom;
|
||||
SUCCEED();
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
class IsContainer : public ::testing::Test
|
||||
{
|
||||
};
|
||||
|
||||
TYPED_TEST_SUITE_P(IsContainer);
|
||||
|
||||
TYPED_TEST_P(IsContainer, Smoke)
|
||||
{
|
||||
bool expected = std::tuple_element_t< 1, TypeParam >::value;
|
||||
bool result =
|
||||
traits::is_container< std::tuple_element_t< 0, TypeParam > >::value;
|
||||
ASSERT_EQ(expected, result);
|
||||
}
|
||||
|
||||
REGISTER_TYPED_TEST_SUITE_P(IsContainer, Smoke);
|
||||
|
||||
// clang-format off
|
||||
using ContainerTypes = ::testing::Types<
|
||||
std::tuple< std::vector< int >, std::integral_constant< bool, true > >,
|
||||
std::tuple< std::vector< std::string >, std::integral_constant< bool, true > >,
|
||||
std::tuple< std::list< std::string >, std::integral_constant< bool, true > >,
|
||||
std::tuple< std::string, std::integral_constant< bool, true > >,
|
||||
std::tuple< std::shared_ptr<std::string>, std::integral_constant< bool, false > >,
|
||||
std::tuple< std::tuple<std::string>, std::integral_constant< bool, false > >,
|
||||
std::tuple< int, std::integral_constant< bool, false > >
|
||||
>;
|
||||
INSTANTIATE_TYPED_TEST_SUITE_P(traits, IsContainer, ContainerTypes);
|
||||
|
||||
struct A { };
|
||||
struct B { };
|
||||
struct C { };
|
||||
struct D { };
|
||||
struct E { };
|
||||
struct F { };
|
||||
struct G { };
|
||||
struct H { };
|
||||
struct I { };
|
||||
struct J { };
|
||||
|
||||
char f(A) { return 'A'; }
|
||||
char f(B) { return 'B'; }
|
||||
char f(C) { return 'C'; }
|
||||
char f(D) { return 'D'; }
|
||||
char f(E) { return 'E'; }
|
||||
char f(F) { return 'F'; }
|
||||
char f(G) { return 'G'; }
|
||||
char f(H) { return 'H'; }
|
||||
char f(I) { return 'I'; }
|
||||
char f(J) { return 'J'; }
|
||||
char f(traits::Bottom) { return '0'; }
|
||||
|
||||
// clang-format on
|
||||
|
||||
template < typename T >
|
||||
class TestSwitch : public ::testing::Test
|
||||
{
|
||||
};
|
||||
|
||||
TYPED_TEST_SUITE_P(TestSwitch);
|
||||
|
||||
TYPED_TEST_P(TestSwitch, Smoke)
|
||||
{
|
||||
char expected = std::tuple_element_t< 0, TypeParam >::value;
|
||||
using InputType = typename std::tuple_element_t< 1, TypeParam >::Type;
|
||||
char result = f(InputType());
|
||||
ASSERT_EQ(expected, result);
|
||||
}
|
||||
|
||||
REGISTER_TYPED_TEST_SUITE_P(TestSwitch, Smoke);
|
||||
|
||||
// clang-format off
|
||||
using namespace traits::Switch;
|
||||
using SwitchTypes = ::testing::Types<
|
||||
std::tuple<std::integral_constant<char, 'A'>, Switch< 0, A, B, C, D, E, F, G, H, I, J > >,
|
||||
std::tuple<std::integral_constant<char, 'B'>, Switch< 1, A, B, C, D, E, F, G, H, I, J > >,
|
||||
std::tuple<std::integral_constant<char, 'C'>, Switch< 2, A, B, C, D, E, F, G, H, I, J > >,
|
||||
std::tuple<std::integral_constant<char, 'D'>, Switch< 3, A, B, C, D, E, F, G, H, I, J > >,
|
||||
std::tuple<std::integral_constant<char, 'E'>, Switch< 4, A, B, C, D, E, F, G, H, I, J > >,
|
||||
std::tuple<std::integral_constant<char, 'F'>, Switch< 5, A, B, C, D, E, F, G, H, I, J > >,
|
||||
std::tuple<std::integral_constant<char, 'G'>, Switch< 6, A, B, C, D, E, F, G, H, I, J > >,
|
||||
std::tuple<std::integral_constant<char, 'H'>, Switch< 7, A, B, C, D, E, F, G, H, I, J > >,
|
||||
std::tuple<std::integral_constant<char, 'I'>, Switch< 8, A, B, C, D, E, F, G, H, I, J > >,
|
||||
std::tuple<std::integral_constant<char, 'J'>, Switch< 9, A, B, C, D, E, F, G, H, I, J > >,
|
||||
std::tuple<std::integral_constant<char, 'J'>, Switch< 9, C, C, C, C, C, C, C, C, C, J > >,
|
||||
std::tuple<std::integral_constant<char, 'C'>, Switch< 6, C, C, C, C, C, C, C, C, C, J > >,
|
||||
std::tuple<std::integral_constant<char, '0'>, Switch< 10, A, B, C, D, E, F, G, H, I, J > >
|
||||
>;
|
||||
|
||||
INSTANTIATE_TYPED_TEST_SUITE_P(traits, TestSwitch, SwitchTypes);
|
||||
|
||||
template<typename T>
|
||||
using is_bool = std::is_same<T, bool>;
|
||||
template<typename T>
|
||||
using is_char = std::is_same<T, char>;
|
||||
template<typename T>
|
||||
using is_string = std::is_same<T, std::string>;
|
||||
|
||||
char dispatch(traits::select::Case<>) { return '0'; }
|
||||
char dispatch(traits::select::Case<is_bool>) { return 'b'; }
|
||||
char dispatch(traits::select::Case<is_char>) { return 'c'; }
|
||||
char dispatch(traits::select::Case<is_string>) { return 's'; }
|
||||
|
||||
template < typename Type >
|
||||
char
|
||||
selectCase()
|
||||
{
|
||||
using Selection = traits::select::Select<Type, is_bool, is_char, is_string >;
|
||||
|
||||
return dispatch(Selection());
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
class Select : public ::testing::Test
|
||||
{
|
||||
};
|
||||
|
||||
TYPED_TEST_SUITE_P(Select);
|
||||
|
||||
TYPED_TEST_P(Select, Smoke)
|
||||
{
|
||||
char expected = std::tuple_element_t< 0, TypeParam >::value;
|
||||
char result = selectCase<std::tuple_element_t< 1, TypeParam > >();
|
||||
ASSERT_EQ(expected, result);
|
||||
}
|
||||
|
||||
REGISTER_TYPED_TEST_SUITE_P(Select, Smoke);
|
||||
|
||||
using SelectTypes = ::testing::Types<
|
||||
std::tuple<std::integral_constant<char, '0'>, double >,
|
||||
std::tuple<std::integral_constant<char, 'b'>, bool >,
|
||||
std::tuple<std::integral_constant<char, 'c'>, char >,
|
||||
std::tuple<std::integral_constant<char, 's'>, std::string >
|
||||
>;
|
||||
|
||||
INSTANTIATE_TYPED_TEST_SUITE_P(traits, Select, SelectTypes);
|
||||
|
||||
// clang-format on
|
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright (c)2018-2019 Rick V. All rights reserved.
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*------------------------------------------------------------------------------
|
||||
* Shared object loaded by lokinet installer to properly detect the presence
|
||||
* of the TAP v9 adapter
|
||||
* -rick
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Windows registry crap */
|
||||
#define MAX_KEY_LENGTH 255
|
||||
#define MAX_VALUE_NAME 16383
|
||||
#define ETHER_ADDR_LEN 6
|
||||
|
||||
/* checks if TAP-Win32 v9 is already installed */
|
||||
BOOL
|
||||
reg_query_helper()
|
||||
{
|
||||
HKEY adapters, adapter;
|
||||
DWORD i, ret, len;
|
||||
char *deviceid = NULL;
|
||||
DWORD sub_keys = 0;
|
||||
BOOL found = FALSE;
|
||||
|
||||
ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
||||
TEXT("SYSTEM\\CurrentControlSet\\Control\\Class\\{"
|
||||
"4D36E972-E325-11CE-BFC1-08002BE10318}"),
|
||||
0, KEY_READ, &adapters);
|
||||
if(ret != ERROR_SUCCESS)
|
||||
return FALSE;
|
||||
|
||||
ret = RegQueryInfoKey(adapters, NULL, NULL, NULL, &sub_keys, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL);
|
||||
if(ret != ERROR_SUCCESS)
|
||||
return FALSE;
|
||||
|
||||
if(sub_keys <= 0)
|
||||
return FALSE;
|
||||
|
||||
/* Walk througt all adapters */
|
||||
for(i = 0; i < sub_keys; i++)
|
||||
{
|
||||
char new_key[MAX_KEY_LENGTH];
|
||||
char data[256];
|
||||
TCHAR key[MAX_KEY_LENGTH];
|
||||
DWORD keylen = MAX_KEY_LENGTH;
|
||||
|
||||
/* Get the adapter key name */
|
||||
ret = RegEnumKeyEx(adapters, i, key, &keylen, NULL, NULL, NULL, NULL);
|
||||
if(ret != ERROR_SUCCESS)
|
||||
continue;
|
||||
|
||||
/* Append it to NETWORK_ADAPTERS and open it */
|
||||
snprintf(new_key, sizeof new_key, "%s\\%s",
|
||||
"SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-"
|
||||
"BFC1-08002BE10318}",
|
||||
key);
|
||||
ret =
|
||||
RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT(new_key), 0, KEY_READ, &adapter);
|
||||
if(ret != ERROR_SUCCESS)
|
||||
continue;
|
||||
|
||||
/* Check its values */
|
||||
len = sizeof data;
|
||||
ret =
|
||||
RegQueryValueEx(adapter, "ComponentId", NULL, NULL, (LPBYTE)data, &len);
|
||||
if(ret != ERROR_SUCCESS)
|
||||
{
|
||||
/* This value doesn't exist in this adaptater tree */
|
||||
goto clean;
|
||||
}
|
||||
/* If its a tap adapter, its all good */
|
||||
/* We only support TAP 9.x, TAP 8.x users must upgrade. */
|
||||
if(strncmp(data, "tap0901", 7) == 0)
|
||||
{
|
||||
DWORD type;
|
||||
|
||||
len = sizeof data;
|
||||
ret = RegQueryValueEx(adapter, "NetCfgInstanceId", NULL, &type,
|
||||
(LPBYTE)data, &len);
|
||||
if(ret != ERROR_SUCCESS)
|
||||
goto clean;
|
||||
found = TRUE;
|
||||
break;
|
||||
}
|
||||
clean:
|
||||
RegCloseKey(adapter);
|
||||
}
|
||||
RegCloseKey(adapters);
|
||||
return found;
|
||||
}
|
Binary file not shown.
Loading…
Reference in New Issue