===================================== Introduction on using the Client APIs ===================================== .. index:: pair: thread-safety of APIs; user guide single: C++ API; thread safety single: C API; thread safety single: Python API; thread safety single: fork() (C library function) single: os.fork() (Python library function) 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. Python ====== .. code:: python class ln.client(client_name, args) class ln.client(client_name, ln_manager=None) .. _guide/api/python/services/direct_api: Using the direct API for calling LN Services in Python ------------------------------------------------------ .. _guide/api/cpp/services/direct_api: Using the direct API for calling LN services in C++ --------------------------------------------------- Service Clients: Wrapper Class and Service Objects -------------------------------------------------- For the client, as we have seen in :ref:`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 :class:`services_wrapper`. Then, we create a new client object, and initialize our derived class with that object as a parameter: .. sourcecode:: python 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") Client Objects .............. .. index:: pair: client.get_service(); Python API summary 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 :meth:`services_wrapper.get_service()`. By calling this method, we can create a new :class:`service` client object: .. sourcecode:: python 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 :meth:`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. .. index:: pair: service client handle; Python API summary() Service Objects ............... A :class:`service` client handle can be used in a very similar way to the :class:`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). Members ^^^^^^^ The service object has two data members, :attr:`service.req` and :attr:`service.response`, corresponding to the "request" and the "response" part of the service message definition. .. index:: pair: service.call; Python API summary pair: service.req; Python API summary pair: service.response; Python API summary Methods ^^^^^^^ The only method of the service object which we need here is the :meth:`service.call` method. The client object can be used as follows: .. sourcecode:: python elevator_svc.req.requested_floor = request_floor elevator_svc.call() response = elevator_svc.resp 1. First, the request input parameters are assigned to the :attr:`service.req` members, one for each element or member that the request part of the service message definition has. 2. Second, the :meth:`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 :attr:`service.response` parameters of the service. .. seealso:: :ref:`reference/python/service/client_objects` in the reference. using the C/C++ binding ======================= for C/C++ programs you need to include the "ln.h" header-file: .. code:: c #include 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: .. code:: bash # 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 ln_client constructor --------------------- **c** .. code:: 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++** .. code:: cpp 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);