-
Notifications
You must be signed in to change notification settings - Fork 45
Implementation
The actual work of launching external processes and communicating with them is
performed by the so called drivers: modules implementing a specified set of
functions. Functions in the Porcelain
module delegate work to the currently
selected driver's implementations.
The basic functionality that provides a saner and more convenient API on top of
ports is implemented in the Porcelain.Driver.Basic
(or just Basic
)
driver.
Additional functionality that includes circumventing the "eof bug" and sending
signals to OS processes will be implemented in the Porcelain.Driver.Goon
(or
just Goon
) driver.
The following tasks are going to be performed by all drivers, so they should be implemented in a shared module.
-
splitting the shell-like invocation into program name and a list of arguments (
Porcelain.shplit
) (see this and this for reference) -
processing and validating given options (currently partly done in the
Porcelain
module) -
defining a set of Erlang port options shared accross all drivers [done]
The receive loop collects messages from the port and dispatches them to the caller according to the chosen output channel.
To implement the async API, drivers spawn additional processes as needed. The output received from the port connected to an external process is fed immediately to the target (where the target can be a file path, a file process, or a stream) or kept in memory until requested or discarded.
This section describes the details of the pure Elixir implementation of the Porcelain API.
It serves as a light shim on top of ports. The main responsibilities of the driver are:
- building the invocation of
Port.open
- implementing a data handler to collect output from the program
The port is opened in stream mode to communicate directly with the external program.
This section describes the details of interacting with the goon
middleman
from Elixir. See below for the description of goon
's implementation.
The main responsibilities of the driver are:
-
building the invocation of
Port.open
-
performing the handshake with
goon
to select the protocol for communications and perform any other required configuration (will most likely be implemented as a set of command-line flags and arguments to thegoon
program) [to be implemented in a future release] -
implementing a data handler to collect output from
goon
-
sending signals to the managed OS process [to be implemented in a future release]
The port is opened in {:packet, 2}
mode. Then, the handshake is performed. In
case of failure, an error tuple is returned to the client. If the handshake
succeeded, the driver enters its receive loop and starts dispatching output to
the caller.
Sending signals is implemented by means of sending messages to the driver which will then forward them to the port program.
Implemented in Go, goon
provides a lightweight solution for patching some
holes in the Erlang port functionality, namely:
- being able to interact with "eof-driven" programs
- being able to send signals to OS processes
It will be detected at run time by default, but the user should be able to
override the setting: to either never use goon
or to require goon
to be
present in PATH
.
In terms of OS processes, the hierarchy when using the Goon driver will look as follows:
+-----------+
| Erlang VM |
+-----------+
||
\/
+------+
| Goon |
+------+
||
\/
+------------------+
| External program |
+------------------+
Arrows pointing down indicate the parent→child relationship between the OS processes.
The Go implementation will use packages from Go's stdlib to work with external processes in a portable way:
The program has to support the following two invocations:
goon -proto <major>.<minor> -ack <data>
goon -proto <major>.<minor> [-in] [-out] [-err <target>] [-dir <cwd>] \
<program> [<arg>...]
The options are as follows:
-
-proto
– protocol version number -
-ack
– used during Porcelain initialization to check if the protocol implementations are in sync between Elixir and Go -
-in
- boolean flag indicating whethergoon
should read stdin -
-out
- boolean flag indicating whethergoon
should write to stdout -
-err
- choose whether stderr should be redirected to stdout ("out"
) or discarded ("nil"
) -
-dir
– set current working directory for the launched program -
<program> [<arg>...]
– the invocation for the external program
The actual implementation should conform to the Goex protocol specification.