Why does Thread.sleep() trigger the subscription to Flux.interval()? - project-reactor

If, in a main() method, I execute this
Flux.just(1,2)
.log()
.subscribe();
I get this in the console:
[ INFO] (main) | onSubscribe([Synchronous Fuseable] FluxArray.ArraySubscription)
[ INFO] (main) | request(unbounded)
[ INFO] (main) | onNext(1)
[ INFO] (main) | onNext(2)
[ INFO] (main) | onComplete()
If instead of just() I use the interval() method:
Flux.interval(Duration.ofMillis(100))
.take(2)
.log()
.subscribe();
the elements are not logged, unless I add Thread.sleep() which gives me:
[ INFO] (main) onSubscribe(FluxTake.TakeSubscriber)
[ INFO] (main) request(unbounded)
[ INFO] (parallel-1) onNext(0)
[ INFO] (parallel-1) onNext(1)
[ INFO] (parallel-1) onComplete()
The question is: why do I require to pause a thread to actually trigger the subscription ?

You need to wait on main thread and let the execution complete. Your main thread terminates before next element is generated. Your first element is generated after 100 ms so you need to wait/block the main thread. Try this:
CountDownLatch latch = new CountDownLatch(1);
Flux.interval(Duration.ofMillis(100))
.take(2)
.doOnComplete(latch::countDown)
.log()
.subscribe();
latch.await(); // wait for doOnComplete
CountDownLatch:
A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.

Flux.interval(..) emits items on a parallel, freeing up your calling thread. This is why your program exits. What you should do is:
Flux.interval(Duration.of(duration))
.take(n)
.doOnNext(this::logElement)
.blockLast();
This will block the calling thread till doOnNext() emits the last time (which should be the second item form the upstream).

Flux.interval(Duration) produces a Flux that is infinite and emits regular ticks from a clock.
So your first example has no concurrency, everything happens on the same thread. The subscription and all events must complete before the method and process ends.
By using Flux.interval you have added concurrency and asynchronous behaviour into your example. Interval is a regular clock like a metronome. You subscribe but immediately complete and the process will usually finish before any work (onNext) is done.

Related

What is (k)ill for in the iex break menu?

I access the Break Menu of eix 1.8.2 by pressing CTRL + C. It looks like this:
BREAK: (a)bort (c)ontinue (p)roc info (i)nfo (l)oaded
(v)ersion (k)ill (D)b-tables (d)istribution
At first I assumed kill would be similar to abort (ie, just ends the session), but no. Instead, pressing k produces a core dump and offers more options:
iex(1)>
BREAK: (a)bort (c)ontinue (p)roc info (i)nfo (l)oaded
(v)ersion (k)ill (D)b-tables (d)istribution
k
Process Information
--------------------------------------------------
=proc:<0.105.0>
State: Waiting
Spawned as: erlang:apply/2
Spawned by: <0.75.0>
Message queue length: 0
Number of heap fragments: 1
Heap fragment data: 5
Link list: [{to,<0.64.0>,#Ref<0.720592203.270008322.27074>}]
Reductions: 4202
Stack+heap: 233
OldHeap: 0
Heap unused: 177
OldHeap unused: 0
BinVHeap: 1
OldBinVHeap: 0
BinVHeap unused: 46421
OldBinVHeap unused: 46422
Memory: 2804
Stack dump:
Program counter: 0x000000001f8230e0 (io:execute_request/2 + 200)
CP: 0x0000000000000000 (invalid)
arity = 0
0x000000001ddcee08 Return addr 0x000000001f8a4ba0 ('Elixir.IEx.Server':io_get/3 + 96)
y(0) #Ref<0.720592203.270008322.27074>
y(1) {false,{get_line,unicode,<<"iex(1)> ">>}}
y(2) <0.64.0>
0x000000001ddcee28 Return addr 0x000000001d53ecf8 (<terminate process normally>)
y(0) <0.105.0>
y(1) <0.75.0>
Internal State: ACT_PRIO_NORMAL | USR_PRIO_NORMAL | PRQ_PRIO_NORMAL
(k)ill (n)ext (r)eturn:
If I press k again, I get another core dump. Pressing n also gives me a core dump and I think it's the same as pressing k. The final option, r, does different things depending on what I've done previously. If I've only pressed k or n a few times, it just ignores it and I have to press enter twice. iex interprets the second enter as it normally would and returns nil.
(k)ill (n)ext (r)eturn:
r
nil
If I've pressed k and n a bunch of times, it either does this:
(k)ill (n)ext (r)eturn:
r
** (EXIT from #PID<0.104.0>) shell process exited with reason: killed
Interactive Elixir (1.8.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)>
09:39:57.929 [info] Application iex exited: killed
or this:
(k)ill (n)ext (r)eturn:
r
09:46:20.268 [info] Application iex exited: killed
09:46:20.269 [info] Application elixir exited: killed
09:46:20.274 [error] GenServer IEx.Pry terminating
** (stop) killed
Last message: {:EXIT, #PID<0.88.0>, :killed}
State: 1
or this:
(k)ill (n)ext (r)eturn:
r
Logger - error: {removed_failing_handler,'Elixir.Logger'}
Logger - error: {removed_failing_handler,'Elixir.Logger'}
Logger - error: {removed_failing_handler,'Elixir.Logger'}
I am unsure how it decides which of those messages should be displayed.
I'm really curious what (k)ill and it's suboptions do and look forward to learning about it. Any direction is appreciated, thanks!
Looking at the source code:
case 'k':
process_killer();
and
switch(j) {
case 'k':
ASSERT(erts_init_process_id != ERTS_INVALID_PID);
/* Send a 'kill' exit signal from init process */
erts_proc_sig_send_exit(NULL, erts_init_process_id,
rp->common.id, am_kill, NIL,
0);
case 'n': br = 1; break;
case 'r': return;
default: return;
}
k seems to be for enumerating and killing individual processes by sending them a kill signal. The different output is because it depends how each processes handles the signal.
The kill command goes through all running processes, and for each of them displays a bunch of information and asks you whether to:
kill it and go to the next process (k)
go to the next process without killing this one (n), or
stop killing processes and go back to the shell (r).
It might be tricky to identify the process you want to kill. One thing you can look at is the Dictionary line, which for most long-running processes has an $initial_call entry telling you which module contains the code that this process is running. For example:
Dictionary: [{'$ancestors',[<0.70.0>]},{iex_evaluator,ack},{'$initial_call',{'Elixir.IEx.Evaluator',init,4}}]
The different messages are displayed depending on which process(es) you killed. For example, it seems like Elixir.IEx.Evaluator is the process running the Elixir shell, which gives you the shell process exited with reason: killed error message.
A way of looking at this is that it shows the fault tolerance of an Elixir application: even if a process somewhere within the system has an error (in this case caused by explicitly killing the process), the supervisors try to restart the process in question and keep the entire system running.
Actually, I've never used this way of killing processes in a running system. If you know the process id ("pid") of the process you want to kill, you can type something like this into the shell:
Process.exit(pid("0.10.0"), :kill)
without having to step through the list of processes.

correct usage of erlang spawn_monitor

Still working through Joe's book, and having hard time fully understanding monitors in general and spawn_monitor in particular. Here's the code I have; the exercise is asking to write a function that will start a process whose job is to print a heartbeat every 5 seconds, and then a function to monitor the above process and restart it. I didn't get to a restart part, because my monitor fails to even detect the process keeling over.
% simple "working" loop
loop_5_print() ->
receive
after 5000 ->
io:format("I'm still alive~n"),
loop_5_print()
end.
% function to spawn and register a named worker
create_reg_keep_alive(Name) when not is_atom(Name) ->
{error, badargs};
create_reg_keep_alive(Name) ->
Pid = spawn(ex, loop_5_print, []),
register(Name, Pid),
{Pid, Name}.
% a simple monitor loop
monitor_loop(AName) ->
Pid = whereis(AName),
io:format("monitoring PID ~p~n", [Pid]),
receive
{'DOWN', _Ref, process, Pid, Why} ->
io:format("~p died because ~p~n",[AName, Why]),
% add the restart logic
monitor_loop(AName)
end.
% function to bootstrapma monitor
my_monitor(AName) ->
case whereis(AName) of
undefined -> {error, no_such_registration};
_Pid -> spawn_monitor(ex, monitor_loop, [AName])
end.
And here's me playing with in:
39> c("ex.erl").
{ok,ex}
40> ex:create_reg_keep_alive(myjob).
{<0.147.0>,myjob}
I'm still alive
I'm still alive
41> ex:my_monitor(myjob).
monitoring PID <0.147.0>
{<0.149.0>,#Ref<0.230612052.2032402433.56637>}
I'm still alive
I'm still alive
42> exit(whereis(myjob), stop).
true
43>
It sure stopped the loop_5_print "worker" - but where's the line that the monitor was supposed to print? The only explanation that I see is that the message emitted by a process quitting in this manner isn't of the pattern on which I am matching inside monitor loop's receive. But that's the only pattern introduced in the book in this chapter, so I'm not buying this explanation..
spawn_monitor is not what you want here. spawn_monitor spawns a process and immediately starts monitoring it. When the spawned process dies, the process that called spawn_monitor gets a message that the process is dead. You need to call erlang:monitor/2 from the process that you want to receive the DOWN messages in, with the second argument being the Pid to monitor.
Just add:
monitor(process, Pid),
after:
Pid = whereis(AName),
and it works:
1> c(ex).
{ok,ex}
2> ex:create_reg_keep_alive(myjob).
{<0.67.0>,myjob}
I'm still alive
I'm still alive
I'm still alive
3> ex:my_monitor(myjob).
monitoring PID <0.67.0>
{<0.69.0>,#Ref<0.2696002348.2586050567.188678>}
I'm still alive
I'm still alive
I'm still alive
4> exit(whereis(myjob), stop).
myjob died because stop
true
monitoring PID undefined

Erlang: spawn_link processes are not killed when everything shuts down normally

Module test:
tester() ->
receive
X ->
erlang:display("message.."),
tester()
end.
initialize() ->
spawn_link(?MODULE, tester, []),
erlang:display("Started successfully.").
REPL:
length(erlang:processes()). -> 23
Pid = spawn_link(test, initialize, []).
length(erlang:processes()). -> 24
exit(Pid).
length(erlang:processes()). -> 24
It seems that the spawned tester process is still running! How do i make sure that when i exit my application, all spawn_link process are killed too?
Well, you are actually starting two Erlang processes, not one. The first one, to which you are sending the exit signal, dies before you even send the exit signal, so the exit has no effect.
The first process you start in the shell in this line:
Pid = spawn_link(test, initialize, []).
This process starts executing the initialize function, in which it starts the second process, and then it dies because there is nothing else to do. This is the process to which you are trying to send the exit signal.
To fix this simply return the correct Pid from the initialize function:
initialize() ->
Pid = spawn_link(?MODULE, tester, []),
erlang:display("Started successfully."),
Pid.
And start it directly:
Pid2 = test:initialize().
Then you will be able to kill it with exit(Pid2).

How to use ssh_connection:exec in Erlang?

This is an interesting situation, focused on the behavior of erlang ssh modules. I had spent a few hours troubleshooting a problem that turned out to reveal that the Erlang ssh_connection *exec/4* function operates asynchronously.
If you issue the ssh_connection:exec/4 function to run a script that takes several seconds to complete, and then in your erlang program you close the ssh connection, the script execution will terminate. My expectation was that the ssh_connection:exec would be synchronous rather than asynchronous.
Because the time to complete the remote script invoked by ssh_connection:exec is unknown, I chose to not issue the closure ssh:close(). I would like to understand the consequences of that:
Will the gc clear it at some point ?
Will it stay open for good during the whole node existence ?
Is there a way to make the ssh_connection:exec synchronous, as I would believe it should be.
Here is an example of the test erl program that I used to verify this issue. As a script you can run a simple sleep 10 (sleep 10 seconds) to emulate a slow running program.
-module(testssh).
-export([test/5]).
test (ServerName, Port, Command, User, Password) ->
crypto:start(),
ssh:start(),
{ok, SshConnectionRef} = ssh:connect(ServerName, Port, [ {user, User}, {password, Password} , {silently_accept_hosts, true} ], 60000 ),
{ok, SshConnectionChannelRef} = ssh_connection:session_channel(SshConnectionRef, 60000),
Status = ssh_connection:exec(SshConnectionRef, SshConnectionChannelRef, Command, 60000),
ssh:close(SshConnectionRef).
Remote script:
#!/bin/sh
sleep 10
I never had to use the ssh application myself, but you should be reading something wrong, it is clear in the doc that the result will be delivered as messages to the caller:
[...] the result will be several messages according to the following pattern. Note that the last message will be a channel close message, as the exec request is a one time execution that closes the channel when it is done[...]
See http://www.erlang.org/doc/man/ssh_connection.html#exec-4
So after you call ssh_connection:exec/4 , test with a loop like this:
wait_for_response(ConnectionRef) ->
receive
{ssh_cm, ConnectionRef, Msg} ->
case Msg of
{closed, _ChannelId} ->
io:format("Done");
_ ->
io:format("Got: ~p", [Msg]),
wait_for_response(ConnectionRef)
end
end.
You should receive the command output, and other ssh messages, and finally a 'closed' message that is your signal that the ssh command has properly finished.

Processes exiting normally

Given two linked processes child and parent, how does process child detect that parent exits (terminates) normally?
I, as an absolute Erlang beginner, thought that a process, when it has nothing else to do, exited using exit(normal). This then signals all linked processes, where
the behaviour of processes that have trap_exit set to false is to ignore the signal, and
the behaviour of processes that have trap_exit set to true is to generate the message {'EXIT', pid, normal} where pid is the process id of the terminating process.
My reason for thinking this is Learn You Some Erlang for Great Good and the Erlang documentation which states the following.
A process is said to terminate normally, if the exit reason is the atom normal. A process with no more code to execute terminates normally.
Apparently that is wrong (?), because exit(normal) shows ** exception exit: normal in the command prompt and makes the code below work. Exiting because there is no more code to execute does not generate the exception and does not make my code work.
As an example, consider the following code.
-module(test).
-export([start/0,test/0]).
start() ->
io:format("Parent (~p): started!\n",[self()]),
P = spawn_link(?MODULE,test,[]),
io:format(
"Parent (~p): child ~p spawned. Waiting for 5 seconds\n",[self(),P]),
timer:sleep(5000),
io:format("Parent (~p): dies out of boredom\n",[self()]),
ok.
test() ->
io:format("Child (~p): I'm... alive!\n",[self()]),
process_flag(trap_exit, true),
loop().
loop() ->
receive
Q = {'EXIT',_,_} ->
io:format("Child process died together with parent (~p)\n",[Q]);
Q ->
io:format("Something else happened... (~p)\n",[Q])
after
2000 -> io:format("Child (~p): still alive...\n", [self()]), loop()
end.
This produces output as follows.
(erlide#127.0.0.1)> test:start().
Parent (<0.145.0>): started!
Parent (<0.145.0>): child <0.176.0> spawned. Waiting for 5 seconds
Child (<0.176.0>): I'm... alive!
Child (<0.176.0>): still alive...
Child (<0.176.0>): still alive...
Parent (<0.145.0>): dies out of boredom
ok
(erlide#127.0.0.1)10> Child (<0.176.0>): still alive...
Child (<0.176.0>): still alive...
Child (<0.176.0>): still alive...
Child (<0.176.0>): still alive...
Child (<0.176.0>): still alive...
Child (<0.176.0>): still alive...
Child (<0.176.0>): still alive...
exit(pid(0,176,0),something).
Child process died together with parent ({'EXIT',<0.194.0>,something})
If had to manually execute the exit(pid(0,176,0),something) command to keep the child from staying alive forever.
Changing ok. in start to exit(normal) makes the execution go like this
(erlide#127.0.0.1)3> test:start().
Parent (<0.88.0>): started!
Parent (<0.88.0>): child <0.114.0> spawned. Waiting for 5 seconds
Child (<0.114.0>): I'm... alive!
Child (<0.114.0>): still alive...
Child (<0.114.0>): still alive...
Parent (<0.88.0>): dies out of boredom
Child process died together with parent ({'EXIT',<0.88.0>,normal})
** exception exit: normal
My concrete questions are the following.
How can I make the above code work as expected. That is, how can I make sure the child process dies together with the parent process without changing the parent process?
Why does exit(normal) generate a ** exception exit: normal in the CLI? It is hard for me to think of an exception as something that is normal. What does the scentence in the Erlang documentation mean?
I think these must be extremely basic questions, but I can't seem to figure this out....
I am using Erlang 5.9.3.1 on Windows (x64).
Erlang shell has the worker for evaluating commands as a separate process, and all commands you type run by the same process. When you your start function finished, worker still alive, and when you kill it by exit(), shell understand as worker exception (because worker will never die in normal case).
So:
You should run start as separate process by spawn or spawn_link
CLI logs all worker exits as exception and normal too
P.S. sorry for my english
P.P.S. spawn(fun() -> test:start() end). works as expected
4> spawn(fun() -> test:start() end).
Parent (<0.41.0>): started!
<0.41.0>
Parent (<0.41.0>): child <0.42.0> spawned. Waiting for 5 seconds
Child (<0.42.0>): I'm... alive!
Child (<0.42.0>): still alive...
Child (<0.42.0>): still alive...
Parent (<0.41.0>): dies out of boredom
Child process died together with parent ({'EXIT',<0.41.0>,normal})
A comment to your question on #PetrKozorezov 's answer. The shell is not behaving specially in any way. The shell worker process is just a normal process so if any process to which it is linked crashes then it will also crash. Another worker process will then be started. This is the normal Erlang way.
Your start/0 function just returns and does NOT terminate its process, it just outputs the "dying of boredom" message. That is why the loop keeps on going, it doesn't get an exit signal because no process has died.
When you change the start/0 function to end with exit(normal) then you do terminate the shell process so an exit signal is sent to the loop process which then gets the {'EXIT',...,...} message and dies.
When #PetrKozorezov spawned your original start/0 function in a separate process which then died after executing start/0 it sent an exit normal signal to the loop process which caused it to die.
This is perfectly normal Erlang behaviour and style. You would normally not end a start function with an exit but leave it up to the caller to decide when to die.
One more small point: as the start function does a spawn_link you would normally call it start_link instead. A start function is assumed to just spawn a process. This is, of course, just a convention, but a common one, so you have not made an error.

Resources