-- MichaelReese - 30 Sep 2016

Saftlib is constructed around the DBus IPC-system. In order to maintain and develop the library, a fairly good understanding of the DBus is required. This page provides a collection of information about DBus.

Collection of documents describing basic concepts of DBus:
DBus basic C-API

The lowest level of DBus implementation is the C-API. This is rarely used directly but via some higher level wrappers in various languages. It ist still useful to look at the DBus API Design Guides: https://dbus.freedesktop.org/doc/dbus-api-design.html

GLib

As part of the GNOME project and the GTK+ GUI, supportive C libraries are developed:
  • GObject provides a set of conventions to allow object oriented programming (OOP) in the C-language
  • Glib provides useful high-level data types like linked lists, hash tables, trees, ...
  • Gio provides abstractions for input/output for files, streams, sockets, .... Among these are also high-level C-bindings ( GDBus ) to the low-level DBus API.

Gio::DBus (Glibmm)

There are C++ wrappers around the Glib libraries. They have the suffix "mm"
  • GTK+ -> Gtkmm
  • Glib -> Glibmm
  • Gio -> contained inside Glibmm as namespace Gio, which in turn contains the namespace Gio::DBus
The DBus binding that is used in Saftlib is Gio::GDbus of Glibmm. This C++ wrapper is not extensively documented. However, there is some documentation about the wrapped GDBus high-level C-API.

A minimal program using Gio::DBus to make a method call over DBus to the saflib daemon looks like this (compile with `pkg-config --cflags --libs giomm-2.4 glibmm-2.4` ):
#include <giomm.h>
#include <glibmm.h>
#include <iostream>

int main(int, char**) { 
  Gio::init(); 
  // Get the bus connection (saft deamon is on the system bus).
  auto connection = Gio::DBus::Connection::get_sync(Gio::DBus::BUS_TYPE_SYSTEM); 
  Glib::RefPtr<Gio::Cancellable> cancellable;
  // Create a proxy object to the bus that can redirect method calls. 
  Glib::RefPtr<Gio::DBus::Proxy> proxy = Gio::DBus::Proxy::create_sync(
      connection, 
      "de.gsi.saftlib",                       // service name
      "/de/gsi/saftlib",                      // object path
      "org.freedesktop.DBus.Introspectable",  // interface name
      cancellable); 
  // Do the method call.
  Glib::VariantContainerBase result = proxy->call_sync("Introspect", cancellable);  
  // Get and output the result.
  Glib::Variant< Glib::ustring > introspection_str; 
  result.get_child(introspection_str); 
  std::cout << "answer is " << introspection_str.get() << std::endl; 
  return 0; 
}

Saftlib simplifies the access to Dbus services (only the ones provided by the SAFTd deamon) by hiding The Gio::DBus interface inside multiple C++ classes that are automatically generated from an XML description of the interface (XXX in the following). The user of saftlib is calling methods of an object of type XXX_Proxy, and the generated saftlib classes redirect the method call to a class XXX where the service is actually implemented. The Proxy object in one process represents functionality that is provided in another process. The generated classes are
  • iXXX
  • iXXX_Proxy
  • iXXX_Service
  • XXX_Proxy
  • XXX_Service
In order to extend the functionality of saftlib (new Services and corresponding Proxy classes), one has to
  • provide a new XML description in the file interfaces/XXX.xml
  • implement the XXX class in files drivers/XXX.h and drivers/XXX.cpp
  • add src/XXX.cpp and src/XXX.h to the saftd_SOURCES target in the toplevel Makefile.am
  • add XXX.xml to the NODES target in interfaces/Makefile.am

In order to extend saftlib to support new types (e.g. file descriptors) the code generation capabilities of saftlib have to be extended. The code generation of saftlib uses XSLT (Extensible Stylesheet Language Transformations).

An overview of the class structure that is created by the XSLT code generator is given in this picture ( saftlib_generated_classes.pdf ) where a device with name "Dice" is described. User code is inside red boxes, generated code is inside blue boxes, Library classes are inside the cloud shaped area.
Passing arrays via file descriptors

a very simple way of passing an array between two processes is using a unix pipe, i.e. a pair of two file descriptors (int fd[2]) where one end (fd[1]) is used for writing data and the other end(fd[0]) is used for reading data. An array of arbitrary size can be transferred through the pipe by the following procedure:
  1. sending the number of array entries
  2. then a block of data representing the array data
writing process reading process
vector<int> data(10000,42);
int size = data.size();
int fd[2];
pipe(fd); // pipe = 2 file
          // descriptors.
          // fd[0] for reading, fd[1] for writing
write(fd[1], &size, sizeof(size));

write(fd[1], &data[0], size*sizeof(data[0]));
vector<int> data;
int size = 0;
int fd[2] = ... ;
   // fd has to refer to same pipe
   //    != same integer value

read(fd[0], &size, sizeof(size));
data.resize(size);
read(fd[0], &data[0], size*sizeof(data[0]));
DBus can be used to transfer the file descriptors of the pipe from the process that created it to the communication partner.

File descriptors in DBus

File descriptors are not part of the DBus interface which can be deduced from the XML-introspection string. That means, using File descriptors for array transfer in a DBus function will make it impossible for arbitrary processes to call this function without knowing the (hidden) details of its implementation. In the case of Saftlib this restriction doesn't matter because all Saftlib DBus clients and service processes are part of the same code base.

A minimalistic example of two programs that exchange large arrays over a pipe (pipe filedescriptors are sent via Dbus) using Glibmm can be found in the attachments: client_read.cpp, server_write.cpp. These programs can be compiled with this Makefile. The program "client_read.cpp" also makes performance measuremnts of the pipe mechanism and the normal DBus array transfer.

Asynchronous DBus calls

The final implementation inside Saftlib is similar to the implementation in the simple example, but not identical. Array data needs to be transferred in both directions (from the proxy to the service and back). DBus supports asynchronous function calls that allow to do this. The mechanism is expalined in these slides: UnixFileDescriptorArrayCopyInSaftlib.pdf

Here is a minimalistic example that represents the saftlib implementation: client.cpp server.cpp pipe_transfer.h makefile

That mechanism in available in Saftlib through a new type "A" in the XML interface description. This type is only supported as function/method parameter type and return value type, not as singal or property type.
<method name="ArrayTest">
  <arg ... type="au"/>
  <arg ... type="au"/>
</method>
<method name="FDPipeTest">
  <arg ... type="Au"/>
  <arg ... type="Au"/>
</method>

Support for nested arrays

Support for nested vectors (i.e. type="AAAu") is realized by C++ template specialization: If the argument is a vector of vectors, the recursive version of the function is called, otherwise the non-recursive one. See below for the writing part (the reading part is quite similar)

template<typename T>
void write_vector_to_pipe(int fd, const T & std_vector) {
  guint32 size = std_vector.size();
  write(fd, &size, sizeof(guint32));
  write(fd, &std_vector[0], size*sizeof(decltype(std_vector.back())));
}
template<typename T>
void write_vector_to_pipe(int fd, const std::vector< std::vector<T, std::allocator<T> >, std::allocator< std::vector<T, std::allocator<T> > > >& std_vector_vector) {
  guint32 size = std_vector_vector.size();
  write(fd, &size, sizeof(guint32));
  for (guint32 i = 0; i < size; ++i) {
    write_vector_to_pipe(fd, std_vector_vector[i]);
  }
}

Providing raw file descriptors to users

The mechanism of passing arrays via file descriptor is completely hidden from the Saftlib user behind the data type "A" that does: open pipe and transfer file descriptors, transfer data, close file descriptors. In some cases, users might want to use a file descriptor themselves in order to accelerate data transfer even more. For exmaple, if data has to be passed to a Saftlib service very often, file descriptors could be left open and reused for every transfer. The start of data transfer could be indicated to the service by a DBus signal.

The interface for file descriptor exchange uses the method parameter type "h" and direction "in" (direction "out" not implemented yet). According to the DBus specification, this is an "Unsigned 32-bit integer representing an index into an out-of-band array of file descriptors, transferred via some platform-specific mechanism (mnemonic: h for handle)". This type was not really supported by Saftlib so far (it just transferred an int that is not usable as file descriptor). Now type "h" will transfer an integer that is a usable filed descriptor. Note, that a method parameter of type "h" does not appear in the DBus introspection string (same as with type "A"). Type "h" parameters can be used to pass one or more file descriptors (e.g. the ends of a pipe or a shared memory area) to the service at initializaion time. The service can keep these descriptors open and use them to do data transfer. It is the responsability of the author of the service driver class to manage data transfer and lifetime of these file descriptors.

<method name="...">
  <arg direction = "in" name="shm"  type="h"/>
  ...
</method>
I Attachment Action Size Date WhoSorted ascending Comment
MakefileEXT Makefile manage 118 bytes 15 Sep 2017 - 09:10 MichaelReese compile client_read and server_write example
UnixFileDescriptorArrayCopyInSaftlib.pdfpdf UnixFileDescriptorArrayCopyInSaftlib.pdf manage 1 MB 02 Jan 2017 - 15:08 MichaelReese explains how array copy in Saftlib works by using a unix pipe and asynchronous DBus function calls
client.cppcpp client.cpp manage 3 K 17 May 2017 - 07:27 MichaelReese  
client_read.cppcpp client_read.cpp manage 5 K 02 Jan 2017 - 14:54 MichaelReese minimalistic example of how to send file descriptors over DBus and use them to send large amounts of data
makefileEXT makefile manage 114 bytes 04 May 2017 - 16:13 MichaelReese  
pipe_transfer.hh pipe_transfer.h manage 1 K 04 May 2017 - 16:12 MichaelReese  
saftlib_generated_classes.pdfpdf saftlib_generated_classes.pdf manage 24 K 02 Jan 2017 - 10:31 MichaelReese  
server.cppcpp server.cpp manage 4 K 04 May 2017 - 16:12 MichaelReese  
server_write.cppcpp server_write.cpp manage 5 K 02 Jan 2017 - 14:54 MichaelReese minimalistic example of how to send file descriptors over DBus and use them to send large amounts of data
Topic revision: r16 - 15 Sep 2017, MichaelReese
This site is powered by FoswikiCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding Foswiki? Send feedback