aboutsummaryrefslogtreecommitdiffstats
path: root/system/doc/tutorial/c_port.xmlsrc
blob: 3c3bc48044fb4e127cb3309f10be0e8ecca035ce (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE chapter SYSTEM "chapter.dtd">

<chapter>
  <header>
    <copyright>
      <year>2000</year><year>2015</year>
      <holder>Ericsson AB. All Rights Reserved.</holder>
    </copyright>
    <legalnotice>
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
      You may obtain a copy of the License at
 
          http://www.apache.org/licenses/LICENSE-2.0

      Unless required by applicable law or agreed to in writing, software
      distributed under the License is distributed on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      See the License for the specific language governing permissions and
      limitations under the License.
    
    </legalnotice>

    <title>Ports</title>
    <prepared></prepared>
    <docno></docno>
    <date></date>
    <rev></rev>
    <file>c_port.xml</file>
  </header>
  <p>This section outlines an example of how to solve the example
    problem in the <seealso marker="example">previous section</seealso>
    by using a port.</p>
  <p>The scenario is illustrated in the following figure:</p>
  <image file="../tutorial/port.gif">
    <icaption>Port Communication</icaption>
  </image>

  <section>
    <title>Erlang Program</title>
    <p>All communication between Erlang and C must be established by
      creating the port. The Erlang process that creates a port is
      said to be <em>the connected process</em> of the port. All
      communication to and from the port must go through the connected
      process. If the connected process terminates, the port also
      terminates (and the external program, if it is written
      properly).</p>
    <p>The port is created using the BIF <c>open_port/2</c> with
      <c>{spawn,ExtPrg}</c> as the first argument. The string
      <c>ExtPrg</c> is the name of the external program, including any
      command line arguments. The second argument is a list of
      options, in this case only <c>{packet,2}</c>. This option says
      that a 2 byte length indicator is to be used to simplify the
      communication between C and Erlang. The Erlang port
      automatically adds the length indicator, but this must be done
      explicitly in the external C program.</p>
    <p>The process is also set to trap exits, which enables detection
      of failure of the external program:</p>
    <pre>
-module(complex1).
-export([start/1, init/1]).

start(ExtPrg) ->
  spawn(?MODULE, init, [ExtPrg]).

init(ExtPrg) ->
  register(complex, self()),
  process_flag(trap_exit, true),
  Port = open_port({spawn, ExtPrg}, [{packet, 2}]),
  loop(Port).</pre>
    <p>Now <c>complex1:foo/1</c> and <c>complex1:bar/1</c> can be
      implemented. Both send a message to the <c>complex</c> process
      and receive the following replies:</p>
    <pre>
foo(X) ->
  call_port({foo, X}).
bar(Y) ->
  call_port({bar, Y}).

call_port(Msg) ->
  complex ! {call, self(), Msg},
  receive
    {complex, Result} ->
      Result
  end.</pre>
    <p>The <c>complex</c> process does the following:</p>
    <list type="bulleted">
       <item>Encodes the message into a sequence of bytes.</item>
       <item>Sends it to the port.</item>
       <item>Waits for a reply.</item>
       <item>Decodes the reply.</item>
       <item>Sends it back to the caller:</item>
     </list>
    <pre>
loop(Port) ->
  receive
    {call, Caller, Msg} ->
      Port ! {self(), {command, encode(Msg)}},
      receive
        {Port, {data, Data}} ->
          Caller ! {complex, decode(Data)}
      end,
      loop(Port)
  end.</pre>
    <p>Assuming that both the arguments and the results from the C
      functions are less than 256, a simple encoding/decoding scheme
      is employed. In this scheme, <c>foo</c> is represented by byte
      1, <c>bar</c> is represented by 2, and the argument/result is
      represented by a single byte as well:</p>
    <pre>
encode({foo, X}) -> [1, X];
encode({bar, Y}) -> [2, Y].

decode([Int]) -> Int.</pre>
    <p>The resulting Erlang program, including functionality for
      stopping the port and detecting port failures, is as follows:
      </p>
      <codeinclude file="complex1.erl" type="erl"/>
  </section>

  <section>
    <title>C Program</title>
    <p>On the C side, it is necessary to write functions for receiving
      and sending data with 2 byte length indicators from/to Erlang.
      By default, the C program is to read from standard input (file
      descriptor 0) and write to standard output (file descriptor 1).
      Examples of such functions, <c>read_cmd/1</c> and
      <c>write_cmd/2</c>, follows:</p>
      <codeinclude file="erl_comm.c" type="erl"/>
    <p>Notice that <c>stdin</c> and <c>stdout</c> are for buffered
      input/output and must <em>not</em> be used for the communication
      with Erlang.</p>
    <p>In the <c>main</c> function, the C program is to listen for a
      message from Erlang and, according to the selected
      encoding/decoding scheme, use the first byte to determine which
      function to call and the second byte as argument to the
      function. The result of calling the function is then to be sent
      back to Erlang:</p>
    <codeinclude file="port.c" tag="" type="none"></codeinclude>
    <p>Notice that the C program is in a <c>while</c>-loop, checking
      for the return value of <c>read_cmd/1</c>. This is because the C
      program must detect when the port closes and terminates.</p>
  </section>

  <section>
    <title>Running the Example</title>
    <p><em>Step 1.</em> Compile the C code:</p>
    <pre>
unix> <input>gcc -o extprg complex.c erl_comm.c port.c</input></pre>
    <p><em>Step 2.</em> Start Erlang and compile the Erlang code:</p>
    <pre>
unix> <input>erl</input>
Erlang (BEAM) emulator version 4.9.1.2

Eshell V4.9.1.2 (abort with ^G)
1> <input>c(complex1).</input>
{ok,complex1}</pre>
    <p><em>Step 3.</em> Run the example:</p>
    <pre>
2> <input>complex1:start("extprg").</input>
&lt;0.34.0>
3> <input>complex1:foo(3).</input>
4
4> <input>complex1:bar(5).</input>
10
5> <input>complex1:stop().</input>
stop</pre>
  </section>
</chapter>