Skip to content

Commit

Permalink
SystemServer: Implement lazy spawning
Browse files Browse the repository at this point in the history
For services explicitly configured as lazy, SystemServer will now listen
on the socket and only spawn the service once a client attempts to connect
to the socket.
  • Loading branch information
bugaevc authored and awesomekling committed Nov 26, 2019
1 parent ab98969 commit 52b0bd0
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 4 deletions.
3 changes: 3 additions & 0 deletions Base/etc/SystemServer.ini
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ Priority=high

[ProtocolServer]
Socket=/tmp/portal/protocol
Lazy=1
Priority=low
KeepAlive=1
User=anon

[LookupServer]
Socket=/tmp/portal/lookup
Lazy=1
Priority=low
KeepAlive=1
User=anon
Expand All @@ -24,6 +26,7 @@ User=anon

[AudioServer]
Socket=/tmp/portal/audio
# TODO: we may want to start it lazily, but right now WindowServer connects to it immediately on startup
Priority=high
KeepAlive=1
User=anon
Expand Down
29 changes: 28 additions & 1 deletion Servers/SystemServer/Service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,31 @@ void Service::setup_socket()
}
}

void Service::setup_notifier()
{
ASSERT(m_lazy);
ASSERT(m_socket_fd >= 0);
ASSERT(!m_socket_notifier);

m_socket_notifier = CNotifier::construct(m_socket_fd, CNotifier::Event::Read, this);
m_socket_notifier->on_ready_to_read = [this] {
dbg() << "Ready to read on behalf of " << name();
remove_child(*m_socket_notifier);
m_socket_notifier = nullptr;
spawn();
};
}

void Service::activate()
{
ASSERT(m_pid < 0);

if (m_lazy)
setup_notifier();
else
spawn();
}

void Service::spawn()
{
dbg() << "Spawning " << name();
Expand Down Expand Up @@ -172,7 +197,7 @@ void Service::did_exit(int exit_code)
m_pid = -1;

if (m_keep_alive)
spawn();
activate();
}

Service::Service(const CConfigFile& config, const StringView& name)
Expand All @@ -198,6 +223,7 @@ Service::Service(const CConfigFile& config, const StringView& name)
ASSERT_NOT_REACHED();

m_keep_alive = config.read_bool_entry(name, "KeepAlive");
m_lazy = config.read_bool_entry(name, "Lazy");

m_socket_path = config.read_entry(name, "Socket");
if (!m_socket_path.is_null()) {
Expand Down Expand Up @@ -227,6 +253,7 @@ void Service::save_to(JsonObject& json)
json.set("priority", m_priority);
json.set("keep_alive", m_keep_alive);
json.set("socket_path", m_socket_path);
json.set("lazy", m_lazy);
json.set("user", m_user);
json.set("uid", m_uid);
json.set("gid", m_gid);
Expand Down
8 changes: 7 additions & 1 deletion Servers/SystemServer/Service.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class Service final : public CObject {
C_OBJECT(Service)

public:
void spawn();
void activate();
void did_exit(int exit_code);

static Service* find_by_pid(pid_t);
Expand All @@ -25,6 +25,8 @@ class Service final : public CObject {
private:
Service(const CConfigFile&, const StringView& name);

void spawn();

// Path to the executable. By default this is /bin/{m_name}.
String m_executable_path;
// Extra arguments, starting from argv[1], to pass when exec'ing.
Expand All @@ -36,6 +38,8 @@ class Service final : public CObject {
bool m_keep_alive { false };
// Path to the socket to create and listen on on behalf of this service.
String m_socket_path;
// Whether we should only spawn this service once somebody connects to the socket.
bool m_lazy;
// The name of the user we should run this service as.
String m_user;
uid_t m_uid { 0 };
Expand All @@ -45,7 +49,9 @@ class Service final : public CObject {
pid_t m_pid { -1 };
// An open fd to the socket.
int m_socket_fd { -1 };
RefPtr<CNotifier> m_socket_notifier;

void resolve_user();
void setup_socket();
void setup_notifier();
};
4 changes: 2 additions & 2 deletions Servers/SystemServer/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,9 @@ int main(int, char**)
for (auto name : config->groups())
services.append(Service::construct(*config, name));

// After we've set them all up, spawn them!
// After we've set them all up, activate them!
for (auto& service : services)
service->spawn();
service->activate();

// This won't return if we're in test mode.
check_for_test_mode();
Expand Down

0 comments on commit 52b0bd0

Please sign in to comment.