wpiutil: Add C++ libuv wrappers (#1166)

- Provide an EventLoopRunner to run uv::Loop on a separate thread.

- Add raw_ostream wrapper for uv::Buffer.
This commit is contained in:
Peter Johnson
2018-07-17 01:06:24 -07:00
committed by GitHub
parent 340b26bada
commit e2314f3528
55 changed files with 5647 additions and 0 deletions

View File

@@ -0,0 +1,89 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "wpi/EventLoopRunner.h"
#include "wpi/SmallVector.h"
#include "wpi/condition_variable.h"
#include "wpi/mutex.h"
#include "wpi/uv/Async.h"
#include "wpi/uv/Loop.h"
using namespace wpi;
class EventLoopRunner::Thread : public SafeThread {
public:
Thread() : m_loop(uv::Loop::Create()) {
// set up async handles
if (!m_loop) return;
// run function
auto doExec = uv::Async::Create(m_loop);
if (!doExec) return;
m_doExec = doExec;
doExec->wakeup.connect([ async = doExec.get(), this ]() {
uv::Loop& loop = async->GetLoopRef();
{
std::lock_guard<wpi::mutex> lock{m_mutex};
for (auto&& func : m_exec) func(loop);
m_exec.clear();
}
m_execDone.notify_all();
});
// exit loop
auto doExit = uv::Async::Create(m_loop);
if (!doExit) return;
m_doExit = doExit;
doExit->wakeup.connect([async = doExit.get()]() {
// close all handles; this will (eventually) stop the loop
async->GetLoopRef().Walk([](uv::Handle& h) { h.Close(); });
});
}
void Main() {
if (m_loop) m_loop->Run();
}
// the loop
std::shared_ptr<uv::Loop> m_loop;
// run function
std::weak_ptr<uv::Async> m_doExec;
wpi::SmallVector<std::function<void(uv::Loop&)>, 4> m_exec;
wpi::condition_variable m_execDone;
// exit loop (thread cleanup)
std::weak_ptr<uv::Async> m_doExit;
};
EventLoopRunner::EventLoopRunner() { m_owner.Start(new Thread); }
EventLoopRunner::~EventLoopRunner() {
if (auto thr = m_owner.GetThread()) {
if (auto doExit = thr->m_doExit.lock()) doExit->Send();
}
}
void EventLoopRunner::ExecAsync(std::function<void(uv::Loop&)> func) {
if (auto thr = m_owner.GetThread()) {
if (auto doExec = thr->m_doExec.lock()) {
thr->m_exec.emplace_back(func);
doExec->Send();
}
}
}
void EventLoopRunner::ExecSync(std::function<void(uv::Loop&)> func) {
if (auto thr = m_owner.GetThread()) {
if (auto doExec = thr->m_doExec.lock()) {
thr->m_exec.emplace_back(func);
doExec->Send();
thr->m_execDone.wait(thr.GetLock(), [&] { return thr->m_exec.empty(); });
}
}
}

View File

@@ -0,0 +1,40 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "wpi/raw_uv_ostream.h"
#include <cstring>
using namespace wpi;
void raw_uv_ostream::write_impl(const char* data, size_t len) {
while (len > 0) {
// allocate additional buffers as required
if (m_left == 0) {
m_bufs.emplace_back(m_alloc());
// we want bufs() to always be valid, so set len=0 and keep track of the
// amount of space remaining separately
m_left = m_bufs.back().len;
m_bufs.back().len = 0;
assert(m_left != 0);
}
size_t amt = std::min(m_left, len);
auto& buf = m_bufs.back();
std::memcpy(buf.base + buf.len, data, amt);
data += amt;
len -= amt;
buf.len += amt;
m_left -= amt;
}
}
uint64_t raw_uv_ostream::current_pos() const {
uint64_t size = 0;
for (auto&& buf : m_bufs) size += buf.len;
return size;
}

View File

@@ -0,0 +1,30 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "wpi/uv/Async.h"
#include "wpi/uv/Loop.h"
namespace wpi {
namespace uv {
std::shared_ptr<Async> Async::Create(Loop& loop) {
auto h = std::make_shared<Async>(private_init{});
int err = uv_async_init(loop.GetRaw(), h->GetRaw(), [](uv_async_t* handle) {
Async& h = *static_cast<Async*>(handle->data);
h.wakeup();
});
if (err < 0) {
loop.ReportError(err);
return nullptr;
}
h->Keep();
return h;
}
} // namespace uv
} // namespace wpi

View File

@@ -0,0 +1,34 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "wpi/uv/Check.h"
#include "wpi/uv/Loop.h"
namespace wpi {
namespace uv {
std::shared_ptr<Check> Check::Create(Loop& loop) {
auto h = std::make_shared<Check>(private_init{});
int err = uv_check_init(loop.GetRaw(), h->GetRaw());
if (err < 0) {
loop.ReportError(err);
return nullptr;
}
h->Keep();
return h;
}
void Check::Start() {
Invoke(&uv_check_start, GetRaw(), [](uv_check_t* handle) {
Check& h = *static_cast<Check*>(handle->data);
h.check();
});
}
} // namespace uv
} // namespace wpi

View File

@@ -0,0 +1,67 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "wpi/uv/FsEvent.h"
#include <cstdlib>
#include "wpi/SmallString.h"
#include "wpi/uv/Loop.h"
namespace wpi {
namespace uv {
std::shared_ptr<FsEvent> FsEvent::Create(Loop& loop) {
auto h = std::make_shared<FsEvent>(private_init{});
int err = uv_fs_event_init(loop.GetRaw(), h->GetRaw());
if (err < 0) {
loop.ReportError(err);
return nullptr;
}
h->Keep();
return h;
}
void FsEvent::Start(const Twine& path, unsigned int flags) {
SmallString<128> pathBuf;
Invoke(
&uv_fs_event_start, GetRaw(),
[](uv_fs_event_t* handle, const char* filename, int events, int status) {
FsEvent& h = *static_cast<FsEvent*>(handle->data);
if (status < 0)
h.ReportError(status);
else
h.fsEvent(filename, events);
},
path.toNullTerminatedStringRef(pathBuf).data(), flags);
}
std::string FsEvent::GetPath() {
// Per the libuv docs, GetPath() always gives us a null-terminated string.
// common case should be small
char buf[128];
size_t size = 128;
int r = uv_fs_event_getpath(GetRaw(), buf, &size);
if (r == 0) {
return buf;
} else if (r == UV_ENOBUFS) {
// need to allocate a big enough buffer
char* buf2 = static_cast<char*>(std::malloc(size));
r = uv_fs_event_getpath(GetRaw(), buf2, &size);
if (r == 0) {
std::string out{buf2};
std::free(buf2);
return out;
}
std::free(buf2);
}
ReportError(r);
return std::string{};
}
} // namespace uv
} // namespace wpi

View File

@@ -0,0 +1,55 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "wpi/uv/GetAddrInfo.h"
#include "wpi/uv/Loop.h"
#include "wpi/uv/util.h"
namespace wpi {
namespace uv {
GetAddrInfoReq::GetAddrInfoReq() {
error = [this](Error err) { GetLoop().error(err); };
}
void GetAddrInfo(Loop& loop, const std::shared_ptr<GetAddrInfoReq>& req,
const Twine& node, const Twine& service,
const addrinfo* hints) {
SmallVector<char, 128> nodeStr;
SmallVector<char, 128> serviceStr;
int err = uv_getaddrinfo(
loop.GetRaw(), req->GetRaw(),
[](uv_getaddrinfo_t* req, int status, addrinfo* res) {
auto& h = *static_cast<GetAddrInfoReq*>(req->data);
if (status < 0)
h.ReportError(status);
else
h.resolved(*res);
uv_freeaddrinfo(res);
h.Release(); // this is always a one-shot
},
node.isNull() ? nullptr : node.toNullTerminatedStringRef(nodeStr).data(),
service.isNull() ? nullptr
: service.toNullTerminatedStringRef(serviceStr).data(),
hints);
if (err < 0)
loop.ReportError(err);
else
req->Keep();
}
void GetAddrInfo(Loop& loop, std::function<void(const addrinfo&)> callback,
const Twine& node, const Twine& service,
const addrinfo* hints) {
auto req = std::make_shared<GetAddrInfoReq>();
req->resolved.connect(callback);
GetAddrInfo(loop, req, node, service, hints);
}
} // namespace uv
} // namespace wpi

View File

@@ -0,0 +1,90 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "wpi/uv/GetNameInfo.h"
#include "wpi/uv/Loop.h"
#include "wpi/uv/util.h"
namespace wpi {
namespace uv {
GetNameInfoReq::GetNameInfoReq() {
error = [this](Error err) { GetLoop().error(err); };
}
void GetNameInfo(Loop& loop, const std::shared_ptr<GetNameInfoReq>& req,
const sockaddr& addr, int flags) {
int err = uv_getnameinfo(loop.GetRaw(), req->GetRaw(),
[](uv_getnameinfo_t* req, int status,
const char* hostname, const char* service) {
auto& h = *static_cast<GetNameInfoReq*>(req->data);
if (status < 0)
h.ReportError(status);
else
h.resolved(hostname, service);
h.Release(); // this is always a one-shot
},
&addr, flags);
if (err < 0)
loop.ReportError(err);
else
req->Keep();
}
void GetNameInfo(Loop& loop,
std::function<void(const char*, const char*)> callback,
const sockaddr& addr, int flags) {
auto req = std::make_shared<GetNameInfoReq>();
req->resolved.connect(callback);
GetNameInfo(loop, req, addr, flags);
}
void GetNameInfo4(Loop& loop, const std::shared_ptr<GetNameInfoReq>& req,
const Twine& ip, unsigned int port, int flags) {
sockaddr_in addr;
int err = NameToAddr(ip, port, &addr);
if (err < 0)
loop.ReportError(err);
else
GetNameInfo(loop, req, reinterpret_cast<const sockaddr&>(addr), flags);
}
void GetNameInfo4(Loop& loop,
std::function<void(const char*, const char*)> callback,
const Twine& ip, unsigned int port, int flags) {
sockaddr_in addr;
int err = NameToAddr(ip, port, &addr);
if (err < 0)
loop.ReportError(err);
else
GetNameInfo(loop, callback, reinterpret_cast<const sockaddr&>(addr), flags);
}
void GetNameInfo6(Loop& loop, const std::shared_ptr<GetNameInfoReq>& req,
const Twine& ip, unsigned int port, int flags) {
sockaddr_in6 addr;
int err = NameToAddr(ip, port, &addr);
if (err < 0)
loop.ReportError(err);
else
GetNameInfo(loop, req, reinterpret_cast<const sockaddr&>(addr), flags);
}
void GetNameInfo6(Loop& loop,
std::function<void(const char*, const char*)> callback,
const Twine& ip, unsigned int port, int flags) {
sockaddr_in6 addr;
int err = NameToAddr(ip, port, &addr);
if (err < 0)
loop.ReportError(err);
else
GetNameInfo(loop, callback, reinterpret_cast<const sockaddr&>(addr), flags);
}
} // namespace uv
} // namespace wpi

View File

@@ -0,0 +1,36 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "wpi/uv/Handle.h"
using namespace wpi::uv;
Handle::~Handle() noexcept {
if (!m_closed) {
uv_close(m_uv_handle, [](uv_handle_t* uv_handle) { delete uv_handle; });
} else {
delete m_uv_handle;
}
}
void Handle::Close() noexcept {
if (!IsClosing()) {
uv_close(m_uv_handle, [](uv_handle_t* handle) {
Handle& h = *static_cast<Handle*>(handle->data);
h.closed();
h.Release(); // free ourselves
});
m_closed = true;
}
}
void Handle::AllocBuf(uv_handle_t* handle, size_t size, uv_buf_t* buf) {
auto& h = *static_cast<Handle*>(handle->data);
*buf = h.m_allocBuf(size);
}
void Handle::DefaultFreeBuf(Buffer& buf) { buf.Deallocate(); }

View File

@@ -0,0 +1,34 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "wpi/uv/Idle.h"
#include "wpi/uv/Loop.h"
namespace wpi {
namespace uv {
std::shared_ptr<Idle> Idle::Create(Loop& loop) {
auto h = std::make_shared<Idle>(private_init{});
int err = uv_idle_init(loop.GetRaw(), h->GetRaw());
if (err < 0) {
loop.ReportError(err);
return nullptr;
}
h->Keep();
return h;
}
void Idle::Start() {
Invoke(&uv_idle_start, GetRaw(), [](uv_idle_t* handle) {
Idle& h = *static_cast<Idle*>(handle->data);
h.idle();
});
}
} // namespace uv
} // namespace wpi

View File

@@ -0,0 +1,64 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "wpi/uv/Loop.h"
using namespace wpi::uv;
Loop::Loop(const private_init&) noexcept {
#ifndef _WIN32
// Ignore SIGPIPE (see https://github.com/joyent/libuv/issues/1254)
static bool once = []() {
signal(SIGPIPE, SIG_IGN);
return true;
}();
(void)once;
#endif
}
Loop::~Loop() noexcept {
if (m_loop) {
m_loop->data = nullptr;
Close();
}
}
std::shared_ptr<Loop> Loop::Create() {
auto loop = std::make_shared<Loop>(private_init{});
if (uv_loop_init(&loop->m_loopStruct) < 0) return nullptr;
loop->m_loop = &loop->m_loopStruct;
loop->m_loop->data = loop.get();
return loop;
}
std::shared_ptr<Loop> Loop::GetDefault() {
static std::shared_ptr<Loop> loop = std::make_shared<Loop>(private_init{});
loop->m_loop = uv_default_loop();
if (!loop->m_loop) return nullptr;
loop->m_loop->data = loop.get();
return loop;
}
void Loop::Close() {
int err = uv_loop_close(m_loop);
if (err < 0) ReportError(err);
}
void Loop::Walk(std::function<void(Handle&)> callback) {
uv_walk(m_loop,
[](uv_handle_t* handle, void* func) {
auto& h = *static_cast<Handle*>(handle->data);
auto& f = *static_cast<std::function<void(Handle&)>*>(func);
f(h);
},
&callback);
}
void Loop::Fork() {
int err = uv_loop_fork(m_loop);
if (err < 0) ReportError(err);
}

View File

@@ -0,0 +1,68 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "wpi/uv/util.h" // NOLINT(build/include_order)
#include <cstring>
#include "wpi/SmallString.h"
namespace wpi {
namespace uv {
int NameToAddr(const Twine& ip, unsigned int port, sockaddr_in* addr) {
SmallString<128> tmp;
StringRef ipStr = ip.toNullTerminatedStringRef(tmp);
if (ipStr.empty()) {
std::memset(addr, 0, sizeof(sockaddr_in));
addr->sin_family = PF_INET;
addr->sin_addr.s_addr = INADDR_ANY;
addr->sin_port = htons(port);
return 0;
} else {
return uv_ip4_addr(ipStr.data(), port, addr);
}
}
int NameToAddr(const Twine& ip, unsigned int port, sockaddr_in6* addr) {
SmallString<128> tmp;
StringRef ipStr = ip.toNullTerminatedStringRef(tmp);
if (ipStr.empty()) {
std::memset(addr, 0, sizeof(sockaddr_in6));
addr->sin6_family = PF_INET6;
addr->sin6_addr = in6addr_any;
addr->sin6_port = htons(port);
return 0;
} else {
return uv_ip6_addr(ipStr.data(), port, addr);
}
}
int NameToAddr(const Twine& ip, in_addr* addr) {
SmallString<128> tmp;
StringRef ipStr = ip.toNullTerminatedStringRef(tmp);
if (ipStr.empty()) {
addr->s_addr = INADDR_ANY;
return 0;
} else {
return uv_inet_pton(AF_INET, ipStr.data(), addr);
}
}
int NameToAddr(const Twine& ip, in6_addr* addr) {
SmallString<128> tmp;
StringRef ipStr = ip.toNullTerminatedStringRef(tmp);
if (ipStr.empty()) {
*addr = in6addr_any;
return 0;
} else {
return uv_inet_pton(AF_INET6, ipStr.data(), addr);
}
}
} // namespace uv
} // namespace wpi

View File

@@ -0,0 +1,34 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "wpi/uv/NetworkStream.h"
namespace wpi {
namespace uv {
ConnectReq::ConnectReq() {
error = [this](Error err) { GetStream().error(err); };
}
void NetworkStream::Listen(int backlog) {
Invoke(&uv_listen, GetRawStream(), backlog,
[](uv_stream_t* handle, int status) {
auto& h = *static_cast<NetworkStream*>(handle->data);
if (status < 0)
h.ReportError(status);
else
h.connection();
});
}
void NetworkStream::Listen(std::function<void()> callback, int backlog) {
connection.connect(callback);
Listen(backlog);
}
} // namespace uv
} // namespace wpi

View File

@@ -0,0 +1,115 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "wpi/uv/Pipe.h"
#include <cstdlib>
#include "wpi/SmallString.h"
namespace wpi {
namespace uv {
std::shared_ptr<Pipe> Pipe::Create(Loop& loop, bool ipc) {
auto h = std::make_shared<Pipe>(private_init{});
int err = uv_pipe_init(loop.GetRaw(), h->GetRaw(), ipc ? 1 : 0);
if (err < 0) {
loop.ReportError(err);
return nullptr;
}
h->Keep();
return h;
}
std::shared_ptr<Pipe> Pipe::Accept() {
auto client = Create(GetLoopRef());
if (!client) return nullptr;
if (!Accept(client)) {
client->Release();
return nullptr;
}
return client;
}
Pipe* Pipe::DoAccept() { return Accept().get(); }
void Pipe::Bind(const Twine& name) {
SmallString<128> nameBuf;
Invoke(&uv_pipe_bind, GetRaw(),
name.toNullTerminatedStringRef(nameBuf).data());
}
void Pipe::Connect(const Twine& name,
const std::shared_ptr<PipeConnectReq>& req) {
SmallString<128> nameBuf;
uv_pipe_connect(req->GetRaw(), GetRaw(),
name.toNullTerminatedStringRef(nameBuf).data(),
[](uv_connect_t* req, int status) {
auto& h = *static_cast<PipeConnectReq*>(req->data);
if (status < 0)
h.ReportError(status);
else
h.connected();
h.Release(); // this is always a one-shot
});
req->Keep();
}
void Pipe::Connect(const Twine& name, std::function<void()> callback) {
auto req = std::make_shared<PipeConnectReq>();
req->connected.connect(callback);
Connect(name, req);
}
std::string Pipe::GetSock() {
// Per libuv docs, the returned buffer is NOT null terminated.
// common case should be small
char buf[128];
size_t size = 128;
int r = uv_pipe_getsockname(GetRaw(), buf, &size);
if (r == 0) {
return std::string{buf, size};
} else if (r == UV_ENOBUFS) {
// need to allocate a big enough buffer
char* buf2 = static_cast<char*>(std::malloc(size));
r = uv_pipe_getsockname(GetRaw(), buf2, &size);
if (r == 0) {
std::string out{buf2, size};
std::free(buf2);
return out;
}
std::free(buf2);
}
ReportError(r);
return std::string{};
}
std::string Pipe::GetPeer() {
// Per libuv docs, the returned buffer is NOT null terminated.
// common case should be small
char buf[128];
size_t size = 128;
int r = uv_pipe_getpeername(GetRaw(), buf, &size);
if (r == 0) {
return std::string{buf, size};
} else if (r == UV_ENOBUFS) {
// need to allocate a big enough buffer
char* buf2 = static_cast<char*>(std::malloc(size));
r = uv_pipe_getpeername(GetRaw(), buf2, &size);
if (r == 0) {
std::string out{buf2, size};
std::free(buf2);
return out;
}
std::free(buf2);
}
ReportError(r);
return std::string{};
}
} // namespace uv
} // namespace wpi

View File

@@ -0,0 +1,49 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "wpi/uv/Poll.h"
#include "wpi/uv/Loop.h"
namespace wpi {
namespace uv {
std::shared_ptr<Poll> Poll::Create(Loop& loop, int fd) {
auto h = std::make_shared<Poll>(private_init{});
int err = uv_poll_init(loop.GetRaw(), h->GetRaw(), fd);
if (err < 0) {
loop.ReportError(err);
return nullptr;
}
h->Keep();
return h;
}
std::shared_ptr<Poll> Poll::CreateSocket(Loop& loop, uv_os_sock_t sock) {
auto h = std::make_shared<Poll>(private_init{});
int err = uv_poll_init_socket(loop.GetRaw(), h->GetRaw(), sock);
if (err < 0) {
loop.ReportError(err);
return nullptr;
}
h->Keep();
return h;
}
void Poll::Start(int events) {
Invoke(&uv_poll_start, GetRaw(), events,
[](uv_poll_t* handle, int status, int events) {
Poll& h = *static_cast<Poll*>(handle->data);
if (status < 0)
h.ReportError(status);
else
h.pollEvent(events);
});
}
} // namespace uv
} // namespace wpi

View File

@@ -0,0 +1,34 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "wpi/uv/Prepare.h"
#include "wpi/uv/Loop.h"
namespace wpi {
namespace uv {
std::shared_ptr<Prepare> Prepare::Create(Loop& loop) {
auto h = std::make_shared<Prepare>(private_init{});
int err = uv_prepare_init(loop.GetRaw(), h->GetRaw());
if (err < 0) {
loop.ReportError(err);
return nullptr;
}
h->Keep();
return h;
}
void Prepare::Start() {
Invoke(&uv_prepare_start, GetRaw(), [](uv_prepare_t* handle) {
Prepare& h = *static_cast<Prepare*>(handle->data);
h.prepare();
});
}
} // namespace uv
} // namespace wpi

View File

@@ -0,0 +1,78 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "wpi/uv/Process.h"
#include "wpi/SmallString.h"
#include "wpi/uv/Loop.h"
#include "wpi/uv/Pipe.h"
namespace wpi {
namespace uv {
ProcessOptions& ProcessOptions::StdioIgnore(size_t index) {
if (index >= m_stdio.size()) m_stdio.resize(index + 1);
m_stdio[index].flags = UV_IGNORE;
m_stdio[index].data.fd = 0;
return *this;
}
ProcessOptions& ProcessOptions::StdioInherit(size_t index, int fd) {
if (index >= m_stdio.size()) m_stdio.resize(index + 1);
m_stdio[index].flags = UV_INHERIT_FD;
m_stdio[index].data.fd = fd;
return *this;
}
ProcessOptions& ProcessOptions::StdioInherit(size_t index, Pipe& pipe) {
if (index >= m_stdio.size()) m_stdio.resize(index + 1);
m_stdio[index].flags = UV_INHERIT_STREAM;
m_stdio[index].data.stream = pipe.GetRawStream();
return *this;
}
ProcessOptions& ProcessOptions::StdioCreatePipe(size_t index, Pipe& pipe,
unsigned int flags) {
if (index >= m_stdio.size()) m_stdio.resize(index + 1);
m_stdio[index].flags = static_cast<uv_stdio_flags>(UV_CREATE_PIPE | flags);
m_stdio[index].data.stream = pipe.GetRawStream();
return *this;
}
std::shared_ptr<Process> Process::Spawn(Loop& loop, const Twine& file,
char** args,
const ProcessOptions& options) {
// convert ProcessOptions to libuv structure
SmallString<128> fileBuf;
uv_process_options_t coptions;
coptions.exit_cb = [](uv_process_t* handle, int64_t status, int signal) {
auto& h = *static_cast<Process*>(handle->data);
h.exited(status, signal);
};
coptions.file = file.toNullTerminatedStringRef(fileBuf).data();
coptions.args = args;
coptions.env = options.m_env;
coptions.cwd = options.m_cwd.empty() ? nullptr : options.m_cwd.c_str();
coptions.flags = options.m_flags;
coptions.stdio_count = options.m_stdio.size();
coptions.stdio = const_cast<uv_stdio_container_t*>(
static_cast<const uv_stdio_container_t*>(options.m_stdio.data()));
coptions.uid = options.m_uid;
coptions.gid = options.m_gid;
auto h = std::make_shared<Process>(private_init{});
int err = uv_spawn(loop.GetRaw(), h->GetRaw(), &coptions);
if (err < 0) {
loop.ReportError(err);
return nullptr;
}
h->Keep();
return h;
}
} // namespace uv
} // namespace wpi

View File

@@ -0,0 +1,36 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "wpi/uv/Signal.h"
#include "wpi/uv/Loop.h"
namespace wpi {
namespace uv {
std::shared_ptr<Signal> Signal::Create(Loop& loop) {
auto h = std::make_shared<Signal>(private_init{});
int err = uv_signal_init(loop.GetRaw(), h->GetRaw());
if (err < 0) {
loop.ReportError(err);
return nullptr;
}
h->Keep();
return h;
}
void Signal::Start(int signum) {
Invoke(&uv_signal_start, GetRaw(),
[](uv_signal_t* handle, int signum) {
Signal& h = *static_cast<Signal*>(handle->data);
h.signal(signum);
},
signum);
}
} // namespace uv
} // namespace wpi

View File

@@ -0,0 +1,106 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "wpi/uv/Stream.h"
#include "wpi/SmallVector.h"
using namespace wpi;
using namespace wpi::uv;
namespace {
class CallbackWriteReq : public WriteReq {
public:
CallbackWriteReq(ArrayRef<Buffer> bufs,
std::function<void(MutableArrayRef<Buffer>, Error)> callback)
: m_bufs{bufs.begin(), bufs.end()} {
finish.connect([=](Error err) { callback(m_bufs, err); });
}
private:
SmallVector<Buffer, 4> m_bufs;
};
} // namespace
namespace wpi {
namespace uv {
ShutdownReq::ShutdownReq() {
error = [this](Error err) { GetStream().error(err); };
}
WriteReq::WriteReq() {
error = [this](Error err) { GetStream().error(err); };
}
void Stream::Shutdown(const std::shared_ptr<ShutdownReq>& req) {
if (Invoke(&uv_shutdown, req->GetRaw(), GetRawStream(),
[](uv_shutdown_t* req, int status) {
auto& h = *static_cast<ShutdownReq*>(req->data);
if (status < 0)
h.ReportError(status);
else
h.complete();
h.Release(); // this is always a one-shot
}))
req->Keep();
}
void Stream::Shutdown(std::function<void()> callback) {
auto req = std::make_shared<ShutdownReq>();
if (callback) req->complete.connect(callback);
Shutdown(req);
}
void Stream::StartRead() {
Invoke(&uv_read_start, GetRawStream(), &Handle::AllocBuf,
[](uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) {
auto& h = *static_cast<Stream*>(stream->data);
Buffer data = *buf;
// nread=0 is simply ignored
if (nread == UV_EOF)
h.end();
else if (nread > 0)
h.data(data, static_cast<size_t>(nread));
else if (nread < 0)
h.ReportError(nread);
// free the buffer
h.FreeBuf(data);
});
}
void Stream::Write(ArrayRef<Buffer> bufs,
const std::shared_ptr<WriteReq>& req) {
if (Invoke(&uv_write, req->GetRaw(), GetRawStream(), bufs.data(), bufs.size(),
[](uv_write_t* r, int status) {
auto& h = *static_cast<WriteReq*>(r->data);
if (status < 0) h.ReportError(status);
h.finish(Error(status));
h.Release(); // this is always a one-shot
}))
req->Keep();
}
void Stream::Write(
ArrayRef<Buffer> bufs,
std::function<void(MutableArrayRef<Buffer>, Error)> callback) {
Write(bufs, std::make_shared<CallbackWriteReq>(bufs, callback));
}
int Stream::TryWrite(ArrayRef<Buffer> bufs) {
int val = uv_try_write(GetRawStream(), bufs.data(), bufs.size());
if (val < 0) {
this->ReportError(val);
return 0;
}
return val;
}
} // namespace uv
} // namespace wpi

View File

@@ -0,0 +1,137 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "wpi/uv/Tcp.h"
#include <cstring>
#include "wpi/uv/util.h"
namespace wpi {
namespace uv {
std::shared_ptr<Tcp> Tcp::Create(Loop& loop, unsigned int flags) {
auto h = std::make_shared<Tcp>(private_init{});
int err = uv_tcp_init_ex(loop.GetRaw(), h->GetRaw(), flags);
if (err < 0) {
loop.ReportError(err);
return nullptr;
}
h->Keep();
return h;
}
std::shared_ptr<Tcp> Tcp::Accept() {
auto client = Create(GetLoopRef());
if (!client) return nullptr;
if (!Accept(client)) {
client->Release();
return nullptr;
}
return client;
}
Tcp* Tcp::DoAccept() { return Accept().get(); }
void Tcp::Bind(const Twine& ip, unsigned int port, unsigned int flags) {
sockaddr_in addr;
int err = NameToAddr(ip, port, &addr);
if (err < 0)
ReportError(err);
else
Bind(reinterpret_cast<const sockaddr&>(addr), flags);
}
void Tcp::Bind6(const Twine& ip, unsigned int port, unsigned int flags) {
sockaddr_in6 addr;
int err = NameToAddr(ip, port, &addr);
if (err < 0)
ReportError(err);
else
Bind(reinterpret_cast<const sockaddr&>(addr), flags);
}
sockaddr_storage Tcp::GetSock() {
sockaddr_storage name;
int len = sizeof(name);
if (!Invoke(&uv_tcp_getsockname, GetRaw(), reinterpret_cast<sockaddr*>(&name),
&len))
std::memset(&name, 0, sizeof(name));
return name;
}
sockaddr_storage Tcp::GetPeer() {
sockaddr_storage name;
int len = sizeof(name);
if (!Invoke(&uv_tcp_getpeername, GetRaw(), reinterpret_cast<sockaddr*>(&name),
&len))
std::memset(&name, 0, sizeof(name));
return name;
}
void Tcp::Connect(const sockaddr& addr,
const std::shared_ptr<TcpConnectReq>& req) {
if (Invoke(&uv_tcp_connect, req->GetRaw(), GetRaw(), &addr,
[](uv_connect_t* req, int status) {
auto& h = *static_cast<TcpConnectReq*>(req->data);
if (status < 0)
h.ReportError(status);
else
h.connected();
h.Release(); // this is always a one-shot
}))
req->Keep();
}
void Tcp::Connect(const sockaddr& addr, std::function<void()> callback) {
auto req = std::make_shared<TcpConnectReq>();
req->connected.connect(callback);
Connect(addr, req);
}
void Tcp::Connect(const Twine& ip, unsigned int port,
const std::shared_ptr<TcpConnectReq>& req) {
sockaddr_in addr;
int err = NameToAddr(ip, port, &addr);
if (err < 0)
ReportError(err);
else
Connect(reinterpret_cast<const sockaddr&>(addr), req);
}
void Tcp::Connect(const Twine& ip, unsigned int port,
std::function<void()> callback) {
sockaddr_in addr;
int err = NameToAddr(ip, port, &addr);
if (err < 0)
ReportError(err);
else
Connect(reinterpret_cast<const sockaddr&>(addr), callback);
}
void Tcp::Connect6(const Twine& ip, unsigned int port,
const std::shared_ptr<TcpConnectReq>& req) {
sockaddr_in6 addr;
int err = NameToAddr(ip, port, &addr);
if (err < 0)
ReportError(err);
else
Connect(reinterpret_cast<const sockaddr&>(addr), req);
}
void Tcp::Connect6(const Twine& ip, unsigned int port,
std::function<void()> callback) {
sockaddr_in6 addr;
int err = NameToAddr(ip, port, &addr);
if (err < 0)
ReportError(err);
else
Connect(reinterpret_cast<const sockaddr&>(addr), callback);
}
} // namespace uv
} // namespace wpi

View File

@@ -0,0 +1,46 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "wpi/uv/Timer.h"
#include "wpi/uv/Loop.h"
namespace wpi {
namespace uv {
std::shared_ptr<Timer> Timer::Create(Loop& loop) {
auto h = std::make_shared<Timer>(private_init{});
int err = uv_timer_init(loop.GetRaw(), h->GetRaw());
if (err < 0) {
loop.ReportError(err);
return nullptr;
}
h->Keep();
return h;
}
void Timer::SingleShot(Loop& loop, Time timeout, std::function<void()> func) {
auto h = Create(loop);
if (!h) return;
h->timeout.connect([ theTimer = h.get(), func ]() {
func();
theTimer->Close();
});
h->Start(timeout);
}
void Timer::Start(Time timeout, Time repeat) {
Invoke(&uv_timer_start, GetRaw(),
[](uv_timer_t* handle) {
Timer& h = *static_cast<Timer*>(handle->data);
h.timeout();
},
timeout.count(), repeat.count());
}
} // namespace uv
} // namespace wpi

View File

@@ -0,0 +1,27 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "wpi/uv/Tty.h"
#include "wpi/uv/Loop.h"
namespace wpi {
namespace uv {
std::shared_ptr<Tty> Tty::Create(Loop& loop, uv_file fd, bool readable) {
auto h = std::make_shared<Tty>(private_init{});
int err = uv_tty_init(loop.GetRaw(), h->GetRaw(), fd, readable ? 1 : 0);
if (err < 0) {
loop.ReportError(err);
return nullptr;
}
h->Keep();
return h;
}
} // namespace uv
} // namespace wpi

View File

@@ -0,0 +1,133 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "wpi/uv/Udp.h"
#include <cstring>
#include "wpi/SmallString.h"
#include "wpi/SmallVector.h"
#include "wpi/uv/util.h"
namespace {
using namespace wpi;
using namespace wpi::uv;
class CallbackUdpSendReq : public UdpSendReq {
public:
CallbackUdpSendReq(
ArrayRef<Buffer> bufs,
std::function<void(MutableArrayRef<Buffer>, Error)> callback)
: m_bufs{bufs.begin(), bufs.end()} {
complete.connect([=](Error err) { callback(m_bufs, err); });
}
private:
SmallVector<Buffer, 4> m_bufs;
};
} // namespace
namespace wpi {
namespace uv {
UdpSendReq::UdpSendReq() {
error = [this](Error err) { GetUdp().error(err); };
}
std::shared_ptr<Udp> Udp::Create(Loop& loop, unsigned int flags) {
auto h = std::make_shared<Udp>(private_init{});
int err = uv_udp_init_ex(loop.GetRaw(), h->GetRaw(), flags);
if (err < 0) {
loop.ReportError(err);
return nullptr;
}
h->Keep();
return h;
}
void Udp::Bind(const Twine& ip, unsigned int port, unsigned int flags) {
sockaddr_in addr;
int err = NameToAddr(ip, port, &addr);
if (err < 0)
ReportError(err);
else
Bind(reinterpret_cast<const sockaddr&>(addr), flags);
}
void Udp::Bind6(const Twine& ip, unsigned int port, unsigned int flags) {
sockaddr_in6 addr;
int err = NameToAddr(ip, port, &addr);
if (err < 0)
ReportError(err);
else
Bind(reinterpret_cast<const sockaddr&>(addr), flags);
}
sockaddr_storage Udp::GetSock() {
sockaddr_storage name;
int len = sizeof(name);
if (!Invoke(&uv_udp_getsockname, GetRaw(), reinterpret_cast<sockaddr*>(&name),
&len))
std::memset(&name, 0, sizeof(name));
return name;
}
void Udp::SetMembership(const Twine& multicastAddr, const Twine& interfaceAddr,
uv_membership membership) {
SmallString<128> multicastAddrBuf;
SmallString<128> interfaceAddrBuf;
Invoke(&uv_udp_set_membership, GetRaw(),
multicastAddr.toNullTerminatedStringRef(multicastAddrBuf).data(),
interfaceAddr.toNullTerminatedStringRef(interfaceAddrBuf).data(),
membership);
}
void Udp::SetMulticastInterface(const Twine& interfaceAddr) {
SmallString<128> interfaceAddrBuf;
Invoke(&uv_udp_set_multicast_interface, GetRaw(),
interfaceAddr.toNullTerminatedStringRef(interfaceAddrBuf).data());
}
void Udp::Send(const sockaddr& addr, ArrayRef<Buffer> bufs,
const std::shared_ptr<UdpSendReq>& req) {
if (Invoke(&uv_udp_send, req->GetRaw(), GetRaw(), bufs.data(), bufs.size(),
&addr, [](uv_udp_send_t* r, int status) {
auto& h = *static_cast<UdpSendReq*>(r->data);
if (status < 0) h.ReportError(status);
h.complete(Error(status));
h.Release(); // this is always a one-shot
}))
req->Keep();
}
void Udp::Send(const sockaddr& addr, ArrayRef<Buffer> bufs,
std::function<void(MutableArrayRef<Buffer>, Error)> callback) {
Send(addr, bufs, std::make_shared<CallbackUdpSendReq>(bufs, callback));
}
void Udp::StartRecv() {
Invoke(&uv_udp_recv_start, GetRaw(), &AllocBuf,
[](uv_udp_t* handle, ssize_t nread, const uv_buf_t* buf,
const sockaddr* addr, unsigned flags) {
auto& h = *static_cast<Udp*>(handle->data);
Buffer data = *buf;
// nread=0 is simply ignored
if (nread > 0)
h.received(data, static_cast<size_t>(nread), *addr, flags);
else if (nread < 0)
h.ReportError(nread);
// free the buffer
h.FreeBuf(data);
});
}
} // namespace uv
} // namespace wpi

View File

@@ -0,0 +1,48 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "wpi/uv/Work.h"
#include "wpi/uv/Loop.h"
namespace wpi {
namespace uv {
WorkReq::WorkReq() {
error = [this](Error err) { GetLoop().error(err); };
}
void QueueWork(Loop& loop, const std::shared_ptr<WorkReq>& req) {
int err = uv_queue_work(loop.GetRaw(), req->GetRaw(),
[](uv_work_t* req) {
auto& h = *static_cast<WorkReq*>(req->data);
h.work();
},
[](uv_work_t* req, int status) {
auto& h = *static_cast<WorkReq*>(req->data);
if (status < 0)
h.ReportError(status);
else
h.afterWork();
h.Release(); // this is always a one-shot
});
if (err < 0)
loop.ReportError(err);
else
req->Keep();
}
void QueueWork(Loop& loop, std::function<void()> work,
std::function<void()> afterWork) {
auto req = std::make_shared<WorkReq>();
req->work.connect(work);
req->afterWork.connect(afterWork);
QueueWork(loop, req);
}
} // namespace uv
} // namespace wpi