Server¶
Public API¶
The server part of xeus provides a public API made in xserver.hpp
. This file contains the base
class xserver
, which must be inherited from any class implementing a server. This is the unique
entry point into the server component used by the kernel core.
xeus-zmq provides the following implementations:
xserver_zmq.hpp
: This file contains the interface of the default server implementation, that can be used directly or extended in order to override parts of its behavior.xserver_control_main.hpp
: This file contains the interface of a server that handles the shell and the control socket on different threads. The main thread listens to the control socket.xserver_shell_main.hpp
: This file contains the interface of a server that handlels the shell and the control sockets on different threads. The main threads listens to the shell socket.
Before we dive into the details of the server implementation, let’s have a look at the public interface:
enum class channel
{
SHELL,
CONTROL
};
class XEUS_API xserver
{
public:
using listener = std::function<void(xmessage)>;
using internal_listener = std::function<nl::json(nl::json)>;
virtual ~xserver() = default;
xserver(const xserver&) = delete;
xserver& operator=(const xserver&) = delete;
xserver(xserver&&) = delete;
xserver& operator=(xserver&&) = delete;
xcontrol_messenger& get_control_messenger();
void send_shell(xmessage message);
void send_control(xmessage message);
void send_stdin(xmessage message);
void publish(xpub_message message, channel c);
void start(xpub_message message);
void abort_queue(const listener& l, long polling_interval);
void stop();
void update_config(xconfiguration& config) const;
void register_shell_listener(const listener& l);
void register_control_listener(const listener& l);
void register_stdin_listener(const listener& l);
void register_internal_listener(const internal_listener& l);
protected:
xserver() = default;
void notify_shell_listener(xmessage msg);
void notify_control_listener(xmessage msg);
void notify_stdin_listener(xmessage msg);
nl::json notify_internal_listener(nl::json msg);
private:
virtual xcontrol_messenger& get_control_messenger_impl() = 0;
virtual void send_shell_impl(xmessage message) = 0;
First thing to notice is the xserver
class makes use of the Non-Virtual Interface pattern. This
allows a clear separation between the client interface (the public methods) and the interface for
subclasses (protected non-virtual methods and private virtual methods).
The client interface can be divided into three parts:
the API to control the server: this is how you configure, start and stop the server. The related methods are
update_config
,start
,stop
andabort_queue
. These methods forward to private pure virtual methods that must be implemented in inheriting classes.the API to send message: this is where you decide on which channel you send the message. The related methods are
send_shell
,send_control
,send_stdin
andpublish
. These methods also forward to virtual methods that must be implemented in inheriting classes.the API to register callbacks: the methods
register_shell_listener
,register_control_listener
andregister_stdin_listener
allow clients (such as the kernel core component) to register functions that will be called when a message is received by the server. This way, the server component is loosely coupled with its clients, it doesn’t need to know anything about them.
The subclass interface contains private virtual methods that must be implemented in inheriting classes to
define the behavior of the server, and protected methods to notify the client that a message has been
received. This makes inheriting classes independent from the way the xserver
class stores and uses
the callbacks.
xserver_zmq¶
The xserver_zmq
class is the default implementation of the server API, its internals are illustrated
in the following diagram:
The default server is made of three threads communicating through internal ZeroMQ sockets. The main
thread is responsible for polling both shell
and controller
channels. When a message is received
on one of these channels, the corresponding callback is invoked. Any code executed in the interpreter
will be executed by the main thread. If the publish
method is called, the main thread sends a message
to the publisher thread.
Having a dedicated thread for publishing messages makes this operation a non-blocking one. When the kernel
main thread needs to publish a message, it simply sends it to the publisher thread through an internal socket
and continues its execution. The publisher thread will poll its internal socket and forward the messages to
the publisher
channel.
The last thread is the heartbeat. It is responsible for notifying the client that the kernel is still alive.
This is done by sending messages on the heartbeat
channel at a regular rate.
The main thread is also connected to the publisher and the heartbeat threads through internal controller
channels. These are used to send stop
messages to the subthread and allow to stop the kernel in a clean
way.
xserver_control_main¶
The xserver_control_main
class is an alternative implementation of the server API, its internals are
illustrated in the following diagram:
This server runs four threads that communicate through internal ZeroMQ sockets. The main thread is
responsible for polling the control
channel while a dedicated thread listens on the shell
channel. Having separated threads for the control
and shell
channel makes it possible to send
messages on a channel while the kernel is already processing a message on the other channel. For instance
one can send on the control
a request to interrupt a computation running on the shell
.
The control thread is also connected to the shell, the publisher and the heartbeat threads through internal
controller
channels. These are used to send stop
messages to the subthread and allow to stop the
kernel in a clean way, similarly to the xserver_zmq
.
The rest of the implementation is similar to that of xserver_zmq
.
xserver_shell_main¶
The xserver_shell_main
class is very similar to the xserver_control_main
class, except that
the main thread listens on the shell
channel as illustrated in the following diagram: