2
2
3
3
#include < zk/future.hpp>
4
4
5
+ #include < cerrno>
5
6
#include < exception>
6
7
#include < iostream>
8
+ #include < system_error>
7
9
8
10
#include < signal.h>
11
+ #include < sys/select.h>
9
12
10
13
#include " classpath.hpp"
11
14
#include " configuration.hpp"
15
+ #include " detail/event_handle.hpp"
12
16
#include " detail/subprocess.hpp"
13
17
14
18
namespace zk ::server
@@ -27,7 +31,8 @@ static void validate_settings(const configuration& settings)
27
31
}
28
32
29
33
server::server (classpath packages, configuration settings) :
30
- _running (true )
34
+ _running (true ),
35
+ _shutdown_event (std::make_unique<detail::event_handle>())
31
36
{
32
37
validate_settings (settings);
33
38
_worker = std::thread ([this , packages = std::move (packages), settings = std::move (settings)] ()
@@ -48,11 +53,33 @@ server::~server() noexcept
48
53
49
54
void server::shutdown (bool wait_for_stop)
50
55
{
51
- _running = false ;
56
+ _running.store (false , std::memory_order_release);
57
+ _shutdown_event->notify_one ();
58
+
52
59
if (wait_for_stop && _worker.joinable ())
53
60
_worker.join ();
54
61
}
55
62
63
+ static void wait_for_event (int fd1, int fd2, int fd3)
64
+ {
65
+ // This could be implemented with epoll instead of select, but since N=3, it doesn't really matter
66
+ ::fd_set read_fds;
67
+ FD_ZERO (&read_fds);
68
+ FD_SET (fd1, &read_fds);
69
+ FD_SET (fd2, &read_fds);
70
+ FD_SET (fd3, &read_fds);
71
+
72
+ int nfds = std::max (std::max (fd1, fd2), fd3) + 1 ;
73
+ int rc = ::select (nfds, &read_fds, nullptr , nullptr , nullptr );
74
+ if (rc < 0 )
75
+ {
76
+ if (errno == EINTR)
77
+ return ;
78
+ else
79
+ throw std::system_error (errno, std::system_category (), " select" );
80
+ }
81
+ }
82
+
56
83
void server::run_process (const classpath& packages, const configuration& settings)
57
84
{
58
85
detail::subprocess::argument_list args = { " -cp" , packages.command_line (),
@@ -70,12 +97,40 @@ void server::run_process(const classpath& packages, const configuration& setting
70
97
71
98
detail::subprocess proc (" java" , std::move (args));
72
99
73
- while (_running.load (std::memory_order_relaxed))
100
+ auto drain_pipes = [&] ()
101
+ {
102
+ bool read_anything = true ;
103
+ while (read_anything)
104
+ {
105
+ read_anything = false ;
106
+
107
+ auto out = proc.stdout ().read ();
108
+ if (!out.empty ())
109
+ {
110
+ read_anything = true ;
111
+ std::cout << out;
112
+ }
113
+
114
+ auto err = proc.stderr ().read ();
115
+ if (!err.empty ())
116
+ {
117
+ read_anything = true ;
118
+ std::cerr << out;
119
+ }
120
+ }
121
+ };
122
+
123
+ while (_running.load (std::memory_order_acquire))
74
124
{
75
- std::cout << proc.stdout ().read ();
76
- std::cerr << proc.stderr ().read ();
125
+ wait_for_event (proc.stdout ().native_read_handle (),
126
+ proc.stderr ().native_read_handle (),
127
+ _shutdown_event->native_handle ()
128
+ );
129
+
130
+ drain_pipes ();
77
131
}
78
- proc.signal (SIGTERM);
132
+ proc.terminate ();
133
+ drain_pipes ();
79
134
}
80
135
81
136
}
0 commit comments