blob: b91d6b69fcd0d4c1f3285cce8c4c7ba52d06becd [file] [log] [blame]
Austin Schuh75263e32022-02-22 18:05:32 -08001// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
4
5#include "ResolverThread.h"
6
7#include "wpi/mutex.h"
8
9using namespace wpi;
10
11ResolverThread::ResolverThread(const private_init&) {}
12
13ResolverThread::~ResolverThread() noexcept {
14 running = false;
15 if (thread.joinable()) {
16 thread.join();
17 }
18}
19
20void ResolverThread::AddServiceRef(DNSServiceRef serviceRef,
21 dnssd_sock_t socket) {
22 std::scoped_lock lock{serviceRefMutex};
23 serviceRefs.emplace_back(
24 std::pair<DNSServiceRef, dnssd_sock_t>{serviceRef, socket});
25 if (serviceRefs.size() == 1) {
26 running = false;
27 if (thread.joinable()) {
28 thread.join();
29 }
30 running = true;
31 thread = std::thread([=] { ThreadMain(); });
32 }
33}
34
35void ResolverThread::RemoveServiceRefInThread(DNSServiceRef serviceRef) {
36 std::scoped_lock lock{serviceRefMutex};
37 serviceRefs.erase(
38 std::find_if(serviceRefs.begin(), serviceRefs.end(),
39 [=](auto& a) { return a.first == serviceRef; }));
40 DNSServiceRefDeallocate(serviceRef);
41}
42
43WPI_EventHandle ResolverThread::RemoveServiceRefOutsideThread(
44 DNSServiceRef serviceRef) {
45 std::scoped_lock lock{serviceRefMutex};
46 WPI_EventHandle handle = CreateEvent(true);
47 serviceRefsToRemove.push_back({serviceRef, handle});
48 return handle;
49}
50
51bool ResolverThread::CleanupRefs() {
52 std::scoped_lock lock{serviceRefMutex};
53 for (auto&& r : serviceRefsToRemove) {
54 serviceRefs.erase(
55 std::find_if(serviceRefs.begin(), serviceRefs.end(),
56 [=](auto& a) { return a.first == r.first; }));
57 DNSServiceRefDeallocate(r.first);
58 wpi::SetEvent(r.second);
59 }
60 serviceRefsToRemove.clear();
61 return serviceRefs.empty();
62}
63
64void ResolverThread::ThreadMain() {
65 std::vector<pollfd> readSockets;
66 std::vector<DNSServiceRef> serviceRefs;
67
68 while (running) {
69 readSockets.clear();
70 serviceRefs.clear();
71
72 for (auto&& i : this->serviceRefs) {
73 readSockets.emplace_back(pollfd{i.second, POLLIN, 0});
74 serviceRefs.emplace_back(i.first);
75 }
76
77 int res = poll(readSockets.begin().base(), readSockets.size(), 100);
78
79 if (res > 0) {
80 for (size_t i = 0; i < readSockets.size(); i++) {
81 if (readSockets[i].revents == POLLIN) {
82 DNSServiceProcessResult(serviceRefs[i]);
83 }
84 }
85 } else if (res == 0) {
86 if (!running) {
87 CleanupRefs();
88 break;
89 }
90 }
91
92 if (CleanupRefs()) {
93 break;
94 }
95 }
96}
97
98static wpi::mutex ThreadLoopLock;
99static std::weak_ptr<ResolverThread> ThreadLoop;
100
101std::shared_ptr<ResolverThread> ResolverThread::Get() {
102 std::scoped_lock lock{ThreadLoopLock};
103 auto locked = ThreadLoop.lock();
104 if (!locked) {
105 locked = std::make_unique<ResolverThread>(private_init{});
106 ThreadLoop = locked;
107 }
108 return locked;
109}