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,50 @@
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#ifndef WPIUTIL_WPI_EVENTLOOPRUNNER_H_
#define WPIUTIL_WPI_EVENTLOOPRUNNER_H_
#include <functional>
#include "wpi/SafeThread.h"
#include "wpi/uv/Loop.h"
namespace wpi {
/**
* Executes an event loop on a separate thread.
*/
class EventLoopRunner {
public:
EventLoopRunner();
virtual ~EventLoopRunner();
/**
* Run a function asynchronously (once) on the loop.
* This is safe to call from any thread, but is NOT safe to call from the
* provided function (it will deadlock).
* @param func function to execute on the loop
*/
void ExecAsync(std::function<void(wpi::uv::Loop&)> func);
/**
* Run a function synchronously (once) on the loop.
* This is safe to call from any thread, but is NOT safe to call from the
* provided function (it will deadlock).
* This does not return until the function finishes executing.
* @param func function to execute on the loop
*/
void ExecSync(std::function<void(wpi::uv::Loop&)> func);
private:
class Thread;
wpi::SafeThreadOwner<Thread> m_owner;
};
} // namespace wpi
#endif // WPIUTIL_WPI_EVENTLOOPRUNNER_H_

View File

@@ -0,0 +1,70 @@
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#ifndef WPIUTIL_WPI_RAW_UV_OSTREAM_H_
#define WPIUTIL_WPI_RAW_UV_OSTREAM_H_
#include "wpi/ArrayRef.h"
#include "wpi/SmallVector.h"
#include "wpi/raw_ostream.h"
#include "wpi/uv/Buffer.h"
namespace wpi {
/**
* raw_ostream style output to a SmallVector of uv::Buffer buffers. Fixed-size
* buffers are allocated and appended as necessary to fit the data being output.
* The SmallVector need not be empty at start.
*/
class raw_uv_ostream : public raw_ostream {
public:
/**
* Construct a new raw_uv_ostream.
* @param bufs Buffers vector. NOT cleared on construction.
* @param allocSize Size to allocate for each buffer; allocation will be
* performed using Buffer::Allocate().
*/
raw_uv_ostream(SmallVectorImpl<uv::Buffer>& bufs, size_t allocSize)
: m_bufs(bufs),
m_alloc([=]() { return uv::Buffer::Allocate(allocSize); }) {
SetUnbuffered();
}
/**
* Construct a new raw_uv_ostream.
* @param bufs Buffers vector. NOT cleared on construction.
* @param alloc Allocator.
*/
raw_uv_ostream(SmallVectorImpl<uv::Buffer>& bufs,
std::function<uv::Buffer()> alloc)
: m_bufs(bufs), m_alloc(alloc) {
SetUnbuffered();
}
~raw_uv_ostream() override = default;
/**
* Returns an ArrayRef to the buffers.
*/
ArrayRef<uv::Buffer> bufs() { return m_bufs; }
void flush() = delete;
private:
void write_impl(const char* data, size_t len) override;
uint64_t current_pos() const override;
SmallVectorImpl<uv::Buffer>& m_bufs;
std::function<uv::Buffer()> m_alloc;
// How much allocated space is left in the current buffer.
size_t m_left = 0;
};
} // namespace wpi
#endif // WPIUTIL_WPI_RAW_UV_OSTREAM_H_

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. */
/*----------------------------------------------------------------------------*/
#ifndef WPIUTIL_WPI_UV_ASYNC_H_
#define WPIUTIL_WPI_UV_ASYNC_H_
#include <uv.h>
#include <memory>
#include "wpi/Signal.h"
#include "wpi/uv/Handle.h"
namespace wpi {
namespace uv {
class Loop;
/**
* Async handle.
* Async handles allow the user to "wakeup" the event loop and have a signal
* generated from another thread.
*/
class Async final : public HandleImpl<Async, uv_async_t> {
struct private_init {};
public:
explicit Async(const private_init&) {}
~Async() noexcept override = default;
/**
* Create an async handle.
*
* @param loop Loop object where this handle runs.
*/
static std::shared_ptr<Async> Create(Loop& loop);
/**
* Create an async handle.
*
* @param loop Loop object where this handle runs.
*/
static std::shared_ptr<Async> Create(const std::shared_ptr<Loop>& loop) {
return Create(*loop);
}
/**
* Wakeup the event loop and emit the event.
*
* Its safe to call this function from any thread.
* An async event will be emitted on the loop thread.
*/
void Send() { Invoke(&uv_async_send, GetRaw()); }
/**
* Signal generated (on event loop thread) when the async event occurs.
*/
sig::Signal<> wakeup;
};
} // namespace uv
} // namespace wpi
#endif // WPIUTIL_WPI_UV_ASYNC_H_

View File

@@ -0,0 +1,92 @@
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#ifndef WPIUTIL_WPI_UV_BUFFER_H_
#define WPIUTIL_WPI_UV_BUFFER_H_
#include <uv.h>
#include <cstring>
#include <initializer_list>
#include <utility>
#include "wpi/ArrayRef.h"
#include "wpi/StringRef.h"
namespace wpi {
namespace uv {
/**
* Data buffer. Convenience wrapper around uv_buf_t.
*/
class Buffer : public uv_buf_t {
public:
Buffer() {
base = nullptr;
len = 0;
}
/*implicit*/ Buffer(const uv_buf_t& oth) { // NOLINT(runtime/explicit)
base = oth.base;
len = oth.len;
}
/*implicit*/ Buffer(StringRef str) // NOLINT(runtime/explicit)
: Buffer{str.data(), str.size()} {}
/*implicit*/ Buffer(ArrayRef<uint8_t> arr) // NOLINT(runtime/explicit)
: Buffer{reinterpret_cast<const char*>(arr.data()), arr.size()} {}
Buffer(char* base_, size_t len_) {
base = base_;
len = len_;
}
Buffer(const char* base_, size_t len_) {
base = const_cast<char*>(base_);
len = len_;
}
ArrayRef<char> data() const { return ArrayRef<char>{base, len}; }
MutableArrayRef<char> data() { return MutableArrayRef<char>{base, len}; }
operator ArrayRef<char>() const { return data(); }
operator MutableArrayRef<char>() { return data(); }
static Buffer Allocate(size_t size) { return Buffer{new char[size], size}; }
static Buffer Dup(StringRef in) {
Buffer buf = Allocate(in.size());
std::memcpy(buf.base, in.data(), in.size());
return buf;
}
static Buffer Dup(ArrayRef<uint8_t> in) {
Buffer buf = Allocate(in.size());
std::memcpy(buf.base, in.begin(), in.size());
return buf;
}
void Deallocate() {
delete[] base;
base = nullptr;
len = 0;
}
Buffer Move() {
Buffer buf = *this;
base = nullptr;
len = 0;
return buf;
}
friend void swap(Buffer& a, Buffer& b) {
using std::swap;
swap(a.base, b.base);
swap(a.len, b.len);
}
};
} // namespace uv
} // namespace wpi
#endif // WPIUTIL_WPI_UV_BUFFER_H_

View File

@@ -0,0 +1,70 @@
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#ifndef WPIUTIL_WPI_UV_CHECK_H_
#define WPIUTIL_WPI_UV_CHECK_H_
#include <uv.h>
#include <memory>
#include "wpi/Signal.h"
#include "wpi/uv/Handle.h"
namespace wpi {
namespace uv {
class Loop;
/**
* Check handle.
* Check handles will generate a signal once per loop iteration, right
* after polling for I/O.
*/
class Check final : public HandleImpl<Check, uv_check_t> {
struct private_init {};
public:
explicit Check(const private_init&) {}
~Check() noexcept override = default;
/**
* Create a check handle.
*
* @param loop Loop object where this handle runs.
*/
static std::shared_ptr<Check> Create(Loop& loop);
/**
* Create a check handle.
*
* @param loop Loop object where this handle runs.
*/
static std::shared_ptr<Check> Create(const std::shared_ptr<Loop>& loop) {
return Create(*loop);
}
/**
* Start the handle.
*/
void Start();
/**
* Stop the handle. The signal will no longer be generated.
*/
void Stop() { Invoke(&uv_check_stop, GetRaw()); }
/**
* Signal generated once per loop iteration after polling for I/O.
*/
sig::Signal<> check;
};
} // namespace uv
} // namespace wpi
#endif // WPIUTIL_WPI_UV_CHECK_H_

View File

@@ -0,0 +1,51 @@
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#ifndef WPIUTIL_WPI_UV_ERROR_H_
#define WPIUTIL_WPI_UV_ERROR_H_
#include <uv.h>
namespace wpi {
namespace uv {
/**
* Error code.
*/
class Error {
public:
Error() : m_err(UV_UNKNOWN) {}
explicit Error(int err) : m_err(err) {}
/**
* Boolean conversion. Returns true if error, false if ok.
*/
explicit operator bool() const { return m_err < 0; }
/**
* Returns the error code.
*/
int code() const { return m_err; }
/**
* Returns the error message.
*/
const char* str() const { return uv_strerror(m_err); }
/**
* Returns the error name.
*/
const char* name() const { return uv_err_name(m_err); }
private:
int m_err;
};
} // namespace uv
} // namespace wpi
#endif // WPIUTIL_WPI_UV_ERROR_H_

View File

@@ -0,0 +1,84 @@
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#ifndef WPIUTIL_WPI_UV_FSEVENT_H_
#define WPIUTIL_WPI_UV_FSEVENT_H_
#include <uv.h>
#include <memory>
#include <string>
#include "wpi/Signal.h"
#include "wpi/Twine.h"
#include "wpi/uv/Handle.h"
namespace wpi {
namespace uv {
class Loop;
/**
* Filesystem Event handle.
*/
class FsEvent final : public HandleImpl<FsEvent, uv_fs_event_t> {
struct private_init {};
public:
explicit FsEvent(const private_init&) {}
~FsEvent() noexcept override = default;
/**
* Create a filesystem event handle.
*
* @param loop Loop object where this handle runs.
*/
static std::shared_ptr<FsEvent> Create(Loop& loop);
/**
* Create a filesystem event handle.
*
* @param loop Loop object where this handle runs.
*/
static std::shared_ptr<FsEvent> Create(const std::shared_ptr<Loop>& loop) {
return Create(*loop);
}
/**
* Start watching the specified path for changes.
*
* @param path Path to watch for changes
* @param events Bitmask of event flags. Only UV_FS_EVENT_RECURSIVE is
* supported (and only on OSX and Windows).
*/
void Start(const Twine& path, unsigned int flags = 0);
/**
* Stop watching for changes.
*/
void Stop() { Invoke(&uv_fs_event_stop, GetRaw()); }
/**
* Get the path being monitored. Signals error and returns empty string if
* an error occurs.
* @return Monitored path.
*/
std::string GetPath();
/**
* Signal generated when a filesystem change occurs. The first parameter
* is the filename (if a directory was passed to Start(), the filename is
* relative to that directory). The second parameter is an ORed mask of
* UV_RENAME and UV_CHANGE.
*/
sig::Signal<const char*, int> fsEvent;
};
} // namespace uv
} // namespace wpi
#endif // WPIUTIL_WPI_UV_FSEVENT_H_

View File

@@ -0,0 +1,125 @@
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#ifndef WPIUTIL_WPI_UV_GETADDRINFO_H_
#define WPIUTIL_WPI_UV_GETADDRINFO_H_
#include <uv.h>
#include <functional>
#include <memory>
#include "wpi/Signal.h"
#include "wpi/Twine.h"
#include "wpi/uv/Request.h"
namespace wpi {
namespace uv {
class Loop;
/**
* GetAddrInfo request.
* For use with `GetAddrInfo()` function family.
*/
class GetAddrInfoReq : public RequestImpl<GetAddrInfoReq, uv_getaddrinfo_t> {
public:
GetAddrInfoReq();
Loop& GetLoop() const { return *static_cast<Loop*>(GetRaw()->loop->data); }
/**
* Resolved lookup signal.
* Parameter is resolved address info.
*/
sig::Signal<const addrinfo&> resolved;
};
/**
* Asynchronous getaddrinfo(3). HandleResolvedAddress() is called on the
* request when the resolution completes. HandleError() is called on the
* request if any errors occur.
*
* Either node or service may be null (`Twine::createNull()`) but not both.
*
* @param loop Event loop
* @param req request
* @param node Either a numerical network address or a network hostname.
* @param service Either a service name or a port number as a string.
* @param hints Optional `addrinfo` data structure with additional address
* type constraints.
*/
void GetAddrInfo(Loop& loop, const std::shared_ptr<GetAddrInfoReq>& req,
const Twine& node, const Twine& service = Twine::createNull(),
const addrinfo* hints = nullptr);
/**
* Asynchronous getaddrinfo(3). HandleResolvedAddress() is called on the
* request when the resolution completes. HandleError() is called on the
* request if any errors occur.
*
* Either node or service may be null (`Twine::createNull()`) but not both.
*
* @param loop Event loop
* @param req request
* @param node Either a numerical network address or a network hostname.
* @param service Either a service name or a port number as a string.
* @param hints Optional `addrinfo` data structure with additional address
* type constraints.
*/
inline void GetAddrInfo(const std::shared_ptr<Loop>& loop,
const std::shared_ptr<GetAddrInfoReq>& req,
const Twine& node,
const Twine& service = Twine::createNull(),
const addrinfo* hints = nullptr) {
GetAddrInfo(*loop, req, node, service, hints);
}
/**
* Asynchronous getaddrinfo(3). The callback is called when the resolution
* completes, and errors are forwarded to the loop. This is a convenience
* wrapper.
*
* Either node or service may be null (`Twine::createNull()`) but not both.
*
* @param loop Event loop
* @param callback Callback function to call when resolution completes
* @param node Either a numerical network address or a network hostname.
* @param service Either a service name or a port number as a string.
* @param hints Optional `addrinfo` data structure with additional address
* type constraints.
*/
void GetAddrInfo(Loop& loop, std::function<void(const addrinfo&)> callback,
const Twine& node, const Twine& service = Twine::createNull(),
const addrinfo* hints = nullptr);
/**
* Asynchronous getaddrinfo(3). The callback is called when the resolution
* completes, and errors are forwarded to the loop. This is a convenience
* wrapper.
*
* Either node or service may be null (`Twine::createNull()`) but not both.
*
* @param loop Event loop
* @param callback Callback function to call when resolution completes
* @param node Either a numerical network address or a network hostname.
* @param service Either a service name or a port number as a string.
* @param hints Optional `addrinfo` data structure with additional address
* type constraints.
*/
inline void GetAddrInfo(const std::shared_ptr<Loop>& loop,
std::function<void(const addrinfo&)> callback,
const Twine& node,
const Twine& service = Twine::createNull(),
const addrinfo* hints = nullptr) {
GetAddrInfo(*loop, callback, node, service, hints);
}
} // namespace uv
} // namespace wpi
#endif // WPIUTIL_WPI_UV_GETADDRINFO_H_

View File

@@ -0,0 +1,228 @@
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#ifndef WPIUTIL_WPI_UV_GETNAMEINFO_H_
#define WPIUTIL_WPI_UV_GETNAMEINFO_H_
#include <uv.h>
#include <functional>
#include <memory>
#include "wpi/Signal.h"
#include "wpi/Twine.h"
#include "wpi/uv/Request.h"
namespace wpi {
namespace uv {
class Loop;
/**
* GetNameInfo request.
* For use with `GetNameInfo()` function family.
*/
class GetNameInfoReq : public RequestImpl<GetNameInfoReq, uv_getnameinfo_t> {
public:
GetNameInfoReq();
Loop& GetLoop() const { return *static_cast<Loop*>(GetRaw()->loop->data); }
/**
* Resolved lookup signal.
* Parameters are hostname and service.
*/
sig::Signal<const char*, const char*> resolved;
};
/**
* Asynchronous getnameinfo(3). HandleResolvedName() is called on the
* request when the resolution completes. HandleError() is called on the
* request if any errors occur.
*
* @param loop Event loop
* @param req request
* @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure.
* @param flags Optional flags to modify the behavior of `getnameinfo`.
*/
void GetNameInfo(Loop& loop, const std::shared_ptr<GetNameInfoReq>& req,
const sockaddr& addr, int flags = 0);
/**
* Asynchronous getnameinfo(3). HandleResolvedName() is called on the
* request when the resolution completes. HandleError() is called on the
* request if any errors occur.
*
* @param loop Event loop
* @param req request
* @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure.
* @param flags Optional flags to modify the behavior of `getnameinfo`.
*/
inline void GetNameInfo(const std::shared_ptr<Loop>& loop,
const std::shared_ptr<GetNameInfoReq>& req,
const sockaddr& addr, int flags = 0) {
GetNameInfo(*loop, req, addr, flags);
}
/**
* Asynchronous getnameinfo(3). The callback is called when the resolution
* completes, and errors are forwarded to the loop.
*
* @param loop Event loop
* @param callback Callback function to call when resolution completes
* @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure.
* @param flags Optional flags to modify the behavior of `getnameinfo`.
*/
void GetNameInfo(Loop& loop,
std::function<void(const char*, const char*)> callback,
const sockaddr& addr, int flags = 0);
/**
* Asynchronous getnameinfo(3). The callback is called when the resolution
* completes, and errors are forwarded to the loop.
*
* @param loop Event loop
* @param callback Callback function to call when resolution completes
* @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure.
* @param flags Optional flags to modify the behavior of `getnameinfo`.
* @return Connection object for the callback
*/
inline void GetNameInfo(const std::shared_ptr<Loop>& loop,
std::function<void(const char*, const char*)> callback,
const sockaddr& addr, int flags = 0) {
GetNameInfo(*loop, callback, addr, flags);
}
/**
* Asynchronous IPv4 getnameinfo(3). HandleResolvedName() is called on the
* request when the resolution completes. HandleError() is called on the
* request if any errors occur.
*
* @param loop Event loop
* @param req request
* @param ip A valid IPv4 address
* @param port A valid port number
* @param flags Optional flags to modify the behavior of `getnameinfo`.
*/
void GetNameInfo4(Loop& loop, const std::shared_ptr<GetNameInfoReq>& req,
const Twine& ip, unsigned int port, int flags = 0);
/**
* Asynchronous IPv4 getnameinfo(3). HandleResolvedName() is called on the
* request when the resolution completes. HandleError() is called on the
* request if any errors occur.
*
* @param loop Event loop
* @param req request
* @param ip A valid IPv4 address
* @param port A valid port number
* @param flags Optional flags to modify the behavior of `getnameinfo`.
*/
inline void GetNameInfo4(const std::shared_ptr<Loop>& loop,
const std::shared_ptr<GetNameInfoReq>& req,
const Twine& ip, unsigned int port, int flags = 0) {
return GetNameInfo4(*loop, req, ip, port, flags);
}
/**
* Asynchronous IPv4 getnameinfo(3). The callback is called when the resolution
* completes, and errors are forwarded to the loop.
*
* @param loop Event loop
* @param callback Callback function to call when resolution completes
* @param ip A valid IPv4 address
* @param port A valid port number
* @param flags Optional flags to modify the behavior of `getnameinfo`.
*/
void GetNameInfo4(Loop& loop,
std::function<void(const char*, const char*)> callback,
const Twine& ip, unsigned int port, int flags = 0);
/**
* Asynchronous IPv4 getnameinfo(3). The callback is called when the resolution
* completes, and errors are forwarded to the loop. This is a convenience
* wrapper.
*
* @param loop Event loop
* @param ip A valid IPv4 address
* @param port A valid port number
* @param callback Callback function to call when resolution completes
* @param flags Optional flags to modify the behavior of `getnameinfo`.
*/
inline void GetNameInfo4(const std::shared_ptr<Loop>& loop,
std::function<void(const char*, const char*)> callback,
const Twine& ip, unsigned int port, int flags = 0) {
return GetNameInfo4(*loop, callback, ip, port, flags);
}
/**
* Asynchronous IPv6 getnameinfo(3). HandleResolvedName() is called on the
* request when the resolution completes. HandleError() is called on the
* request if any errors occur.
*
* @param loop Event loop
* @param req request
* @param ip A valid IPv6 address
* @param port A valid port number
* @param flags Optional flags to modify the behavior of `getnameinfo`.
*/
void GetNameInfo6(Loop& loop, const std::shared_ptr<GetNameInfoReq>& req,
const Twine& ip, unsigned int port, int flags = 0);
/**
* Asynchronous IPv6 getnameinfo(3). HandleResolvedName() is called on the
* request when the resolution completes. HandleError() is called on the
* request if any errors occur.
*
* @param loop Event loop
* @param req request
* @param ip A valid IPv6 address
* @param port A valid port number
* @param flags Optional flags to modify the behavior of `getnameinfo`.
*/
inline void GetNameInfo6(const std::shared_ptr<Loop>& loop,
const std::shared_ptr<GetNameInfoReq>& req,
const Twine& ip, unsigned int port, int flags = 0) {
GetNameInfo6(*loop, req, ip, port, flags);
}
/**
* Asynchronous IPv6 getnameinfo(3). The callback is called when the resolution
* completes, and errors are forwarded to the loop. This is a convenience
* wrapper.
*
* @param loop Event loop
* @param callback Callback function to call when resolution completes
* @param ip A valid IPv6 address
* @param port A valid port number
* @param flags Optional flags to modify the behavior of `getnameinfo`.
*/
void GetNameInfo6(Loop& loop,
std::function<void(const char*, const char*)> callback,
const Twine& ip, unsigned int port, int flags = 0);
/**
* Asynchronous IPv6 getnameinfo(3). The callback is called when the resolution
* completes, and errors are forwarded to the loop. This is a convenience
* wrapper.
*
* @param loop Event loop
* @param callback Callback function to call when resolution completes
* @param ip A valid IPv6 address
* @param port A valid port number
* @param flags Optional flags to modify the behavior of `getnameinfo`.
*/
inline void GetNameInfo6(const std::shared_ptr<Loop>& loop,
std::function<void(const char*, const char*)> callback,
const Twine& ip, unsigned int port, int flags = 0) {
return GetNameInfo6(*loop, callback, ip, port, flags);
}
} // namespace uv
} // namespace wpi
#endif // WPIUTIL_WPI_UV_GETNAMEINFO_H_

View File

@@ -0,0 +1,272 @@
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#ifndef WPIUTIL_WPI_UV_HANDLE_H_
#define WPIUTIL_WPI_UV_HANDLE_H_
#include <uv.h>
#include <functional>
#include <memory>
#include <utility>
#include "wpi/Signal.h"
#include "wpi/StringRef.h"
#include "wpi/uv/Buffer.h"
#include "wpi/uv/Error.h"
#include "wpi/uv/Loop.h"
namespace wpi {
namespace uv {
/**
* Handle.
* Handles are not moveable or copyable and cannot be directly constructed.
* This class provides shared_ptr ownership and shared_from_this.
* Use the specific handle type Create() functions to create handles.
*/
class Handle : public std::enable_shared_from_this<Handle> {
public:
using Type = uv_handle_type;
Handle(const Handle&) = delete;
Handle(Handle&&) = delete;
Handle& operator=(const Handle&) = delete;
Handle& operator=(Handle&&) = delete;
virtual ~Handle() noexcept;
/**
* Get the type of the handle.
*
* A base handle offers no functionality to promote it to the actual handle
* type. By means of this function, the type of the underlying handle as
* specified by Type is made available.
*
* @return The actual type of the handle.
*/
Type GetType() const noexcept { return m_uv_handle->type; }
/**
* Get the name of the type of the handle. E.g. "pipe" for pipe handles.
*/
StringRef GetTypeName() const noexcept {
return uv_handle_type_name(m_uv_handle->type);
}
/**
* Get the loop where this handle runs.
*
* @return The loop.
*/
std::shared_ptr<Loop> GetLoop() const noexcept {
return GetLoopRef().shared_from_this();
}
/**
* Get the loop where this handle runs.
*
* @return The loop.
*/
Loop& GetLoopRef() const noexcept {
return *static_cast<Loop*>(m_uv_handle->loop->data);
}
/**
* Check if the handle is active.
*
* What _active_ means depends on the type of handle:
*
* * An AsyncHandle handle is always active and cannot be deactivated,
* except by closing it with uv_close().
* * A PipeHandle, TcpHandle, UDPHandle, etc. handle - basically any handle
* that deals with I/O - is active when it is doing something that involves
* I/O, like reading, writing, connecting, accepting new connections, etc.
* * A CheckHandle, IdleHandle, TimerHandle, etc. handle is active when it
* has been started with a call to `Start()`.
*
* Rule of thumb: if a handle of type `FooHandle` has a `Start()` member
* method, then its active from the moment that method is called. Likewise,
* `Stop()` deactivates the handle again.
*
* @return True if the handle is active, false otherwise.
*/
bool IsActive() const noexcept { return uv_is_active(m_uv_handle) != 0; }
/**
* Check if a handle is closing or closed.
*
* This function should only be used between the initialization of the
* handle and the arrival of the close callback.
*
* @return True if the handle is closing or closed, false otherwise.
*/
bool IsClosing() const noexcept { return uv_is_closing(m_uv_handle) != 0; }
/**
* Request handle to be closed.
*
* This **must** be called on each handle before memory is released.
* In-progress requests are cancelled and this can result in error() being
* emitted.
*
* The handle will emit closed() when finished.
*/
void Close() noexcept;
/**
* Reference the given handle.
*
* References are idempotent, that is, if a handle is already referenced
* calling this function again will have no effect.
*/
void Reference() noexcept { uv_ref(m_uv_handle); }
/**
* Unreference the given handle.
*
* References are idempotent, that is, if a handle is not referenced calling
* this function again will have no effect.
*/
void Unreference() noexcept { uv_unref(m_uv_handle); }
/**
* Check if the given handle is referenced.
* @return True if the handle is referenced, false otherwise.
*/
bool HasReference() const noexcept { return uv_has_ref(m_uv_handle) != 0; }
/**
* Return the size of the underlying handle type.
* @return The size of the underlying handle type.
*/
size_t RawSize() const noexcept { return uv_handle_size(m_uv_handle->type); }
/**
* Get the underlying handle data structure.
*
* @return The underlying handle data structure.
*/
uv_handle_t* GetRawHandle() const noexcept { return m_uv_handle; }
/**
* Set the functions used for allocating and releasing buffers. The size
* passed to the allocator function is a "suggested" size--it's just an
* indication, not related in any way to the pending data to be read. The
* user is free to allocate the amount of memory they decide. For example,
* applications with custom allocation schemes may decide to use a different
* size which matches the memory chunks they already have for other purposes.
*
* @warning Be very careful changing the allocator after the loop has started
* running; there are no interlocks between this and buffers currently in
* flight.
*
* @param alloc Allocation function
* @param dealloc Deallocation function
*/
void SetBufferAllocator(std::function<Buffer(size_t)> alloc,
std::function<void(Buffer&)> dealloc) {
m_allocBuf = alloc;
m_freeBuf = dealloc;
}
/**
* Free a buffer. Uses the function provided to SetBufFree() or
* Buffer::Deallocate by default.
*
* @param buf The buffer
*/
void FreeBuf(Buffer& buf) const noexcept { m_freeBuf(buf); }
/**
* Gets user-defined data.
* @return User-defined data if any, nullptr otherwise.
*/
template <typename T = void>
std::shared_ptr<T> GetData() const {
return std::static_pointer_cast<T>(m_data);
}
/**
* Sets user-defined data.
* @param data User-defined arbitrary data.
*/
void SetData(std::shared_ptr<void> data) { m_data = std::move(data); }
/**
* Error signal
*/
sig::Signal<Error> error;
/**
* Closed signal
*/
sig::Signal<> closed;
/**
* Report an error.
* @param err Error code
*/
void ReportError(int err) { error(Error(err)); }
protected:
explicit Handle(uv_handle_t* uv_handle) : m_uv_handle{uv_handle} {
m_uv_handle->data = this;
}
void Keep() noexcept { m_self = shared_from_this(); }
void Release() noexcept { m_self.reset(); }
static void AllocBuf(uv_handle_t* handle, size_t size, uv_buf_t* buf);
static void DefaultFreeBuf(Buffer& buf);
template <typename F, typename... Args>
bool Invoke(F&& f, Args&&... args) {
auto err = std::forward<F>(f)(std::forward<Args>(args)...);
if (err < 0) ReportError(err);
return err == 0;
}
private:
std::shared_ptr<Handle> m_self;
uv_handle_t* m_uv_handle;
bool m_closed = false;
std::function<Buffer(size_t)> m_allocBuf{&Buffer::Allocate};
std::function<void(Buffer&)> m_freeBuf{&DefaultFreeBuf};
std::shared_ptr<void> m_data;
};
/**
* Handle.
*/
template <typename T, typename U>
class HandleImpl : public Handle {
public:
std::shared_ptr<T> shared_from_this() {
return std::static_pointer_cast<T>(Handle::shared_from_this());
}
std::shared_ptr<const T> shared_from_this() const {
return std::static_pointer_cast<const T>(Handle::shared_from_this());
}
/**
* Get the underlying handle data structure.
*
* @return The underlying handle data structure.
*/
U* GetRaw() const noexcept {
return reinterpret_cast<U*>(this->GetRawHandle());
}
protected:
HandleImpl() : Handle{reinterpret_cast<uv_handle_t*>(new U)} {}
};
} // namespace uv
} // namespace wpi
#endif // WPIUTIL_WPI_UV_HANDLE_H_

View File

@@ -0,0 +1,79 @@
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#ifndef WPIUTIL_WPI_UV_IDLE_H_
#define WPIUTIL_WPI_UV_IDLE_H_
#include <uv.h>
#include <memory>
#include "wpi/Signal.h"
#include "wpi/uv/Handle.h"
namespace wpi {
namespace uv {
class Loop;
/**
* Idle handle.
*
* Idle handles will generate a signal once per loop iteration, right
* before the Prepare handles.
*
* The notable difference with Prepare handles is that when there are active
* idle handles, the loop will perform a zero timeout poll instead of blocking
* for I/O.
*
* @warning Despite the name, idle handles will signal every loop iteration,
* not when the loop is actually "idle". This also means they can easly become
* CPU hogs.
*/
class Idle final : public HandleImpl<Idle, uv_idle_t> {
struct private_init {};
public:
explicit Idle(const private_init&) {}
~Idle() noexcept override = default;
/**
* Create an idle handle.
*
* @param loop Loop object where this handle runs.
*/
static std::shared_ptr<Idle> Create(Loop& loop);
/**
* Create an idle handle.
*
* @param loop Loop object where this handle runs.
*/
static std::shared_ptr<Idle> Create(const std::shared_ptr<Loop>& loop) {
return Create(*loop);
}
/**
* Start the handle.
*/
void Start();
/**
* Stop the handle. The signal will no longer be generated.
*/
void Stop() { Invoke(&uv_idle_stop, GetRaw()); }
/**
* Signal generated once per loop iteration prior to Prepare signals.
*/
sig::Signal<> idle;
};
} // namespace uv
} // namespace wpi
#endif // WPIUTIL_WPI_UV_IDLE_H_

View File

@@ -0,0 +1,230 @@
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#ifndef WPIUTIL_WPI_UV_LOOP_H_
#define WPIUTIL_WPI_UV_LOOP_H_
#include <uv.h>
#include <chrono>
#include <functional>
#include <memory>
#include <utility>
#include "wpi/Signal.h"
#include "wpi/uv/Error.h"
namespace wpi {
namespace uv {
class Handle;
/**
* Event loop.
*
* The event loop is the central part of uv functionality. It takes care of
* polling for I/O and scheduling signals to be generated based on different
* sources of events.
*
* The event loop is not moveable, copyable, or directly constructible. Use
* Create() to create an event loop, or GetDefault() to get the default loop
* if you know your program will only have a single one.
*/
class Loop final : public std::enable_shared_from_this<Loop> {
struct private_init {};
public:
using Time = std::chrono::duration<uint64_t, std::milli>;
enum Mode {
kDefault = UV_RUN_DEFAULT,
kOnce = UV_RUN_ONCE,
kNoWait = UV_RUN_NOWAIT
};
explicit Loop(const private_init&) noexcept;
Loop(const Loop&) = delete;
Loop& operator=(const Loop&) = delete;
Loop(Loop&& oth) = delete;
Loop& operator=(Loop&& oth) = delete;
~Loop() noexcept;
/**
* Create a new event loop. The created loop is not the default event loop.
*
* @return The newly created loop. May return nullptr if a failure occurs.
*/
static std::shared_ptr<Loop> Create();
/**
* Create the default event loop. Only use this event loop if a single loop
* is needed for the entire application.
*
* @return The newly created loop. May return nullptr if a failure occurs.
*/
static std::shared_ptr<Loop> GetDefault();
/**
* Release all internal loop resources.
*
* Call this function only when the loop has finished executing and all open
* handles and requests have been closed, or the loop will emit an error.
*
* error() will be emitted in case of errors.
*/
void Close();
/**
* Run the event loop.
*
* Available modes are:
*
* * `Loop::kDefault`: Run the event loop until there are no
* active and referenced handles or requests.
* * `Loop::kOnce`: Run a single event loop iteration. Note that this
* function blocksif there are no pending callbacks.
* * `Loop::kNoWait`: Run a single event loop iteration, but don't block
* if there are no pending callbacks.
*
* @return True when done, false in all other cases.
*/
bool Run(Mode mode = kDefault) {
return uv_run(m_loop, static_cast<uv_run_mode>(static_cast<int>(mode))) ==
0;
}
/**
* Check if there are active resources.
*
* @return True if there are active resources in the loop.
*/
bool IsAlive() const noexcept { return uv_loop_alive(m_loop) != 0; }
/**
* Stop the event loop.
*
* This will cause Run() to end as soon as possible.
* This will happen not sooner than the next loop iteration.
* If this function was called before blocking for I/O, the loop wont block
* for I/O on this iteration.
*/
void Stop() noexcept { uv_stop(m_loop); }
/**
* Get backend file descriptor.
*
* Only kqueue, epoll and event ports are supported.
* This can be used in conjunction with `run(Loop::kNoWait)` to poll
* in one thread and run the event loops callbacks in another.
*
* @return The backend file descriptor.
*/
int GetDescriptor() const noexcept { return uv_backend_fd(m_loop); }
/**
* Get the poll timeout.
*
* @return A `std::pair` composed of a boolean value that is true in case of
* valid timeout, false otherwise, and the timeout
* (`std::chrono::duration<uint64_t, std::milli>`).
*/
std::pair<bool, Time> GetTimeout() const noexcept {
auto to = uv_backend_timeout(m_loop);
return std::make_pair(to == -1, Time{to});
}
/**
* Return the current timestamp in milliseconds.
*
* The timestamp is cached at the start of the event loop tick.
* The timestamp increases monotonically from some arbitrary point in
* time.
* Dont make assumptions about the starting point, you will only get
* disappointed.
*
* @return The current timestamp in milliseconds (actual type is
* `std::chrono::duration<uint64_t, std::milli>`).
*/
Time Now() const noexcept { return Time{uv_now(m_loop)}; }
/**
* Update the event loops concept of _now_.
*
* The current time is cached at the start of the event loop tick in order
* to reduce the number of time-related system calls.
* You wont normally need to call this function unless you have callbacks
* that block the event loop for longer periods of time, where _longer_ is
* somewhat subjective but probably on the order of a millisecond or more.
*/
void UpdateTime() noexcept { uv_update_time(m_loop); }
/**
* Walk the list of handles.
*
* The callback will be executed once for each handle that is still active.
*
* @param callback A function to be invoked once for each active handle.
*/
void Walk(std::function<void(Handle&)> callback);
/**
* Reinitialize any kernel state necessary in the child process after
* a fork(2) system call.
*
* Previously started watchers will continue to be started in the child
* process.
*
* It is necessary to explicitly call this function on every event loop
* created in the parent process that you plan to continue to use in the
* child, including the default loop (even if you dont continue to use it
* in the parent). This function must be called before calling any API
* function using the loop in the child. Failure to do so will result in
* undefined behaviour, possibly including duplicate events delivered to
* both parent and child or aborting the child process.
*
* When possible, it is preferred to create a new loop in the child process
* instead of reusing a loop created in the parent. New loops created in the
* child process after the fork should not use this function.
*
* Note that this function is not implemented on Windows.
* Note also that this function is experimental in `libuv`. It may contain
* bugs, and is subject to change or removal. API and ABI stability is not
* guaranteed.
*
* error() will be emitted in case of errors.
*/
void Fork();
/**
* Get the underlying event loop data structure.
*
* @return The underlying event loop data structure.
*/
uv_loop_t* GetRaw() const noexcept { return m_loop; }
/**
* Error signal
*/
sig::Signal<Error> error;
/**
* Reports error.
* @param err Error code
*/
void ReportError(int err) { error(Error(err)); }
private:
uv_loop_t* m_loop;
uv_loop_t m_loopStruct;
};
} // namespace uv
} // namespace wpi
#endif // WPIUTIL_WPI_UV_LOOP_H_

View File

@@ -0,0 +1,156 @@
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#ifndef WPIUTIL_WPI_UV_NETWORKSTREAM_H_
#define WPIUTIL_WPI_UV_NETWORKSTREAM_H_
#include <uv.h>
#include <functional>
#include <memory>
#include "wpi/Signal.h"
#include "wpi/uv/Stream.h"
namespace wpi {
namespace uv {
class NetworkStream;
/**
* Connection request.
*/
class ConnectReq : public RequestImpl<ConnectReq, uv_connect_t> {
public:
ConnectReq();
NetworkStream& GetStream() const {
return *static_cast<NetworkStream*>(GetRaw()->handle->data);
}
/**
* Connection completed signal.
*/
sig::Signal<> connected;
};
/**
* Network stream handle.
* This is an abstract type; there are two network stream implementations (Tcp
* and Pipe).
*/
class NetworkStream : public Stream {
public:
static constexpr int kDefaultBacklog = 128;
std::shared_ptr<NetworkStream> shared_from_this() {
return std::static_pointer_cast<NetworkStream>(Handle::shared_from_this());
}
std::shared_ptr<const NetworkStream> shared_from_this() const {
return std::static_pointer_cast<const NetworkStream>(
Handle::shared_from_this());
}
/**
* Start listening for incoming connections. When a new incoming connection
* is received the connection signal is generated.
* @param backlog the number of connections the kernel might queue, same as
* listen(2).
*/
void Listen(int backlog = kDefaultBacklog);
/**
* Start listening for incoming connections. This is a convenience wrapper
* around `Listen(int)` that also connects a callback to the connection
* signal. When a new incoming connection is received the connection signal
* is generated (and the callback is called).
* @param callback the callback to call when a connection is received.
* `Accept()` should be called from this callback.
* @param backlog the number of connections the kernel might queue, same as
* listen(2).
*/
void Listen(std::function<void()> callback, int backlog = kDefaultBacklog);
/**
* Accept incoming connection.
*
* This call is used in conjunction with `Listen()` to accept incoming
* connections. Call this function after receiving a ListenEvent event to
* accept the connection.
* An error signal will be emitted in case of errors.
*
* When the connection signal is emitted it is guaranteed that this
* function will complete successfully the first time. If you attempt to use
* it more than once, it may fail.
* It is suggested to only call this function once per connection signal.
*
* @return The stream handle for the accepted connection, or nullptr on error.
*/
std::shared_ptr<NetworkStream> Accept() {
return DoAccept()->shared_from_this();
}
/**
* Accept incoming connection.
*
* This call is used in conjunction with `Listen()` to accept incoming
* connections. Call this function after receiving a connection signal to
* accept the connection.
* An error signal will be emitted in case of errors.
*
* When the connection signal is emitted it is guaranteed that this
* function will complete successfully the first time. If you attempt to use
* it more than once, it may fail.
* It is suggested to only call this function once per connection signal.
*
* @param client Client stream object.
* @return False on error.
*/
bool Accept(const std::shared_ptr<NetworkStream>& client) {
return Invoke(&uv_accept, GetRawStream(), client->GetRawStream());
}
/**
* Signal generated when an incoming connection is received.
*/
sig::Signal<> connection;
protected:
explicit NetworkStream(uv_stream_t* uv_stream) : Stream{uv_stream} {}
virtual NetworkStream* DoAccept() = 0;
};
template <typename T, typename U>
class NetworkStreamImpl : public NetworkStream {
public:
std::shared_ptr<T> shared_from_this() {
return std::static_pointer_cast<T>(Handle::shared_from_this());
}
std::shared_ptr<const T> shared_from_this() const {
return std::static_pointer_cast<const T>(Handle::shared_from_this());
}
/**
* Get the underlying handle data structure.
*
* @return The underlying handle data structure.
*/
U* GetRaw() const noexcept {
return reinterpret_cast<U*>(this->GetRawHandle());
}
protected:
NetworkStreamImpl() : NetworkStream{reinterpret_cast<uv_stream_t*>(new U)} {}
};
} // namespace uv
} // namespace wpi
#endif // WPIUTIL_WPI_UV_NETWORKSTREAM_H_

View File

@@ -0,0 +1,194 @@
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#ifndef WPIUTIL_WPI_UV_PIPE_H_
#define WPIUTIL_WPI_UV_PIPE_H_
#include <uv.h>
#include <functional>
#include <memory>
#include <string>
#include "wpi/Twine.h"
#include "wpi/uv/NetworkStream.h"
namespace wpi {
namespace uv {
class Loop;
class PipeConnectReq;
/**
* Pipe handle.
* Pipe handles provide an abstraction over local domain sockets on Unix and
* named pipes on Windows.
*/
class Pipe final : public NetworkStreamImpl<Pipe, uv_pipe_t> {
struct private_init {};
public:
explicit Pipe(const private_init&) {}
~Pipe() noexcept override = default;
/**
* Create a pipe handle.
*
* @param loop Loop object where this handle runs.
* @param ipc Indicates if this pipe will be used for handle passing between
* processes.
*/
static std::shared_ptr<Pipe> Create(Loop& loop, bool ipc = false);
/**
* Create a pipe handle.
*
* @param loop Loop object where this handle runs.
* @param ipc Indicates if this pipe will be used for handle passing between
* processes.
*/
static std::shared_ptr<Pipe> Create(const std::shared_ptr<Loop>& loop,
bool ipc = false) {
return Create(*loop, ipc);
}
/**
* Accept incoming connection.
*
* This call is used in conjunction with `Listen()` to accept incoming
* connections. Call this function after receiving a ListenEvent event to
* accept the connection.
* An error signal will be emitted in case of errors.
*
* When the connection signal is emitted it is guaranteed that this
* function will complete successfully the first time. If you attempt to use
* it more than once, it may fail.
* It is suggested to only call this function once per connection signal.
*
* @return The stream handle for the accepted connection, or nullptr on error.
*/
std::shared_ptr<Pipe> Accept();
/**
* Accept incoming connection.
*
* This call is used in conjunction with `Listen()` to accept incoming
* connections. Call this function after receiving a connection signal to
* accept the connection.
* An error signal will be emitted in case of errors.
*
* When the connection signal is emitted it is guaranteed that this
* function will complete successfully the first time. If you attempt to use
* it more than once, it may fail.
* It is suggested to only call this function once per connection signal.
*
* @param client Client stream object.
* @return False on error.
*/
bool Accept(const std::shared_ptr<Pipe>& client) {
return NetworkStream::Accept(client);
}
/**
* Open an existing file descriptor or HANDLE as a pipe.
*
* @note The passed file descriptor or HANDLE is not checked for its type, but
* it's required that it represents a valid pipe.
*
* @param file A valid file handle (either a file descriptor or a HANDLE).
*/
void Open(uv_file file) { Invoke(&uv_pipe_open, GetRaw(), file); }
/**
* Bind the pipe to a file path (Unix) or a name (Windows).
*
* @note Paths on Unix get truncated to `sizeof(sockaddr_un.sun_path)` bytes,
* typically between 92 and 108 bytes.
*
* @param name File path (Unix) or name (Windows).
*/
void Bind(const Twine& name);
/**
* Connect to the Unix domain socket or the named pipe.
*
* @note Paths on Unix get truncated to `sizeof(sockaddr_un.sun_path)` bytes,
* typically between 92 and 108 bytes.
*
* HandleConnected() is called on the request when the connection has been
* established.
* HandleError() is called on the request in case of errors during the
* connection.
*
* @param name File path (Unix) or name (Windows).
* @param req connection request
*/
void Connect(const Twine& name, const std::shared_ptr<PipeConnectReq>& req);
/**
* Connect to the Unix domain socket or the named pipe.
*
* @note Paths on Unix get truncated to `sizeof(sockaddr_un.sun_path)` bytes,
* typically between 92 and 108 bytes.
*
* The callback is called when the connection has been established. Errors
* are reported to the stream error handler.
*
* @param name File path (Unix) or name (Windows).
* @param callback Callback function to call when connection established
*/
void Connect(const Twine& name, std::function<void()> callback);
/**
* Get the name of the Unix domain socket or the named pipe.
* @return The name (will be empty if an error occurred).
*/
std::string GetSock();
/**
* Get the name of the Unix domain socket or the named pipe to which the
* handle is connected.
* @return The name (will be empty if an error occurred).
*/
std::string GetPeer();
/**
* Set the number of pending pipe instance handles when the pipe server is
* waiting for connections.
* @note This setting applies to Windows only.
* @param count Number of pending handles.
*/
void SetPendingInstances(int count) {
uv_pipe_pending_instances(GetRaw(), count);
}
/**
* Alters pipe permissions, allowing it to be accessed from processes run
* by different users. Makes the pipe writable or readable by all users.
* Mode can be UV_WRITABLE, UV_READABLE, or both. This function is blocking.
* @param flags chmod flags
*/
void Chmod(int flags) { Invoke(&uv_pipe_chmod, GetRaw(), flags); }
private:
Pipe* DoAccept() override;
};
/**
* Pipe connection request.
*/
class PipeConnectReq : public ConnectReq {
public:
Pipe& GetStream() const {
return *static_cast<Pipe*>(&ConnectReq::GetStream());
}
};
} // namespace uv
} // namespace wpi
#endif // WPIUTIL_WPI_UV_PIPE_H_

View File

@@ -0,0 +1,93 @@
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#ifndef WPIUTIL_WPI_UV_POLL_H_
#define WPIUTIL_WPI_UV_POLL_H_
#include <uv.h>
#include <memory>
#include "wpi/Signal.h"
#include "wpi/uv/Handle.h"
namespace wpi {
namespace uv {
class Loop;
/**
* Poll handle.
*/
class Poll final : public HandleImpl<Poll, uv_poll_t> {
struct private_init {};
public:
explicit Poll(const private_init&) {}
~Poll() noexcept override = default;
/**
* Create a poll handle using a file descriptor.
*
* @param loop Loop object where this handle runs.
* @param fd File descriptor.
*/
static std::shared_ptr<Poll> Create(Loop& loop, int fd);
/**
* Create a poll handle using a file descriptor.
*
* @param loop Loop object where this handle runs.
* @param fd File descriptor.
*/
static std::shared_ptr<Poll> Create(const std::shared_ptr<Loop>& loop,
int fd) {
return Create(*loop, fd);
}
/**
* Create a poll handle using a socket descriptor.
*
* @param loop Loop object where this handle runs.
* @param sock Socket descriptor.
*/
static std::shared_ptr<Poll> CreateSocket(Loop& loop, uv_os_sock_t sock);
/**
* Create a poll handle using a socket descriptor.
*
* @param loop Loop object where this handle runs.
* @param sock Socket descriptor.
*/
static std::shared_ptr<Poll> CreateSocket(const std::shared_ptr<Loop>& loop,
uv_os_sock_t sock) {
return CreateSocket(*loop, sock);
}
/**
* Start polling the file descriptor.
*
* @param events Bitmask of events (UV_READABLE, UV_WRITEABLE, UV_PRIORITIZED,
* and UV_DISCONNECT).
*/
void Start(int events);
/**
* Stop polling the file descriptor.
*/
void Stop() { Invoke(&uv_poll_stop, GetRaw()); }
/**
* Signal generated when a poll event occurs.
*/
sig::Signal<int> pollEvent;
};
} // namespace uv
} // namespace wpi
#endif // WPIUTIL_WPI_UV_POLL_H_

View File

@@ -0,0 +1,70 @@
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#ifndef WPIUTIL_WPI_UV_PREPARE_H_
#define WPIUTIL_WPI_UV_PREPARE_H_
#include <uv.h>
#include <memory>
#include "wpi/Signal.h"
#include "wpi/uv/Handle.h"
namespace wpi {
namespace uv {
class Loop;
/**
* Prepare handle.
* Prepare handles will generate a signal once per loop iteration, right
* before polling for I/O.
*/
class Prepare final : public HandleImpl<Prepare, uv_prepare_t> {
struct private_init {};
public:
explicit Prepare(const private_init&) {}
~Prepare() noexcept override = default;
/**
* Create a prepare handle.
*
* @param loop Loop object where this handle runs.
*/
static std::shared_ptr<Prepare> Create(Loop& loop);
/**
* Create a prepare handle.
*
* @param loop Loop object where this handle runs.
*/
static std::shared_ptr<Prepare> Create(const std::shared_ptr<Loop>& loop) {
return Create(*loop);
}
/**
* Start the handle.
*/
void Start();
/**
* Stop the handle. The signal will no longer be generated.
*/
void Stop() { Invoke(&uv_prepare_stop, GetRaw()); }
/**
* Signal generated once per loop iteration prior to polling for I/O.
*/
sig::Signal<> prepare;
};
} // namespace uv
} // namespace wpi
#endif // WPIUTIL_WPI_UV_PREPARE_H_

View File

@@ -0,0 +1,226 @@
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#ifndef WPIUTIL_WPI_UV_PROCESS_H_
#define WPIUTIL_WPI_UV_PROCESS_H_
#include <uv.h>
#include <memory>
#include <string>
#include "wpi/Signal.h"
#include "wpi/SmallVector.h"
#include "wpi/Twine.h"
#include "wpi/uv/Handle.h"
namespace wpi {
namespace uv {
class Loop;
class Pipe;
/**
* Process options.
*/
class ProcessOptions final {
friend class Process;
public:
/**
* Set environment variables for the subprocess. If not set or set to
* nullptr, the parent's environment is used.
* @param env environment variables
*/
ProcessOptions& SetEnv(char** env) {
m_env = env;
return *this;
}
/**
* Set the current working directory for the subprocess.
* @param cwd current working directory
*/
ProcessOptions& SetCwd(const Twine& cwd) {
m_cwd = cwd.str();
return *this;
}
/**
* Set the child process' user id.
* @param uid user id
*/
ProcessOptions& SetUid(uv_uid_t uid) noexcept {
m_uid = uid;
m_flags |= UV_PROCESS_SETUID;
return *this;
}
/**
* Set the child process' group id.
* @param gid group id
*/
ProcessOptions& SetGid(uv_gid_t gid) noexcept {
m_gid = gid;
m_flags |= UV_PROCESS_SETGID;
return *this;
}
/**
* Set flags.
* @param flags Bitmask values from uv_process_flags.
*/
ProcessOptions& SetFlags(unsigned int flags) noexcept {
m_flags |= flags;
return *this;
}
/**
* Clear flags.
* @param flags Bitmask values from uv_process_flags.
*/
ProcessOptions& ClearFlags(unsigned int flags) noexcept {
m_flags &= ~flags;
return *this;
}
/**
* Explicitly ignore a stdio.
* @param index stdio index
*/
ProcessOptions& StdioIgnore(size_t index);
/**
* Inherit a file descriptor from the parent process.
* @param index stdio index
* @param fd parent file descriptor
*/
ProcessOptions& StdioInherit(size_t index, int fd);
/**
* Inherit a pipe from the parent process.
* @param index stdio index
* @param pipe pipe
*/
ProcessOptions& StdioInherit(size_t index, Pipe& pipe);
/**
* Create a pipe between the child and the parent.
* @param index stdio index
* @param pipe pipe
* @param flags Some combination of UV_READABLE_PIPE, UV_WRITABLE_PIPE, and
* UV_OVERLAPPED_PIPE (Windows only, ignored on Unix).
*/
ProcessOptions& StdioCreatePipe(size_t index, Pipe& pipe, unsigned int flags);
private:
char** m_env = nullptr;
std::string m_cwd;
unsigned int m_flags = 0;
struct StdioContainer : public uv_stdio_container_t {
StdioContainer() {
flags = UV_IGNORE;
data.fd = 0;
}
};
SmallVector<StdioContainer, 4> m_stdio;
uv_uid_t m_uid = 0;
uv_gid_t m_gid = 0;
};
/**
* Process handle.
* Process handles will spawn a new process and allow the user to control it
* and establish communication channels with it using streams.
*/
class Process final : public HandleImpl<Process, uv_process_t> {
struct private_init {};
public:
explicit Process(const private_init&) {}
~Process() noexcept override = default;
/**
* Disables inheritance for file descriptors / handles that this process
* inherited from its parent. The effect is that child processes spawned
* by this process don't accidentally inherit these handles.
*
* It is recommended to call this function as early in your program as
* possible, before the inherited file descriptors can be closed or
* duplicated.
*/
static void DisableStdioInheritance() { uv_disable_stdio_inheritance(); }
/**
* Starts a process. If the process is not successfully spawned, an error
* is generated on the loop and this function returns nullptr.
*
* Possible reasons for failing to spawn would include (but not be limited to)
* the file to execute not existing, not having permissions to use the setuid
* or setgid specified, or not having enough memory to allocate for the new
* process.
*
* @param loop Loop object where this handle runs.
* @param file Path pointing to the program to be executed
* @param args Command line arguments
* @param options Process options
*/
static std::shared_ptr<Process> Spawn(
Loop& loop, const Twine& file, char** args,
const ProcessOptions& options = ProcessOptions{});
/**
* Starts a process. If the process is not successfully spawned, an error
* is generated on the loop and this function returns nullptr.
*
* Possible reasons for failing to spawn would include (but not be limited to)
* the file to execute not existing, not having permissions to use the setuid
* or setgid specified, or not having enough memory to allocate for the new
* process.
*
* @param loop Loop object where this handle runs.
* @param file Path pointing to the program to be executed
* @param args Command line arguments
* @param options Process options
*/
static std::shared_ptr<Process> Spawn(
const std::shared_ptr<Loop>& loop, const Twine& file, char** args,
const ProcessOptions& options = ProcessOptions{}) {
return Spawn(*loop, file, args, options);
}
/**
* Sends the specified signal to the process.
* @param signum signal number
*/
void Kill(int signum) { Invoke(&uv_process_kill, GetRaw(), signum); }
/**
* Sends the specified signal to the given PID.
* @param pid process ID
* @param signum signal number
* @return 0 on success, otherwise error code.
*/
static int Kill(int pid, int signum) noexcept { return uv_kill(pid, signum); }
/**
* Get the process ID.
* @return Process ID.
*/
uv_pid_t GetPid() const noexcept { return GetRaw()->pid; }
/**
* Signal generated when the process exits. The parameters are the exit
* status and the signal that caused the process to terminate, if any.
*/
sig::Signal<int64_t, int> exited;
};
} // namespace uv
} // namespace wpi
#endif // WPIUTIL_WPI_UV_PROCESS_H_

View File

@@ -0,0 +1,171 @@
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#ifndef WPIUTIL_WPI_UV_REQUEST_H_
#define WPIUTIL_WPI_UV_REQUEST_H_
#include <uv.h>
#include <functional>
#include <memory>
#include "wpi/uv/Error.h"
namespace wpi {
namespace uv {
/**
* Request. Requests are not moveable or copyable.
* This class provides shared_ptr ownership and shared_from_this.
*/
class Request : public std::enable_shared_from_this<Request> {
public:
using Type = uv_req_type;
Request(const Request&) = delete;
Request(Request&&) = delete;
Request& operator=(const Request&) = delete;
Request& operator=(Request&&) = delete;
virtual ~Request() noexcept = default;
/**
* Get the type of the request.
*
* A base request offers no functionality to promote it to the actual request
* type. By means of this function, the type of the underlying request as
* specified by Type is made available.
*
* @return The actual type of the request.
*/
Type GetType() const noexcept { return m_uv_req->type; }
/**
* Get the name of the type of the request. E.g. "connect" for connect.
*/
const char* GetTypeName() const noexcept {
return uv_req_type_name(m_uv_req->type);
}
/**
* Cancel a pending request.
*
* This method fails if the request is executing or has finished
* executing.
* It can emit an error signal in case of errors.
*
* @return True in case of success, false otherwise.
*/
bool Cancel() { return uv_cancel(m_uv_req) == 0; }
/**
* Return the size of the underlying request type.
* @return The size of the underlying request type.
*/
size_t RawSize() const noexcept { return uv_req_size(m_uv_req->type); }
/**
* Get the underlying request data structure.
*
* @return The underlying request data structure.
*/
uv_req_t* GetRawReq() noexcept { return m_uv_req; }
/**
* Get the underlying request data structure.
*
* @return The underlying request data structure.
*/
const uv_req_t* GetRawReq() const noexcept { return m_uv_req; }
/**
* Keep this request in memory even if no outside shared_ptr references
* remain. To release call Release().
*
* Derived classes can override this method for different memory management
* approaches (e.g. pooled storage of requests).
*/
virtual void Keep() noexcept { m_self = shared_from_this(); }
/**
* No longer force holding this request in memory. Does not immediately
* destroy the object unless no outside shared_ptr references remain.
*
* Derived classes can override this method for different memory management
* approaches (e.g. pooled storage of requests).
*/
virtual void Release() noexcept { m_self.reset(); }
/**
* Error callback. By default, this is set up to report errors to the handle
* that created this request.
* @param err error code
*/
std::function<void(Error)> error;
/**
* Report an error.
* @param err Error code
*/
void ReportError(int err) { error(Error(err)); }
protected:
/**
* Constructor.
*/
explicit Request(uv_req_t* uv_req) : m_uv_req{uv_req} {
m_uv_req->data = this;
}
private:
std::shared_ptr<Request> m_self;
uv_req_t* m_uv_req;
};
/**
* Request. Requests are not moveable or copyable.
* @tparam T CRTP derived class
* @tparam U underlying libuv request type
*/
template <typename T, typename U>
class RequestImpl : public Request {
public:
std::shared_ptr<T> shared_from_this() {
return std::static_pointer_cast<T>(this->shared_from_this());
}
std::shared_ptr<const T> shared_from_this() const {
return std::static_pointer_cast<const T>(this->shared_from_this());
}
/**
* Get the underlying request data structure.
*
* @return The underlying request data structure.
*/
U* GetRaw() noexcept { return &m_uv_req; }
/**
* Get the underlying request data structure.
*
* @return The underlying request data structure.
*/
const U* GetRaw() const noexcept { return &m_uv_req; }
protected:
/**
* Constructor.
*/
RequestImpl() : Request{reinterpret_cast<uv_req_t*>(&m_uv_req)} {}
private:
U m_uv_req;
};
} // namespace uv
} // namespace wpi
#endif // WPIUTIL_WPI_UV_REQUEST_H_

View File

@@ -0,0 +1,84 @@
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#ifndef WPIUTIL_WPI_UV_SIGNAL_H_
#define WPIUTIL_WPI_UV_SIGNAL_H_
#include <uv.h>
#include <memory>
#include "wpi/Signal.h"
#include "wpi/uv/Handle.h"
namespace wpi {
namespace uv {
class Loop;
/**
* Signal handle.
*/
class Signal final : public HandleImpl<Signal, uv_signal_t> {
struct private_init {};
public:
explicit Signal(const private_init&) {}
~Signal() noexcept override = default;
/**
* Create a signal handle.
*
* @param loop Loop object where this handle runs.
*/
static std::shared_ptr<Signal> Create(Loop& loop);
/**
* Create a signal handle.
*
* @param loop Loop object where this handle runs.
*/
static std::shared_ptr<Signal> Create(const std::shared_ptr<Loop>& loop) {
return Create(*loop);
}
/**
* Start watching for the given signal.
*
* @param signum Signal to watch for.
*/
void Start(int signum);
/**
* Start watching for the given signal. Same as Start() but the signal
* handler is reset the moment the signal is received.
*
* @param signum Signal to watch for.
*/
void StartOneshot(int signum);
/**
* Stop watching for the signal.
*/
void Stop() { Invoke(&uv_signal_stop, GetRaw()); }
/**
* Get the signal being monitored.
* @return Signal number.
*/
int GetSignal() const { return GetRaw()->signum; }
/**
* Signal generated when a signal occurs.
*/
sig::Signal<int> signal;
};
} // namespace uv
} // namespace wpi
#endif // WPIUTIL_WPI_UV_SIGNAL_H_

View File

@@ -0,0 +1,249 @@
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#ifndef WPIUTIL_WPI_UV_STREAM_H_
#define WPIUTIL_WPI_UV_STREAM_H_
#include <uv.h>
#include <functional>
#include <memory>
#include "wpi/ArrayRef.h"
#include "wpi/Signal.h"
#include "wpi/uv/Buffer.h"
#include "wpi/uv/Handle.h"
#include "wpi/uv/Request.h"
namespace wpi {
namespace uv {
class Stream;
/**
* Shutdown request.
*/
class ShutdownReq : public RequestImpl<ShutdownReq, uv_shutdown_t> {
public:
ShutdownReq();
Stream& GetStream() const {
return *static_cast<Stream*>(GetRaw()->handle->data);
}
/**
* Shutdown completed signal.
*/
sig::Signal<> complete;
};
/**
* Write request.
*/
class WriteReq : public RequestImpl<WriteReq, uv_write_t> {
public:
WriteReq();
Stream& GetStream() const {
return *static_cast<Stream*>(GetRaw()->handle->data);
}
/**
* Write completed signal. This is called even if an error occurred.
* @param err error value
*/
sig::Signal<Error> finish;
};
/**
* Stream handle.
* Stream handles provide an abstraction of a duplex communication channel.
* This is an abstract type; there are three stream implementations (Tcp,
* Pipe, and Tty).
*/
class Stream : public Handle {
public:
std::shared_ptr<Stream> shared_from_this() {
return std::static_pointer_cast<Stream>(Handle::shared_from_this());
}
std::shared_ptr<const Stream> shared_from_this() const {
return std::static_pointer_cast<const Stream>(Handle::shared_from_this());
}
/**
* Shutdown the outgoing (write) side of a duplex stream. It waits for pending
* write requests to complete. HandleShutdownComplete() is called on the
* request after shutdown is complete.
*
* @param req shutdown request
*/
void Shutdown(const std::shared_ptr<ShutdownReq>& req);
/**
* Shutdown the outgoing (write) side of a duplex stream. It waits for pending
* write requests to complete. The callback is called after shutdown is
* complete. Errors will be reported to the stream error handler.
*
* @param callback Callback function to call when shutdown completes
* @return Connection object for the callback
*/
void Shutdown(std::function<void()> callback = nullptr);
/**
* Start reading data from an incoming stream.
*
* A data signal will be emitted several times until there is no more
* data to read or `StopRead()` is called.
* An end signal will be emitted when there is no more data to read.
*/
void StartRead();
/**
* Stop reading data from the stream.
*
* This function is idempotent and may be safely called on a stopped stream.
*/
void StopRead() { Invoke(&uv_read_stop, GetRawStream()); }
/**
* Write data to the stream.
*
* Data are written in order. The lifetime of the data pointers passed in
* the `bufs` parameter must exceed the lifetime of the write request.
* An easy way to ensure this is to have the write request keep track of
* the data and use either its Complete() function or destructor to free the
* data.
*
* HandleWriteComplete() will be called on the request object when the data
* has been written. HandleWriteComplete() is called even if an error occurs.
* HandleError() will be called on the request object in case of errors.
*
* @param bufs The buffers to be written to the stream.
* @param req write request
*/
void Write(ArrayRef<Buffer> bufs, const std::shared_ptr<WriteReq>& req);
/**
* Write data to the stream.
*
* Data are written in order. The lifetime of the data pointers passed in
* the `bufs` parameter must exceed the lifetime of the write request.
* The callback can be used to free data after the request completes.
*
* The callback will be called when the data has been written. Errors will
* be reported to the stream error handler.
*
* @param bufs The buffers to be written to the stream.
* @param callback Callback function to call when the write completes
*/
void Write(ArrayRef<Buffer> bufs,
std::function<void(MutableArrayRef<Buffer>, Error)> callback);
/**
* Queue a write request if it can be completed immediately.
*
* Same as `Write()`, but wont queue a write request if it cant be
* completed immediately.
* An error signal will be emitted in case of errors.
*
* @param bufs The buffers to be written to the stream.
* @return Number of bytes written.
*/
int TryWrite(ArrayRef<Buffer> bufs);
/**
* Check if the stream is readable.
* @return True if the stream is readable, false otherwise.
*/
bool IsReadable() const noexcept {
return uv_is_readable(GetRawStream()) == 1;
}
/**
* @brief Checks if the stream is writable.
* @return True if the stream is writable, false otherwise.
*/
bool IsWritable() const noexcept {
return uv_is_writable(GetRawStream()) == 1;
}
/**
* Enable or disable blocking mode for a stream.
*
* When blocking mode is enabled all writes complete synchronously. The
* interface remains unchanged otherwise, e.g. completion or failure of the
* operation will still be reported through events which are emitted
* asynchronously.
*
* @param enable True to enable blocking mode, false otherwise.
* @return True in case of success, false otherwise.
*/
bool SetBlocking(bool enable) noexcept {
return uv_stream_set_blocking(GetRawStream(), enable) == 0;
}
/**
* Gets the amount of queued bytes waiting to be sent.
* @return Amount of queued bytes waiting to be sent.
*/
size_t GetWriteQueueSize() const noexcept {
return GetRawStream()->write_queue_size;
}
/**
* Get the underlying stream data structure.
*
* @return The underlying stream data structure.
*/
uv_stream_t* GetRawStream() const noexcept {
return reinterpret_cast<uv_stream_t*>(GetRawHandle());
}
/**
* Signal generated when data was read on a stream.
*/
sig::Signal<Buffer&, size_t> data;
/**
* Signal generated when no more read data is available.
*/
sig::Signal<> end;
protected:
explicit Stream(uv_stream_t* uv_stream)
: Handle{reinterpret_cast<uv_handle_t*>(uv_stream)} {}
};
template <typename T, typename U>
class StreamImpl : public Stream {
public:
std::shared_ptr<T> shared_from_this() {
return std::static_pointer_cast<T>(Handle::shared_from_this());
}
std::shared_ptr<const T> shared_from_this() const {
return std::static_pointer_cast<const T>(Handle::shared_from_this());
}
/**
* Get the underlying handle data structure.
*
* @return The underlying handle data structure.
*/
U* GetRaw() const noexcept {
return reinterpret_cast<U*>(this->GetRawHandle());
}
protected:
StreamImpl() : Stream{reinterpret_cast<uv_stream_t*>(new U)} {}
};
} // namespace uv
} // namespace wpi
#endif // WPIUTIL_WPI_UV_STREAM_H_

View File

@@ -0,0 +1,322 @@
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#ifndef WPIUTIL_WPI_UV_TCP_H_
#define WPIUTIL_WPI_UV_TCP_H_
#include <uv.h>
#include <chrono>
#include <functional>
#include <memory>
#include "wpi/Twine.h"
#include "wpi/uv/NetworkStream.h"
namespace wpi {
namespace uv {
class Loop;
class TcpConnectReq;
/**
* TCP handle.
* TCP handles are used to represent both TCP streams and servers.
*/
class Tcp final : public NetworkStreamImpl<Tcp, uv_tcp_t> {
struct private_init {};
public:
using Time = std::chrono::duration<uint64_t, std::milli>;
explicit Tcp(const private_init&) {}
~Tcp() noexcept override = default;
/**
* Create a TCP handle.
*
* @param loop Loop object where this handle runs.
* @param flags Flags
*/
static std::shared_ptr<Tcp> Create(Loop& loop,
unsigned int flags = AF_UNSPEC);
/**
* Create a TCP handle.
*
* @param loop Loop object where this handle runs.
* @param flags Flags
*/
static std::shared_ptr<Tcp> Create(const std::shared_ptr<Loop>& loop,
unsigned int flags = AF_UNSPEC) {
return Create(*loop, flags);
}
/**
* Accept incoming connection.
*
* This call is used in conjunction with `Listen()` to accept incoming
* connections. Call this function after receiving a ListenEvent event to
* accept the connection.
* An error signal will be emitted in case of errors.
*
* When the connection signal is emitted it is guaranteed that this
* function will complete successfully the first time. If you attempt to use
* it more than once, it may fail.
* It is suggested to only call this function once per connection signal.
*
* @return The stream handle for the accepted connection, or nullptr on error.
*/
std::shared_ptr<Tcp> Accept();
/**
* Accept incoming connection.
*
* This call is used in conjunction with `Listen()` to accept incoming
* connections. Call this function after receiving a connection signal to
* accept the connection.
* An error signal will be emitted in case of errors.
*
* When the connection signal is emitted it is guaranteed that this
* function will complete successfully the first time. If you attempt to use
* it more than once, it may fail.
* It is suggested to only call this function once per connection signal.
*
* @param client Client stream object.
* @return False on error.
*/
bool Accept(const std::shared_ptr<Tcp>& client) {
return NetworkStream::Accept(client);
}
/**
* Open an existing file descriptor or SOCKET as a TCP handle.
*
* @note The passed file descriptor or SOCKET is not checked for its type, but
* it's required that it represents a valid stream socket.
*
* @param sock A valid socket handle (either a file descriptor or a SOCKET).
*/
void Open(uv_os_sock_t sock) { Invoke(&uv_tcp_open, GetRaw(), sock); }
/**
* Enable/Disable Nagle's algorithm.
* @param enable True to enable it, false otherwise.
* @return True in case of success, false otherwise.
*/
bool SetNoDelay(bool enable) { return uv_tcp_nodelay(GetRaw(), enable) == 0; }
/**
* Enable/Disable TCP keep-alive.
* @param enable True to enable it, false otherwise.
* @param time Initial delay in seconds (use
* `std::chrono::duration<unsigned int>`).
* @return True in case of success, false otherwise.
*/
bool SetKeepAlive(bool enable, Time time = Time{0}) {
return uv_tcp_keepalive(GetRaw(), enable, time.count()) == 0;
}
/**
* Enable/Disable simultaneous asynchronous accept requests.
*
* Enable/Disable simultaneous asynchronous accept requests that are
* queued by the operating system when listening for new TCP
* connections.
* This setting is used to tune a TCP server for the desired performance.
* Having simultaneous accepts can significantly improve the rate of
* accepting connections (which is why it is enabled by default) but may
* lead to uneven load distribution in multi-process setups.
*
* @param enable True to enable it, false otherwise.
* @return True in case of success, false otherwise.
*/
bool SetSimultaneousAccepts(bool enable) {
return uv_tcp_simultaneous_accepts(GetRaw(), enable) == 0;
}
/**
* Bind the handle to an IPv4 or IPv6 address and port.
*
* A successful call to this function does not guarantee that the call to
* `Listen()` or `Connect()` will work properly.
* An error signal can be emitted because of either this function or the
* ones mentioned above.
*
* @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure.
* @param flags Optional additional flags.
*/
void Bind(const sockaddr& addr, unsigned int flags = 0) {
Invoke(&uv_tcp_bind, GetRaw(), &addr, flags);
}
/**
* Bind the handle to an IPv4 address and port.
*
* A successful call to this function does not guarantee that the call to
* `Listen()` or `Connect()` will work properly.
* An error signal can be emitted because of either this function or the
* ones mentioned above.
*
* Available flags are:
*
* @param ip The address to which to bind.
* @param port The port to which to bind.
* @param flags Optional additional flags.
*/
void Bind(const Twine& ip, unsigned int port, unsigned int flags = 0);
/**
* Bind the handle to an IPv6 address and port.
*
* A successful call to this function does not guarantee that the call to
* `Listen()` or `Connect()` will work properly.
* An error signal can be emitted because of either this function or the
* ones mentioned above.
*
* Available flags are:
*
* @param ip The address to which to bind.
* @param port The port to which to bind.
* @param flags Optional additional flags.
*/
void Bind6(const Twine& ip, unsigned int port, unsigned int flags = 0);
/**
* Get the current address to which the handle is bound.
* @return The address (will be zeroed if an error occurred).
*/
sockaddr_storage GetSock();
/**
* Get the address of the peer connected to the handle.
* @return The address (will be zeroed if an error occurred).
*/
sockaddr_storage GetPeer();
/**
* Establish an IPv4 or IPv6 TCP connection.
*
* On Windows if the addr is initialized to point to an unspecified address
* (`0.0.0.0` or `::`) it will be changed to point to localhost. This is
* done to match the behavior of Linux systems.
*
* HandleConnected() is called on the request when the connection has been
* established.
* HandleError() is called on the request in case of errors during the
* connection.
*
* @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure.
* @param req connection request
*/
void Connect(const sockaddr& addr, const std::shared_ptr<TcpConnectReq>& req);
/**
* Establish an IPv4 or IPv6 TCP connection.
*
* On Windows if the addr is initialized to point to an unspecified address
* (`0.0.0.0` or `::`) it will be changed to point to localhost. This is
* done to match the behavior of Linux systems.
*
* The callback is called when the connection has been established. Errors
* are reported to the stream error handler.
*
* @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure.
* @param callback Callback function to call when connection established
*/
void Connect(const sockaddr& addr, std::function<void()> callback);
/**
* Establish an IPv4 TCP connection.
*
* On Windows if the addr is initialized to point to an unspecified address
* (`0.0.0.0` or `::`) it will be changed to point to localhost. This is
* done to match the behavior of Linux systems.
*
* HandleConnected() is called on the request when the connection has been
* established.
* HandleError() is called on the request in case of errors during the
* connection.
*
* @param ip The address to which to connect to.
* @param port The port to which to connect to.
* @param req connection request
*/
void Connect(const Twine& ip, unsigned int port,
const std::shared_ptr<TcpConnectReq>& req);
/**
* Establish an IPv4 TCP connection.
*
* On Windows if the addr is initialized to point to an unspecified address
* (`0.0.0.0` or `::`) it will be changed to point to localhost. This is
* done to match the behavior of Linux systems.
*
* The callback is called when the connection has been established. Errors
* are reported to the stream error handler.
*
* @param ip The address to which to connect to.
* @param port The port to which to connect to.
* @param callback Callback function to call when connection established
*/
void Connect(const Twine& ip, unsigned int port,
std::function<void()> callback);
/**
* Establish an IPv6 TCP connection.
*
* On Windows if the addr is initialized to point to an unspecified address
* (`0.0.0.0` or `::`) it will be changed to point to localhost. This is
* done to match the behavior of Linux systems.
*
* HandleConnected() is called on the request when the connection has been
* established.
* HandleError() is called on the request in case of errors during the
* connection.
*
* @param ip The address to which to connect to.
* @param port The port to which to connect to.
* @param req connection request
*/
void Connect6(const Twine& ip, unsigned int port,
const std::shared_ptr<TcpConnectReq>& req);
/**
* Establish an IPv6 TCP connection.
*
* On Windows if the addr is initialized to point to an unspecified address
* (`0.0.0.0` or `::`) it will be changed to point to localhost. This is
* done to match the behavior of Linux systems.
*
* The callback is called when the connection has been established. Errors
* are reported to the stream error handler.
*
* @param ip The address to which to connect to.
* @param port The port to which to connect to.
* @param callback Callback function to call when connection established
*/
void Connect6(const Twine& ip, unsigned int port,
std::function<void()> callback);
private:
Tcp* DoAccept() override;
};
/**
* TCP connection request.
*/
class TcpConnectReq : public ConnectReq {
public:
Tcp& GetStream() const {
return *static_cast<Tcp*>(&ConnectReq::GetStream());
}
};
} // namespace uv
} // namespace wpi
#endif // WPIUTIL_WPI_UV_TCP_H_

View File

@@ -0,0 +1,139 @@
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#ifndef WPIUTIL_WPI_UV_TIMER_H_
#define WPIUTIL_WPI_UV_TIMER_H_
#include <uv.h>
#include <chrono>
#include <functional>
#include <memory>
#include "wpi/Signal.h"
#include "wpi/uv/Handle.h"
namespace wpi {
namespace uv {
class Loop;
/**
* Timer handle.
* Timer handles are used to schedule signals to be called in the future.
*/
class Timer final : public HandleImpl<Timer, uv_timer_t> {
struct private_init {};
public:
using Time = std::chrono::duration<uint64_t, std::milli>;
explicit Timer(const private_init&) {}
~Timer() noexcept override = default;
/**
* Create a timer handle.
*
* @param loop Loop object where this handle runs.
*/
static std::shared_ptr<Timer> Create(Loop& loop);
/**
* Create a timer handle.
*
* @param loop Loop object where this handle runs.
*/
static std::shared_ptr<Timer> Create(const std::shared_ptr<Loop>& loop) {
return Create(*loop);
}
/**
* Create a timer that calls a functor after a given time interval.
*
* @param loop Loop object where the timer should run.
* @param timeout Time interval
* @param func Functor
*/
static void SingleShot(Loop& loop, Time timeout, std::function<void()> func);
/**
* Create a timer that calls a functor after a given time interval.
*
* @param loop Loop object where the timer should run.
* @param timeout Time interval
* @param func Functor
*/
static void SingleShot(const std::shared_ptr<Loop>& loop, Time timeout,
std::function<void()> func) {
return SingleShot(*loop, timeout, func);
}
/**
* Start the timer.
*
* If timeout is zero, an event is emitted on the next event loop
* iteration. If repeat is non-zero, an event is emitted first
* after timeout milliseconds and then repeatedly after repeat milliseconds.
*
* @param timeout Milliseconds before to emit an event (use
* `std::chrono::duration<uint64_t, std::milli>`).
* @param repeat Milliseconds between successive events (use
* `std::chrono::duration<uint64_t, std::milli>`).
*/
void Start(Time timeout, Time repeat = Time{0});
/**
* Stop the timer.
*/
void Stop() { Invoke(&uv_timer_stop, GetRaw()); }
/**
* Stop the timer and restart it if it was repeating.
*
* Stop the timer, and if it is repeating restart it using the repeat value
* as the timeout.
* If the timer has never been started before it emits sigError.
*/
void Again() { Invoke(&uv_timer_again, GetRaw()); }
/**
* Set the repeat interval value.
*
* The timer will be scheduled to run on the given interval and will follow
* normal timer semantics in the case of a time-slice overrun.
* For example, if a 50ms repeating timer first runs for 17ms, it will be
* scheduled to run again 33ms later. If other tasks consume more than the
* 33ms following the first timer event, then another event will be emitted
* as soon as possible.
*
* If the repeat value is set from a listener bound to an event, it does
* not immediately take effect. If the timer was non-repeating before, it
* will have been stopped. If it was repeating, then the old repeat value
* will have been used to schedule the next timeout.
*
* @param repeat Repeat interval in milliseconds (use
* `std::chrono::duration<uint64_t, std::milli>`).
*/
void SetRepeat(Time repeat) { uv_timer_set_repeat(GetRaw(), repeat.count()); }
/**
* Get the timer repeat value.
* @return Timer repeat value in milliseconds (as a
* `std::chrono::duration<uint64_t, std::milli>`).
*/
Time GetRepeat() const { return Time{uv_timer_get_repeat(GetRaw())}; }
/**
* Signal generated when the timeout event occurs.
*/
sig::Signal<> timeout;
};
} // namespace uv
} // namespace wpi
#endif // WPIUTIL_WPI_UV_TIMER_H_

View File

@@ -0,0 +1,88 @@
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#ifndef WPIUTIL_WPI_UV_TTY_H_
#define WPIUTIL_WPI_UV_TTY_H_
#include <uv.h>
#include <memory>
#include <utility>
#include "wpi/uv/Stream.h"
namespace wpi {
namespace uv {
class Loop;
class Tty;
/**
* TTY handle.
* TTY handles represent a stream for the console.
*/
class Tty final : public StreamImpl<Tty, uv_tty_t> {
struct private_init {};
public:
explicit Tty(const private_init&) {}
~Tty() noexcept override = default;
/**
* Create a TTY handle.
*
* @param loop Loop object where this handle runs.
* @param fd File descriptor, usually 0=stdin, 1=stdout, 2=stderr
* @param readable Specifies if you plan on calling StartRead(). stdin is
* readable, stdout is not.
*/
static std::shared_ptr<Tty> Create(Loop& loop, uv_file fd, bool readable);
/**
* Create a TTY handle.
*
* @param loop Loop object where this handle runs.
* @param fd File descriptor, usually 0=stdin, 1=stdout, 2=stderr
* @param readable Specifies if you plan on calling StartRead(). stdin is
* readable, stdout is not.
*/
static std::shared_ptr<Tty> Create(const std::shared_ptr<Loop>& loop,
uv_file fd, bool readable) {
return Create(*loop, fd, readable);
}
/**
* Set the TTY using the specified terminal mode.
*
* @param mode terminal mode
*/
void SetMode(uv_tty_mode_t mode) {
int err = uv_tty_set_mode(GetRaw(), mode);
if (err < 0) ReportError(err);
}
/**
* Reset TTY settings to default values for the next process to take over.
* Typically called when the program exits.
*/
void ResetMode() { Invoke(&uv_tty_reset_mode); }
/**
* Gets the current window size.
* @return Window size (pair of width and height).
*/
std::pair<int, int> GetWindowSize() {
int width = 0, height = 0;
Invoke(&uv_tty_get_winsize, GetRaw(), &width, &height);
return std::make_pair(width, height);
}
};
} // namespace uv
} // namespace wpi
#endif // WPIUTIL_WPI_UV_TTY_H_

View File

@@ -0,0 +1,266 @@
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#ifndef WPIUTIL_WPI_UV_UDP_H_
#define WPIUTIL_WPI_UV_UDP_H_
#include <uv.h>
#include <functional>
#include <memory>
#include "wpi/ArrayRef.h"
#include "wpi/Signal.h"
#include "wpi/Twine.h"
#include "wpi/uv/Handle.h"
#include "wpi/uv/Request.h"
namespace wpi {
namespace uv {
class Loop;
class Udp;
/**
* UDP send request.
*/
class UdpSendReq : public RequestImpl<UdpSendReq, uv_udp_send_t> {
public:
UdpSendReq();
Udp& GetUdp() const { return *static_cast<Udp*>(GetRaw()->handle->data); }
/**
* Send completed signal. This is called even if an error occurred.
* @param err error value
*/
sig::Signal<Error> complete;
};
/**
* UDP handle.
* UDP handles encapsulate UDP communication for both clients and servers.
*/
class Udp final : public HandleImpl<Udp, uv_udp_t> {
struct private_init {};
public:
explicit Udp(const private_init&) {}
~Udp() noexcept override = default;
/**
* Create a UDP handle.
*
* @param loop Loop object where this handle runs.
* @param flags Flags
*/
static std::shared_ptr<Udp> Create(Loop& loop,
unsigned int flags = AF_UNSPEC);
/**
* Create a UDP handle.
*
* @param loop Loop object where this handle runs.
* @param flags Flags
*/
static std::shared_ptr<Udp> Create(const std::shared_ptr<Loop>& loop,
unsigned int flags = AF_UNSPEC) {
return Create(*loop, flags);
}
/**
* Open an existing file descriptor or SOCKET as a UDP handle.
*
* @param sock A valid socket handle (either a file descriptor or a SOCKET).
*/
void Open(uv_os_sock_t sock) { Invoke(&uv_udp_open, GetRaw(), sock); }
/**
* Bind the handle to an IPv4 or IPv6 address and port.
*
* @param addr Initialized `sockaddr_in` or `sockaddr_in6` data structure.
* @param flags Optional additional flags.
*/
void Bind(const sockaddr& addr, unsigned int flags = 0) {
Invoke(&uv_udp_bind, GetRaw(), &addr, flags);
}
/**
* Bind the handle to an IPv4 address and port.
*
* @param ip The address to which to bind.
* @param port The port to which to bind.
* @param flags Optional additional flags.
*/
void Bind(const Twine& ip, unsigned int port, unsigned int flags = 0);
/**
* Bind the handle to an IPv6 address and port.
*
* @param ip The address to which to bind.
* @param port The port to which to bind.
* @param flags Optional additional flags.
*/
void Bind6(const Twine& ip, unsigned int port, unsigned int flags = 0);
/**
* Get the current address to which the handle is bound.
* @return The address (will be zeroed if an error occurred).
*/
sockaddr_storage GetSock();
/**
* Set membership for a multicast address.
*
* @param multicastAddr Multicast address to set membership for
* @param interfaceAddr Interface address
* @param membership Should be UV_JOIN_GROUP or UV_LEAVE_GROUP
*/
void SetMembership(const Twine& multicastAddr, const Twine& interfaceAddr,
uv_membership membership);
/**
* Set IP multicast loop flag. Makes multicast packets loop back to local
* sockets.
*
* @param enabled True for enabled, false for disabled
*/
void SetMulticastLoop(bool enabled) {
Invoke(&uv_udp_set_multicast_loop, GetRaw(), enabled ? 1 : 0);
}
/**
* Set the multicast TTL.
*
* @param ttl Time to live (1-255)
*/
void SetMulticastTtl(int ttl) {
Invoke(&uv_udp_set_multicast_ttl, GetRaw(), ttl);
}
/**
* Set the multicast interface to send or receive data on.
*
* @param interfaceAddr Interface address
*/
void SetMulticastInterface(const Twine& interfaceAddr);
/**
* Set broadcast on or off.
*
* @param enabled True for enabled, false for disabled
*/
void SetBroadcast(bool enabled) {
Invoke(&uv_udp_set_broadcast, GetRaw(), enabled ? 1 : 0);
}
/**
* Set the time to live (TTL).
*
* @param ttl Time to live (1-255)
*/
void SetTtl(int ttl) { Invoke(&uv_udp_set_ttl, GetRaw(), ttl); }
/**
* Send data over the UDP socket. If the socket has not previously been bound
* with Bind() it will be bound to 0.0.0.0 (the "all interfaces" IPv4 address)
* and a random port number.
*
* Data are written in order. The lifetime of the data pointers passed in
* the `bufs` parameter must exceed the lifetime of the send request.
* The callback can be used to free data after the request completes.
*
* HandleSendComplete() will be called on the request object when the data
* has been written. HandleSendComplete() is called even if an error occurs.
* HandleError() will be called on the request object in case of errors.
*
* @param addr sockaddr_in or sockaddr_in6 with the address and port of the
* remote peer.
* @param bufs The buffers to be written to the stream.
* @param req write request
*/
void Send(const sockaddr& addr, ArrayRef<Buffer> bufs,
const std::shared_ptr<UdpSendReq>& req);
/**
* Send data over the UDP socket. If the socket has not previously been bound
* with Bind() it will be bound to 0.0.0.0 (the "all interfaces" IPv4 address)
* and a random port number.
*
* Data are written in order. The lifetime of the data pointers passed in
* the `bufs` parameter must exceed the lifetime of the send request.
* The callback can be used to free data after the request completes.
*
* The callback will be called when the data has been sent. Errors will
* be reported via the error signal.
*
* @param addr sockaddr_in or sockaddr_in6 with the address and port of the
* remote peer.
* @param bufs The buffers to be sent.
* @param callback Callback function to call when the data has been sent.
*/
void Send(const sockaddr& addr, ArrayRef<Buffer> bufs,
std::function<void(MutableArrayRef<Buffer>, Error)> callback);
/**
* Same as Send(), but won't queue a send request if it can't be completed
* immediately.
*
* @param addr sockaddr_in or sockaddr_in6 with the address and port of the
* remote peer.
* @param bufs The buffers to be send.
* @return Number of bytes sent.
*/
int TrySend(const sockaddr& addr, ArrayRef<Buffer> bufs) {
int val = uv_udp_try_send(GetRaw(), bufs.data(), bufs.size(), &addr);
if (val < 0) {
this->ReportError(val);
return 0;
}
return val;
}
/**
* Prepare for receiving data. If the socket has not previously been bound
* with Bind() it is bound to 0.0.0.0 (the "all interfaces" IPv4 address) and
* a random port number.
*
* A received signal will be emitted for each received data packet until
* `StopRecv()` is called.
*/
void StartRecv();
/**
* Stop listening for incoming datagrams.
*/
void StopRecv() { Invoke(&uv_udp_recv_stop, GetRaw()); }
/**
* Gets the amount of queued bytes waiting to be sent.
* @return Amount of queued bytes waiting to be sent.
*/
size_t GetSendQueueSize() const noexcept { return GetRaw()->send_queue_size; }
/**
* Gets the amount of queued packets waiting to be sent.
* @return Amount of queued packets waiting to be sent.
*/
size_t GetSendQueueCount() const noexcept {
return GetRaw()->send_queue_count;
}
/**
* Signal generated for each incoming datagram. Parameters are the buffer,
* the number of bytes received, the address of the sender, and flags.
*/
sig::Signal<Buffer&, size_t, const sockaddr&, unsigned> received;
};
} // namespace uv
} // namespace wpi
#endif // WPIUTIL_WPI_UV_UDP_H_

View File

@@ -0,0 +1,97 @@
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#ifndef WPIUTIL_WPI_UV_WORK_H_
#define WPIUTIL_WPI_UV_WORK_H_
#include <uv.h>
#include <functional>
#include <memory>
#include "wpi/Signal.h"
#include "wpi/uv/Request.h"
namespace wpi {
namespace uv {
class Loop;
/**
* Work request.
* For use with `QueueWork()` function family.
*/
class WorkReq : public RequestImpl<WorkReq, uv_work_t> {
public:
WorkReq();
Loop& GetLoop() const { return *static_cast<Loop*>(GetRaw()->loop->data); }
/**
* Function(s) that will be run on the thread pool.
*/
sig::Signal<> work;
/**
* Function(s) that will be run on the loop thread after the work on the
* thread pool has been completed by the work callback.
*/
sig::Signal<> afterWork;
};
/**
* Initializes a work request which will run on the thread pool.
*
* @param loop Event loop
* @param req request
*/
void QueueWork(Loop& loop, const std::shared_ptr<WorkReq>& req);
/**
* Initializes a work request which will run on the thread pool.
*
* @param loop Event loop
* @param req request
*/
inline void QueueWork(const std::shared_ptr<Loop>& loop,
const std::shared_ptr<WorkReq>& req) {
QueueWork(*loop, req);
}
/**
* Initializes a work request which will run on the thread pool. The work
* callback will be run on a thread from the thread pool, and the afterWork
* callback will be called on the loop thread after the work completes. Any
* errors are forwarded to the loop.
*
* @param loop Event loop
* @param work Work callback (called from separate thread)
* @param afterWork After work callback (called on loop thread)
*/
void QueueWork(Loop& loop, std::function<void()> work,
std::function<void()> afterWork);
/**
* Initializes a work request which will run on the thread pool. The work
* callback will be run on a thread from the thread pool, and the afterWork
* callback will be called on the loop thread after the work completes. Any
* errors are forwarded to the loop.
*
* @param loop Event loop
* @param work Work callback (called from separate thread)
* @param afterWork After work callback (called on loop thread)
*/
inline void QueueWork(const std::shared_ptr<Loop>& loop,
std::function<void()> work,
std::function<void()> afterWork) {
QueueWork(*loop, work, afterWork);
}
} // namespace uv
} // namespace wpi
#endif // WPIUTIL_WPI_UV_WORK_H_

View File

@@ -0,0 +1,131 @@
/*----------------------------------------------------------------------------*/
/* 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. */
/*----------------------------------------------------------------------------*/
#ifndef WPIUTIL_WPI_UV_UTIL_H_
#define WPIUTIL_WPI_UV_UTIL_H_
#include <uv.h>
#include <cstring>
#include "wpi/Twine.h"
namespace wpi {
namespace uv {
/**
* Convert a binary structure containing an IPv4 address to a string.
* @param addr Binary structure
* @param ip Output string (any type that has `assign(char*, char*)`)
* @param port Output port number
* @return Error (same as `uv_ip4_name()`).
*/
template <typename T>
int AddrToName(const sockaddr_in& addr, T* ip, unsigned int* port) {
char name[128];
int err = uv_ip4_name(&addr, name, 128);
if (err == 0) {
ip->assign(name, name + std::strlen(name));
*port = ntohs(addr.sin_port);
} else {
ip->assign(name, name);
}
return err;
}
/**
* Convert a binary structure containing an IPv6 address to a string.
* @param addr Binary structure
* @param ip Output string (any type that has `assign(char*, char*)`)
* @param port Output port number
* @return Error (same as `uv_ip6_name()`).
*/
template <typename T>
int AddrToName(const sockaddr_in6& addr, T* ip, unsigned int* port) {
char name[128];
int err = uv_ip6_name(&addr, name, 128);
if (err == 0) {
ip->assign(name, name + std::strlen(name));
*port = ntohs(addr.sin6_port);
} else {
ip->assign(name, name);
}
return err;
}
/**
* Convert a binary IPv4 address to a string.
* @param addr Binary address
* @param ip Output string (any type that has `assign(char*, char*)`)
* @return Error (same as `uv_inet_ntop()`).
*/
template <typename T>
int AddrToName(const in_addr& addr, T* ip) {
char name[128];
int err = uv_inet_ntop(AF_INET, &addr, name, 128);
if (err == 0)
ip->assign(name, name + std::strlen(name));
else
ip->assign(name, name);
return err;
}
/**
* Convert a binary IPv6 address to a string.
* @param addr Binary address
* @param ip Output string (any type that has `assign(char*, char*)`)
* @return Error (same as `uv_inet_ntop()`).
*/
template <typename T>
int AddrToName(const in6_addr& addr, T* ip) {
char name[128];
int err = uv_inet_ntop(AF_INET6, &addr, name, 128);
if (err == 0)
ip->assign(name, name + std::strlen(name));
else
ip->assign(name, name);
return err;
}
/**
* Convert a string containing an IPv4 address to a binary structure.
* @param ip IPv4 address string
* @param port Port number
* @param addr Output binary structure
* @return Error (same as `uv_ip4_addr()`).
*/
int NameToAddr(const Twine& ip, unsigned int port, sockaddr_in* addr);
/**
* Convert a string containing an IPv6 address to a binary structure.
* @param ip IPv6 address string
* @param port Port number
* @param addr Output binary structure
* @return Error (same as `uv_ip6_addr()`).
*/
int NameToAddr(const Twine& ip, unsigned int port, sockaddr_in6* addr);
/**
* Convert a string containing an IPv4 address to binary format.
* @param ip IPv4 address string
* @param addr Output binary
* @return Error (same as `uv_inet_pton()`).
*/
int NameToAddr(const Twine& ip, in_addr* addr);
/**
* Convert a string containing an IPv6 address to binary format.
* @param ip IPv6 address string
* @param addr Output binary
* @return Error (same as `uv_inet_pton()`).
*/
int NameToAddr(const Twine& ip, in6_addr* addr);
} // namespace uv
} // namespace wpi
#endif // WPIUTIL_WPI_UV_UTIL_H_