Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions src/test/jtx/Env.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include <xrpl/protocol/STTx.h>

#include <functional>
#include <future>
#include <source_location>
#include <string>
#include <tuple>
Expand Down Expand Up @@ -393,6 +394,49 @@ class Env
return close(std::chrono::seconds(5));
}

/** Close and advance the ledger, then synchronize with the server's
io_context to ensure all async operations initiated by the close have
been started.

This function performs the same ledger close as close(), but additionally
ensures that all tasks posted to the server's io_context (such as
WebSocket subscription message sends) have been initiated before returning.

What it guarantees:
- All async operations posted before syncClose() have been STARTED
- For WebSocket sends: async_write_some() has been called
- The actual I/O completion may still be pending (async)

What it does NOT guarantee:
- Async operations have COMPLETED
- WebSocket messages have been received by clients
- However, for localhost connections, the remaining latency is typically
microseconds, making tests reliable

Use this instead of close() when:
- Test code immediately checks for subscription messages
- Race conditions between test and worker threads must be avoided
- Deterministic test behavior is required

@param timeout Maximum time to wait for the barrier task to execute
@return true if close succeeded and barrier executed within timeout,
false otherwise
*/
[[nodiscard]] bool
syncClose(std::chrono::system_clock::duration timeout = std::chrono::seconds{1})
{
XRPL_ASSERT(
app().getNumberOfThreads() == 1,
"syncClose() is only useful on an application with a single thread");
auto const result = close();
auto server_barrier = std::make_shared<std::promise<void>>();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Don't know if xrpld uses this style or camelCase but i guess camelCase should be preferred here

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot, can you fix this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

auto future = server_barrier->get_future();
boost::asio::post(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wasn't there a util we made to post instead of using asio directly?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that was for spawn(), not post()

app().getIOContext(), [server_barrier]() { server_barrier->set_value(); });
auto const status = future.wait_for(timeout);
return result && status == std::future_status::ready;
}

/** Turn on JSON tracing.
With no arguments, trace all
*/
Expand Down
2 changes: 2 additions & 0 deletions src/test/jtx/envconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ std::unique_ptr<Config> admin_localnet(std::unique_ptr<Config>);

std::unique_ptr<Config> secure_gateway_localnet(std::unique_ptr<Config>);

std::unique_ptr<Config> single_thread_io(std::unique_ptr<Config>);

/// @brief adjust configuration with params needed to be a validator
///
/// this is intended for use with envconfig, as in
Expand Down
6 changes: 6 additions & 0 deletions src/test/jtx/impl/envconfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@ secure_gateway_localnet(std::unique_ptr<Config> cfg)
(*cfg)[PORT_WS].set("secure_gateway", "127.0.0.0/8");
return cfg;
}
std::unique_ptr<Config>
single_thread_io(std::unique_ptr<Config> cfg)
{
cfg->IO_WORKERS = 1;
return cfg;
}

auto constexpr defaultseed = "shUwVw52ofnCUX5m7kPTKzJdr4HEH";

Expand Down
Loading
Loading