Skip to content

Commit 12e232a

Browse files
committed
Capture windows shutdown signal.
1 parent b5477ba commit 12e232a

File tree

11 files changed

+287
-81
lines changed

11 files changed

+287
-81
lines changed

Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ console_bs_SOURCES = \
9696
console/executor_store.cpp \
9797
console/executor_test_reader.cpp \
9898
console/executor_test_writer.cpp \
99+
console/executor_windows.cpp \
99100
console/localize.hpp \
100101
console/main.cpp \
101102
console/stack_trace.cpp \

builds/cmake/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,7 @@ if (with-console)
334334
"../../console/executor_store.cpp"
335335
"../../console/executor_test_reader.cpp"
336336
"../../console/executor_test_writer.cpp"
337+
"../../console/executor_windows.cpp"
337338
"../../console/libbitcoin.ico"
338339
"../../console/localize.hpp"
339340
"../../console/main.cpp"

builds/msvc/vs2022/bs/bs.vcxproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@
139139
<ClCompile Include="..\..\..\..\console\executor_store.cpp" />
140140
<ClCompile Include="..\..\..\..\console\executor_test_reader.cpp" />
141141
<ClCompile Include="..\..\..\..\console\executor_test_writer.cpp" />
142+
<ClCompile Include="..\..\..\..\console\executor_windows.cpp" />
142143
<ClCompile Include="..\..\..\..\console\main.cpp" />
143144
<ClCompile Include="..\..\..\..\console\stack_trace.cpp" />
144145
</ItemGroup>

builds/msvc/vs2022/bs/bs.vcxproj.filters

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@
8181
<ClCompile Include="..\..\..\..\console\executor_test_writer.cpp">
8282
<Filter>src</Filter>
8383
</ClCompile>
84+
<ClCompile Include="..\..\..\..\console\executor_windows.cpp">
85+
<Filter>src</Filter>
86+
</ClCompile>
8487
<ClCompile Include="..\..\..\..\console\main.cpp">
8588
<Filter>src</Filter>
8689
</ClCompile>
@@ -116,4 +119,4 @@
116119
<Filter>resource</Filter>
117120
</ResourceCompile>
118121
</ItemGroup>
119-
</Project>
122+
</Project>

console/executor.cpp

Lines changed: 68 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,11 @@ namespace server {
3131
using boost::format;
3232
using namespace std::placeholders;
3333

34-
std::atomic_bool executor::cancel_{};
34+
// static initializers.
3535
std::thread executor::stop_poller_{};
3636
std::promise<bool> executor::stopping_{};
37+
std::atomic<bool> executor::initialized_{};
38+
std::atomic<int> executor::signal_{ unsignalled };
3739

3840
executor::executor(parser& metadata, std::istream& input, std::ostream& output,
3941
std::ostream&)
@@ -56,45 +58,35 @@ executor::executor(parser& metadata, std::istream& input, std::ostream& output,
5658
metadata.configured.log.verbose
5759
}
5860
{
59-
initialize_stop();
60-
}
61+
BC_ASSERT(!initialized_);
62+
initialized_ = true;
6163

62-
// Stop signal.
63-
// ----------------------------------------------------------------------------
64+
initialize_stop();
6465

6566
#if defined(HAVE_MSC)
66-
BOOL WINAPI executor::win32_handler(DWORD signal)
67+
create_hidden_window();
68+
#endif
69+
}
70+
71+
executor::~executor()
6772
{
68-
////if (auto* log = fopen("shutdown.log", "a"))
69-
////{
70-
//// fprintf(log, "Signal %lu at %llu\n", signal, GetTickCount64());
71-
//// fflush(log);
72-
//// fclose(log);
73-
////}
73+
initialized_ = false;
7474

75-
switch (signal)
76-
{
77-
case CTRL_C_EVENT:
78-
case CTRL_BREAK_EVENT:
79-
case CTRL_CLOSE_EVENT:
80-
case CTRL_LOGOFF_EVENT:
81-
case CTRL_SHUTDOWN_EVENT:
82-
executor::handle_stop({});
83-
return TRUE;
84-
default:
85-
return FALSE;
86-
}
87-
}
75+
#if defined(HAVE_MSC)
76+
destroy_hidden_window();
8877
#endif
78+
}
79+
80+
// Stop signal.
81+
// ----------------------------------------------------------------------------
82+
// static
8983

90-
// Call only once.
9184
void executor::initialize_stop()
9285
{
9386
poll_for_stopping();
9487

9588
#if defined(HAVE_MSC)
96-
// TODO: use RegisterServiceCtrlHandlerEx for service registration.
97-
::SetConsoleCtrlHandler(&executor::win32_handler, TRUE);
89+
::SetConsoleCtrlHandler(&executor::control_handler, TRUE);
9890
#else
9991
// Restart interrupted system calls.
10092
struct sigaction action
@@ -123,34 +115,72 @@ void executor::initialize_stop()
123115
#endif
124116
}
125117

118+
// Handle the stop signal and invoke stop method (requries signal safe code).
119+
void executor::handle_stop(int signal)
120+
{
121+
stop(signal);
122+
}
123+
124+
// Manage race between console stop and server stop.
125+
void executor::stop(int signal)
126+
{
127+
////if (auto* log = fopen("shutdown.log", "a"))
128+
////{
129+
//// fprintf(log, "stop %lu at %llu\n", signal, GetTickCount64());
130+
//// fflush(log);
131+
//// fclose(log);
132+
////}
133+
134+
// Implementation is limited to signal safe code.
135+
static_assert(std::atomic<int>::is_always_lock_free);
136+
137+
// Capture first handled signal value.
138+
auto unset = unsignalled;
139+
signal_.compare_exchange_strong(unset, signal, std::memory_order_acq_rel);
140+
}
141+
142+
// Any thread can monitor this for stopping.
143+
bool executor::canceled()
144+
{
145+
return signal_.load(std::memory_order_acquire) != unsignalled;
146+
}
147+
148+
// Spinning must be used in signal handler, cannot wait on a promise.
126149
void executor::poll_for_stopping()
127150
{
151+
using namespace std::this_thread;
152+
128153
stop_poller_ = std::thread([]()
129154
{
130-
while (!cancel_.load(std::memory_order_acquire))
131-
std::this_thread::sleep_for(std::chrono::milliseconds(10));
155+
while (!canceled())
156+
sleep_for(std::chrono::milliseconds(10));
132157

133158
stopping_.set_value(true);
134159
});
135160
}
136161

162+
// Blocks until stopping is signalled by poller.
137163
void executor::wait_for_stopping()
138164
{
139165
stopping_.get_future().wait();
140166
if (stop_poller_.joinable())
141167
stop_poller_.join();
142168
}
143169

144-
// Implementation is limited to signal safe code.
145-
void executor::handle_stop(int)
170+
// Suspend verbose logging and log the stop signal.
171+
void executor::log_stopping()
146172
{
147-
stop();
148-
}
173+
const auto signal = signal_.load();
174+
if (signal == signal_none)
175+
return;
149176

150-
// Manage the race between console stop and server stop.
151-
void executor::stop()
152-
{
153-
cancel_.store(true, std::memory_order_release);
177+
// A high level of consolve logging can obscure and delay stop.
178+
toggle_.at(network::levels::protocol) = false;
179+
toggle_.at(network::levels::verbose) = false;
180+
toggle_.at(network::levels::proxy) = false;
181+
182+
logger(format(BS_NODE_INTERRUPTED) % signal);
183+
logger(BS_NETWORK_STOPPING);
154184
}
155185

156186
// Event handlers.

console/executor.hpp

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,10 @@
2828
#include <bitcoin/server.hpp>
2929
#include <boost/format.hpp>
3030

31-
// This class is just an ad-hoc user interface wrapper on the node.
32-
// It will be factored and cleaned up for final release.
33-
3431
namespace libbitcoin {
3532
namespace server {
3633

34+
// This class is just an ad-hoc user interface wrapper on the node.
3735
class executor
3836
{
3937
public:
@@ -43,21 +41,38 @@ class executor
4341
executor(parser& metadata, std::istream&, std::ostream& output,
4442
std::ostream& error);
4543

44+
// Clean up.
45+
~executor();
46+
4647
// Called from main.
4748
bool dispatch();
4849

4950
private:
51+
static constexpr int unsignalled{ -1 };
52+
static constexpr int signal_none{ -2 };
53+
5054
// Executor (static).
5155
static void initialize_stop();
5256
static void poll_for_stopping();
5357
static void wait_for_stopping();
5458
static void handle_stop(int code);
55-
static void stop();
59+
static void stop(int signal=signal_none);
60+
static bool canceled();
61+
5662
#if defined(HAVE_MSC)
57-
static BOOL WINAPI win32_handler(DWORD signal);
63+
static BOOL WINAPI control_handler(DWORD signal);
64+
static LRESULT CALLBACK window_proc(HWND handle, UINT message,
65+
WPARAM wparam, LPARAM lparam);
66+
67+
void create_hidden_window();
68+
void destroy_hidden_window();
69+
70+
HWND window_{};
71+
std::thread thread_{};
5872
#endif
5973

6074
// Executor.
75+
void log_stopping();
6176
void handle_started(const system::code& ec);
6277
void handle_subscribed(const system::code& ec, size_t key);
6378
void handle_running(const system::code& ec);
@@ -155,8 +170,9 @@ class executor
155170
static const std::unordered_map<uint8_t, std::string> fired_;
156171

157172
// Shutdown.
158-
static std::atomic_bool cancel_;
159173
static std::thread stop_poller_;
174+
static std::atomic<int> signal_;
175+
static std::atomic<bool> initialized_;
160176
static std::promise<bool> stopping_;
161177
std::promise<bool> log_suspended_{};
162178

console/executor_runner.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ void executor::stopper(const std::string& message)
3636
capture_.stop();
3737

3838
// Stop log, causing final message to be buffered by handler.
39-
log_.stop(message, network::levels::application);
39+
log_.stop(message,levels::application);
4040

4141
// Suspend process termination until final message is buffered.
4242
log_suspended_.get_future().wait();
@@ -46,7 +46,7 @@ void executor::subscribe_connect()
4646
{
4747
node_->subscribe_connect
4848
(
49-
[&](const code&, const network::channel::ptr&)
49+
[&](const code&, const channel::ptr&)
5050
{
5151
log_.write(levels::verbose) <<
5252
"{in:" << node_->inbound_channel_count() << "}"
@@ -162,13 +162,12 @@ bool executor::do_run()
162162
logger(BS_NETWORK_STARTING);
163163
node_->start(std::bind(&executor::handle_started, this, _1));
164164

165-
// Wait on signal to stop node (<ctrl-c>).
165+
// Wait on signal to stop node (<ctrl-c>, etc).
166166
wait_for_stopping();
167-
toggle_.at(levels::protocol) = false;
168-
logger(BS_NETWORK_STOPPING);
169167

170168
// Stop network (if not already stopped by self).
171169
// Blocks on join of server/node/network threadpool.
170+
log_stopping();
172171
node_->close();
173172

174173
// Sizes and records change, buckets don't.

0 commit comments

Comments
 (0)