Skip to content

Commit 7504b19

Browse files
authored
Merge 941b2c1 into 2d426d7
2 parents 2d426d7 + 941b2c1 commit 7504b19

File tree

1 file changed

+143
-0
lines changed

1 file changed

+143
-0
lines changed

proposal/2-concurrency.md

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
# Proposal: Concurrency on Nash
2+
3+
There has been some discussion on how to provide concurrency to nash.
4+
There is a [discussion here](https://github.com/NeowayLabs/nash/issues/224)
5+
on how concurrency could be added as a set of built-in functions.
6+
7+
As we progressed discussing it seemed desirable to have a concurrency
8+
that enforced no sharing between concurrent functions. It eliminates
9+
races and forces all communication to happen explicitly, and the
10+
performance overhead would not be a problem to a high level language
11+
as nash.
12+
13+
## Lightweight Processes
14+
15+
This idea is inspired on Erlang concurrency model. Since Nash does
16+
not aspire to do everything that Erlang does (like distributed programming)
17+
so this is not a copy, we just take some things as inspiration.
18+
19+
Why call this a process ? On the [Erlang docs](http://erlang.org/doc/getting_started/conc_prog.html)
20+
there is a interesting definition of process:
21+
22+
```
23+
the term "process" is usually used when the threads of execution share no
24+
data with each other and the term "thread" when they share data in some way.
25+
Threads of execution in Erlang share no data,
26+
that is why they are called processes
27+
```
28+
29+
In this context the process word is used to mean a concurrent thread of
30+
execution that does not share any data. The only means of communication
31+
are through message passing. Since these processes are lightweight
32+
creating a lot of them will be cheap (at least must cheaper than
33+
OS processes).
34+
35+
Instead of using channel instances in this model you send messages
36+
to processes (actor model), it works pretty much like a networking
37+
model using UDP datagrams.
38+
39+
The idea is to leverage this as a syntactic construction of the language
40+
to make it as explicit and easy as possible to use.
41+
42+
This idea introduces 4 new concepts, 3 built-in functions and one
43+
new keyword.
44+
45+
The keyword **spawn** is used to spawn a function as a new process.
46+
The function **send** is used to send messages to a process.
47+
The function **receive** is used to receive messages from a process.
48+
The function **self** returns the pid of the process calling it.
49+
50+
An example of a simple ping/pong:
51+
52+
```
53+
pid <= spawn fn () {
54+
ping, senderpid <= receive()
55+
echo $ping
56+
send($senderpid, "pong")
57+
}()
58+
59+
send($pid, "ping", self())
60+
pong <= receive()
61+
62+
echo $pong
63+
```
64+
65+
TODO:
66+
67+
* If send is never blocking, what if process queue gets too big ? just go on until memory exhausts ?
68+
* What happens when you send to a invalid pid ? (or a pid of a process that is not running anymore)
69+
* Example on how would fan-out/fan-in look with this idea
70+
71+
## Extend rfork
72+
73+
Converging to a no shared state between concurrent functions initiated
74+
the idea of using the current rfork built-in as a means to express
75+
concurrency on Nash. This would already be possible today, the idea
76+
is just to make it even easier, specially the communication between
77+
different concurrent processes.
78+
79+
This idea enables an even greater amount of isolation between concurrent
80+
processes since rfork enables different namespaces isolation (besides memory),
81+
but it has the obvious fallback of not being very lightweight.
82+
83+
Since the idea of nash is to write simple scripts this does not seem
84+
to be a problem. If it is on the future we can create lightweight concurrent
85+
processes (green threads) that works orthogonally with rfork.
86+
87+
The prototype for the new rfork would be something like this:
88+
89+
```sh
90+
chan <= rfork [ns_param1, ns_param2] (chan) {
91+
//some code
92+
}
93+
```
94+
95+
The code on the rfork block does not have access to the
96+
lexical outer scope but it receives as a parameter a channel
97+
instance.
98+
99+
This channel instance can be used by the forked processes and
100+
by the creator of the process to communicate. We could use built-in functions:
101+
102+
```sh
103+
chan <= rfork [ns_param1, ns_param2] (chan) {
104+
cwrite($chan, "hi")
105+
}
106+
107+
a <= cread($chan)
108+
```
109+
110+
Or some syntactic extension:
111+
112+
```sh
113+
chan <= rfork [ns_param1, ns_param2] (chan) {
114+
$chan <- "hi"
115+
}
116+
117+
a <= <-$chan
118+
```
119+
120+
Since this channel is meant only to be used to communicate with
121+
the created process, it will be closed when the process exit:
122+
123+
```sh
124+
chan <= rfork [ns_param1, ns_param2] (chan) {
125+
}
126+
127+
# returns empty string when channel is closed
128+
<-$chan
129+
```
130+
131+
Fan out and fan in should be pretty trivial:
132+
133+
```sh
134+
chan1 <= rfork [ns_param1, ns_param2] (chan) {
135+
}
136+
137+
chan2 <= rfork [ns_param1, ns_param2] (chan) {
138+
}
139+
140+
# waiting for both to finish
141+
<-$chan1
142+
<-$chan2
143+
```

0 commit comments

Comments
 (0)