// 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. #include "ResolverThread.h" #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{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 readSockets; std::vector 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 ThreadLoop; std::shared_ptr ResolverThread::Get() { std::scoped_lock lock{ThreadLoopLock}; auto locked = ThreadLoop.lock(); if (!locked) { locked = std::make_unique(private_init{}); ThreadLoop = locked; } return locked; }