aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ose/doc/src/ose_signals_chapter.xml
blob: bcf22595779e513270a5853c6085870e38a8b437 (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
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE chapter SYSTEM "chapter.dtd">

<chapter>
  <header>
    <copyright>
      <year>2013</year><year>2014</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>Interacting with Enea OSE</title>
    <prepared>Lukas Larsson</prepared>
    <docno></docno>
    <date>2014-01-08</date>
    <rev>A</rev>
    <file>ose_signals_chapter.xml</file>
  </header>

  <marker id="introduction"></marker>
  <section>
    <title>Introduction</title>
    <p>The main way which programs on Enea OSE interact is through the
    usage of message passing, much the same way as Erlang processes
    communicate. There are two ways in which an Erlang programmer can
    interact with the signals sent from other Enea OSE processes; either
    through the provided <c>ose</c> module, or by writing a custom linked-in
    driver. This User's Guide describes and provides examples for both
    approaches.
    </p>
  </section>

  <marker id="erlang"></marker>
  <section>
    <title>Signals in Erlang</title>
    <p>Erlang/OTP on OSE provides a erlang module called
    <seealso marker="ose:ose">ose</seealso> that can be used to interact
    with other OSE processes using message passing. The api in the module
    is very similar to the native OSE api, so for details of how the
    functions work please refer to the official OSE documenation. Below
    is an example usage of the API.
    </p>
    <code>1> P1 = ose:open("p1").
#Port&gt;0.344>
2> ose:hunt(P1,"p2").
{#Port&gt;0.344>,1}
3> P2 = ose:open("p2").
#Port&gt;0.355>
4> flush().
Shell got {mailbox_up,#Port&gt;0.344>,{#Port&gt;0.344>,1},852189}
ok
5> ose:listen(P1,[1234]).
ok
6> ose:send(P2,ose:get_id(P1),1234,&gt;&gt;"hello">>).
ok
7> flush().
Shell got {message,#Port&gt;0.344>,{852189,1245316,1234,&gt;&gt;"hello">>}}
ok</code>
  </section>

  <marker id="driver"></marker>
  <section>
    <title>Signals in a Linked-in driver</title>
    <p>
      Writing Linked-in drivers for OSE is very similar to how it is done
      for Unix/Windows. It is only the way in which the driver subscribes
      and consumed external events that is different. In Unix (and Windows)
      file descriptiors (and Event Objects) are used to select on. On OSE
      we use signals to deliver the same functionality. There are two large
      differences between a signal and an fd.
    </p>
    <p>
      In OSE it is not possible for a signal number to be a unique identifier
      for a resource in the same way as an fd is. For example; let's say we
      implement a driver that does an asynchronous hunt that uses signal
      number 1234 as the hunt_sig. If we want to be able to have multiple
      hunt ports running at the same time we have to have someway of routing
      the signal to the correct port. This is achieved by supplying a secondary
      id that can be retrieved through the meta-data or payload of the signal,
      e.g:
      <code>ErlDrvEvent event = erl_drv_ose_event_alloc(1234,port,resolver);</code>
      The event you get back from
      <seealso marker="ose_erl_driver#erl_drv_ose_event_alloc">
      erl_drv_ose_event_alloc</seealso> can then be used by
      <seealso marker="erts:erl_driver#driver_select">driver_select</seealso>
      to subscribe to signals. The first argument is just the signal number
      that we are interested in. The second is the id that we choose to use,
      in this case the port id that we got in the
      <seealso marker="erts:driver_entry#start">start</seealso> callback is
      used. The third argument is a function pointer to a function that can
      be used to figure out the id from a given signal. The fourth argument can
      point to any additional data you might want to associate with the event.
      There is a complete. You can examine the data contained in the event with
      <seealso marker="ose_erl_driver#erl_drv_ose_event_fetch">erl_drv_ose_event_fetch</seealso>
      , eg:
      <code>erl_drv_ose_event_fetch(event, &amp;signal, &amp;port, (void **)&amp;extra);</code>
      example of what this could look like in
      <seealso marker="#example">the next section</seealso>.
      <note>It is very important to issue the driver_select call before
      any of the signals you are interested in are sent. If driver_select
      is called after the signal is sent, there is a high probability that it
      will be lost.</note>
    </p>
    <p>
      The other difference from unix is that in OSE the payload of the event
      (i.e. the signal data) is already received when the ready_output/input
      callbacks are called. This means that you access the data of a signal
      by calling <seealso marker="ose_erl_driver#erl_drv_ose_get_signal">
      erl_drv_ose_get_signal</seealso>. Additionally multiple signals might be
      associated with the event, so you should call
      <seealso marker="ose_erl_driver#erl_drv_ose_get_signal">
      erl_drv_ose_get_signal</seealso> until <c>NULL</c> is returned.
    </p>
  </section>

  <marker id="example"></marker>
  <section>
    <title>Example Linked-in driver</title>
<code>#include "erl_driver.h"
#include "ose.h"

struct huntsig {
  SIGSELECT signo;
  ErlDrvPort port;
};

union SIGNAL {
  SIGSELECT signo;
  struct huntsig;
}

/* Here we have to get the id from the signal. In this case we use the
   port id since we have control over the data structure of the signal.
   It is however possible to use anything in here. The only restriction
   is that the same id has to be used for all signals of the same number.*/
ErlDrvOseEventId resolver(union SIGNAL *sig) {
  return (ErlDrvOseEventId)sig->huntsig.port;
}

static int drv_init(void) { return 0; };

static ErlDrvData drv_start(ErlDrvPort port, char *command) {
  return (ErlDrvData)port;
}

static ErlDrvSSizeT control(ErlDrvData driver_data, unsigned int cmd,
                            char *buf, ErlDrvSizeT len,
                            char **rbuf, ErlDrvSizeT rlen) {
  ErlDrvPort port = (ErlDrvPort)driver_data;

  /* An example of extra data to associate with the event */
  char *extra_data = driver_alloc(80);
  snprintf("extra_data, "Event, sig_no: 1234, and port: %d", port);

  /* Create a new event to select on */
  ErlDrvOseEvent evt = erl_drv_ose_event_alloc(1234,port,resolver, extra_data);

  /* Make sure to do the select call _BEFORE_ the signal arrives.
     The signal might get lost if the hunt call is done before the
     select. */
  driver_select(port,evt,ERL_DRV_READ|ERL_DRV_USE,1);

  union SIGNAL *sig = alloc(sizeof(union SIGNAL),1234);
  sig->huntsig.port = port;
  hunt("testprocess",0,NULL,&amp;sig);
  return 0;
}

static void ready_input(ErlDrvData driver_data, ErlDrvEvent evt) {
  char *extra_data;
  /* Get the first signal payload from the event */
  union SIGNAL *sig = erl_drv_ose_get_signal(evt);
  ErlDrvPort port = (ErlDrvPort)driver_data;
  while (sig != NULL) {
    if (sig->signo == 1234) {
      /* Print out the string we added as the extra parameter */
      erl_drv_ose_event_fetch(evt, NULL, NULL, (void **)&amp;extra_data);
      printf("We've received: %s\n", extra_data);

      /* If it is our signal we send a message with the sender of the signal
         to the controlling erlang process */
      ErlDrvTermData reply[] = { ERL_DRV_UINT, (ErlDrvUInt)sender(&amp;sig) };
      erl_drv_send_term(port,reply,sizeof(reply) / sizeof(reply[0]));
    }

    /* Cleanup the signal and deselect on the event.
       Note that the event itself has to be free'd in the stop_select
       callback. */
    free_buf(&amp;sig);
    driver_select(port,evt,ERL_DRV_READ|ERL_DRV_USE,0);

    /* There could be more than one signal waiting in this event, so
       we have to loop until sig == NULL */
    sig = erl_drv_ose_get_signal(evt);
  }
}

static void stop_select(ErlDrvEvent event, void *reserved)
{
  /* Free the extra_data */
  erl_drv_ose_event_fetch(evt, NULL, NULL, (void **)&amp;extra_data);
  driver_free(extra_data);

  /* Free the event itself */
  erl_drv_ose_event_free(event);
}

/**
 * Setup the driver entry for the Erlang runtime
 **/
ErlDrvEntry ose_signal_driver_entry = {
  .init                         = drv_init,
  .start                        = drv_start,
  .stop                         = drv_stop,
  .ready_input                  = ready_input,
  .driver_name                  = DRIVER_NAME,
  .control                      = control,
  .extended_marker              = ERL_DRV_EXTENDED_MARKER,
  .major_version                = ERL_DRV_EXTENDED_MAJOR_VERSION,
  .minor_version                = ERL_DRV_EXTENDED_MINOR_VERSION,
  .driver_flags                 = ERL_DRV_FLAG_USE_PORT_LOCKING,
  .stop_select                  = stop_select
};
</code>
  </section>

</chapter>