Skip to content

Commit

Permalink
migrate from boost::asio to the MPD EventLoop
Browse files Browse the repository at this point in the history
Two years ago, ncmpc 0.32 (commit 74cc24b) made the switch from
GLib's GMainLoop to boost::asio, because that appeared to be a decent
non-blocking I/O event loop for C++, but it turned out that the API
was complicated to use, broke all the time and the generated code was
heavily bloated.

Even though importing more than 7000 lines of code from MPD sounds
like replacing one kind of bloat with another, this reduces ncmpc's
executable size from 380 kB to 240 kB (37% smaller).  The "mini" build
is reduced from 149 kB to 79 kB (47% smaller).

Closes #73
  • Loading branch information
MaxKellermann committed Oct 14, 2020
1 parent 73e8d1e commit dfd2602
Show file tree
Hide file tree
Showing 41 changed files with 358 additions and 752 deletions.
1 change: 1 addition & 0 deletions NEWS
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
ncmpc 0.41 - not yet released
* new main loop (copying code from MPD, replacing boost::asio)
* lyrics: remove the "lyricwiki" plugin because the site is gone

ncmpc 0.40 - (2020-10-07)
Expand Down
1 change: 0 additions & 1 deletion meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,6 @@ ncmpc = executable('ncmpc',
'src/Command.cxx',
'src/Bindings.cxx',
'src/GlobalBindings.cxx',
'src/UserInput.cxx',
'src/AsyncUserInput.cxx',
'src/KeyName.cxx',
'src/Match.cxx',
Expand Down
54 changes: 0 additions & 54 deletions src/AsioGetIoService.hxx

This file was deleted.

42 changes: 0 additions & 42 deletions src/AsioServiceFwd.hxx

This file was deleted.

32 changes: 12 additions & 20 deletions src/AsyncUserInput.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,11 @@ translate_key(int key)
}

void
AsyncUserInput::OnReadable(const boost::system::error_code &error)
AsyncUserInput::OnSocketReady(unsigned) noexcept
{
if (error) {
get_io_context().stop();
return;
}

int key = wgetch(&w);
if (ignore_key(key)) {
AsyncWait();
if (ignore_key(key))
return;
}

#ifdef HAVE_GETMOUSE
if (key == KEY_MOUSE) {
Expand All @@ -68,39 +61,38 @@ AsyncUserInput::OnReadable(const boost::system::error_code &error)
do_mouse_event({event.x, event.y}, event.bstate);
end_input_event();

AsyncWait();
return;
}
#endif

Command cmd = translate_key(key);
if (cmd == Command::NONE) {
AsyncWait();
if (cmd == Command::NONE)
return;
}

begin_input_event();

if (!do_input_event(get_io_context(), cmd))
if (!do_input_event(socket_event.GetEventLoop(), cmd))
return;

end_input_event();
AsyncWait();
return;
}

AsyncUserInput::AsyncUserInput(boost::asio::io_service &io_service, WINDOW &_w)
:UserInput(io_service), w(_w)
AsyncUserInput::AsyncUserInput(EventLoop &event_loop, WINDOW &_w) noexcept
:socket_event(event_loop, BIND_THIS_METHOD(OnSocketReady),
SocketDescriptor(STDIN_FILENO)),
w(_w)
{
AsyncWait();
socket_event.ScheduleRead();
}

void
keyboard_unread(boost::asio::io_service &io_service, int key)
keyboard_unread(EventLoop &event_loop, int key)
{
if (ignore_key(key))
return;

Command cmd = translate_key(key);
if (cmd != Command::NONE)
do_input_event(io_service, cmd);
do_input_event(event_loop, cmd);
}
17 changes: 7 additions & 10 deletions src/AsyncUserInput.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,23 @@
#ifndef ASYNC_USER_INPUT_HXX
#define ASYNC_USER_INPUT_HXX

#include "UserInput.hxx"
#include "event/SocketEvent.hxx"

#include <curses.h>

class AsyncUserInput : public UserInput {
class AsyncUserInput {
SocketEvent socket_event;

WINDOW &w;

public:
AsyncUserInput(boost::asio::io_service &io_service, WINDOW &_w);
AsyncUserInput(EventLoop &event_loop, WINDOW &_w) noexcept;

private:
void AsyncWait() {
UserInput::AsyncWait(std::bind(&AsyncUserInput::OnReadable, this,
std::placeholders::_1));
}

void OnReadable(const boost::system::error_code &error);
void OnSocketReady(unsigned flags) noexcept;
};

void
keyboard_unread(boost::asio::io_service &io_service, int key);
keyboard_unread(EventLoop &event_loop, int key);

#endif
12 changes: 3 additions & 9 deletions src/DelayedSeek.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -42,25 +42,19 @@ DelayedSeek::Commit() noexcept
void
DelayedSeek::Cancel() noexcept
{
commit_timer.cancel();
commit_timer.Cancel();
}

void
DelayedSeek::OnTimer(const boost::system::error_code &error) noexcept
DelayedSeek::OnTimer() noexcept
{
if (error)
return;

Commit();
}

void
DelayedSeek::ScheduleTimer() noexcept
{
boost::system::error_code error;
commit_timer.expires_from_now(std::chrono::milliseconds(500), error);
commit_timer.async_wait(std::bind(&DelayedSeek::OnTimer,
this, std::placeholders::_1));
commit_timer.Schedule(std::chrono::milliseconds(500));
}

bool
Expand Down
12 changes: 5 additions & 7 deletions src/DelayedSeek.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@
#ifndef NCMPC_DELAYED_SEEK_HXX
#define NCMPC_DELAYED_SEEK_HXX

#include "AsioServiceFwd.hxx"

#include <boost/asio/steady_timer.hpp>
#include "event/TimerEvent.hxx"

struct mpdclient;

Expand All @@ -36,12 +34,12 @@ class DelayedSeek {
int id = -1;
unsigned time;

boost::asio::steady_timer commit_timer;
TimerEvent commit_timer;

public:
DelayedSeek(boost::asio::io_service &io_service,
DelayedSeek(EventLoop &event_loop,
struct mpdclient &_c) noexcept
:c(_c), commit_timer(io_service) {}
:c(_c), commit_timer(event_loop, BIND_THIS_METHOD(OnTimer)) {}

~DelayedSeek() noexcept {
Cancel();
Expand All @@ -61,7 +59,7 @@ public:
void Cancel() noexcept;

private:
void OnTimer(const boost::system::error_code &error) noexcept;
void OnTimer() noexcept;
void ScheduleTimer() noexcept;
};

Expand Down
36 changes: 17 additions & 19 deletions src/Instance.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#include "Instance.hxx"
#include "Options.hxx"
#include "event/SignalMonitor.hxx"

#include <signal.h>

Expand All @@ -44,36 +45,32 @@ static constexpr TagMask global_tag_whitelist{
#endif

Instance::Instance()
:io_service(),
#ifndef _WIN32
sigterm(io_service, SIGTERM, SIGINT, SIGHUP),
sigwinch(io_service, SIGWINCH, SIGCONT),
#endif
client(io_service,
:client(event_loop,
options.host.empty() ? nullptr : options.host.c_str(),
options.port,
options.timeout_ms,
options.password.empty() ? nullptr : options.password.c_str()),
seek(io_service, client),
reconnect_timer(io_service),
update_timer(io_service),
seek(event_loop, client),
reconnect_timer(event_loop, BIND_THIS_METHOD(OnReconnectTimer)),
update_timer(event_loop, BIND_THIS_METHOD(OnUpdateTimer)),
#ifndef NCMPC_MINI
check_key_bindings_timer(io_service),
check_key_bindings_timer(event_loop, BIND_THIS_METHOD(OnCheckKeyBindings)),
#endif
screen_manager(io_service),
screen_manager(event_loop),
#ifdef ENABLE_LIRC
lirc_input(io_service),
lirc_input(event_loop),
#endif
user_input(io_service, *screen_manager.main_window.w)
user_input(event_loop, *screen_manager.main_window.w)
{
screen_manager.Init(&client);

sigterm.async_wait([this](const auto &, int){
this->io_service.stop();
});

#ifndef _WIN32
AsyncWaitSigwinch();
SignalMonitorInit(event_loop);
SignalMonitorRegister(SIGTERM, BIND_THIS_METHOD(Quit));
SignalMonitorRegister(SIGINT, BIND_THIS_METHOD(Quit));
SignalMonitorRegister(SIGHUP, BIND_THIS_METHOD(Quit));
SignalMonitorRegister(SIGWINCH, BIND_THIS_METHOD(OnSigwinch));
SignalMonitorRegister(SIGCONT, BIND_THIS_METHOD(OnSigwinch));
#endif

#ifdef HAVE_TAG_WHITELIST
Expand All @@ -92,12 +89,13 @@ Instance::Instance()
Instance::~Instance()
{
screen_manager.Exit();
SignalMonitorFinish();
}

void
Instance::Run()
{
screen_manager.Update(client, seek);

io_service.run();
event_loop.Run();
}
Loading

0 comments on commit dfd2602

Please sign in to comment.