Merge remote-tracking branch 'origin/staging' into fix-libabyss-352

pull/359/head
Jeff Becker 5 years ago
commit 71302ee48b
No known key found for this signature in database
GPG Key ID: F357B3B42F6F9B05

@ -226,6 +226,11 @@ extern "C"
int
tuntap_sys_set_ipv4(struct device *, t_tun_in_addr *, uint32_t);
#if defined(Windows)
int
tuntap_sys_set_dns(struct device *dev, t_tun_in_addr *s, uint32_t mask);
#endif
#if defined(FreeBSD)
int
tuntap_sys_set_ipv4_tap(struct device *, t_tun_in_addr *, uint32_t);

@ -89,12 +89,12 @@ namespace abyss
auto idx = line.find_first_of(' ');
if(idx == string_view::npos)
return false;
Header.Method = line.substr(0, idx);
Header.Method = std::string(line.substr(0, idx));
line = line.substr(idx + 1);
idx = line.find_first_of(' ');
if(idx == string_view::npos)
return false;
Header.Path = line.substr(0, idx);
Header.Path = std::string(line.substr(0, idx));
m_State = eReadHTTPHeaders;
return true;
}

@ -19,6 +19,7 @@ set(LIB_UTIL_SRC
util/mem.cpp
util/queue_manager.cpp
util/queue.cpp
util/printer.cpp
util/status.cpp
util/str.cpp
util/string_view.cpp
@ -27,6 +28,7 @@ set(LIB_UTIL_SRC
util/threadpool.cpp
util/time.cpp
util/timer.cpp
util/traits.cpp
util/types.cpp
)
@ -36,7 +38,7 @@ target_include_directories(${UTIL_LIB} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../inc
# cut back on fluff
if (NOT WIN32)
target_link_libraries(${UTIL_LIB} PUBLIC absl::optional absl::variant cppbackport)
target_link_libraries(${UTIL_LIB} PUBLIC absl::optional absl::variant absl::strings absl::hash cppbackport)
endif(NOT WIN32)
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")

@ -95,6 +95,14 @@ namespace llarp
return out << "[secretkey]";
}
std::ostream &
print(std::ostream &stream, int level, int spaces) const
{
Printer printer(stream, level, spaces);
printer.printValue("secretkey");
return stream;
}
PubKey
toPublic() const
{

@ -4,6 +4,7 @@
#include <util/buffer.hpp>
#include <util/endian.hpp>
#include <util/logger.hpp>
#include <util/printer.hpp>
#include <array>
@ -270,5 +271,20 @@ namespace llarp
}
}
std::ostream&
Message::print(std::ostream& stream, int level, int spaces) const
{
Printer printer(stream, level, spaces);
printer.printAttributeAsHex("dns message id", hdr_id);
printer.printAttributeAsHex("fields", hdr_fields);
printer.printAttribute("questions", questions);
printer.printAttribute("answers", answers);
printer.printAttribute("nameserer", authorities);
printer.printAttribute("additional", additional);
return stream;
}
} // namespace dns
} // namespace llarp

@ -72,24 +72,8 @@ namespace llarp
bool
Decode(llarp_buffer_t* buf) override;
friend std::ostream&
operator<<(std::ostream& out, const Message& msg)
{
out << "[dns message id=" << std::hex << msg.hdr_id
<< " fields=" << msg.hdr_fields << " questions=[ ";
for(const auto& qd : msg.questions)
out << qd << ", ";
out << "] answers=[ ";
for(const auto& an : msg.answers)
out << an << ", ";
out << "] nameserver=[ ";
for(const auto& ns : msg.authorities)
out << ns << ", ";
out << "] additional=[ ";
for(const auto& ar : msg.additional)
out << ar << ", ";
return out << "]";
}
std::ostream&
print(std::ostream& stream, int level, int spaces) const;
MsgID_t hdr_id;
Fields_t hdr_fields;
@ -98,6 +82,13 @@ namespace llarp
std::vector< ResourceRecord > authorities;
std::vector< ResourceRecord > additional;
};
inline std::ostream&
operator<<(std::ostream& out, const Message& msg)
{
msg.print(out, -1, -1);
return out;
}
} // namespace dns
} // namespace llarp

@ -1,6 +1,7 @@
#include <dns/question.hpp>
#include <util/logger.hpp>
#include <util/printer.hpp>
namespace llarp
{
@ -47,5 +48,16 @@ namespace llarp
}
return true;
}
std::ostream&
Question::print(std::ostream& stream, int level, int spaces) const
{
Printer printer(stream, level, spaces);
printer.printAttribute("qname", qname);
printer.printAttributeAsHex("qtype", qtype);
printer.printAttributeAsHex("qclass", qclass);
return stream;
}
} // namespace dns
} // namespace llarp

@ -23,6 +23,9 @@ namespace llarp
bool
Decode(llarp_buffer_t* buf) override;
std::ostream&
print(std::ostream& stream, int level, int spaces) const;
bool
operator==(const Question& other) const
{
@ -30,17 +33,17 @@ namespace llarp
&& qclass == other.qclass;
}
friend std::ostream&
operator<<(std::ostream& out, const Question& q)
{
return out << "qname=" << q.qname << " qtype=" << (int)q.qtype
<< " qclass=" << (int)q.qclass;
}
Name_t qname;
QType_t qtype;
QClass_t qclass;
};
inline std::ostream&
operator<<(std::ostream& out, const Question& q)
{
q.print(out, -1, -1);
return out;
}
} // namespace dns
} // namespace llarp

@ -1,6 +1,7 @@
#include <dns/rr.hpp>
#include <util/logger.hpp>
#include <util/printer.hpp>
namespace llarp
{
@ -80,5 +81,18 @@ namespace llarp
}
return true;
}
std::ostream&
ResourceRecord::print(std::ostream& stream, int level, int spaces) const
{
Printer printer(stream, level, spaces);
printer.printAttribute("RR name", rr_name);
printer.printAttribute("type", rr_type);
printer.printAttribute("class", rr_class);
printer.printAttribute("ttl", ttl);
printer.printAttribute("rdata", rData.size());
return stream;
}
} // namespace dns
} // namespace llarp

@ -29,13 +29,8 @@ namespace llarp
bool
Decode(llarp_buffer_t* buf) override;
friend std::ostream&
operator<<(std::ostream& out, const ResourceRecord& rr)
{
return out << "[RR name=" << rr.rr_name << " type=" << rr.rr_type
<< " class=" << rr.rr_class << " ttl=" << rr.ttl
<< " rdata=[" << rr.rData.size() << " bytes]";
}
std::ostream&
print(std::ostream& stream, int level, int spaces) const;
Name_t rr_name;
RRType_t rr_type;
@ -43,6 +38,12 @@ namespace llarp
RR_TTL_t ttl;
RR_RData_t rData;
};
inline std::ostream&
operator<<(std::ostream& out, const ResourceRecord& rr)
{
return rr.print(out, -1, -1);
}
} // namespace dns
} // namespace llarp

@ -17,6 +17,7 @@
#error No async event loop for your platform, subclass llarp_ev_loop
#endif
// This is dead now isn't it -rick
void
llarp_ev_loop_alloc(struct llarp_ev_loop **ev)
{

@ -6,6 +6,7 @@ static HANDLE tun_event_queue = INVALID_HANDLE_VALUE;
// we hand the kernel our thread handles to process completion events
static HANDLE* kThreadPool;
static int poolSize;
static CRITICAL_SECTION HandlerMtx;
// list of TUN listeners (useful for exits or other nodes with multiple TUNs)
std::list< win32_tun_io* > tun_listeners;
@ -57,6 +58,12 @@ win32_tun_io::setup()
if(tunif->tun_fd == INVALID_HANDLE_VALUE)
return false;
// Create a critical section to synchronise access to the TUN handler.
// This *probably* has the effect of making packets move in order now
// as only one IOCP thread will have access to the TUN handler at a
// time
InitializeCriticalSection(&HandlerMtx);
return true;
}
@ -141,9 +148,11 @@ tun_ev_loop(void* unused)
// of the tun logic
for(const auto& tun : tun_listeners)
{
EnterCriticalSection(&HandlerMtx);
if(tun->t->tick)
tun->t->tick(tun->t);
tun->flush_write();
LeaveCriticalSection(&HandlerMtx);
}
continue; // let's go at it once more
}
@ -163,18 +172,24 @@ tun_ev_loop(void* unused)
delete pkt;
continue;
}
// EnterCriticalSection(&HandlerMtx);
if(ev->t->recvpkt)
ev->t->recvpkt(ev->t, llarp_buffer_t(pkt->buf, size));
ev->read(ev->readbuf, sizeof(ev->readbuf));
// LeaveCriticalSection(&HandlerMtx);
}
else
{
// ok let's queue another read!
// EnterCriticalSection(&HandlerMtx);
ev->read(ev->readbuf, sizeof(ev->readbuf));
// LeaveCriticalSection(&HandlerMtx);
}
EnterCriticalSection(&HandlerMtx);
if(ev->t->tick)
ev->t->tick(ev->t);
ev->flush_write();
LeaveCriticalSection(&HandlerMtx);
delete pkt; // don't leak
}
llarp::LogDebug("exit TUN event loop thread from system managed thread pool");
@ -210,6 +225,7 @@ exit_tun_loop()
itr = tun_listeners.erase(itr);
}
CloseHandle(tun_event_queue);
DeleteCriticalSection(&HandlerMtx);
}
}

@ -6,6 +6,7 @@
#include <net/net.hpp>
#include <util/bencode.h>
#include <util/mem.h>
#include <util/printer.hpp>
#include <string.h>
@ -162,4 +163,17 @@ namespace llarp
/** end */
return bencode_end(buff);
}
std::ostream &
AddressInfo::print(std::ostream &stream, int level, int spaces) const
{
char tmp[128] = {0};
inet_ntop(AF_INET6, (void *)&ip, tmp, sizeof(tmp));
Printer printer(stream, level, spaces);
printer.printAttribute("ip", tmp);
printer.printAttribute("port", port);
return stream;
}
} // namespace llarp

@ -52,19 +52,8 @@ namespace llarp
bool
DecodeKey(const llarp_buffer_t& k, llarp_buffer_t* buf) override;
friend std::ostream&
operator<<(std::ostream& out, const AddressInfo& a)
{
char tmp[128] = {0};
inet_ntop(AF_INET6, (void*)&a.ip, tmp, sizeof(tmp));
out << tmp << ".";
#if defined(ANDROID) || defined(RPI)
snprintf(tmp, sizeof(tmp), "%u", a.port);
return out << tmp;
#else
return out << std::to_string(a.port);
#endif
}
std::ostream&
print(std::ostream& stream, int level, int spaces) const;
struct Hash
{
@ -76,6 +65,12 @@ namespace llarp
};
};
inline std::ostream&
operator<<(std::ostream& out, const AddressInfo& a)
{
return a.print(out, -1, -1);
}
bool
operator==(const AddressInfo& lhs, const AddressInfo& rhs);

@ -84,22 +84,27 @@ namespace llarp
}
std::ostream&
operator<<(std::ostream& out, const ExitInfo& xi)
ExitInfo::print(std::ostream& stream, int level, int spaces) const
{
Printer printer(stream, level, spaces);
std::ostringstream ss;
char tmp[128] = {0};
if(inet_ntop(AF_INET6, (void*)&xi.address, tmp, sizeof(tmp)))
out << std::string(tmp);
if(inet_ntop(AF_INET6, (void*)&address, tmp, sizeof(tmp)))
ss << tmp;
else
return out;
out << std::string("/");
return stream;
ss << std::string("/");
#if defined(ANDROID) || defined(RPI)
snprintf(tmp, sizeof(tmp), "%zu",
llarp::bits::count_array_bits(xi.netmask.s6_addr));
return out << tmp;
llarp::bits::count_array_bits(netmask.s6_addr));
ss << tmp;
#else
return out << std::to_string(
llarp::bits::count_array_bits(xi.netmask.s6_addr));
ss << std::to_string(llarp::bits::count_array_bits(netmask.s6_addr));
#endif
printer.printValue(ss.str());
return stream;
}
} // namespace llarp

@ -53,10 +53,16 @@ namespace llarp
ExitInfo &
operator=(const ExitInfo &other);
std::ostream &
print(std::ostream &stream, int level, int spaces) const;
};
std::ostream &
operator<<(std::ostream &out, const ExitInfo &xi);
inline std::ostream &
operator<<(std::ostream &out, const ExitInfo &xi)
{
return xi.print(out, -1, -1);
}
} // namespace llarp
#endif

@ -18,6 +18,18 @@ namespace llarp
{
namespace path
{
std::ostream&
TransitHopInfo::print(std::ostream& stream, int level, int spaces) const
{
Printer printer(stream, level, spaces);
printer.printAttribute("tx", txID);
printer.printAttribute("rx", rxID);
printer.printAttribute("upstream", upstream);
printer.printAttribute("downstream", downstream);
return stream;
}
PathContext::PathContext(AbstractRouter* router)
: m_Router(router), m_AllowTransit(false)
{
@ -347,6 +359,17 @@ namespace llarp
RemovePathSet(ctx);
}
std::ostream&
TransitHop::print(std::ostream& stream, int level, int spaces) const
{
Printer printer(stream, level, spaces);
printer.printAttribute("TransitHop", info);
printer.printAttribute("started", started);
printer.printAttribute("lifetime", lifetime);
return stream;
}
PathHopConfig::PathHopConfig()
{
}

@ -50,14 +50,8 @@ namespace llarp
RouterID upstream;
RouterID downstream;
friend std::ostream&
operator<<(std::ostream& out, const TransitHopInfo& info)
{
out << "<tx=" << info.txID << " rx=" << info.rxID;
out << " upstream=" << info.upstream
<< " downstream=" << info.downstream;
return out << ">";
}
std::ostream&
print(std::ostream& stream, int level, int spaces) const;
bool
operator==(const TransitHopInfo& other) const
@ -102,6 +96,12 @@ namespace llarp
};
};
inline std::ostream&
operator<<(std::ostream& out, const TransitHopInfo& info)
{
return info.print(out, -1, -1);
}
struct IHopHandler
{
virtual ~IHopHandler(){};
@ -172,12 +172,8 @@ namespace llarp
return m_LastActivity;
}
friend std::ostream&
operator<<(std::ostream& out, const TransitHop& h)
{
return out << "[TransitHop " << h.info << " started=" << h.started
<< " lifetime=" << h.lifetime << "]";
}
std::ostream&
print(std::ostream& stream, int level, int spaces) const;
bool
Expired(llarp_time_t now) const override;
@ -268,6 +264,12 @@ namespace llarp
AbstractRouter* r) override;
};
inline std::ostream&
operator<<(std::ostream& out, const TransitHop& h)
{
return h.print(out, -1, -1);
}
/// configuration for a single hop when building a path
struct PathHopConfig : public util::IStateful
{

@ -55,4 +55,16 @@ namespace llarp
return true;
}
std::ostream&
PoW::print(std::ostream& stream, int level, int spaces) const
{
Printer printer(stream, level, spaces);
printer.printAttribute("pow timestamp", timestamp);
printer.printAttribute("lifetime", extendedLifetime);
printer.printAttribute("nonce", nonce);
return stream;
}
} // namespace llarp

@ -39,14 +39,15 @@ namespace llarp
return !(*this == other);
}
friend std::ostream&
operator<<(std::ostream& out, const PoW& p)
{
return out << "[pow timestamp=" << p.timestamp
<< " lifetime=" << p.extendedLifetime << " nonce=" << p.nonce
<< "]";
}
std::ostream&
print(std::ostream& stream, int level, int spaces) const;
};
inline std::ostream&
operator<<(std::ostream& out, const PoW& p)
{
return p.print(out, -1, -1);
}
} // namespace llarp
#endif

@ -7,6 +7,7 @@
#include <util/buffer.hpp>
#include <util/logger.hpp>
#include <util/mem.hpp>
#include <util/printer.hpp>
#include <util/time.hpp>
#include <fstream>
@ -382,4 +383,20 @@ namespace llarp
return *this;
}
std::ostream &
RouterContact::print(std::ostream &stream, int level, int spaces) const
{
Printer printer(stream, level, spaces);
printer.printAttribute("k", pubkey);
printer.printAttribute("updated", last_updated);
printer.printAttribute("netid", netID);
printer.printAttribute("v", version);
printer.printAttribute("ai", addrs);
printer.printAttribute("xi", exits);
printer.printAttribute("e", enckey);
printer.printAttribute("z", signature);
return stream;
}
} // namespace llarp

@ -40,10 +40,12 @@ namespace llarp
return !(*this == other);
}
friend std::ostream &
operator<<(std::ostream &out, const NetID &id)
std::ostream &
print(std::ostream &stream, int level, int spaces) const
{
return out << id.ToString();
Printer printer(stream, level, spaces);
printer.printValue(ToString());
return stream;
}
std::string
@ -56,6 +58,12 @@ namespace llarp
BEncode(llarp_buffer_t *buf) const;
};
inline std::ostream &
operator<<(std::ostream &out, const NetID &id)
{
return id.print(out, -1, -1);
}
/// RouterContact
struct RouterContact final : public IBEncodeMessage, public util::IStateful
{
@ -182,21 +190,8 @@ namespace llarp
return last_updated < other.last_updated;
}
friend std::ostream &
operator<<(std::ostream &out, const RouterContact &rc)
{
out << "[RouterContact k=" << rc.pubkey;
out << " updated=" << rc.last_updated;
out << " netid=" << rc.netID;
out << " v=" << rc.version;
out << " ai=[ ";
for(const auto &addr : rc.addrs)
out << addr << " ";
out << " ] xi=[ ";
for(const auto &xi : rc.exits)
out << xi << " ";
return out << " ] e=" << rc.enckey << " z=" << rc.signature << " ]";
}
std::ostream &
print(std::ostream &stream, int level, int spaces) const;
bool
Read(const char *fname);
@ -209,6 +204,12 @@ namespace llarp
VerifySignature(llarp::Crypto *crypto) const;
};
inline std::ostream &
operator<<(std::ostream &out, const RouterContact &rc)
{
return rc.print(out, -1, -1);
}
using RouterLookupHandler =
std::function< void(const std::vector< RouterContact > &) >;
} // namespace llarp

@ -105,12 +105,8 @@ namespace llarp
return Addr() < other.Addr();
}
friend std::ostream&
operator<<(std::ostream& out, const ServiceInfo& i)
{
return out << "[e=" << i.enckey << " s=" << i.signkey
<< " v=" << i.version << " x=" << i.vanity << "]";
}
std::ostream&
print(std::ostream& stream, int level, int spaces) const;
/// .loki address
std::string
@ -145,6 +141,12 @@ namespace llarp
private:
Address m_CachedAddr;
};
inline std::ostream&
operator<<(std::ostream& out, const ServiceInfo& i)
{
return i.print(out, -1, -1);
}
} // namespace service
} // namespace llarp

@ -65,5 +65,17 @@ namespace llarp
latency = 0;
expiresAt = 0;
}
std::ostream&
Introduction::print(std::ostream& stream, int level, int spaces) const
{
Printer printer(stream, level, spaces);
printer.printAttribute("k", router);
printer.printAttribute("p", pathID);
printer.printAttribute("v", version);
printer.printAttribute("x", expiresAt);
return stream;
}
} // namespace service
} // namespace llarp

@ -50,12 +50,8 @@ namespace llarp
~Introduction();
friend std::ostream&
operator<<(std::ostream& out, const Introduction& i)
{
return out << "k=" << i.router << " p=" << i.pathID
<< " v=" << i.version << " x=" << i.expiresAt;
}
std::ostream&
print(std::ostream& stream, int level, int spaces) const;
bool
BEncode(llarp_buffer_t* buf) const override;
@ -95,6 +91,12 @@ namespace llarp
}
};
};
inline std::ostream&
operator<<(std::ostream& out, const Introduction& i)
{
return i.print(out, -1, -1);
}
} // namespace service
} // namespace llarp

@ -179,5 +179,32 @@ namespace llarp
t = std::max(intro.expiresAt, t);
return t;
}
std::ostream&
IntroSet::print(std::ostream& stream, int level, int spaces) const
{
Printer printer(stream, level, spaces);
printer.printAttribute("A", A);
printer.printAttribute("I", I);
printer.printAttribute("K", K);
std::string _topic = topic.ToString();
if(!_topic.empty())
{
printer.printAttribute("topic", _topic);
}
else
{
printer.printAttribute("topic", topic);
}
printer.printAttribute("T", T);
printer.printAttribute("W", W);
printer.printAttribute("V", version);
printer.printAttribute("Z", Z);
return stream;
}
} // namespace service
} // namespace llarp

@ -114,32 +114,8 @@ namespace llarp
return T < other.T;
}
friend std::ostream&
operator<<(std::ostream& out, const IntroSet& i)
{
out << "A=[" << i.A << "] I=[";
for(const auto& intro : i.I)
{
out << intro << ", ";
}
out << "]";
out << "K=" << i.K;
auto topic = i.topic.ToString();
if(topic.size())
{
out << " topic=" << topic;
}
else
{
out << " topic=" << i.topic;
}
out << " T=" << i.T;
if(i.W)
{
out << " W=" << *i.W;
}
return out << " V=" << i.version << " Z=" << i.Z;
}
std::ostream&
print(std::ostream& stream, int level, int spaces) const;
llarp_time_t
GetNewestIntroExpiration() const;
@ -160,6 +136,12 @@ namespace llarp
Verify(llarp::Crypto* crypto, llarp_time_t now) const;
};
inline std::ostream&
operator<<(std::ostream& out, const IntroSet& i)
{
return i.print(out, -1, -1);
}
using IntroSetLookupHandler =
std::function< void(const std::vector< IntroSet >&) >;

@ -15,7 +15,7 @@ namespace llarp
parser.IterAll([&](const llarp::ConfigParser::String_t& name,
const llarp::ConfigParser::Section_t& section) {
llarp::service::Config::section_t values;
values.first = name;
values.first.assign(name.begin(), name.end());
for(const auto& item : section)
values.second.emplace_back(item.first, item.second);
services.emplace_back(values);

@ -82,5 +82,17 @@ namespace llarp
return CalculateAddress(m_CachedAddr.as_array());
}
std::ostream&
ServiceInfo::print(std::ostream& stream, int level, int spaces) const
{
Printer printer(stream, level, spaces);
printer.printAttribute("e", enckey);
printer.printAttribute("s", signkey);
printer.printAttribute("v", version);
printer.printAttribute("x", vanity);
return stream;
}
} // namespace service
} // namespace llarp

@ -4,6 +4,8 @@
#include <util/bencode.h>
#include <util/encode.hpp>
#include <util/logger.hpp>
#include <util/printer.hpp>
#include <util/traits.hpp>
#include <array>
#include <cstddef>
@ -257,6 +259,15 @@ namespace llarp
return std::string(HexEncode(*this, strbuf));
}
std::ostream&
print(std::ostream& stream, int level, int spaces) const
{
Printer printer(stream, level, spaces);
printer.printValue(ToHex());
return stream;
}
struct Hash
{
size_t
@ -273,7 +284,6 @@ namespace llarp
type; // why did we align to the nearest double-precision float
AlignedStorage val;
};
} // namespace llarp
#endif

@ -75,14 +75,10 @@ bencode_write_bytestring(llarp_buffer_t* buff, const void* data, size_t sz)
bool
bencode_write_uint64(llarp_buffer_t* buff, uint64_t i)
{
#ifndef __LP64__
if(!buff->writef("i%llu", i))
#else
#if(__APPLE__ && __MACH__)
#if !defined(__LP64__) || (__APPLE__ && __MACH__)
if(!buff->writef("i%llu", i))
#else
if(!buff->writef("i%lu", i))
#endif
#endif
{
return false;

@ -10,9 +10,11 @@ namespace llarp
{
struct ConfigParser
{
using String_t = llarp::string_view;
using Section_t = std::unordered_multimap< String_t, String_t >;
using Config_impl_t = std::unordered_map< String_t, Section_t >;
using String_t = llarp::string_view;
using Section_t =
std::unordered_multimap< String_t, String_t, string_view_hash >;
using Config_impl_t =
std::unordered_map< String_t, Section_t, string_view_hash >;
/// clear parser
void
Clear();

@ -1,6 +1,8 @@
#ifndef LLARP_UTIL_JSON_HPP
#define LLARP_UTIL_JSON_HPP
#include <util/string_view.hpp>
#include <rapidjson/document.h>
#include <rapidjson/writer.h>
@ -79,11 +81,6 @@ namespace llarp
using Writer = rapidjson::Writer< Stream >;
} // namespace json
#if __cplusplus >= 201703L
using string_view = std::string_view;
#else
using string_view = std::string;
#endif
namespace json
{
struct IParser

@ -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

@ -6,7 +6,9 @@
#include <string>
namespace llarp
{
using string_view = std::string_view;
using string_view = std::string_view;
using string_view_hash = std::hash< string_view >;
static std::string
string_view_string(const string_view& v)
{
@ -14,15 +16,17 @@ namespace llarp
}
} // namespace llarp
#else
#include <string>
#include <absl/hash/hash.h>
#include <absl/strings/string_view.h>
namespace llarp
{
using string_view = std::string;
using string_view = absl::string_view;
using string_view_hash = absl::Hash< string_view >;
static std::string
string_view_string(const string_view& v)
{
return v;
return std::string(v);
};
} // namespace llarp
#endif

@ -1,18 +1,13 @@
#ifndef LLARP_THREADING_HPP
#define LLARP_THREADING_HPP
#include <mutex>
#if defined(__MINGW32__) && !defined(_GLIBCXX_HAS_GTHREADS)
#if defined(RPI)
#error this should not be set
#endif
#define _MINGW32_NO_THREADS
#include <win32/threads/mingw.condition_variable.h>
#include <win32/threads/mingw.mutex.h>
#include <win32/threads/mingw.thread.h>
#else
// We only support posix threads:
// MSYS2 has a full native C++11 toolset, and a suitable
// cross-compilation system can be assembled on Linux and UNIX
// Last time i checked, Red Hat has this problem (no libpthread)
// Not sure about the other distros generally -rick
#include <condition_variable>
#include <thread>
#endif
#include <future>
#include <memory>
#include <cassert>

@ -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

@ -34,9 +34,11 @@ list(APPEND TEST_SRC
util/test_llarp_util_bits.cpp
util/test_llarp_util_encode.cpp
util/test_llarp_util_ini.cpp
util/test_llarp_util_printer.cpp
util/test_llarp_util_queue_manager.cpp
util/test_llarp_util_queue.cpp
util/test_llarp_util_thread_pool.cpp
util/test_llarp_util_traits.cpp
)
add_executable(${TEST_EXE}

@ -2,11 +2,12 @@
#include <util/ini.hpp>
struct TestINIParser : public ::testing::Test
{
struct TestINIParser : public ::testing::Test
{
llarp::ConfigParser parser;
void TearDown()
void
TearDown()
{
parser.Clear();
}
@ -21,7 +22,7 @@ TEST_F(TestINIParser, TestParseOneSection)
{
llarp::ConfigParser::Section_t sect;
// this is an anti pattern don't write this kind of code with configpaser
auto assertVisit = [&sect](const auto & section) -> bool {
auto assertVisit = [&sect](const auto& section) -> bool {
sect = section;
return true;
};
@ -34,7 +35,7 @@ TEST_F(TestINIParser, TestParseOneSection)
#if __cplusplus >= 201703L
ASSERT_STREQ(llarp::string_view_string(itr->second).c_str(), "val");
#else
ASSERT_STREQ(itr->second.c_str(), "val");
ASSERT_EQ(itr->second, "val");
#endif
}
@ -42,7 +43,7 @@ TEST_F(TestINIParser, TestParseSectionDuplicateKeys)
{
ASSERT_TRUE(parser.LoadString("[test]\nkey1=val1\nkey1=val2"));
size_t num = 0;
auto visit =[&num](const auto & section) -> bool {
auto visit = [&num](const auto& section) -> bool {
num = section.count("key1");
return true;
};
@ -52,5 +53,7 @@ TEST_F(TestINIParser, TestParseSectionDuplicateKeys)
TEST_F(TestINIParser, TestParseInvalid)
{
ASSERT_FALSE(parser.LoadString("srged5ghe5\nf34wtge5\nw34tgfs4ygsd5yg=4;\n#g4syhgd5\n"));
ASSERT_FALSE(
parser.LoadString("srged5ghe5\nf34wtge5\nw34tgfs4ygsd5yg=4;\n#"
"g4syhgd5\n"));
}

@ -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

@ -262,6 +262,7 @@
// AsmJS __asmjs__
// WebAssembly __wasm__
// Fuchsia __Fuchsia__
// OpenBSD __OpenBSD__
//
// Note that since Android defines both __ANDROID__ and __linux__, one
// may probe for either Linux or Android by simply testing for __linux__.
@ -275,7 +276,7 @@
#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
defined(__ros__) || defined(__native_client__) || defined(__asmjs__) || \
defined(__wasm__) || defined(__Fuchsia__) || defined(__sun) || \
defined(__ASYLO__)
defined(__ASYLO__) || defined(__OpenBSD__)
#define ABSL_HAVE_MMAP 1
#endif
@ -286,7 +287,7 @@
#ifdef ABSL_HAVE_PTHREAD_GETSCHEDPARAM
#error ABSL_HAVE_PTHREAD_GETSCHEDPARAM cannot be directly set
#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
defined(__ros__)
defined(__ros__) || defined(__OpenBSD__)
#define ABSL_HAVE_PTHREAD_GETSCHEDPARAM 1
#endif
@ -311,7 +312,7 @@
// platforms.
#ifdef ABSL_HAVE_SEMAPHORE_H
#error ABSL_HAVE_SEMAPHORE_H cannot be directly set
#elif defined(__linux__) || defined(__ros__)
#elif defined(__linux__) || defined(__ros__) || defined(__OpenBSD__)
#define ABSL_HAVE_SEMAPHORE_H 1
#endif

@ -125,7 +125,7 @@ static bool SetupAlternateStackOnce() {
#else
const size_t page_mask = sysconf(_SC_PAGESIZE) - 1;
#endif
size_t stack_size = (std::max(SIGSTKSZ, 65536) + page_mask) & ~page_mask;
size_t stack_size = (std::max((unsigned)SIGSTKSZ, (unsigned)65536) + page_mask) & ~page_mask;
#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
defined(THREAD_SANITIZER)
// Account for sanitizer instrumentation requiring additional stack space.

@ -24,19 +24,6 @@
/*#include <strsafe.h>*/
#include "tuntap.h"
// io packet for TUN read/write
// this _should_ be the same size as
// the corresponding C++ struct
struct asio_evt_pkt
{
OVERLAPPED pkt; // must be first, since this is part of the IO call
_Bool write; // true, or false if read pkt
size_t sz; // if this doesn't match what is in the packet, note the error
};
// function from c++
struct asio_evt_pkt *
getTunEventPkt();
// DDK macros
#define CTL_CODE(DeviceType, Function, Method, Access) \
@ -339,7 +326,8 @@ tuntap_get_mtu(struct device *dev)
return 0;
}
/* I _think_ it's possible to do this on windows, might be a setting in the reg db */
/* I _think_ it's possible to do this on windows, might be a setting in the reg
* db */
int
tuntap_set_mtu(struct device *dev, int mtu)
{
@ -417,7 +405,8 @@ tuntap_sys_set_ipv4(struct device *dev, t_tun_in_addr *s, uint32_t mask)
return 0;
}
/* To be implemented at a later time? I'm not quite certain TAP-Windows v9.x supports inet6 */
/* To be implemented at a later time? I'm not quite certain TAP-Windows v9.x
* supports inet6 */
int
tuntap_sys_set_ipv6(struct device *dev, t_tun_in6_addr *s, uint32_t mask)
{
@ -429,7 +418,8 @@ tuntap_sys_set_ipv6(struct device *dev, t_tun_in6_addr *s, uint32_t mask)
return -1;
}
/* Anything below this comment is unimplemented, either due to lack of OS support, or duplicated functionality elsewhere */
/* Anything below this comment is unimplemented, either due to lack of OS
* support, or duplicated functionality elsewhere */
int
tuntap_read(struct device *dev, void *buf, size_t size)
{

@ -69,21 +69,27 @@ Source: "{tmp}\tuntapv9.7z"; DestDir: "{app}"; Flags: ignoreversion external del
Source: "{tmp}\tuntapv9_n6.7z"; DestDir: "{app}"; Flags: ignoreversion external deleteafterinstall skipifsourcedoesntexist; MinVersion: 0,6.0
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
Source: "regdbhelper.dll"; Flags: dontcopy
[UninstallDelete]
Type: filesandordirs; Name: "{app}\tap-windows*"
Type: filesandordirs; Name: "{app}\inet6_driver"; MinVersion: 0,5.0; OnlyBelowVersion: 0,5.1
Type: filesandordirs; Name: "{userappdata}\.lokinet"
[UninstallRun]
Filename: "{app}\tap-windows-9.21.2\remove.bat"; WorkingDir: "{app}\tap-windows-9.21.2"; MinVersion: 0,6.0; Flags: runascurrentuser
Filename: "{app}\tap-windows-9.9.2\remove.bat"; WorkingDir: "{app}\tap-windows-9.9.2"; OnlyBelowVersion: 0,6.0; Flags: runascurrentuser
[UninstallRun]
Filename: "{app}\tap-windows-9.21.2\remove.bat"; WorkingDir: "{app}\tap-windows-9.21.2"; Flags: runascurrentuser waituntilterminated skipifdoesntexist; MinVersion: 0,6.0
Filename: "{app}\tap-windows-9.9.2\remove.bat"; WorkingDir: "{app}\tap-windows-9.9.2"; Flags: runascurrentuser waituntilterminated skipifdoesntexist; OnlyBelowVersion: 0,6.0
[Dirs]
Name: "{userappdata}\.lokinet"
[Code]
var
TapInstalled: Boolean;
function reg_query_helper(): Integer;
external 'reg_query_helper@files:regdbhelper.dll cdecl setuponly';
procedure CurStepChanged(CurStep: TSetupStep);
var
Version: TWindowsVersion;
@ -93,10 +99,15 @@ begin
GetWindowsVersionEx(Version);
if Version.NTPlatform and (Version.Major = 5) and (Version.Minor = 0) and (FileExists(ExpandConstant('{tmp}\inet6.7z')) = true) then
// I need a better message...
MsgBox('Restart your computer, then set up IPv6 in Network Connections.\n[Adapter] > Properties > Install... > Protocol > Microsoft IPv6 Driver...', mbInformation, MB_OK);
MsgBox('Restart your computer, then set up IPv6 in Network Connections. [Adapter] > Properties > Install... > Protocol > Microsoft IPv6 Driver...', mbInformation, MB_OK);
end;
end;
function IsTapInstalled(): Boolean;
begin
Result := TapInstalled;
end;
procedure InitializeWizard();
var
Version: TWindowsVersion;
@ -105,8 +116,17 @@ begin
GetWindowsVersionEx(Version);
// if we already have a generic openvpn tap driver installed, then skip all the downloading
// lokinet is coded to enumerate all generic tap virtual adapters and use the first free device
if (FileExists(ExpandConstant('{sys}\drivers\tap0901.sys')) = false) then
begin
if (reg_query_helper() = 0) then
begin
TapInstalled := false;
end
else
begin
TapInstalled := true;
end;
if (reg_query_helper() = 0) then
begin
if Version.NTPlatform and
(Version.Major < 6) then
begin
@ -144,10 +164,10 @@ Filename: "{app}\{#MyAppExeName}"; Flags: nowait postinstall skipifsilent; Descr
Filename: "{tmp}\7z.exe"; Parameters: "x tuntapv9.7z"; WorkingDir: "{app}"; Flags: runascurrentuser waituntilterminated skipifdoesntexist; Description: "extract TUN/TAP-v9 driver"; StatusMsg: "Extracting driver..."; OnlyBelowVersion: 0, 6.0
Filename: "{tmp}\7z.exe"; Parameters: "x tuntapv9_n6.7z"; WorkingDir: "{app}"; Flags: runascurrentuser waituntilterminated skipifdoesntexist; Description: "extract TUN/TAP-v9 driver"; StatusMsg: "Extracting driver..."; MinVersion: 0, 6.0
Filename: "{tmp}\7z.exe"; Parameters: "x inet6.7z"; WorkingDir: "{app}"; Flags: skipifdoesntexist runascurrentuser waituntilterminated skipifdoesntexist; Description: "extract inet6 driver"; StatusMsg: "Extracting IPv6 driver..."; MinVersion: 0, 5.0; OnlyBelowVersion: 0, 5.1
Filename: "{tmp}\lokinet-bootstrap.exe"; Parameters:"https://i2p.rocks/bootstrap.signed %APPDATA%\.lokinet\bootstrap.signed"; WorkingDir: "{app}"; Flags: runascurrentuser waituntilterminated; Description: "bootstrap dht"; StatusMsg: "Downloading initial RC..."
Filename: "{tmp}\lokinet-bootstrap.exe"; Parameters:"https://i2p.rocks/bootstrap.signed {userappdata}\.lokinet\bootstrap.signed"; WorkingDir: "{app}"; Flags: runascurrentuser waituntilterminated; Description: "bootstrap dht"; StatusMsg: "Downloading initial RC..."
; then ask to install drivers
Filename: "{app}\tap-windows-9.9.2\install.bat"; WorkingDir: "{app}\tap-windows-9.9.2\"; Flags: runascurrentuser waituntilterminated skipifdoesntexist; Description: "Install TUN/TAP-v9 driver"; StatusMsg: "Installing driver..."; OnlyBelowVersion: 0, 6.0; Check: not FileExists(ExpandConstant('{sys}\drivers\tap0901.sys'))
Filename: "{app}\tap-windows-9.21.2\install.bat"; WorkingDir: "{app}\tap-windows-9.21.2\"; Flags: runascurrentuser waituntilterminated skipifdoesntexist; Description: "Install TUN/TAP-v9 driver"; StatusMsg: "Installing driver..."; MinVersion: 0, 6.0; Check: not FileExists(ExpandConstant('{sys}\drivers\tap0901.sys'))
Filename: "{app}\tap-windows-9.9.2\install.bat"; WorkingDir: "{app}\tap-windows-9.9.2\"; Flags: runascurrentuser waituntilterminated skipifdoesntexist; Description: "Install TUN/TAP-v9 driver"; StatusMsg: "Installing driver..."; OnlyBelowVersion: 0, 6.0; Check: not IsTapInstalled
Filename: "{app}\tap-windows-9.21.2\install.bat"; WorkingDir: "{app}\tap-windows-9.21.2\"; Flags: runascurrentuser waituntilterminated skipifdoesntexist; Description: "Install TUN/TAP-v9 driver"; StatusMsg: "Installing driver..."; MinVersion: 0, 6.0; Check: not IsTapInstalled
; install inet6 if not present. (I'd assume netsh displays something helpful if inet6 is already set up and configured.)
; if it doesn't exist, then the inet6 driver appears to be installed
Filename: "{app}\inet6_driver\setup\hotfix.exe"; Parameters: "/m /z"; WorkingDir: "{app}\inet6_driver\setup\"; Flags: runascurrentuser waituntilterminated skipifdoesntexist; Description: "Install IPv6 driver"; StatusMsg: "Installing IPv6..."; OnlyBelowVersion: 0, 5.1; Check: not FileExists(ExpandConstant('{sys}\drivers\tcpip6.sys'))

@ -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…
Cancel
Save