Session suspension and restoration protocol

Roman Gilg subdiff at gmail.com
Mon Jun 18 15:05:25 UTC 2018


Hi all,

I have worked the past few days on a protocol for client session
suspension and restoration and I need to get some feedback if I'm
running in the right direction. I did get some input from Sway and
GNOME devs at the start on what they would want such a protocol to do
in general, and I did try to respect that when working on the
protocol. Main features of the protocol as pasted below are:
* provides an extensible object creation mechanism to let the
compositor announce support for a subset of three increasing levels of
suspension and let the client opt-in to some or all of these supported
suspension levels
* these three levels are:
  1. sleep: allows destroying surfaces and their later restoration
  2. quit: allows connection closing and restart of the client by the
     compositor via an implementation-independent D-Bus interface
  3. shutdown: client will get restored after a server shutdown in a
     new Wayland session via the same D-Bus interface.
* using D-Bus interface only to secure against sandboxed clients
* if the client opts-in to level 2, 3 or both, the compositor might
  try to also restore the client after a client or compositor crash
* the program flow would be always:
  1. compositor announces supported suspension levels
  2. client opts-in to one or several of the supported suspension
     levels by creating protocol objects for the respective levels
  3. when the compositor wants to suspend the client (for example on
     low memory or shutdown) it sends an event to the respective
     suspension object, which the client can acknowledge or it must
     destroy the object to cancel the suspension.
  So while client and server agree on a subset of usable suspension
  levels, in the end only the compositor activates a suspension.

The protocol is written from scratch, but it shares some similarities
with Mike's proposed xdg-session-management protocol
(https://lists.freedesktop.org/archives/wayland-devel/2018-February/037236.html).

In advance thank you for your feedback!


<?xml version="1.0" encoding="UTF-8"?>
<protocol name="session_suspension_v1">
  <copyright>
    Copyright © 2018 Roman Gilg

    Permission is hereby granted, free of charge, to any person obtaining a
    copy of this software and associated documentation files (the "Software"),
    to deal in the Software without restriction, including without limitation
    the rights to use, copy, modify, merge, publish, distribute, sublicense,
    and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice (including the next
    paragraph) shall be included in all copies or substantial portions of the
    Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    DEALINGS IN THE SOFTWARE.
  </copyright>
  <description summary="suspend and restore clients with their surfaces">
    A protocol for suspending and restoring clients. Multiple modes allow
    clients and server to match their common support for different levels of
    suspension from suspending and restoring only the client's surfaces until
    full client restoration after a system reboot.

    Warning! The protocol described in this file is experimental and backward
    incompatible changes may be made. Backward compatible changes may be added
    together with the corresponding interface version bump. Backward
    incompatible changes are done by bumping the version number in the protocol
    and interface names and resetting the interface version. Once the protocol
    is to be declared stable, the 'z' prefix and the version number in the
    protocol and interface names are removed and the interface version number
    is reset.
  </description>

  <interface name="zxdg_session_suspension_manager" version="1">
    <description summary="restores clients for session restarts">
      A compositor implementing this interface announces that it is capable of
      some form of session suspension.

      Clients can register themselves in order to get more information about
      the available suspension levels and decide which ones of these they want
      to support.

      Clients register themselves in two steps by first sending the
      register_session_suspension request and then creating other child
      interfaces relative to which levels of restoration they and the
      compositor agree on to support.

      In the current version the protocol supports suspension of surfaces
      and their subsequent restoration while keeping the Wayland connection
      between client and server alive, restoration after the client quits the
      connection voluntarily or by crashing and restoration at the beginning of
      a new compositor session after a compositor shutdown by regular means or
      because of a compositor crash.

      Here a compositor session is defined as the full lifespan of the Wayland
      server connection being available to clients, while a client session is
      the period in which a specific client server connection exists.
    </description>

    <enum name="error">
      <entry name="already_exists" value="0"
             summary="an object for the reference already exists"/>
      <entry name="defunct_children" value="1"
             summary="parent object destroyed before child objects"/>
      <entry name="suspension_violation" value="1"
             summary="unallowed request sent while session suspended"/>
    </enum>

    <request name="register_session_suspension">
      <description summary="client shows interest in session suspension">
        Returns a zxdg_session_suspension object, which registers the client to
        the session suspension and restoration mechanism of the compositor.

        With its child interfaces it enables the client to register itself to
        the different levels of restoration supported by the compositor.

        In certain high levels the compositor will ask the client through a
        special D-Bus interface to restore its internal state. This interface
        must be made available by the client on the address specified by the
        desktop_id argument. For this form of restoration a D-Bus activation
        service should be provided by the client such that the compositor can
        dbus-activate the client with a call to the interface. More information
        on desktop file IDs and D-Bus Activation can be found in the Desktop
        Entry Specification [0].

        The special interface name for the D-Bus call is
        zxdg_session_suspension_v1 and the method being called by the
        compositor on this interface is simply called 'restore' which takes an
        uint32 token set by the compositor, which is announced to the client
        through the zxdg_session_suspension.registered event.

        The client needs to remember this token, because by that it can identify
        the internal data it needs to restore after the D-Bus call has been
        received.

        For example a client provides the desktop_id org.foo.bar and receives
        the client_id 1234 through the zxdg_session_suspension.registered event.

        If the compositor supports the shutdown level of restoration and the
        client opts-in to it, then after the compositor shuts down the Wayland
        server with the restoration information for this client being retained,
        the compositor or some session manager process mandated by the
        compositor will issue on the next Wayland server launch the D-Bus
        command:

        org.foo.bar /zxdg_session_suspension_v1 restore(1234)

        On Wayland server launch the application org.foo.bar is most likely not
        running, so it gets D-Bus activated through the respective service
        file. After it has been activated, the method call will be delivered to
        the application and it can act upon the restoration request by loading
        former internal data and asking the compositor to restore its surfaces.

        To restore a previous client session a restore_id argument can be
        provided to this call, which must match the client_id received in the
        last client session.

        If for this client a zxdg_session_suspension object already exists,
        calling this request will result in a protocol error.

        [0] https://specifications.freedesktop.org/desktop-entry-spec
      </description>
      <arg name="id" type="new_id" interface="zxdg_session_suspension"
           summary="register a new restore session"/>
      <arg name="restore_id" type="uint"
           summary="for restoration must match client_id of previous session"/>
      <arg name="desktop_id" type="string" allow-null="true"
           summary="desktop file id to restore the client via d-bus"/>
    </request>
  </interface>

  <interface name="zxdg_session_suspension" version="1">
    <description summary="show interest in session suspension">
      The main session suspension object, created by the client in order opt-in
      to the compositor suspension and restoration mechanism.

      After the object has been created the server sends the registered event,
      which tells the client what levels of suspension the server supports.
    </description>

    <request name="destroy" type="destructor">
      <description summary="destroy the object">
        Used by the client to notify the server that it will no longer use this
        object.

        Sending this request before all child objects have been destroyed is
        illegal and a protocol error will be raised.
      </description>
    </request>

    <request name="register_sleep">
      <description summary="opt-in to client session sleeping">
        Creates a zxdg_session_sleep object. As long as this object exists, the
        client is registered on the server side as being ready to go into the
        sleep state.
      </description>
      <arg name="id" type="new_id" interface="zxdg_session_sleep"
           summary="client session sleeping object"/>
    </request>

    <request name="register_quit">
      <description summary="opt-in to client session quitting">
        Creates a zxdg_session_quit object. As long as this object exists, the
        client is registered on the server side as being ready to go into the
        quit state, which means that it is able to quit its Wayland connection
        and it will be restored via D-Bus when the server sees fit.

        A client therfore should not call this request if it did not provide a
        desktop_id when creating the zxdg_session_suspension interface.
      </description>
      <arg name="id" type="new_id" interface="zxdg_session_quit"
           summary="client session quitting object"/>
    </request>

    <request name="register_shutdown">
      <description summary="opt-in to compositor session shutdown restoration">
        Creates a zxdg_session_shutdown object. As long as this object exists,
        the client is registered on the server side as being ready to go into
        the shutdown state when the compositor kills the Wayland server for
        getting restored in a new compositor session via D-Bus.

        A client therfore should not call this request if it did not provide a
        desktop_id when creating the zxdg_session_suspension interface.
      </description>
      <arg name="id" type="new_id" interface="zxdg_session_shutdown"
           summary="server shutdown restoration object"/>
    </request>

    <request name="register_surface">
      <description summary="track the surface data for future restoration">
        Asks the compositor to track the xdg_toplevel surface for restoration.
        The compositor will provide a zxdg_session_surface object.

        Calling this request while the client is in a suspension state results
        in a protocol error.

        Calling this request on an xdg_toplevel for which a zxdg_session_surface
        object already exists results in a protocol error.
      </description>
      <arg name="id" type="new_id" interface="zxdg_session_surface"
           summary="session surface object"/>
      <arg name="surface" type="object" interface="xdg_toplevel"/>
    </request>

    <request name="restore_surface">
      <description summary="ask to restore the surface">
        Requests the provided xdg_toplevel surface to be restored with the data
        the compositor has saved for the surface_restore_id in combination with
        the restore_id set earlier in the call to create the
        zxdg_session_suspension object.

        If the compositor does not find any data for this restore_id,
        surface_restore_id combination, it will ignore the request without
        further notice.

        This request must be sent before the first commit to the xdg_toplevel
        and before a register_surface request for the surface has been sent,
        otherwise the request will be ignored by the compositor.

        Calling this request while the client is in a suspension state results
        in a protocol error.

        If the compositor applies the saved settings associated with the
        surface_restore_id it will eventually only send a normal configure
        event to the xdg_toplevel.
      </description>
      <arg name="surface" type="object" interface="xdg_toplevel"/>
      <arg name="surface_restore_id" type="uint"
           summary="must match surface_id from before suspension"/>
    </request>

    <enum name="suspension_level" bitfield="true">
        <description summary="TODO">
          Enum bitfield of session suspension levels. Used in the registered
          event to indicate which levels the compositor supports.
        </description>
      <entry name="sleep" value="0">
      </entry>
      <entry name="quit" value="1">
      </entry>
      <entry name="shutdown" value="2">
      </entry>
    </enum>

    <event name="registered">
      <description summary="client state being tracked">
        Announces the supported levels of session suspension.

        Under normal circumstances also provies the unique identifier
        client_id, that is used to restore the session via D-Bus.

        But the compositor might at any time decide to ultimately no longer
        allow this client to be suspended and restored through this object.
        In this case the compositor will send this event with 0 as client_id
        argument. Then the zxdg_session_suspension object and all child objects
        are no longer functional and should be deleted. An active suspension is
        broken and the restoration data is lost. Doing this is therefore a very
        severe step to take and a compositor should in general try to avoid it
        or do it directly on creation of the zxdg_session_suspension object.

        In general client_id 0 will be sent, when the client is in a state,
        that is unset through the D-Bus signal.
      </description>
      <arg name="client_id" type="uint"
           summary="unique id larger 0 for later restore, 0 for defunct"/>
      <arg name="supported_levels" type="uint" enum="level"
           summary="bit mask of supported restoration levels"/>
    </event>
  </interface>

  <interface name="zxdg_session_sleep" version="1">
    <description summary="opt-in to client sleep">
      While this object exists, the client is registered to the server as being
      ready to go into the sleep suspension state.

      The sleep suspension state is active after the client has received the
      sleep event and untill it receives the wakeup event or it destroys this
      object.

      If the client registered to a higher level of suspension as well, it must
      be ready to go directly from this state into the higher level without
      receiving the wakeup event before and therefore without restoring its
      surfaces in between. The sleep state is implicitly unset in this case.
    </description>

    <request name="destroy" type="destructor">
      <description summary="destroy the sleep object">
        Used by the client to notify the server that it will no longer use this
        object. That means the client can no longer go into the sleep state via
        this object.

        If the sleep state is active while this request is sent the client will
        have the opportunity to recover its suspended surfaces.

        A sleep event sent at the same time the client requests destruction has
        no further implications besides the client being not in the sleep state
        and not being ready for it anymore.
      </description>
    </request>

    <event name="sleep">
      <description summary="server wants the client to go to sleep">
        By sending this event the server puts the client into the sleep state.
        In the sleep state the client is encouraged to destroy its surfaces and
        restore them again after the client left the sleep state either when
        receiving the wakeup event or by destroying the object.

        The reason for sending out this event might be that the client is at
        the moment hidden and the server wants to save system ressources.

        For example this might be sensible on an embedded system with a single
        application interface or in a high powered system, but with multiple
        virtual screens.

        After this event has been received the client can destroy surfaces,
        which had been registered through register_surface securely and expect
        the compositor to restore their position and geometry after the
        suspension has been reverted again.
      </description>
    </event>

    <event name="wakeup">
      <description summary="compositor needs client to be restored">
        This event cancels the sleep state. Afterwards the client must restore
        its destroyed surfaces. Otherwise its visual depiction to the user is
        most likely broken.
      </description>
    </event>
  </interface>

  <interface name="zxdg_session_quit" version="1">
    <description summary="opt-in to connection quitting">
      While this object exists, the client is registered to the server as being
      ready to go into the quit suspension state.

      The quit state becomes active, when the client responds with an ack
      request on receiving the quit event. The client can cancel going
      into the quit state by sending the destroy request instead of ack. If the
      client does not react to the quit event the compositor might deem it
      broken and cancel any suspension by sending the client_id 0 through the
      registered event of the zxdg_session_suspension interface. In order to
      make sure that the server did not deem the client broken, the client
      should therefore listen for one more event cycle that this did not
      happen before destroying the zxdg_session_suspension interface.

      In the quit state the client is only allowed any more to destroy objects
      of the zxdg_session_suspension interface. Any other request on the
      interface or on one of its child interfaces results in a protocol error.

      At the moment the quit state becomes active, the compositor saves the
      state of the registered surfaces for later restoration. Any later commits
      will be lost.

      The quit state ends when the compositor sends the D-Bus signal to
      restart the client.

      When this object exists, the compositor might try to restore a client via
      the D-Bus signal in case of a client or compositor crash.

      If the client was registered to the shutdown level of suspension as well,
      it must be ready to go directly from this state into the shutdown state
      and without receiving over events on the zxdg_session_suspension
      interface and its child interfaces or a D-Bus signal to ressurect before
      that. In this case the quit state is implicitly unset and the client is
      restarted only in a new Wayland session and possibly after a reboot of
      the whole system.
    </description>

    <request name="destroy" type="destructor">
      <description summary="destroy the quitting object">
        Used by the client to notify the server that the client will no longer
        use this object.
      </description>
    </request>

    <request name="ack">
      <description summary="acknowledge a quit event">
        Respond to a quit event. At the moment this request is sent, the quit
        state is entered.

        It is up to the client to kill its Wayland connection after this
        request has been sent.
      </description>
    </request>

    <event name="quit">
      <description summary="server wants client to quit and restore it later">
        By sending this event the server tells the client that it wants the
        client to go into the quit state. The client can accept this by
        responding with the ack request or refuse by destroying the object.
        Not reacting timely will result in the compositor deeming the client
        broken and the cancellation of any suspension registration by sending
        client_id 0 through the registered event of the zxdg_session_suspension
        interface.

        The reason for sending out this event might be that the client is at
        the moment hidden and the server wants to save system ressources.

        After this event has been sent the zxdg_session object and all its
        child objects become defunct and should only be destroyed any more.
      </description>
    </event>
  </interface>

  <interface name="zxdg_session_shutdown" version="1">
    <description summary="opt-in to restoration in new compositor session">
      While this object exists, the client is registered to the server as being
      ready to go into the shutdown suspension state.

      The shutdown state becomes active, when the client responds with an ack
      request on receiving the shutdown event. The client can cancel going
      into the shutdown state by sending the destroy request instead of ack. If
      the client does not react to the shutdown event the compositor might deem
      it broken and cancel any suspension by sending the client_id 0 through
      the registered event of the zxdg_session_suspension interface. In order
      to make sure that the server did not deem the client broken, the client
      should therefore listen for one more event cycle that this did not happen
      before destroying the zxdg_session_suspension interface.

      In the shutdown state the client is only allowed any more to destroy
      objects of the zxdg_session_suspension interface or resend the ack event.
      Any other request on the interface or one of its child interfaces results
      in a protocol error.

      At the moment the shutdown state becomes active, the compositor saves the
      state of the registered surfaces for later restoration. Any later commits
      will be lost.

      The shutdown state ends when the compositor sends the D-Bus signal to
      restart the client.

      When this object exists, the compositor might try to restore a client via
      the D-Bus signal in case of a client or compositor crash.
    </description>

    <request name="destroy" type="destructor">
      <description summary="destroy the suspension object">
        Used by the client to notify the server that the client will no longer
        use this object.
      </description>
    </request>

    <enum name="desire">
      <entry name="proceed" value="1"/>
      <entry name="hold" value="2"/>
    </enum>

    <request name="ack">
      <description summary="ask for shutdown delay">
        Respond to a shutdown event. At the moment this request is sent, the
        shutdown state is entered.

        The client has the chance to delay the compositor killing the Wayland
        server by replying with the hold enum value.

        The client is expected to send ack again with the proceed value to let
        the compositor know that it finished its remaining cleanup tasks and
        the compositor can continue shutting down.

        The compositor is not obliged to honor any hold request or wait till
        the client has resent the signal with the proceed enum before shutting
        down the connection. If the compositor overrules the hold, it should
        resend the shutdown event to holding clients.

        Holding the shutdown is not meant as a method to block for a prolonged
        time, but only for a few seconds. Compositors are encouraged to
        overrule any client holding the shutdown longer than 3-4 seconds.
      </description>
      <arg name="desire" type="uint" enum="desire"
           summary="indicate desire to proceed or hold"/>
    </request>

    <event name="shutdown">
      <description summary="server shutdown, ask for restoration">
        Announces that the compositor will shutdown the server soon and that it
        wants to restore the client on next server start with current surface
        states being recoverable.

        If there are registered clients for shutdown suspension after this
        event has been sent the compositor is expected to stall the killing of
        client connections for one second in order to give registered clients
        the opportunity to send back their ack requests.
      </description>
    </event>
  </interface>

  <interface name="zxdg_session_surface" version="1">
    <description summary="track a surface for future restoration">
      Provides means to control the restoration of an xdg_toplevel through the
      provided surface_id in combination with the client_id received by the
      zxdg_session_suspension object.

      If the associated xdg_toplevel is destroyed while this object persists
      and no suspension state is active the object becomes defunct and no
      restoration information is memorized by the compositor about the
      surface. The client should destroy the object.
    </description>

    <request name="destroy" type="destructor">
      <description summary="destroy the session object">
        Used by the client to notify the server that it will no longer use this
        object. If no suspension state is set while this request is sent, no
        restoration data about this surface is memorized by the compositor.
      </description>
    </request>

    <event name="registered">
      <description summary="surface state being tracked">
        Informs the client under normal cirumstances that the xdg_toplevel
        surface state will be tracked from now on. For that it provides the
        unique identifier surface_id, which the client is supposed to use on
        next launch in order to restore the surface state with the
        restore_surface request.

        But the compositor might at any time decide to ultimately no longer
        track this surface. In this case the compositor will send the event
        with surface_id 0. The client should destroy the object afterwards.
      </description>
      <arg name="surface_id" type="uint"
           summary="unique id for later restore"/>
    </event>
  </interface>
</protocol>


More information about the wayland-devel mailing list