blob: 14526709e1dededa214cd3fae5b91dd0de6b0bea [file] [log] [blame]
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#if defined(__APPLE__)
#include "ResolverThread.h"
#include <algorithm>
#include <wpi/mutex.h>
using namespace wpi;
ResolverThread::ResolverThread(const private_init&) {}
ResolverThread::~ResolverThread() noexcept {
running = false;
if (thread.joinable()) {
thread.join();
}
}
void ResolverThread::AddServiceRef(DNSServiceRef serviceRef,
dnssd_sock_t socket) {
std::scoped_lock lock{serviceRefMutex};
serviceRefs.emplace_back(
std::pair<DNSServiceRef, dnssd_sock_t>{serviceRef, socket});
if (serviceRefs.size() == 1) {
running = false;
if (thread.joinable()) {
thread.join();
}
running = true;
thread = std::thread([=] { ThreadMain(); });
}
}
void ResolverThread::RemoveServiceRefInThread(DNSServiceRef serviceRef) {
std::scoped_lock lock{serviceRefMutex};
serviceRefs.erase(
std::find_if(serviceRefs.begin(), serviceRefs.end(),
[=](auto& a) { return a.first == serviceRef; }));
DNSServiceRefDeallocate(serviceRef);
}
WPI_EventHandle ResolverThread::RemoveServiceRefOutsideThread(
DNSServiceRef serviceRef) {
std::scoped_lock lock{serviceRefMutex};
WPI_EventHandle handle = CreateEvent(true);
serviceRefsToRemove.push_back({serviceRef, handle});
return handle;
}
bool ResolverThread::CleanupRefs() {
std::scoped_lock lock{serviceRefMutex};
for (auto&& r : serviceRefsToRemove) {
serviceRefs.erase(
std::find_if(serviceRefs.begin(), serviceRefs.end(),
[=](auto& a) { return a.first == r.first; }));
DNSServiceRefDeallocate(r.first);
wpi::SetEvent(r.second);
}
serviceRefsToRemove.clear();
return serviceRefs.empty();
}
void ResolverThread::ThreadMain() {
std::vector<pollfd> readSockets;
std::vector<DNSServiceRef> serviceRefs;
while (running) {
readSockets.clear();
serviceRefs.clear();
for (auto&& i : this->serviceRefs) {
readSockets.emplace_back(pollfd{i.second, POLLIN, 0});
serviceRefs.emplace_back(i.first);
}
int res = poll(readSockets.begin().base(), readSockets.size(), 100);
if (res > 0) {
for (size_t i = 0; i < readSockets.size(); i++) {
if (readSockets[i].revents == POLLIN) {
DNSServiceProcessResult(serviceRefs[i]);
}
}
} else if (res == 0) {
if (!running) {
CleanupRefs();
break;
}
}
if (CleanupRefs()) {
break;
}
}
}
static wpi::mutex ThreadLoopLock;
static std::weak_ptr<ResolverThread> ThreadLoop;
std::shared_ptr<ResolverThread> ResolverThread::Get() {
std::scoped_lock lock{ThreadLoopLock};
auto locked = ThreadLoop.lock();
if (!locked) {
locked = std::make_unique<ResolverThread>(private_init{});
ThreadLoop = locked;
}
return locked;
}
#endif // defined(__APPLE__)