mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-07-05 03:21:42 +00:00
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:
272
wpiutil/src/main/native/include/wpi/uv/Handle.h
Normal file
272
wpiutil/src/main/native/include/wpi/uv/Handle.h
Normal 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 it’s 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_
|
||||
Reference in New Issue
Block a user