Skip to content

Commit

Permalink
[erts] Introduce erl_drv_command_error()
Browse files Browse the repository at this point in the history
  • Loading branch information
rickard-green committed Feb 15, 2023
1 parent d4e7154 commit 3bd8e1c
Show file tree
Hide file tree
Showing 15 changed files with 407 additions and 19 deletions.
33 changes: 33 additions & 0 deletions erts/doc/src/erl_driver.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2049,6 +2049,39 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code>
</desc>
</func>

<func>
<name since="OTP @OTP-18464@"><ret>int</ret>
<nametext>erl_drv_command_error(ErlDrvPort port,
char *error_reason)</nametext></name>
<fsummary>Generate an error exception in a port_command() call</fsummary>
<desc>
<p>
Generate an <c>error</c> exception, with the reason
<c>error_reason</c> as an atom, in a call to the
<seemfa marker="erlang#port_command/2">
<c>erlang:port_command/2</c></seemfa> BIF or the
<seemfa marker="erlang#port_command/3">
<c>erlang:port_command/3</c></seemfa> BIF. The error can only be
generated from the drivers <seecref marker="driver_entry#outputv">
<c>outputv()</c></seecref> or <seecref marker="driver_entry#output">
<c>output()</c></seecref> callbacks, which implements port command,
when the port command has been initiated via one of the synchronous
<c>port_command()</c> BIFs. When the port command has been
initiated by the asynchronous <c>Port ! {Owner, {command, Data}}</c>
construct, this call will fail and <em>no</em> exception will be
generated.
</p>
<p>
<c>error_reason</c> needs to be a null terminated latin1 string. If
the string is longer than 255 characters (excluding the null
termination character), it will be truncated to 255 characters.
</p>
<p>
Returns zero on success and a non-zero integer on failure.
</p>
</desc>
</func>

<func>
<name since=""><ret>void</ret><nametext>erl_drv_cond_broadcast(ErlDrvCond
*cnd)</nametext></name>
Expand Down
14 changes: 14 additions & 0 deletions erts/doc/src/erlang.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5655,7 +5655,16 @@ receive_replies(ReqId, N, Acc) ->
<item>
<p>If <c><anno>Data</anno></c> is an invalid I/O list.</p>
</item>
<tag><c>Error</c></tag>
<item>
<p>Where <c>Error</c> is an atom defined by the driver code
executed by the port.</p>
</item>
</taglist>
<p>Note that the <c><anno>Port</anno> ! {PortOwner, {command, Data}}</c>
construct <em>only</em> can fail with a <c>badarg</c> <c>error</c>
exception if <c>Port</c> is an atom which is not registered as a port
or a process.</p>
<warning>
<p>Do not send data to an unknown port. Any undefined behavior is
possible (including node crash) depending on how the port driver
Expand Down Expand Up @@ -5719,6 +5728,11 @@ receive_replies(ReqId, N, Acc) ->
driver of the port does not allow forcing through
a busy port.
</item>
<tag><c>Error</c></tag>
<item>
<p>Where <c>Error</c> is an atom defined by the driver code
executed by the port.</p>
</item>
</taglist>
<warning>
<p>Do not send data to an unknown port. Any undefined behavior is
Expand Down
15 changes: 11 additions & 4 deletions erts/emulator/beam/erl_bif_port.c
Original file line number Diff line number Diff line change
Expand Up @@ -194,12 +194,12 @@ BIF_RETTYPE erts_internal_port_command_3(BIF_ALIST_3)
}
else {
Eterm ref;
Eterm cmd_error = THE_NON_VALUE;
#ifdef DEBUG
ref = NIL;
#endif

switch (erts_port_output(BIF_P, flags, prt, prt->common.id,
BIF_ARG_2, &ref)) {
BIF_ARG_2, &ref, &cmd_error)) {
case ERTS_PORT_OP_BADARG:
case ERTS_PORT_OP_DROPPED:
ERTS_BIF_PREP_RET(res, am_badarg);
Expand All @@ -222,9 +222,16 @@ BIF_RETTYPE erts_internal_port_command_3(BIF_ALIST_3)
/* Signal order preserved by reply... */
BIF_RET(ref);
break;
case ERTS_PORT_OP_DONE:
ERTS_BIF_PREP_RET(res, am_true);
case ERTS_PORT_OP_DONE: {
Eterm result = am_true;
if (is_value(cmd_error)) {
Eterm *hp = HAlloc(BIF_P, 3);
ASSERT(is_atom(cmd_error));
result = TUPLE2(hp, am_error, cmd_error);
}
ERTS_BIF_PREP_RET(res, result);
break;
}
default:
ERTS_INTERNAL_ERROR("Unexpected erts_port_output() result");
break;
Expand Down
5 changes: 4 additions & 1 deletion erts/emulator/beam/erl_driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@

#define ERL_DRV_EXTENDED_MARKER (0xfeeeeeed)
#define ERL_DRV_EXTENDED_MAJOR_VERSION 3
#define ERL_DRV_EXTENDED_MINOR_VERSION 3
#define ERL_DRV_EXTENDED_MINOR_VERSION 4

/*
* The emulator will refuse to load a driver with a major version
Expand Down Expand Up @@ -622,6 +622,9 @@ EXTERN char *driver_dl_error(void);
EXTERN int erl_drv_putenv(const char *key, char *value);
EXTERN int erl_drv_getenv(const char *key, char *value, size_t *value_size);

/* port_command() synchronous error... */
EXTERN int erl_drv_command_error(ErlDrvPort dprt, char *string);

/* spawn start init ack */
EXTERN void erl_drv_init_ack(ErlDrvPort ix, ErlDrvData res);

Expand Down
5 changes: 4 additions & 1 deletion erts/emulator/beam/erl_port.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ struct _erl_drv_port {
erts_atomic_t run_queue;
erts_atomic_t connected; /* A connected process */
Eterm caller; /* Current caller. */
Eterm cmd_error;
erts_atomic_t data; /* Data associated with port. */
Uint bytes_in; /* Number of bytes read */
Uint bytes_out; /* Number of bytes written */
Expand All @@ -176,6 +177,8 @@ struct _erl_drv_port {
} *async_open_port; /* Reference used with async open port */
};

#define ERTS_PORT_CMD_ERROR_NOT_ALLOWED THE_NON_VALUE
#define ERTS_PORT_CMD_ERROR_ALLOWED NIL

void erts_init_port_data(Port *);
void erts_cleanup_port_data(Port *);
Expand Down Expand Up @@ -1006,7 +1009,7 @@ ErtsPortOpResult erts_port_command(Process *, int, Port *, Eterm, Eterm *);
/*
* Signals from processes to ports.
*/
ErtsPortOpResult erts_port_output(Process *, int, Port *, Eterm, Eterm, Eterm *);
ErtsPortOpResult erts_port_output(Process *, int, Port *, Eterm, Eterm, Eterm *, Eterm *);
ErtsPortOpResult erts_port_exit(Process *, int, Port *, Eterm, Eterm, Eterm *);
ErtsPortOpResult erts_port_connect(Process *, int, Port *, Eterm, Eterm, Eterm *);
ErtsPortOpResult erts_port_link(Process *, Port *, ErtsLink *, Eterm *);
Expand Down
Loading

0 comments on commit 3bd8e1c

Please sign in to comment.