6.4. Introduction on using the Client APIs

6.4.1. Thread Safety

Thread safety is different between APIs:

  • When creating and configuring an LN client, this should always be done from a single thread.

  • The C and the C++ API is able to access the same port or service safely from multiple threads. In other words, the communication routines like port::read() and port::write are fully thread-safe.

  • In Python, any object of the LN client library, including port, packet and service objects, should normally only be accessed from a single thread. If access from multiple threads is needed, such objects should always be protected by a lock. To avoid the difficulties that are inherent with locking, it is often a better idea to separate the program into different processes which communicate via LN messages.

  • also, it is not possible to use os.fork() or fork() in an LN client.

6.4.2. Python

class ln.client(client_name, args)
class ln.client(client_name, ln_manager=None)

6.4.2.1. Using the direct API for calling LN Services in Python

6.4.2.2. Using the direct API for calling LN services in C++

6.4.2.3. Service Clients: Wrapper Class and Service Objects

For the client, as we have seen in our service client example, the approach is similar, with the difference that we need to get a service client handle and to use methods and data members from that service client object. This works as follows:

First, we derive a class from the API class services_wrapper. Then, we create a new client object, and initialize our derived class with that object as a parameter:

class ElevatorServiceClient(ln.services_wrapper):
    def __init__(self):
        self.clnt = ln.client("elevator_service_client", sys.argv)
        ln.services_wrapper.__init__(self, self.clnt, "elevator/elevator_service")

6.4.2.3.1. Client Objects

6.4.2.3.1.1. Methods: get_service()

The client object, that we already described, has a method which we did not use so far, for the purpose of service communication. It is called services_wrapper.get_service(). By calling this method, we can create a new service client object:

elevator_svc = self.clnt.get_service("elevator/elevator_service.request_elevator",
                                  "elevator/elevator_service/elevator_call")

This object is best stored as a class member, so that it does not need to be created repeatedly.

Again, the first parameter of the services_wrapper.get_service() call is the name of the method of the service provider which we are going to call. The second name is the name of the message definition which is going to be used for the call.

6.4.2.3.2. Service Objects

A service client handle can be used in a very similar way to the port objects which we saw above. Its counterpart is a service provider handle (which has technically the same type as the client handle, but is configured differently).

6.4.2.3.2.1. Members

The service object has two data members, service.req and service.response, corresponding to the “request” and the “response” part of the service message definition.

6.4.2.3.2.2. Methods

The only method of the service object which we need here is the service.call() method.

The client object can be used as follows:

elevator_svc.req.requested_floor = request_floor
elevator_svc.call()
response = elevator_svc.resp
  1. First, the request input parameters are assigned to the service.req members, one for each element or member that the request part of the service message definition has.

  2. Second, the service.call() method is invoked. This method blocks until the service call has returned and yielded a result.

  3. Finally, the result of the call can be read from the members of the service.response parameters of the service.

See also

Client Objects in the reference.

6.4.3. using the C/C++ binding

for C/C++ programs you need to include the “ln.h” header-file:

#include <ln/ln.h>

and you need to link against the libln-library. for this to work you need to tell your compiler and linker where to find those. in case of gcc/g++ it could look like this:

# compiling:
$ g++ -o my_program.o -c my_program.cpp -I${PREFIX}/include
# linking:
$ g++ -o my_program my_program.o -L${PREFIX}/lib/sled11-x86-gcc4.x -lln

6.4.3.1. ln_client constructor

c

/* C */
int ln_init(ln_client* clnt, const char* client_name,
                int argc, char** argv);
int ln_init_to_manager(ln_client* clnt, const char* client_name,
                       const char* ln_manager);

C++

ln::client(std::string client_name);
ln::client(std::string client_name, int argc, char* argv[]);
ln::client(std::string client_name, std::string ln_manager);
ln::client(ln_client clnt);