[Erlang Systems]

5 Advanced IG Topics

This section describes the more advanced features of the Interface Generator and includes the following topics:

5.1 IG Generator Switches

The generator accepts a few switches which guide the stub generation process.

cback_mod
The cback_mod switch sets the name of the Erlang module where all callbacks from C will be found. For example: ig:gen(ex1, [{cback_mod, my_cb_mod}]). The name of the callback module defaults to _cb appended to the input file name.
nouse_stdio
IG stubs normally use stdin and stdout (which are file descriptors 0 and 1) for port communication, but this switch assigns file descriptors 2 and 3 instead.

5.2 Sockets

IG is capable of running its communication over sockets. Sockets provide the advantage of using a server implemented on a different machine, ports are local, and the socket implementation enforces a more stringent use of the communication protocol. IG allows the C program to behave both as a server using calls from Erlang to C, and as a client using calls from C to Erlang. This could lead to confusion in the communication if both concepts are used at the same time. We could find ourselves in a race condition where both sides simultaneously decide to initiate some function calls. Both sides would then treat calls from the other side as the return value to their call (see figure below). We need not explain why this is unlikely to produce correct behaviour.

fig3
Example of Race Condition

This problem can be solved by using two communication links between Erlang and C. One link is used for messages initiated from Erlang, and the other for messages initiated from C. However, it is difficult to have two ports to the same C program but very easy to have two sockets. Therefore, socket communication is preferred if calls will be initiated from both parties.

Socket communication requires the following additions:

The igsock.o file is included in the distribution and some examples of main loops are included in the examples directory. The new linker command line must be augmented with -lsocket -lnsl -lresolv.

Note!

There is an erl_interface_nodns library for users who have no DNS service running.

IG generated socket communication is started differently to ordinary port communication. There are two new start functions, client/2 and server/1. The former is used when the Erlang side is a client and the C side acts as a server. The latter is used when the Erlang side is a server to the C side. The client/2 function takes two arguments, the first is the host name where the C program is running, and the second is the port number at which the C program is expecting to communicate. The server/1 only takes a port number as an argument and it is up to the C program to find the correct host where the Erlang stub is running. This is all basic socket programming practice.

5.3 Socket Example

This example is based on the previous ex1 example in this section. We will add some libraries at link time and then start the Erlang stub in a different way than port start. We also have to start the C program manually from the Unix prompt. We will use two Unix shells, one for the C program and one for the Erlang program.

The first step is to make a new executable with other libraries than port operation, and with a different main loop. The igsock_server main loop is taken from the examples directory.

UNIX 1> gcc -L. -o ex1 ex1.c ex1_stub.c igio.o igsock.o
igsock_server.c -lsocket -lnsl -lresolv -lerl_interface

We now start the program with a random port number. In our case, we choose 6758 but any free port number will do.

UNIX 1> ./ex1 6758

Nothing happens at this point, because the C program waits for Erlang to connect to the port. We start Erlang in the usual way and start the IG stub as a client to the C server. We then make the usual call to foo just to make sure that it works. Note the use of net_adm:localhost/0 which returns the host name as a string. We are in fact connecting to a socket which can be on any machine in the world.

UNIX 2> erl
Erlang (JAM) emulator version 4.4.2
 
Eshell V4.4.2  (abort with ^G)
1> P = ex1:client(net_adm:localhost(), 6758).
<0.23.0>
2> ex1:foo(P, "Andrew", 55).
{ok,55}
3> ex1:stop(P).             
ok

The call to foo also results in a printout from C. In previous examples this has been printed in the Erlang shell, but this time it is printed in the Unix shell used for the C program. A printout of the type shown below appears in the Unix shell where the C program was started.

foo: name='Andrew', age=55
    

Two things are worth mentioning about this example:

5.4 Using C Macros

IG supports a convenient subset of the standard C macros and pre-processor directives. Macros can be used for declaring constant values, and pre-processor directives can be used for conditional declaration of macros. Macros and conditionals are generated to an Erlang header file and the mapping is straight forward. Therefore, the conditionals do not affect the generation of stubs because they are only generated to the Erlang header file. It is not possible to conditionally declare interface functions in header files intended for IG.

The IG pre-processor directives are only generated to the .hrl file, not the .erl file.

/*IG*/ #define <name> [<const_value>].
Puts a -define( <name>, <const_value> ). or a -define( <name>, true ). in the .hrl file. The second form is used when the constant value is omitted. The constant value can be either an integer, a boolean value, a string, or a previously defined macro name. This makes it easy to use C macro values in Erlang.
/*IG*/ #ifdef <name>.
This works like ordinary C style pre-processor conditional directives. Puts an -ifdef( <name> ). in the .hrl file. Useful for conditional declarations of macros in the Erlang header file (.hrl).
/*IG*/ #ifndef <name>
Puts an -ifndef( <name> ). in the .hrl file.
/*IG*/ #else
Puts an -else. in the .hrl file.
/*IG*/ #endif
Puts an -endif. in the .hrl file.

In the following example, we have the input file pre.h:

/*IG*/ #define FWRITE 1
/*IG*/ #define FREAD  2
FILE* open_file(char* fname, int mode); /* where mode is FWRITE or FREAD */

This will be translated to the following Erlang header file:

-define(FWRITE, 1).
-define(FREAD, 2).
    

This header file enables the Erlang programmer to write Erlang code which looks as follows:

-module(nils).
-export([n/0]).
-include("pre.hrl").
n() ->
    P = pre:start(),
    Fd = pre:file_open(P, "cake", ?FWRITE).
    

Note!

The Erlang code shown above is very similar to an equivalent C code.

5.5 IG Files

IG needs some magic files to work, namely the IO routines, the main loop and the value representation. The IO routines are contained in the file igio.o and the value representation is contained in the erl_interface library. The value representation is a C view of the Erlang values, but it is not the representation used within the emulator.

IG also comes with an example main loop in the file igmain.o. It is not mandatory because the main loop may need to be replaced when the method of communication changes . The main loop in the distribution supports port communication only but there are some examples of socket communication main loops in the examples directory. We have made a fair attempt to provide IO and main loops that fit large areas of usage, but we recognize that we cannot cover all territory.

5.6 Inside IG

This section attempts to explain how IG really works. It contains details about the implementation and process structure. The reader is encouraged to read generated stub code and the igserver.erl file for reference. This information is not a requirement for using IG correctly and can be skipped at the discretion of the reader.

When an IG generated program is set up, one process is started which is in charge of the port at the Erlang side, and a C program is started which can handle the communication on the port. The Erlang process runs a module called igserver which handles marshalling of arguments and all port communication. It is the igserver process that recognizes the standard Erlang system messages which implement the software manager functions. igserver also handles communication link details.

5.6.1 A Real Function Call

We will now follow a function call from Erlang to C. First, an IG generated interface stub function is called from a user process. Each interface function from the header file is given a number at generate time and this number is used when the stub function communicates with igserver, along with any arguments. The stub function passes a message to the igserver stating the function number and any parameters.

The igserver then forwards this request for a function call to the C side where it will first be received by the IG IO routines, decoded using the erl_interface library, and finally presented to the IG generated C stub dispatch function. The dispatcher will then extract the function number and use this number as an index in its array of function pointers. The pointers in the array point to specially generated intermediate functions, one for each stub function. These functions unpack all arguments, make the actual function call to the user function, and then pack the return value and send it back to Erlang.

The igserver then receives its answer which it sends back to the original user process. All this is pictured in the figure below.

fig4
The IG Process

Copyright © 1991-98 Ericsson Telecom AB