Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ console_bs_SOURCES = \
console/executor_store.cpp \
console/executor_test_reader.cpp \
console/executor_test_writer.cpp \
console/executor_windows.cpp \
console/localize.hpp \
console/main.cpp \
console/stack_trace.cpp \
Expand Down
1 change: 1 addition & 0 deletions builds/cmake/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ if (with-console)
"../../console/executor_store.cpp"
"../../console/executor_test_reader.cpp"
"../../console/executor_test_writer.cpp"
"../../console/executor_windows.cpp"
"../../console/libbitcoin.ico"
"../../console/localize.hpp"
"../../console/main.cpp"
Expand Down
Binary file added builds/msvc/libbitcoin.ico
Binary file not shown.
Binary file modified builds/msvc/resource.h
Binary file not shown.
Binary file modified builds/msvc/resource.rc
Binary file not shown.
1 change: 1 addition & 0 deletions builds/msvc/vs2022/bs/bs.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@
<ClCompile Include="..\..\..\..\console\executor_store.cpp" />
<ClCompile Include="..\..\..\..\console\executor_test_reader.cpp" />
<ClCompile Include="..\..\..\..\console\executor_test_writer.cpp" />
<ClCompile Include="..\..\..\..\console\executor_windows.cpp" />
<ClCompile Include="..\..\..\..\console\main.cpp" />
<ClCompile Include="..\..\..\..\console\stack_trace.cpp" />
</ItemGroup>
Expand Down
5 changes: 4 additions & 1 deletion builds/msvc/vs2022/bs/bs.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@
<ClCompile Include="..\..\..\..\console\executor_test_writer.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\console\executor_windows.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\console\main.cpp">
<Filter>src</Filter>
</ClCompile>
Expand Down Expand Up @@ -116,4 +119,4 @@
<Filter>resource</Filter>
</ResourceCompile>
</ItemGroup>
</Project>
</Project>
106 changes: 68 additions & 38 deletions console/executor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@ namespace server {
using boost::format;
using namespace std::placeholders;

std::atomic_bool executor::cancel_{};
// static initializers.
std::thread executor::stop_poller_{};
std::promise<bool> executor::stopping_{};
std::atomic<bool> executor::initialized_{};
std::atomic<int> executor::signal_{ unsignalled };

executor::executor(parser& metadata, std::istream& input, std::ostream& output,
std::ostream&)
Expand All @@ -56,45 +58,35 @@ executor::executor(parser& metadata, std::istream& input, std::ostream& output,
metadata.configured.log.verbose
}
{
initialize_stop();
}
BC_ASSERT(!initialized_);
initialized_ = true;

// Stop signal.
// ----------------------------------------------------------------------------
initialize_stop();

#if defined(HAVE_MSC)
BOOL WINAPI executor::win32_handler(DWORD signal)
create_hidden_window();
#endif
}

executor::~executor()
{
////if (auto* log = fopen("shutdown.log", "a"))
////{
//// fprintf(log, "Signal %lu at %llu\n", signal, GetTickCount64());
//// fflush(log);
//// fclose(log);
////}
initialized_ = false;

switch (signal)
{
case CTRL_C_EVENT:
case CTRL_BREAK_EVENT:
case CTRL_CLOSE_EVENT:
case CTRL_LOGOFF_EVENT:
case CTRL_SHUTDOWN_EVENT:
executor::handle_stop({});
return TRUE;
default:
return FALSE;
}
}
#if defined(HAVE_MSC)
destroy_hidden_window();
#endif
}

// Stop signal.
// ----------------------------------------------------------------------------
// static

// Call only once.
void executor::initialize_stop()
{
poll_for_stopping();

#if defined(HAVE_MSC)
// TODO: use RegisterServiceCtrlHandlerEx for service registration.
::SetConsoleCtrlHandler(&executor::win32_handler, TRUE);
::SetConsoleCtrlHandler(&executor::control_handler, TRUE);
#else
// Restart interrupted system calls.
struct sigaction action
Expand Down Expand Up @@ -123,34 +115,72 @@ void executor::initialize_stop()
#endif
}

// Handle the stop signal and invoke stop method (requries signal safe code).
void executor::handle_stop(int signal)
{
stop(signal);
}

// Manage race between console stop and server stop.
void executor::stop(int signal)
{
////if (auto* log = fopen("shutdown.log", "a"))
////{
//// fprintf(log, "stop %lu at %llu\n", signal, GetTickCount64());
//// fflush(log);
//// fclose(log);
////}

// Implementation is limited to signal safe code.
static_assert(std::atomic<int>::is_always_lock_free);

// Capture first handled signal value.
auto unset = unsignalled;
signal_.compare_exchange_strong(unset, signal, std::memory_order_acq_rel);
}

// Any thread can monitor this for stopping.
bool executor::canceled()
{
return signal_.load(std::memory_order_acquire) != unsignalled;
}

// Spinning must be used in signal handler, cannot wait on a promise.
void executor::poll_for_stopping()
{
using namespace std::this_thread;

stop_poller_ = std::thread([]()
{
while (!cancel_.load(std::memory_order_acquire))
std::this_thread::sleep_for(std::chrono::milliseconds(10));
while (!canceled())
sleep_for(std::chrono::milliseconds(10));

stopping_.set_value(true);
});
}

// Blocks until stopping is signalled by poller.
void executor::wait_for_stopping()
{
stopping_.get_future().wait();
if (stop_poller_.joinable())
stop_poller_.join();
}

// Implementation is limited to signal safe code.
void executor::handle_stop(int)
// Suspend verbose logging and log the stop signal.
void executor::log_stopping()
{
stop();
}
const auto signal = signal_.load();
if (signal == signal_none)
return;

// Manage the race between console stop and server stop.
void executor::stop()
{
cancel_.store(true, std::memory_order_release);
// A high level of consolve logging can obscure and delay stop.
toggle_.at(network::levels::protocol) = false;
toggle_.at(network::levels::verbose) = false;
toggle_.at(network::levels::proxy) = false;

logger(format(BS_NODE_INTERRUPTED) % signal);
logger(BS_NETWORK_STOPPING);
}

// Event handlers.
Expand Down
28 changes: 22 additions & 6 deletions console/executor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,10 @@
#include <bitcoin/server.hpp>
#include <boost/format.hpp>

// This class is just an ad-hoc user interface wrapper on the node.
// It will be factored and cleaned up for final release.

namespace libbitcoin {
namespace server {

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

// Clean up.
~executor();

// Called from main.
bool dispatch();

private:
static constexpr int unsignalled{ -1 };
static constexpr int signal_none{ -2 };

// Executor (static).
static void initialize_stop();
static void poll_for_stopping();
static void wait_for_stopping();
static void handle_stop(int code);
static void stop();
static void stop(int signal=signal_none);
static bool canceled();

#if defined(HAVE_MSC)
static BOOL WINAPI win32_handler(DWORD signal);
static BOOL WINAPI control_handler(DWORD signal);
static LRESULT CALLBACK window_proc(HWND handle, UINT message,
WPARAM wparam, LPARAM lparam);

void create_hidden_window();
void destroy_hidden_window();

HWND window_{};
std::thread thread_{};
#endif

// Executor.
void log_stopping();
void handle_started(const system::code& ec);
void handle_subscribed(const system::code& ec, size_t key);
void handle_running(const system::code& ec);
Expand Down Expand Up @@ -155,8 +170,9 @@ class executor
static const std::unordered_map<uint8_t, std::string> fired_;

// Shutdown.
static std::atomic_bool cancel_;
static std::thread stop_poller_;
static std::atomic<int> signal_;
static std::atomic<bool> initialized_;
static std::promise<bool> stopping_;
std::promise<bool> log_suspended_{};

Expand Down
9 changes: 4 additions & 5 deletions console/executor_runner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ void executor::stopper(const std::string& message)
capture_.stop();

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

// Suspend process termination until final message is buffered.
log_suspended_.get_future().wait();
Expand All @@ -46,7 +46,7 @@ void executor::subscribe_connect()
{
node_->subscribe_connect
(
[&](const code&, const network::channel::ptr&)
[&](const code&, const channel::ptr&)
{
log_.write(levels::verbose) <<
"{in:" << node_->inbound_channel_count() << "}"
Expand Down Expand Up @@ -162,13 +162,12 @@ bool executor::do_run()
logger(BS_NETWORK_STARTING);
node_->start(std::bind(&executor::handle_started, this, _1));

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

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

// Sizes and records change, buckets don't.
Expand Down
Loading
Loading