Add readiness types and callbacks to sfSocketSelector

This commit is contained in:
Lukas Dürrenberger committed 2026-10-04 22:12:01 +02:00
1 parent d95f00f212
commit af34a2890e
4 files changed
+278 -22

No files matched your search

+128 -10
View File
@@ -32,6 +32,31 @@
#include <CSFML/Network/Types.h> #include <CSFML/Network/Types.h>
#include <CSFML/System/Time.h> #include <CSFML/System/Time.h>
#include <stdbool.h>
#include <stdint.h>
////////////////////////////////////////////////////////////
/// \brief Readiness types a socket selector can check for
///
/// The values can be combined with a bitwise OR.
///
////////////////////////////////////////////////////////////
typedef enum
{
sfSocketSelectorReceive = 1 << 0, ///< Check if sockets are ready to be received from
sfSocketSelectorSend = 1 << 1 ///< Check if sockets are ready to be sent to
} sfSocketSelectorReadiness;
////////////////////////////////////////////////////////////
/// \brief Callback that is called when a socket is ready
///
/// \param readiness Readiness of the socket, a combination of sfSocketSelectorReadiness values
/// \param userData User data passed when adding the socket
///
////////////////////////////////////////////////////////////
typedef void (*sfSocketSelectorCallback)(uint32_t readiness, void* userData);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Create a new selector /// \brief Create a new selector
@@ -66,13 +91,64 @@ CSFML_NETWORK_API void sfSocketSelector_destroy(const sfSocketSelector* selector
/// so you have to make sure that the socket is not destroyed /// so you have to make sure that the socket is not destroyed
/// while it is stored in the selector. /// while it is stored in the selector.
/// ///
/// The socket is checked for being ready to receive.
///
/// \param selector Socket selector object /// \param selector Socket selector object
/// \param socket Pointer to the socket to add /// \param socket Pointer to the socket to add
/// ///
/// \return True if the socket was successfully added
///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
CSFML_NETWORK_API void sfSocketSelector_addTcpListener(sfSocketSelector* selector, sfTcpListener* socket); CSFML_NETWORK_API bool sfSocketSelector_addTcpListener(sfSocketSelector* selector, sfTcpListener* socket);
CSFML_NETWORK_API void sfSocketSelector_addTcpSocket(sfSocketSelector* selector, sfTcpSocket* socket); CSFML_NETWORK_API bool sfSocketSelector_addTcpSocket(sfSocketSelector* selector, sfTcpSocket* socket);
CSFML_NETWORK_API void sfSocketSelector_addUdpSocket(sfSocketSelector* selector, sfUdpSocket* socket); CSFML_NETWORK_API bool sfSocketSelector_addUdpSocket(sfSocketSelector* selector, sfUdpSocket* socket);
////////////////////////////////////////////////////////////
/// \brief Add a new socket to a socket selector with the readiness to check for and an optional callback
///
/// This function keeps a weak pointer to the socket,
/// so you have to make sure that the socket is not destroyed
/// while it is stored in the selector.
///
/// The readiness to wait for can be specified as a combination
/// of sfSocketSelectorReceive and sfSocketSelectorSend.
///
/// If a callback is provided, it will be called with the
/// readiness of the socket when sfSocketSelector_dispatchReadyCallbacks
/// is called after sfSocketSelector_wait returned true. Using
/// callbacks scales better than checking every socket with
/// sfSocketSelector_isXxxReadyFor.
///
/// Adding a socket that has already been added updates its
/// readiness and callback.
///
/// \param selector Socket selector object
/// \param socket Pointer to the socket to add
/// \param readiness Readiness to wait for, combination of sfSocketSelectorReadiness values
/// \param callback Callback to call when the socket is ready, can be NULL
/// \param userData User data that will be passed to the callback
///
/// \return True if the socket was successfully added
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API bool sfSocketSelector_addTcpListenerWithReadiness(
sfSocketSelector* selector,
sfTcpListener* socket,
uint32_t readiness,
sfSocketSelectorCallback callback,
void* userData);
CSFML_NETWORK_API bool sfSocketSelector_addTcpSocketWithReadiness(
sfSocketSelector* selector,
sfTcpSocket* socket,
uint32_t readiness,
sfSocketSelectorCallback callback,
void* userData);
CSFML_NETWORK_API bool sfSocketSelector_addUdpSocketWithReadiness(
sfSocketSelector* selector,
sfUdpSocket* socket,
uint32_t readiness,
sfSocketSelectorCallback callback,
void* userData);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Remove a socket from a socket selector /// \brief Remove a socket from a socket selector
@@ -83,10 +159,12 @@ CSFML_NETWORK_API void sfSocketSelector_addUdpSocket(sfSocketSelector* selector,
/// \param selector Socket selector object /// \param selector Socket selector object
/// \param socket Pointer to the socket to remove /// \param socket Pointer to the socket to remove
/// ///
/// \return True if the socket was successfully removed
///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
CSFML_NETWORK_API void sfSocketSelector_removeTcpListener(sfSocketSelector* selector, sfTcpListener* socket); CSFML_NETWORK_API bool sfSocketSelector_removeTcpListener(sfSocketSelector* selector, sfTcpListener* socket);
CSFML_NETWORK_API void sfSocketSelector_removeTcpSocket(sfSocketSelector* selector, sfTcpSocket* socket); CSFML_NETWORK_API bool sfSocketSelector_removeTcpSocket(sfSocketSelector* selector, sfTcpSocket* socket);
CSFML_NETWORK_API void sfSocketSelector_removeUdpSocket(sfSocketSelector* selector, sfUdpSocket* socket); CSFML_NETWORK_API bool sfSocketSelector_removeUdpSocket(sfSocketSelector* selector, sfUdpSocket* socket);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Remove all the sockets stored in a selector /// \brief Remove all the sockets stored in a selector
@@ -101,11 +179,13 @@ CSFML_NETWORK_API void sfSocketSelector_removeUdpSocket(sfSocketSelector* select
CSFML_NETWORK_API void sfSocketSelector_clear(sfSocketSelector* selector); CSFML_NETWORK_API void sfSocketSelector_clear(sfSocketSelector* selector);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Wait until one or more sockets are ready to receive /// \brief Wait until one or more sockets are ready
/// ///
/// This function returns as soon as at least one socket has /// This function returns as soon as at least one socket is
/// some data available to be received. To know which sockets are /// ready for the readiness it was added with (by default
/// ready, use the sfSocketSelector_isXxxReady functions. /// having some data available to be received). To know which
/// sockets are ready, use the sfSocketSelector_isXxxReady
/// functions or sfSocketSelector_dispatchReadyCallbacks.
/// If you use a timeout and no socket is ready before the timeout /// If you use a timeout and no socket is ready before the timeout
/// is over, the function returns false. /// is over, the function returns false.
/// ///
@@ -136,3 +216,41 @@ CSFML_NETWORK_API bool sfSocketSelector_wait(sfSocketSelector* selector, sfTime
CSFML_NETWORK_API bool sfSocketSelector_isTcpListenerReady(const sfSocketSelector* selector, sfTcpListener* socket); CSFML_NETWORK_API bool sfSocketSelector_isTcpListenerReady(const sfSocketSelector* selector, sfTcpListener* socket);
CSFML_NETWORK_API bool sfSocketSelector_isTcpSocketReady(const sfSocketSelector* selector, sfTcpSocket* socket); CSFML_NETWORK_API bool sfSocketSelector_isTcpSocketReady(const sfSocketSelector* selector, sfTcpSocket* socket);
CSFML_NETWORK_API bool sfSocketSelector_isUdpSocketReady(const sfSocketSelector* selector, sfUdpSocket* socket); CSFML_NETWORK_API bool sfSocketSelector_isUdpSocketReady(const sfSocketSelector* selector, sfUdpSocket* socket);
////////////////////////////////////////////////////////////
/// \brief Test a socket to know if it is ready for the given readiness
///
/// This function must be used after a call to
/// sfSocketSelector_wait, to know which sockets are ready.
///
/// \param selector Socket selector object
/// \param socket Socket to test
/// \param readiness Readiness to check for, combination of sfSocketSelectorReadiness values
///
/// \return true if the socket is ready, false otherwise
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API bool sfSocketSelector_isTcpListenerReadyFor(const sfSocketSelector* selector,
sfTcpListener* socket,
uint32_t readiness);
CSFML_NETWORK_API bool sfSocketSelector_isTcpSocketReadyFor(const sfSocketSelector* selector,
sfTcpSocket* socket,
uint32_t readiness);
CSFML_NETWORK_API bool sfSocketSelector_isUdpSocketReadyFor(const sfSocketSelector* selector,
sfUdpSocket* socket,
uint32_t readiness);
////////////////////////////////////////////////////////////
/// \brief Call the callbacks of all sockets that are ready
///
/// After sfSocketSelector_wait returned true, at least one
/// socket is ready. Calling this function will call the
/// callbacks of all the sockets that became ready during
/// the wait. Calling this function multiple times after a
/// single call to sfSocketSelector_wait will run the callbacks
/// multiple times.
///
/// \param selector Socket selector object
///
////////////////////////////////////////////////////////////
CSFML_NETWORK_API void sfSocketSelector_dispatchReadyCallbacks(sfSocketSelector* selector);
+105 -12
View File
@@ -31,6 +31,22 @@
#include <CSFML/Network/TcpSocketStruct.hpp> #include <CSFML/Network/TcpSocketStruct.hpp>
#include <CSFML/Network/UdpSocketStruct.hpp> #include <CSFML/Network/UdpSocketStruct.hpp>
#include <functional>
namespace
{
////////////////////////////////////////////////////////////
[[nodiscard]] std::function<void(sf::SocketSelector::ReadinessType)> convertCallback(sfSocketSelectorCallback callback,
void* userData)
{
if (!callback)
return {};
return [callback, userData](sf::SocketSelector::ReadinessType readiness) { callback(readiness, userData); };
}
} // namespace
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
sfSocketSelector* sfSocketSelector_create() sfSocketSelector* sfSocketSelector_create()
@@ -55,44 +71,86 @@ void sfSocketSelector_destroy(const sfSocketSelector* selector)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void sfSocketSelector_addTcpListener(sfSocketSelector* selector, sfTcpListener* socket) bool sfSocketSelector_addTcpListener(sfSocketSelector* selector, sfTcpListener* socket)
{ {
assert(selector); assert(selector);
assert(socket); assert(socket);
selector->add(*socket); return selector->add(*socket);
} }
void sfSocketSelector_addTcpSocket(sfSocketSelector* selector, sfTcpSocket* socket) bool sfSocketSelector_addTcpSocket(sfSocketSelector* selector, sfTcpSocket* socket)
{ {
assert(selector); assert(selector);
assert(socket); assert(socket);
selector->add(*socket); return selector->add(*socket);
} }
void sfSocketSelector_addUdpSocket(sfSocketSelector* selector, sfUdpSocket* socket) bool sfSocketSelector_addUdpSocket(sfSocketSelector* selector, sfUdpSocket* socket)
{ {
assert(selector); assert(selector);
assert(socket); assert(socket);
selector->add(*socket); return selector->add(*socket);
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void sfSocketSelector_removeTcpListener(sfSocketSelector* selector, sfTcpListener* socket) bool sfSocketSelector_addTcpListenerWithReadiness(
sfSocketSelector* selector,
sfTcpListener* socket,
uint32_t readiness,
sfSocketSelectorCallback callback,
void* userData)
{ {
assert(selector); assert(selector);
assert(socket); assert(socket);
selector->remove(*socket); return selector->add(*socket, readiness, convertCallback(callback, userData));
} }
void sfSocketSelector_removeTcpSocket(sfSocketSelector* selector, sfTcpSocket* socket)
////////////////////////////////////////////////////////////
bool sfSocketSelector_addTcpSocketWithReadiness(
sfSocketSelector* selector,
sfTcpSocket* socket,
uint32_t readiness,
sfSocketSelectorCallback callback,
void* userData)
{ {
assert(selector); assert(selector);
assert(socket); assert(socket);
selector->remove(*socket); return selector->add(*socket, readiness, convertCallback(callback, userData));
} }
void sfSocketSelector_removeUdpSocket(sfSocketSelector* selector, sfUdpSocket* socket)
////////////////////////////////////////////////////////////
bool sfSocketSelector_addUdpSocketWithReadiness(
sfSocketSelector* selector,
sfUdpSocket* socket,
uint32_t readiness,
sfSocketSelectorCallback callback,
void* userData)
{ {
assert(selector); assert(selector);
assert(socket); assert(socket);
selector->remove(*socket); return selector->add(*socket, readiness, convertCallback(callback, userData));
}
////////////////////////////////////////////////////////////
bool sfSocketSelector_removeTcpListener(sfSocketSelector* selector, sfTcpListener* socket)
{
assert(selector);
assert(socket);
return selector->remove(*socket);
}
bool sfSocketSelector_removeTcpSocket(sfSocketSelector* selector, sfTcpSocket* socket)
{
assert(selector);
assert(socket);
return selector->remove(*socket);
}
bool sfSocketSelector_removeUdpSocket(sfSocketSelector* selector, sfUdpSocket* socket)
{
assert(selector);
assert(socket);
return selector->remove(*socket);
} }
@@ -131,3 +189,38 @@ bool sfSocketSelector_isUdpSocketReady(const sfSocketSelector* selector, sfUdpSo
assert(socket); assert(socket);
return selector->isReady(*socket); return selector->isReady(*socket);
} }
////////////////////////////////////////////////////////////
bool sfSocketSelector_isTcpListenerReadyFor(const sfSocketSelector* selector, sfTcpListener* socket, uint32_t readiness)
{
assert(selector);
assert(socket);
return selector->isReady(*socket, readiness);
}
////////////////////////////////////////////////////////////
bool sfSocketSelector_isTcpSocketReadyFor(const sfSocketSelector* selector, sfTcpSocket* socket, uint32_t readiness)
{
assert(selector);
assert(socket);
return selector->isReady(*socket, readiness);
}
////////////////////////////////////////////////////////////
bool sfSocketSelector_isUdpSocketReadyFor(const sfSocketSelector* selector, sfUdpSocket* socket, uint32_t readiness)
{
assert(selector);
assert(socket);
return selector->isReady(*socket, readiness);
}
////////////////////////////////////////////////////////////
void sfSocketSelector_dispatchReadyCallbacks(sfSocketSelector* selector)
{
assert(selector);
selector->dispatchReadyCallbacks();
}
+1
View File
@@ -70,6 +70,7 @@ add_executable(test-csfml-network
Network/Ftp.test.cpp Network/Ftp.test.cpp
Network/Http.test.cpp Network/Http.test.cpp
Network/IpAddress.test.cpp Network/IpAddress.test.cpp
Network/SocketSelector.test.cpp
Network/SocketStatus.test.cpp Network/SocketStatus.test.cpp
Network/TcpSocket.test.cpp Network/TcpSocket.test.cpp
) )
+44
View File
@@ -0,0 +1,44 @@
#include <CSFML/Network/SocketSelector.h>
#include <CSFML/Network/TcpListener.h>
#include <CSFML/Network/TcpSocket.h>
#include <SFML/Network/SocketSelector.hpp>
#include <catch2/catch_test_macros.hpp>
TEST_CASE("[Network] sfSocketSelector")
{
SECTION("sfSocketSelectorReadiness")
{
STATIC_CHECK(sfSocketSelectorReceive == static_cast<int>(sf::SocketSelector::Receive));
STATIC_CHECK(sfSocketSelectorSend == static_cast<int>(sf::SocketSelector::Send));
}
SECTION("Ready callbacks")
{
sfTcpListener* listener = sfTcpListener_create();
REQUIRE(sfTcpListener_listen(listener, 0, sfIpAddress_LocalHost) == sfSocketDone);
sfTcpSocket* client = sfTcpSocket_create();
REQUIRE(sfTcpSocket_connect(client, sfIpAddress_LocalHost, sfTcpListener_getLocalPort(listener), sfSeconds(1)) ==
sfSocketDone);
sfSocketSelector* selector = sfSocketSelector_create();
uint32_t readiness = 0;
const auto callback = [](uint32_t ready, void* userData) { *static_cast<uint32_t*>(userData) |= ready; };
CHECK(sfSocketSelector_addTcpListenerWithReadiness(selector, listener, sfSocketSelectorReceive, callback, &readiness));
REQUIRE(sfSocketSelector_wait(selector, sfSeconds(1)));
CHECK(sfSocketSelector_isTcpListenerReady(selector, listener));
CHECK(sfSocketSelector_isTcpListenerReadyFor(selector, listener, sfSocketSelectorReceive));
sfSocketSelector_dispatchReadyCallbacks(selector);
CHECK(readiness == sfSocketSelectorReceive);
CHECK(sfSocketSelector_removeTcpListener(selector, listener));
sfSocketSelector_destroy(selector);
sfTcpSocket_destroy(client);
sfTcpListener_destroy(listener);
}
}