Some minor changes

This commit is contained in:
WolverinDEV 2019-10-13 17:12:50 +02:00
parent 7822d20efe
commit e39f01bde5
8 changed files with 198 additions and 47 deletions

@ -1 +1 @@
Subproject commit 2c928229b1aab0306a02d2b7820fd93f3a3623b9 Subproject commit 6b6e8774b8cb37158d6ee09658559e7dff4dd1c3

View File

@ -4,6 +4,7 @@
#include <event2/thread.h> #include <event2/thread.h>
#include <random> #include <random>
#include <ed25519/ed25519.h> #include <ed25519/ed25519.h>
#include <misc/base64.h>
using namespace std; using namespace std;
using namespace std::chrono; using namespace std::chrono;
@ -23,12 +24,43 @@ using namespace license;
inline bool isValid() { return (end.time_since_epoch().count() == 0 || std::chrono::system_clock::now() < this->end); } inline bool isValid() { return (end.time_since_epoch().count() == 0 || std::chrono::system_clock::now() < this->end); }
}; };
*/ */
std::array<uint8_t, 195> intermediate_key{0x02, 0x00, 0x2d, 0x6b, 0xe3, 0x4d, 0x3c, 0xbb, 0x19, 0x1e, 0x46, 0x25, 0x72, 0x22, 0xa3, 0x53, 0x6d, 0x2d, 0xc3, 0xd1, 0x2c, 0xc8, 0xea, 0xf2, 0xf8, 0xe5, 0xd5, 0x0f, 0x6c, 0x7f, 0xeb, 0x63, 0x8a, 0xc4, 0x37, 0xb2, 0x33, 0x5e, 0x06, 0x31, 0xcf, 0x2c, 0xc6, 0xbe, 0x5e, 0x9c, 0xf1, 0xe6, 0xb3, 0xc3, 0x69, 0xd7, 0xf0, 0x05, 0x90, 0x2f, 0x65, 0x03, 0x60, 0x12, 0xa0, 0x20, 0x83, 0xe2, 0x6b, 0x4f, 0x46, 0xab, 0xfd, 0xa6, 0x22, 0x0b, 0x11, 0x9a, 0x34, 0xfe, 0x7b, 0xa3, 0x1b, 0x12, 0xce, 0x30, 0xf7, 0x0a, 0x32, 0x26, 0x23, 0x10, 0xfe, 0x58, 0xb6, 0xc7, 0x5d, 0x3d, 0xc6, 0x14, 0xdc, 0x10, 0xa0, 0xc0, 0x78, 0x10, 0x45, 0x41, 0x36, 0x1a, 0x1c, 0x9f, 0x60, 0x25, 0xd9, 0x69, 0xc9, 0x0b, 0xb7, 0xb4, 0x64, 0xab, 0x6c, 0x22, 0xff, 0xaf, 0x86, 0x26, 0x94, 0xcc, 0x7f, 0x59, 0x52, 0xa3, 0x56, 0x7f, 0x3e, 0x86, 0x04, 0x4c, 0xe0, 0x0e, 0xb3, 0xb1, 0x23, 0x51, 0xf7, 0xf0, 0x14, 0x5d, 0xfd, 0x48, 0xfb, 0x16, 0xe6, 0xc6, 0xca, 0xf2, 0x8d, 0xc8, 0xce, 0xf1, 0x2b, 0x12, 0x9e, 0xd1, 0x7a, 0x80, 0x3a, 0x9b, 0x46, 0xe7, 0xca, 0x34, 0x04, 0xae, 0x3d, 0x12, 0xcd, 0x4a, 0xc6, 0xe1, 0xf1, 0xe4, 0xd8, 0xca, 0x68, 0x36, 0xd3, 0x94, 0x0d, 0xef, 0x93, 0x86, 0xb9, 0x3a, 0xa4, 0x10, 0x52};
int main(int ac, char** av){ int main(int ac, char** av){
auto state = evthread_use_pthreads(); auto state = evthread_use_pthreads();
assert(state == 0); assert(state == 0);
string error; string error;
uint8_t errc = 0;
#if 0
std::array<uint8_t, 32> prv_key{};
auto license = v2::License::create({}, prv_key);
assert(license->private_data_editable());
license->push_entry<v2::hierarchy::Intermediate>(system_clock::now(), system_clock::now() + hours{24 * 365 * 1000}, "TeaSpeak (Test)");
assert(license->write_private_data({0}));
error = license->write(errc);
assert(errc == 0);
cout << "Intermediate: " << hex;
for(size_t index = 0; index < error.length(); index++) {
uint8_t e = error[index];
cout << " 0x" << (e <= 0xF ? "0" : "") << (uint32_t) e << ",";
}
cout << endl;
cout << "Intermediate: " << base64::encode(error) << endl;
#endif
auto license = v2::License::read(intermediate_key.data(), intermediate_key.size(), errc);
license->push_entry<v2::hierarchy::Server>(system_clock::now(), system_clock::now() + hours{24 * 365 * 1000}, "TeaSpeak Official server", "contact@teaspeak.de");
assert(license->write_private_data({0}));
error = license->write(errc);
assert(errc == 0);
__asm__("nop");
cout << "Errc: " << errc << endl;
cout << "Write: " << base64::encode(error) << endl;
#if 0
std::array<uint8_t, 32> private_key, public_key; std::array<uint8_t, 32> private_key, public_key;
std::random_device rd; std::random_device rd;
@ -51,6 +83,7 @@ int main(int ac, char** av){
cout << endl; cout << endl;
return true; return true;
#endif
#if 0 #if 0
srand(system_clock::now().time_since_epoch().count()); srand(system_clock::now().time_since_epoch().count());

View File

@ -119,7 +119,6 @@ namespace license::v2 {
} }
std::shared_ptr<License> License::create(const std::vector<std::shared_ptr<const license::v2::HierarchyEntry>> &hierarchy, const std::array<uint8_t, 32> &prv_key) { std::shared_ptr<License> License::create(const std::vector<std::shared_ptr<const license::v2::HierarchyEntry>> &hierarchy, const std::array<uint8_t, 32> &prv_key) {
assert(!hierarchy.empty());
auto result = shared_ptr<License>(new License{}); auto result = shared_ptr<License>(new License{});
result->_version = 2; result->_version = 2;
@ -175,20 +174,24 @@ namespace license::v2 {
/* "decode" the data */ /* "decode" the data */
{ {
auto index = 0; auto index = sizeof(header);
while(index + 4 < decoded_buffer_length) { auto index_decoded = 0;
auto& memory = *(uint32_t*) (&*decoded_buffer + index); while(index + 4 < length) {
auto& memory = *(uint32_t*) (&*decoded_buffer + index_decoded);
memory = *(uint32_t*) (buffer + index); memory = *(uint32_t*) (buffer + index);
memory ^= (uint32_t) crypt_key_gen(); memory ^= (uint32_t) crypt_key_gen();
index += 4; index += 4;
index_decoded += 4;
} }
while(index < decoded_buffer_length) { while(index < length) {
auto& memory = *(uint8_t*) (&*decoded_buffer + index); auto& memory = *(uint8_t*) (&*decoded_buffer + index_decoded);
memory = *(uint8_t*) (buffer + index); memory = *(uint8_t*) (buffer + index);
memory ^= (uint8_t) crypt_key_gen(); memory ^= (uint8_t) crypt_key_gen();
index++; index++;
index_decoded++;
} }
} }
memcpy(&body_header, &*decoded_buffer, sizeof(body_header)); memcpy(&body_header, &*decoded_buffer, sizeof(body_header));
if(decoded_buffer_length < sizeof(body_header) + body_header.length_hierarchy + body_header.length_private_data) { if(decoded_buffer_length < sizeof(body_header) + body_header.length_hierarchy + body_header.length_private_data) {
error = 2; /* buffer too small */ error = 2; /* buffer too small */
@ -243,7 +246,7 @@ namespace license::v2 {
return result; return result;
} }
std::string License::write(int &error) { std::string License::write(uint8_t &error) {
if(!this->private_data || !this->private_buffer_length) { if(!this->private_data || !this->private_buffer_length) {
error = 2; /* missing private data */ error = 2; /* missing private data */
return ""; return "";
@ -278,8 +281,8 @@ namespace license::v2 {
error = 3; /* failed to write hierarchy */ error = 3; /* failed to write hierarchy */
return ""; return "";
} }
body_header.length_hierarchy = offset - begin_offset;
body_header.length_hierarchy = offset - begin_offset;
digest::sha1((char*) &*buffer + begin_offset, body_header.length_hierarchy, body_header.checksum_hierarchy); digest::sha1((char*) &*buffer + begin_offset, body_header.length_hierarchy, body_header.checksum_hierarchy);
} }
@ -335,6 +338,8 @@ namespace license::v2 {
} }
bool License::write_private_data(const LicensePrivateWriteOptions& write_options) { bool License::write_private_data(const LicensePrivateWriteOptions& write_options) {
if(this->_hierarchy.empty()) return false;
uint8_t private_key[64]; //ed25519_sign requires 64 bytes (may it expects a public key in front?) uint8_t private_key[64]; //ed25519_sign requires 64 bytes (may it expects a public key in front?)
if(!this->private_data->calculate_private_key(private_key, this->_hierarchy.size() - 1)) if(!this->private_data->calculate_private_key(private_key, this->_hierarchy.size() - 1))
return false; return false;
@ -409,7 +414,7 @@ namespace license::v2 {
std::array<uint8_t, 32> result{}; std::array<uint8_t, 32> result{};
ge_p3_tobytes((uint8_t*) result.data(), &parent_key); ge_p3_tobytes((uint8_t*) result.data(), &parent_key);
return {}; return result;
} }
bool License::push_entry(const std::shared_ptr<const HierarchyEntry> &entry, size_t* index) { bool License::push_entry(const std::shared_ptr<const HierarchyEntry> &entry, size_t* index) {
@ -542,7 +547,7 @@ namespace license::v2 {
bool LicensePrivate::write(uint8_t *buffer, size_t &offset, size_t length, const LicensePrivateWriteOptions& options) { bool LicensePrivate::write(uint8_t *buffer, size_t &offset, size_t length, const LicensePrivateWriteOptions& options) {
if(options.precalculated_key_index < -1) { if(options.precalculated_key_index < -1) {
if(buffer) { if(buffer) {
if((offset + 2) < length) return false; if((offset + 2) > length) return false;
*(buffer + offset++) = 0; /* no precalculated private key */ *(buffer + offset++) = 0; /* no precalculated private key */
*(buffer + offset++) = 0; /* no raw private keys */ *(buffer + offset++) = 0; /* no raw private keys */
} else { } else {
@ -553,23 +558,24 @@ namespace license::v2 {
if(index < 0) return false; /* we will NEVER write the root key */ if(index < 0) return false; /* we will NEVER write the root key */
if(buffer) { if(buffer) {
if((offset + 2 + 32) < length) return false; if((offset + 2 + 32) > length) return false;
*(buffer + offset++) = 1; *(buffer + offset++) = 1;
{ {
*(buffer + offset++) = index; *(buffer + offset++) = index;
if(!this->calculate_private_key(buffer + offset, index)) if(!this->calculate_private_key(buffer + offset, index))
return false; return false;
offset += 32;
} }
if((offset + 1) < length) return false; if((offset + 1) > length) return false;
auto& private_key_count = *(buffer + offset++); auto& private_key_count = *(buffer + offset++);
private_key_count = 0; private_key_count = 0;
for(auto& [key_index, key] : this->private_keys) { for(auto& [key_index, key] : this->private_keys) {
if(key_index <= index) if(key_index <= index)
continue; continue;
if((offset + 1 + key.size()) < length) return false; if((offset + 1 + key.size()) > length) return false;
*(buffer + offset++) = key_index; *(buffer + offset++) = key_index;
memcpy(buffer + offset, key.data(), key.size()); memcpy(buffer + offset, key.data(), key.size());
@ -592,12 +598,12 @@ namespace license::v2 {
} }
if(buffer) { if(buffer) {
if((offset + 4) < length) return false; if((offset + 4) > length) return false;
*(uint32_t*) (buffer + offset) = this->meta_data.size(); *(uint32_t*) (buffer + offset) = this->meta_data.size();
offset += 4; offset += 4;
for(auto& [key, value] : this->meta_data) { for(auto& [key, value] : this->meta_data) {
if((offset + 3 + key.length() + value.length()) < length) return false; if((offset + 3 + key.length() + value.length()) > length) return false;
*(buffer + offset++) = key.length(); *(buffer + offset++) = key.length();
*(uint16_t*)(buffer + offset) = value.length(); *(uint16_t*)(buffer + offset) = value.length();
@ -625,7 +631,7 @@ namespace license::v2 {
auto hierarchy = handle->hierarchy(); auto hierarchy = handle->hierarchy();
auto base_index = this->precalculated_private_key_index; auto base_index = this->precalculated_private_key_index;
if(base_index >= hierarchy.size()) return false; if(base_index >= (int64_t) hierarchy.size()) return false;
if(base_index < -1) return true; /* means we don't have a private key */ if(base_index < -1) return true; /* means we don't have a private key */
while(base_index < hierarchy.size()) { while(base_index < hierarchy.size()) {
@ -635,12 +641,12 @@ namespace license::v2 {
return true; return true;
} }
bool LicensePrivate::private_key_calculable(uint8_t index) const { bool LicensePrivate::private_key_calculable(int index) const {
auto handle = this->_handle.lock(); auto handle = this->_handle.lock();
if(!handle) return false; if(!handle) return false;
auto hierarchy = handle->hierarchy(); auto hierarchy = handle->hierarchy();
if(index >= hierarchy.size()) return false; if(index >= (int64_t) hierarchy.size()) return false;
auto base_index = this->precalculated_private_key_index; auto base_index = this->precalculated_private_key_index;
@ -726,6 +732,9 @@ namespace license::v2 {
if(!result->allocate_read_body(body_length)) return nullptr; if(!result->allocate_read_body(body_length)) return nullptr;
if(body_length > 0) { if(body_length > 0) {
result->allocate_read_body(body_length);
if(!result->read_body) return nullptr;
result->read_body_length = body_length;
memcpy(result->read_body, buffer + offset, body_length); memcpy(result->read_body, buffer + offset, body_length);
offset += body_length; offset += body_length;
} }
@ -779,11 +788,11 @@ namespace license::v2 {
return true; return true;
} }
size_t length = 42 + this->read_body_length, offset = 0; size_t length = 43 + this->read_body_length, offset = 0;
auto buffer = (uint8_t*) malloc(length); auto buffer = unique_ptr<uint8_t, decltype(::free)*>((uint8_t*) malloc(length), ::free);
if(!buffer) return false; if(!buffer) return false;
if(this->write(buffer, offset, length)) { if(this->write(&*buffer, offset, length)) {
digest::sha512((char*) buffer, length, this->_hash.data()); digest::sha512((char*) &*buffer, length, this->_hash.data());
this->_hash_set = true; this->_hash_set = true;
} }
@ -800,14 +809,53 @@ namespace license::v2 {
std::shared_ptr<const HierarchyEntry> Intermediate::create(const uint8_t *pub_key, const std::chrono::system_clock::time_point &begin, const std::chrono::system_clock::time_point & end, const std::string &description) { std::shared_ptr<const HierarchyEntry> Intermediate::create(const uint8_t *pub_key, const std::chrono::system_clock::time_point &begin, const std::chrono::system_clock::time_point & end, const std::string &description) {
assert(description.size() < 256); assert(description.size() < 256);
auto result = shared_ptr<HierarchyEntry>(new HierarchyEntry{hierarchy::Intermediate::type, pub_key, begin, end}); auto buffer_length = description.size() + 1;
if(!result || !result->allocate_read_body(description.size() + 1)) return nullptr; uint8_t* buffer;
memcpy(result->read_body + 1, description.data(), description.length()); auto result = BodyInterpreter::_create<Intermediate>(pub_key, begin, end, buffer_length, buffer);
*result->read_body = (uint8_t) description.length(); if(!result) return nullptr;
memcpy(buffer + 1, description.data(), description.length());
*buffer = (uint8_t) description.length();
return result; return result;
} }
bool Server::has_username() {
return *(this->_memory + 1) > 0;
}
std::string_view Server::contact_email() {
return {(const char*) this->_memory + 2, *this->_memory};
}
std::string_view Server::username() {
return {(const char*) this->_memory + 2 + *this->_memory, *(this->_memory + 1)};
}
std::shared_ptr<const HierarchyEntry> Server::create(const uint8_t *pub_key, const std::chrono::system_clock::time_point &begin, const std::chrono::system_clock::time_point & end, const std::string &email, const std::optional<std::string> &username) {
assert(email.size() < 256);
assert(!username.has_value() || username->size() < 256);
auto buffer_length = 2 + email.size() + (username.has_value() ? username->length() : 0);
uint8_t* buffer;
auto result = BodyInterpreter::_create<Intermediate>(pub_key, begin, end, buffer_length, buffer);
if(!result) return nullptr;
memcpy(buffer + 2, email.data(), email.length());
*buffer = (uint8_t) email.length();
if(username.has_value())
memcpy(buffer + 2 + email.length(), username->data(), username->length());
*(buffer + 1) = (uint8_t) (username.has_value() ? username->length() : 0);
return result;
}
std::shared_ptr<const HierarchyEntry> Ephemeral::create(const uint8_t *pub_key, const std::chrono::system_clock::time_point &begin, const std::chrono::system_clock::time_point & end) {
uint8_t* buffer;
return BodyInterpreter::_create<Ephemeral>(pub_key, begin, end, 0, buffer);
}
} }
static int test() { static int test() {

View File

@ -84,7 +84,7 @@ namespace license {
std::array<uint8_t, 32> generate_public_key(const uint8_t* /* public key root */, int /* length */ = -1) const; std::array<uint8_t, 32> generate_public_key(const uint8_t* /* public key root */, int /* length */ = -1) const;
std::string write(int& /* error */); std::string write(uint8_t& /* error */);
bool private_data_editable() const; bool private_data_editable() const;
bool write_private_data(const LicensePrivateWriteOptions& /* write options */); bool write_private_data(const LicensePrivateWriteOptions& /* write options */);
@ -128,7 +128,7 @@ namespace license {
void register_raw_private_key(uint8_t /* index */, const uint8_t* /* key */); void register_raw_private_key(uint8_t /* index */, const uint8_t* /* key */);
bool has_raw_private_key(uint8_t /* index */) const; bool has_raw_private_key(uint8_t /* index */) const;
bool private_key_calculable(uint8_t /* index */) const; bool private_key_calculable(int /* index */) const;
bool calculate_private_key(uint8_t* /* response */, uint8_t /* index */) const; bool calculate_private_key(uint8_t* /* response */, uint8_t /* index */) const;
private: private:
std::weak_ptr<License> _handle; std::weak_ptr<License> _handle;
@ -141,11 +141,11 @@ namespace license {
}; };
namespace hierarchy { namespace hierarchy {
struct Intermediate; struct BodyInterpreter;
} }
struct HierarchyEntry { struct HierarchyEntry {
friend struct hierarchy::Intermediate; friend struct hierarchy::BodyInterpreter;
public: public:
~HierarchyEntry(); ~HierarchyEntry();
@ -172,12 +172,12 @@ namespace license {
template <typename I> template <typename I>
inline I interpret_as() const { inline I interpret_as() const {
assert(this->interpret_as<I>()); assert(this->interpretable_as<I>());
return I{this->read_body, this->read_body_length}; return I{this->read_body, this->read_body_length};
} }
template <typename I> template <typename I>
inline bool interpretable_as() const { return I::type == this->_entry_type; } inline bool interpretable_as() const { return I::_type == this->_entry_type; }
inline bool hash(uint8_t* /* hash result [64] */) const; inline bool hash(uint8_t* /* hash result [64] */) const;
protected: protected:
@ -207,20 +207,54 @@ namespace license {
}; };
namespace hierarchy { namespace hierarchy {
struct type {
enum value : uint8_t {
Intermediate = 1,
Server = 2,
Ephemeral = 8
};
static constexpr const char* name(const value& value) {
switch (value) {
case Intermediate:
return "Intermediate";
case Server:
return "Server";
case Ephemeral:
return "Ephemeral";
default:
return "Unknown";
}
}
};
struct BodyInterpreter { struct BodyInterpreter {
public: public:
BodyInterpreter() = delete; BodyInterpreter() = delete;
protected: protected:
template <typename T>
static std::shared_ptr<HierarchyEntry> _create(
const uint8_t *pub_key,
const std::chrono::system_clock::time_point &begin,
const std::chrono::system_clock::time_point &end,
size_t buffer_size, uint8_t*& buffer_ptr
) {
auto result = std::shared_ptr<HierarchyEntry>(new HierarchyEntry{T::_type, pub_key, begin, end});
if(!result || !result->allocate_read_body(buffer_size)) return nullptr;
result->read_body_length = buffer_size;
buffer_ptr = result->read_body;
return result;
}
BodyInterpreter(const uint8_t* memory, size_t length) { this->_memory = memory; this->_length = length; } BodyInterpreter(const uint8_t* memory, size_t length) { this->_memory = memory; this->_length = length; }
const uint8_t* _memory = nullptr; const uint8_t* _memory = nullptr;
size_t _length = 0; size_t _length = 0;
}; };
struct Intermediate : public BodyInterpreter { struct Intermediate : public BodyInterpreter {
friend struct HierarchyEntry;
public: public:
static constexpr uint8_t type = 1; static constexpr uint8_t _type = type::Intermediate;
static std::shared_ptr<const HierarchyEntry> create( static std::shared_ptr<const HierarchyEntry> create(
const uint8_t* /* public key */, const uint8_t* /* public key */,
const std::chrono::system_clock::time_point& /* begin */, const std::chrono::system_clock::time_point& /* begin */,
@ -232,6 +266,38 @@ namespace license {
private: private:
Intermediate(const uint8_t* memory, size_t length) : BodyInterpreter(memory, length) {} Intermediate(const uint8_t* memory, size_t length) : BodyInterpreter(memory, length) {}
}; };
struct Server : public BodyInterpreter {
public:
static constexpr uint8_t _type = type::Server;
static std::shared_ptr<const HierarchyEntry> create(
const uint8_t* /* public key */,
const std::chrono::system_clock::time_point& /* begin */,
const std::chrono::system_clock::time_point& /* end */,
const std::string& /* email */,
const std::optional<std::string>& /* value */
);
std::string_view contact_email();
bool has_username();
std::string_view username();
private:
Server(const uint8_t* memory, size_t length) : BodyInterpreter(memory, length) {}
};
struct Ephemeral : public BodyInterpreter {
public:
static constexpr uint8_t _type = type::Ephemeral;
static std::shared_ptr<const HierarchyEntry> create(
const uint8_t* /* public key */,
const std::chrono::system_clock::time_point& /* begin */,
const std::chrono::system_clock::time_point& /* end */
);
private:
Ephemeral(const uint8_t* memory, size_t length) : BodyInterpreter(memory, length) {}
};
} }
} }

View File

@ -160,7 +160,7 @@ bool ConnectedClient::notifyClientPermList(ClientDbId cldbid, const std::shared_
bool ConnectedClient::notifyChannelGroupList() { bool ConnectedClient::notifyChannelGroupList() {
Command cmd(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifychannelgrouplist" : ""); Command cmd(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifychannelgrouplist" : "");
int index = 0; int index = 0;
for (const auto &group : (this->server ? this->server->groups : serverInstance->getGroupManager().get())->availableChannelGroups(false)) { for (const auto &group : (this->server ? this->server->groups : serverInstance->getGroupManager().get())->availableChannelGroups(true)) {
if(group->target() == GroupTarget::GROUPTARGET_CHANNEL) { if(group->target() == GroupTarget::GROUPTARGET_CHANNEL) {
cmd[index]["cgid"] = group->groupId(); cmd[index]["cgid"] = group->groupId();
} else { } else {

View File

@ -106,6 +106,19 @@ void VoiceIOManager::adjustExecutors(size_t size) {
} }
} }
IOEventLoop::~IOEventLoop() {
if(this->executor.joinable()) {
auto handle = this->executor.native_handle();
timespec timeout{};
clock_gettime(CLOCK_REALTIME, &timeout);
timeout.tv_sec += 5;
auto join_result = pthread_timedjoin_np(handle, nullptr, &timeout);
if(join_result == EBUSY)
logError(LOG_INSTANCE, "Failed to shutdown IO event loop. Error: {}", join_result);
}
assert(!this->executor.joinable());
}
/** /**
* @warning executor lock must be locked! * @warning executor lock must be locked!
*/ */

View File

@ -53,16 +53,7 @@ namespace ts {
struct IOEventLoop { struct IOEventLoop {
IOEventLoop() = default; IOEventLoop() = default;
~IOEventLoop() { ~IOEventLoop();
if(this->executor.joinable()) {
auto handle = this->executor.native_handle();
timespec timeout{};
clock_gettime(CLOCK_REALTIME, &timeout);
timeout.tv_sec += 5;
auto join_result = pthread_timedjoin_np(handle, nullptr, &timeout);
}
assert(!this->executor.joinable());
}
int bound_thread = -1; /* -1 represents that this loop is bound to no thread at all */ int bound_thread = -1; /* -1 represents that this loop is bound to no thread at all */

2
shared

@ -1 +1 @@
Subproject commit cb73d9df3258eaf59e4799c9e54d5f865e891734 Subproject commit e30c03029fb2be1a84a2706cf9bdd4f28cce9051