libfilezilla
nonblocking_process.cpp

A simple demonstration of using fz::processThis example spawns the timer_fizzbuzz.cpp demo and controls it via the redirected IO, in non-blocking mode.

#include <iostream>
#include <string>
namespace {
// Helper function to extract a directory from argv[0] so that the
// demos can be run independent of the current working directory.
fz::native_string get_program_dir(int argc, char ** argv)
{
std::string_view path;
if (argc > 0) {
path = argv[0];
#ifdef FZ_WINDOWS
auto delim = path.find_last_of("/\\");
#else
auto delim = path.find_last_of("/");
#endif
if (delim == std::string::npos) {
path = std::string_view();
}
else {
path = path.substr(0, delim + 1);
}
}
return fz::to_native(path);
}
#ifdef FZ_WINDOWS
auto suffix = fzT(".exe");
#else
auto suffix = fzT("");
#endif
}
class runner final : public fz::event_handler
{
public:
runner(fz::thread_pool & pool, fz::event_loop & loop)
: fz::event_handler(loop)
, process_(pool, *this)
{}
~runner()
{
process_.kill();
}
virtual void operator()(fz::event_base const& ev) override
{
fz::dispatch<fz::process_event>(ev, this, &runner::on_process_event);
}
void on_process_event(fz::process *, fz::process_event_flag flag)
{
on_read();
}
}
void on_read()
{
// We don't want to do anything else while waiting, so we don't need a thread to receive data asynchronously
std::string input;
while (!done_) {
char buf[100];
fz::rwresult r = process_.read(buf, 100);
if (!r) {
if (r.error_ == fz::rwresult::wouldblock) {
return;
}
std::cerr << "Could not read from process" << std::endl;
exit(1);
}
else if (!r.value_) {
std::cerr << "Unexpected EOF from process" << std::endl;
exit(1);
}
input += std::string(buf, r.value_);
// Extract complete lines from the input
auto delim = input.find_first_of("\r\n");
while (delim != std::string::npos) {
std::string line = input.substr(0, delim);
input = input.substr(delim + 1);
delim = input.find_first_of("\r\n");
if (!line.empty()) {
std::cout << "Received line from process: " << line << std::endl;
if (line == "woof") {
done_ = true;
// Send a line to the process
if (!process_.write("0\n")) {
std::cerr << "Sending data to the process failed. Looks like it could not be started or has quit early." << std::endl;
exit(1);
}
std::cout << "Told process to quit." << std::endl;
}
}
}
}
while (true) {
char buf[100];
fz::rwresult r = process_.read(buf, 100);
if (!r) {
if (r.error_ == fz::rwresult::wouldblock) {
return;
}
std::cerr << "Could not read from process" << std::endl;
exit(1);
}
else if (!r.value_) {
std::cerr << "Received the expected EOF from process" << std::endl;
break;
}
}
event_loop_.stop();
}
fz::process process_;
bool done_{};
};
int main(int argc, char *argv[])
{
fz::event_loop loop(fz::event_loop::threadless);
runner h(pool, loop);
// Start the timer_fizzbuzz demo which should be in the same directory as the process demo
if (!h.process_.spawn(get_program_dir(argc, argv) + fzT("timer_fizzbuzz") + suffix)) {
std::cerr << "Could not spawn process" << std::endl;
return 1;
}
std::cout << "Spawned process" << std::endl;
// Send a line to the process
buf.append("6\n");
while (!buf.empty()) {
fz::rwresult r = h.process_.write(buf.get(), buf.size());
if (!r) {
std::cerr << "Sending data to the process failed. Looks like it could not be started or has quit early." << std::endl;
return 1;
}
buf.consume(r.value_);
}
std::cout << "Waiting on process to print woof..." << std::endl;
loop.run();
}
Declares fz::buffer.
The buffer class is a simple buffer where data can be appended at the end and consumed at the front....
Definition: buffer.hpp:27
void consume(size_t consumed)
Removes consumed bytes from the beginning of the buffer.
unsigned char const * get() const
Undefined if buffer is empty.
Definition: buffer.hpp:45
void append(unsigned char const *data, size_t len)
Appends the passed data to the buffer.
Common base class for all events.
Definition: event.hpp:23
Simple handler for asynchronous event processing.
Definition: event_handler.hpp:55
void remove_handler()
Deactivates handler, removes all pending events and stops all timers for this handler.
virtual void operator()(event_base const &)=0
Called by the event loop in the worker thread with the event to process.
A threaded event loop that supports sending events and timers.
Definition: event_loop.hpp:34
The process class manages an asynchronous process with redirected IO.
Definition: process.hpp:61
Holds the result of read/write operations.
Definition: fsresult.hpp:74
size_t value_
Undefined if error_ is not none.
Definition: fsresult.hpp:116
@ wouldblock
The operation would have blocked, but the file descriptor is marked non-blocking.
Definition: fsresult.hpp:92
A dumb thread-pool for asynchronous tasks.
Definition: thread_pool.hpp:64
Declares the event_handler class.
The namespace used by libfilezilla.
Definition: apply.hpp:17
std::wstring native_string
A string in the system's native character type and encoding. Note: This typedef changes depending on...
Definition: string.hpp:34
process_event_flag
The type of a process event.
Definition: process.hpp:26
@ read
Data has become available.
native_string to_native(std::string_view const &in)
Converts std::string to native_string.
Header for the #process class.
#define fzT(x)
Macro for a string literal in system-native character type. Note: Macro definition changes depending...
Definition: string.hpp:257
Declares thread_pool and async_task.