mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-19 00:41:43 +00:00
The existing code takes 750ms to start per resolver. This is too long, especially in mrccomm where we start 4 on initialize, and 3 any time the team number changes. Solve this by handling the async setup more correctly. Assuming it returns pending, we're assuming the announcer is good. Then on stop(), we cancel the register in case its still pending, wait for the callback, and then deregister.
215 lines
6.8 KiB
C++
215 lines
6.8 KiB
C++
// 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.
|
|
|
|
#ifndef UNICODE
|
|
#define UNICODE
|
|
#endif
|
|
|
|
#include "wpi/net/MulticastServiceAnnouncer.h"
|
|
|
|
#include <Windows.h>
|
|
#include <WinDNS.h>
|
|
|
|
#include <memory>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "wpi/net/hostname.hpp"
|
|
#include "wpi/util/ConvertUTF.hpp"
|
|
#include "wpi/util/SmallString.hpp"
|
|
#include "wpi/util/SmallVector.hpp"
|
|
#include "wpi/util/StringExtras.hpp"
|
|
|
|
#pragma comment(lib, "dnsapi")
|
|
|
|
using namespace wpi::net;
|
|
|
|
struct ImplBase {
|
|
PDNS_SERVICE_INSTANCE serviceInstance = nullptr;
|
|
DNS_SERVICE_CANCEL serviceCancel;
|
|
HANDLE registerEvent = nullptr;
|
|
};
|
|
|
|
struct MulticastServiceAnnouncer::Impl : ImplBase {
|
|
std::wstring serviceType;
|
|
std::wstring serviceInstanceName;
|
|
std::wstring hostName;
|
|
int port;
|
|
std::vector<std::wstring> keys;
|
|
std::vector<PCWSTR> keyPtrs;
|
|
std::vector<std::wstring> values;
|
|
std::vector<PCWSTR> valuePtrs;
|
|
|
|
template <typename T>
|
|
Impl(std::string_view serviceName, std::string_view serviceType, int port,
|
|
std::span<const std::pair<T, T>> txt);
|
|
};
|
|
|
|
template <typename T>
|
|
MulticastServiceAnnouncer::Impl::Impl(std::string_view serviceName,
|
|
std::string_view serviceType, int port,
|
|
std::span<const std::pair<T, T>> txt) {
|
|
this->port = port;
|
|
|
|
wpi::util::SmallVector<wchar_t, 128> wideStorage;
|
|
std::string hostName = wpi::net::GetHostname() + ".local";
|
|
|
|
for (auto&& i : txt) {
|
|
wideStorage.clear();
|
|
wpi::util::sys::windows::UTF8ToUTF16(i.first, wideStorage);
|
|
this->keys.emplace_back(
|
|
std::wstring{wideStorage.data(), wideStorage.size()});
|
|
wideStorage.clear();
|
|
wpi::util::sys::windows::UTF8ToUTF16(i.second, wideStorage);
|
|
this->values.emplace_back(
|
|
std::wstring{wideStorage.data(), wideStorage.size()});
|
|
}
|
|
|
|
for (size_t i = 0; i < this->keys.size(); i++) {
|
|
this->keyPtrs.emplace_back(this->keys[i].c_str());
|
|
this->valuePtrs.emplace_back(this->values[i].c_str());
|
|
}
|
|
|
|
wpi::util::SmallString<128> storage;
|
|
|
|
wideStorage.clear();
|
|
wpi::util::sys::windows::UTF8ToUTF16(hostName, wideStorage);
|
|
|
|
this->hostName = std::wstring{wideStorage.data(), wideStorage.size()};
|
|
|
|
wideStorage.clear();
|
|
if (wpi::util::ends_with_lower(serviceType, ".local")) {
|
|
wpi::util::sys::windows::UTF8ToUTF16(serviceType, wideStorage);
|
|
} else {
|
|
storage.clear();
|
|
storage.append(serviceType);
|
|
storage.append(".local");
|
|
wpi::util::sys::windows::UTF8ToUTF16(storage.str(), wideStorage);
|
|
}
|
|
this->serviceType = std::wstring{wideStorage.data(), wideStorage.size()};
|
|
|
|
wideStorage.clear();
|
|
storage.clear();
|
|
storage.append(serviceName);
|
|
storage.append(".");
|
|
storage.append(serviceType);
|
|
if (!wpi::util::ends_with_lower(serviceType, ".local")) {
|
|
storage.append(".local");
|
|
}
|
|
|
|
wpi::util::sys::windows::UTF8ToUTF16(storage.str(), wideStorage);
|
|
this->serviceInstanceName =
|
|
std::wstring{wideStorage.data(), wideStorage.size()};
|
|
}
|
|
|
|
MulticastServiceAnnouncer::MulticastServiceAnnouncer(
|
|
std::string_view serviceName, std::string_view serviceType, int port) {
|
|
std::span<const std::pair<std::string_view, std::string_view>> txt;
|
|
pImpl = std::make_unique<Impl>(serviceName, serviceType, port, txt);
|
|
}
|
|
|
|
MulticastServiceAnnouncer::MulticastServiceAnnouncer(
|
|
std::string_view serviceName, std::string_view serviceType, int port,
|
|
std::span<const std::pair<std::string, std::string>> txt) {
|
|
pImpl = std::make_unique<Impl>(serviceName, serviceType, port, txt);
|
|
}
|
|
|
|
MulticastServiceAnnouncer::MulticastServiceAnnouncer(
|
|
std::string_view serviceName, std::string_view serviceType, int port,
|
|
std::span<const std::pair<std::string_view, std::string_view>> txt) {
|
|
pImpl = std::make_unique<Impl>(serviceName, serviceType, port, txt);
|
|
}
|
|
|
|
MulticastServiceAnnouncer::~MulticastServiceAnnouncer() noexcept {
|
|
Stop();
|
|
}
|
|
|
|
bool MulticastServiceAnnouncer::HasImplementation() const {
|
|
return true;
|
|
}
|
|
|
|
static void WINAPI DnsServiceRegisterCallback(DWORD /*Status*/,
|
|
PVOID pQueryContext,
|
|
PDNS_SERVICE_INSTANCE pInstance) {
|
|
HANDLE registerEvent = reinterpret_cast<HANDLE>(pQueryContext);
|
|
if (pInstance != nullptr) {
|
|
DnsServiceFreeInstance(pInstance);
|
|
}
|
|
SetEvent(registerEvent);
|
|
}
|
|
|
|
void MulticastServiceAnnouncer::Start() {
|
|
if (pImpl->serviceInstance) {
|
|
return;
|
|
}
|
|
|
|
PDNS_SERVICE_INSTANCE serviceInst = DnsServiceConstructInstance(
|
|
pImpl->serviceInstanceName.c_str(), pImpl->hostName.c_str(), nullptr,
|
|
nullptr, pImpl->port, 0, 0, static_cast<DWORD>(pImpl->keyPtrs.size()),
|
|
pImpl->keyPtrs.data(), pImpl->valuePtrs.data());
|
|
if (serviceInst == nullptr) {
|
|
return;
|
|
}
|
|
|
|
DNS_SERVICE_REGISTER_REQUEST registerRequest = {};
|
|
std::memset(&pImpl->serviceCancel, 0, sizeof(DNS_SERVICE_CANCEL));
|
|
pImpl->registerEvent = CreateEvent(NULL, true, false, NULL);
|
|
registerRequest.pQueryContext = pImpl->registerEvent;
|
|
registerRequest.pRegisterCompletionCallback = DnsServiceRegisterCallback;
|
|
registerRequest.Version = DNS_QUERY_REQUEST_VERSION1;
|
|
registerRequest.unicastEnabled = false;
|
|
registerRequest.pServiceInstance = serviceInst;
|
|
registerRequest.InterfaceIndex = 0;
|
|
|
|
if (DnsServiceRegister(®isterRequest, &pImpl->serviceCancel) !=
|
|
DNS_REQUEST_PENDING) {
|
|
DnsServiceFreeInstance(serviceInst);
|
|
CloseHandle(pImpl->registerEvent);
|
|
pImpl->registerEvent = nullptr;
|
|
return;
|
|
}
|
|
|
|
pImpl->serviceInstance = serviceInst;
|
|
}
|
|
|
|
static void WINAPI DnsServiceDeRegisterCallback(
|
|
DWORD /*Status*/, PVOID pQueryContext, PDNS_SERVICE_INSTANCE pInstance) {
|
|
HANDLE deregisterEvent = reinterpret_cast<HANDLE>(pQueryContext);
|
|
|
|
if (pInstance != nullptr) {
|
|
DnsServiceFreeInstance(pInstance);
|
|
}
|
|
|
|
SetEvent(deregisterEvent);
|
|
}
|
|
|
|
void MulticastServiceAnnouncer::Stop() {
|
|
if (pImpl->serviceInstance == nullptr) {
|
|
return;
|
|
}
|
|
|
|
DnsServiceRegisterCancel(&pImpl->serviceCancel);
|
|
WaitForSingleObject(pImpl->registerEvent, INFINITE);
|
|
CloseHandle(pImpl->registerEvent);
|
|
pImpl->registerEvent = nullptr;
|
|
|
|
HANDLE deregisterEvent = CreateEvent(NULL, true, false, NULL);
|
|
DNS_SERVICE_REGISTER_REQUEST registerRequest = {};
|
|
registerRequest.pQueryContext = deregisterEvent;
|
|
registerRequest.pRegisterCompletionCallback = DnsServiceDeRegisterCallback;
|
|
registerRequest.Version = DNS_QUERY_REQUEST_VERSION1;
|
|
registerRequest.unicastEnabled = false;
|
|
registerRequest.pServiceInstance = pImpl->serviceInstance;
|
|
registerRequest.InterfaceIndex = 0;
|
|
|
|
if (DnsServiceDeRegister(®isterRequest, nullptr) == DNS_REQUEST_PENDING) {
|
|
WaitForSingleObject(deregisterEvent, INFINITE);
|
|
}
|
|
|
|
DnsServiceFreeInstance(pImpl->serviceInstance);
|
|
pImpl->serviceInstance = nullptr;
|
|
CloseHandle(deregisterEvent);
|
|
}
|