Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the ability to send a signal to a subprocess #127

Open
wants to merge 8 commits into
base: gz-utils2
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
34 changes: 34 additions & 0 deletions include/gz/utils/Subprocess.hh
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,20 @@
namespace utils
{

enum class Signals
{
#ifdef WIN32
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT = 1,
CTRL_CLOSE_EVENT = 2,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT = 6,
#else
SIGNAL_SIGINT = 2,
SIGNAL_SIGQUIT = 3,
#endif
};

/// \brief Create a RAII-type object that encapsulates a subprocess.
class Subprocess
{
Expand Down Expand Up @@ -183,6 +197,26 @@
return false;
}

public: bool Signal(int signum)
{
if (this->process != nullptr)
return subprocess_signal(this->process, signum) != 0;
else
return false;

Check warning on line 205 in include/gz/utils/Subprocess.hh

View check run for this annotation

Codecov / codecov/patch

include/gz/utils/Subprocess.hh#L205

Added line #L205 was not covered by tests

}

public: bool SendExitSignal()
{
int signal = 0;
#ifdef WIN32
signal = static_cast<int>(Signals::CTRL_C_EVENT);
#else
signal = static_cast<int>(Signals::SIGNAL_SIGINT);
#endif
return this->Signal(signal);
}

public: bool Terminate()
{
if (this->process != nullptr)
Expand Down
11 changes: 11 additions & 0 deletions include/gz/utils/detail/subprocess.h
Original file line number Diff line number Diff line change
Expand Up @@ -923,6 +923,17 @@ FILE *subprocess_stderr(const struct subprocess_s *const process) {
}
}

int subprocess_signal(const subprocess_s *const process,
int signum)
{
#if defined(_WIN32)
return GenerateConsoleCtrlEvent(signum, process->hProcess);
Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like this is not compiling on windows.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I haven't had a chance to boot my laptop today. will take a look in a bit.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This should be resolved now.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually, hold, this doesn't fix the test I was trying to fix.

#else
return kill(process->child, signum);
#endif

}

int subprocess_join(struct subprocess_s *const process,
int *const out_return_code) {
#if defined(_WIN32)
Expand Down
34 changes: 34 additions & 0 deletions test/integration/subprocess_TEST.cc
Original file line number Diff line number Diff line change
Expand Up @@ -200,5 +200,39 @@ TEST(Subprocess, Environment)
EXPECT_EQ(std::string::npos, cout.find("GZ_BAZ_KEY=GZ_BAZ_VAL"));
EXPECT_NE(std::string::npos, cout.find("QUX_KEY=QUX_VAL"));
}
}

/////////////////////////////////////////////////
TEST(Subprocess, Signal)
{
auto start = std::chrono::steady_clock::now();
auto proc = Subprocess({kExecutablePath,
"--output=cerr",
"--iterations=100",
"--iteration-ms=10"});
EXPECT_TRUE(proc.Alive());

// Sleep for >1 iteration before sending signal
std::this_thread::sleep_for(std::chrono::milliseconds(20));

// Signal
proc.SendExitSignal();

// Block until the executable is done
auto ret = proc.Join();
EXPECT_EQ(1u, ret);

auto end = std::chrono::steady_clock::now();
auto elapsed =
std::chrono::duration_cast<std::chrono::milliseconds>(end - start);

// Check that process finished in less than half a second
// If the signal failed to send, then the process would run for over 1 second
EXPECT_LT(elapsed.count(), 500);

EXPECT_FALSE(proc.Alive());
auto cout = proc.Stdout();
auto cerr = proc.Stderr();
EXPECT_TRUE(cout.empty());
EXPECT_FALSE(cerr.empty());
}
Loading