Kinx Library - Process
Tags: ruby, javascript, kinx, process
Published: 2020-07-12 12:04:25

The script language [Kinx](https://github.com/Kray-G/kinx) is published with the concept of **Looks like JavaScript, Feels like Ruby, Stable like AC/DC(?)**.

I did introduce the `Kinx` at [this post](https://dev.to/krayg/kinx-as-a-script-language-4iko) before, but I feel documents are too few for a lot of people to know how to use it. So I decided I should post some information about Kinx. Especially for undocumented things on [README.md](https://github.com/Kray-G/kinx), [QuickReference.md](https://github.com/Kray-G/kinx/blob/master/doc/QuickReference.md), or [Kinx Specification](https://github.com/Kray-G/kinx/tree/master/doc/spec).

Of course, although I don't know who need it, I also decided I don't care about it. I will be glad if there is one person who is waiting for this.

Anyway, it is `Process` this time. It is because this will be useful in practice but it is not documented yet.

* Reference
    * First motivation ... [The post of introduction](https://dev.to/krayg/kinx-as-a-script-language-4iko)
        * Kinx, I wanted a scripting language with C style syntax.
    * Repository ... https://github.com/Kray-G/kinx
        * I am waiting for pull requests.

# Child Process in Kinx

## Process class

### using Process

The Process library is not built-in, so it is explicitly loaded using the `using` directive.

```javascript
using Process;
```

### Exec

Create a Process object with `new Process(command, opts)`. The argument is a command name and an array of arguments, or a command string. In the case of an array, it is like passing arguments separately, and in the case of a command line string, it is analyzed internally and automatically decomposed into an array format.

* Array: for example, `["ls", "-1"]`.
* String: for example, `"ls -1"`.

The created process object has the following methods.

|   Method   |                                           Overview                                            |
| ---------- | --------------------------------------------------------------------------------------------- |
| `run()`    | Starts the process.                                                                           |
| `launch()` | Starts the process and detaches it.                                                           |
| `std()`    | Returns the options passed as arguments. <br/>`{ in: opts.in, out: opts.out, err: opts.err }` |

Not done yet only with created a new object. It is started when `run()` or `launch()` is called. `run()` returns an object of the `ProcessController` class to control the child process.

#### Run

By doing `run()`, an instance of `ProcessController` class is returned.

```javascript
var p = new Process(["cmd","arg1"]).run();
```

#### Launch

`launch()` returns nothing, which means it returns null. This is the method of not taking care of the child after starting.

```javascript
new Process(["cmd","arg1"]).launch();
```

#### ProcessController

The `ProcessController` class instance returned by `run()` has the following methods.

|   Method    |                                                    Outline                                                      |
| ----------- | --------------------------------------------------------------------------------------------------------------- |
| `isAlive()` | true if the process is alive, false if it has already exited, or false after being `detach`                     |
| `wait()`    | Waits for the end of the process, and then returns the exit code of the process. After `detach`, 0 is returned. |
| `detach()`  | Detaches the process.                                                                                           |

`detach()` is to detach the process after starting it. On Linux, the operation is a little different from when detached with `launch()`, but what to be done is the same. On Windows, the internal operation is absolutely the same.

On Linux, it is detached by the so-called **double-fork** method in order to detach it at process startup, but this can only be used at process startup. It is practically impossible to detach it after the process is started, and the child will survive as a zombie unless it is properly `wait` or `waitpid` in the parent process.

So, right after doing `detach()`, Kinx starts a thread just for `waitpid` and takes care of the child until death.

By the way, double-fork on Linux is...

* When the parent process dies, the child process will be connected to the init process and the init process will do `wait` for that child.

By using the functionality of the init above, you can fork again from the process that was once forked, then by quickly terminating the first forked process and let init manage the grandchild process.

The top parent process must not forget the `waitpid` for the child that first forked. Only the grandchild is the target what you let the init process take care of. Of course Kinx is properly doing it, so you don't have to care about those.

### Wait

An example of waiting for the end and acquiring the end code is as follows.

```javascript
var p = new Process(["cmd", "arg1"]).run();
var status = p.wait();
```

If you are doing `detach`, you cannot get it, then 0 is returned.

### Detach

This is the `detach` but I have already described above. The process can also be detached by `detach` after starting the process. If detached successfully, there becomes nothing every relationship between the process and the child. You don't have to do `wait` and wait for the end. Or rather, you can't do it even if you want to care.

```javascript
var p = new Process(["cmd", "arg1"]).run();
p.detach();
```

### Pipe

This section it is about the pipe everybody wants to use. The main purpose of making `Process` is a pipe. The most desired function is that you can freely connect standard input/output with child processes by a pipe to exchange information.

Specify the pipe with `opts` of `new Process(cmd, opts)`. The following three types of parameters are available.

| Parameter |                                                   Outline                                                   |
| --------- | ----------------------------------------------------------------------------------------------------------- |
| `in`      | Specify standard input. <br /> Can specify a pipe object, character string, or `$stdin`                  |
| `out`     | Specify standard output. <br /> Can specify a pipe object, string, `$stdout`, or `$stderr`             |
| `err`     | Specifies the standard error output. <br /> Can specify a pipe object, string, `$stdout`, or `$stderr` |

* Pipe object ... An object for using a pipe. Details will be described later.
* Character string ... Input source, output destination file as file name.
* `$stdin`, `$stdout`, `$stderr` ... Bind the input source and output destination of the child process to the standard input/output of this proocess.

#### Pipe object

Create a pipe object by `new Pipe()`, and it returns an array of two objects, `[Read, Write]`. The pipe object has the following methods for each read and write.

Normally, specify the `Write` pipe as the `out` or `err` of the child process, and read from the `Read` pipe.

##### Read Pipe

Do not close the pipe before doing `run()` because it is set after doing `run()`.

|  Method   |                                             Outline                                              |
| --------- | ------------------------------------------------------------------------------------------------ |
| `peek()`  | Returns 0 if there is no data in the pipe, and a number greater than 0 if there. -1 is an error. |
| `read()`  | Gets all pipe data as a string. If there is no data, it returns an empty string.                 |
| `close()` | Closes the pipe.                                                                                 |

##### Write Pipe

Do not close the pipe before doing `run()` because it is set after doing `run()`.

|    Method     |                                            Outline                                            |
| ------------- | --------------------------------------------------------------------------------------------- |
| `write(data)` | Writes data to the pipe. When not all can be written, the number of written bytes will be returned. |
| `close()`     | Closes the pipe.                                                                              |

##### Example

The general form is as follows.

```javascript
using Process;

var [r1, w1] = new Pipe();
var p1 = new Process([ "ls", "-1" ], { out: w1 }).run();
w1.close(); // You can close it as it is no longer used
while (p1.isAlive() || r1.peek() > 0) {
    var buf = r1.read();
    if (buf.length() < 0) {
        System.println("Error...");
        return -1;
    } else if (buf.length() > 0) {
        System.print(buf);
    } else {
        // System.println("no input...");
    }
}
System.println("");
```

When using Write Pipe on the parent process side, it looks like this.

```javascript
using Process;

// stdin read from pipe and output to standard output
[r1, w1] = new Pipe();
var p1 = new Process("cat", { in: r1, out: $stdout }).run();
r1.close(); // You can close it as it is no longer used

// send to stdin of p1
var nwrite = w1.write("Message\n");
w1.close(); // Pipe close, transmission end

p1.wait();
```

By the way, this way you can control the standard output and standard error output.

```javascript
new Process("cmd", { out: $stdout, err: $stdout }); // merge standard error output to standard output
new Process("cmd", { out: $stderr, err: $stderr }); // join standard output to standard error output
new Process("cmd", { out: $stderr, err: $stdout }); // swap
```

### Pipeline

Since connecting pipes is a rather troublesome work (in other words, which one is `read`...?), I also defined a `Process.pipeline` that does all at once. Put a callback function at the end and use it as follows.

```javascript
var r = Process.pipeline(cmd1, cmd2, cmd3/* , ... */) { &(i, o, pipeline):
    // i ... write pipe to stdin for first command
    // o ... read pipe from stdout of last command
    // pipeline ... pipeline object
    //    pipeline.input ............. same as i above
    //    pipeline.output ............ same as o above
    //    pipeline.peek() ............ same as pipeline.output.peek()
    //    pipeline.read() ............ same as pipeline.output.read()
    //    same as pipeline.write() ... pipeline.input.write()
    //    pipeline.isAlive() ......... true if any process in the pipeline is alive
    //    pipeline.wait() ............ waits for all processes in the pipeline to complete,
    //                                 return the exit code as an array

    // The return value of the callback becomes the return value of Process.pipeline() as it is.
    return pipeline.wait();
};
```

The last block is a callback function. This can be included in the argument as the last parameter as follows. But I added this separated syntax by a recent requests.

```javascript
// Kinx has prepared following syntax
//   if you use a lambda in function call.
// This means a function object including lambda
//   can be passed by arguments.
// A function object can be set not only to
//   the last argument but also to any place in arguments.

var r = Process.pipeline(cmd1, cmd2, cmd3/* , ... */, &(i, o, pipeline) => {
    ...
});

// or

var r = Process.pipeline(cmd1, cmd2, cmd3/* , ... */, function(i, o, pipeline) {
    ...
});

// Recently, Kinx supports a block as a function object
//   if the last argument is a function.
// This block syntax can be put outside
//   function call argument list.

var r = Process.pipeline(cmd1, cmd2, cmd3/* , ... */) { (i, o, pipeline):
    ...
};
```

It can be used even without calling back.

```javascript
var pipeline = Process.pipeline(cmd1, cmd2, cmd3 /* , ... */);
// pipeline ... pipeline object
// omitted below.
```

# Conclusion

It is a good to use the child process by the script as a unified manner because the way of dealing with it is different between Windows and Linux. However, the commands themselves are different. I'm a Windows user, but I use [UnxUtils](https://en.wikipedia.org/wiki/UnxUtils) to allow some Unix commands to be available at the command prompt as well. (I don't like Cygwin because it changes the environment...)

So, see you next time, if someone waits for me.