Skip to content

Commit 6d9d85b

Browse files
Allow monitor options in proc_lib:start_monitor/5
Co-authored-by: Jan Uhlig <[email protected]>
1 parent 403e057 commit 6d9d85b

File tree

3 files changed

+78
-26
lines changed

3 files changed

+78
-26
lines changed

lib/stdlib/src/gen.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ monitor_return({Error, Mon}) when is_reference(Mon) ->
178178
%% Failure; wait for spawned process to terminate
179179
%% and release resources, then return the error...
180180
receive
181-
{'DOWN', Mon, process, _Pid, _Reason} ->
181+
{_Tag, Mon, process, _Pid, _Reason} ->
182182
ok
183183
end,
184184
Error.

lib/stdlib/src/proc_lib.erl

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -98,15 +98,17 @@ processes that terminate as a result of this process terminating.
9898
%%
9999
-doc """
100100
A restricted set of [spawn options](`t:spawn_option/0`). Most notably `monitor`
101-
is _not_ part of these options.
101+
and `{monitor, MonitorOpts}` are _not_ part of these options.
102102
""".
103103
-type start_spawn_option() :: 'link'
104+
| {'link', [erlang:link_option()]}
104105
| {'priority', erlang:priority_level()}
105106
| {'fullsweep_after', non_neg_integer()}
106107
| {'min_heap_size', non_neg_integer()}
107108
| {'min_bin_vheap_size', non_neg_integer()}
108109
| {'max_heap_size', erlang:max_heap_size()}
109-
| {'message_queue_data', erlang:message_queue_data() }.
110+
| {'message_queue_data', erlang:message_queue_data()}
111+
| {'async_dist', boolean()}.
110112
%% and this macro is used to verify that there are no monitor options
111113
%% which also needs to be kept in sync all kinds of monitor options
112114
%% in erlang:spawn_opt_options().
@@ -417,11 +419,11 @@ Argument `SpawnOpts`, if specified, is passed as the last argument to the
417419

418420
> #### Note {: .info }
419421
>
420-
> Using spawn option `monitor` is not allowed. It causes the function to fail
421-
> with reason `badarg`.
422+
> Using spawn option `monitor` or `{monitor, Opts}` is not allowed. It causes
423+
> the function to fail with reason `badarg`.
422424
>
423-
> Using spawn option `link` will set a link to the spawned process, just like
424-
> [start_link/3,4,5](`start_link/3`).
425+
> Using spawn option `link` or `{link, Opts}` will set a link to the spawned
426+
> process, just like [start_link/3,4,5](`start_link/3`).
425427
""".
426428
-spec start(Module, Function, Args, Time, SpawnOpts) -> Ret when
427429
Module :: module(),
@@ -497,8 +499,11 @@ process.
497499

498500
> #### Note {: .info }
499501
>
500-
> Using spawn option `monitor` is not allowed. It causes the function to fail
501-
> with reason `badarg`.
502+
> Using spawn option `monitor` or `{monitor, Opts}` is not allowed. It causes
503+
> the function to fail with reason `badarg`.
504+
>
505+
> Using spawn option `link` has no effect. The spawn option `{link, Opts}` can
506+
> be used to customize the link that will be set to the spawned process.
502507
""".
503508
-spec start_link(Module, Function, Args, Time, SpawnOpts) -> Ret when
504509
Module :: module(),
@@ -510,8 +515,9 @@ process.
510515

511516
start_link(M,F,A,Timeout,SpawnOpts) when is_atom(M), is_atom(F), is_list(A) ->
512517
?VERIFY_NO_MONITOR_OPT(M, F, A, Timeout, SpawnOpts),
518+
SpawnOpts1 = ensure_spawn_option(link, SpawnOpts),
513519
sync_start(
514-
?MODULE:spawn_opt(M, F, A, [link,monitor|SpawnOpts]), Timeout).
520+
?MODULE:spawn_opt(M, F, A, [monitor | SpawnOpts1]), Timeout).
515521

516522

517523
-doc(#{equiv => start_monitor(Module, Function, Args, infinity)}).
@@ -556,28 +562,28 @@ when this function times out and kills the spawned process.
556562

557563
> #### Note {: .info }
558564
>
559-
> Using spawn option `monitor` is not allowed. It causes the function to fail
560-
> with reason `badarg`.
565+
> Using spawn option `monitor` has no effect. The spawn option `{monitor, Opts}`
566+
> can be used to customize the monitor that will be set on the spawned process.
561567
>
562-
> Using spawn option `link` will set a link to the spawned process, just like
563-
> [start_link/3,4,5](`start_link/3`).
568+
> Using spawn option `link` or `{link, Opts}` will set a link to the spawned
569+
> process, just like [start_link/3,4,5](`start_link/3`).
564570
""".
565571
-doc(#{since => <<"OTP 23.0">>}).
566572
-spec start_monitor(Module, Function, Args, Time, SpawnOpts) -> {Ret, Mon} when
567573
Module :: module(),
568574
Function :: atom(),
569575
Args :: [term()],
570576
Time :: timeout(),
571-
SpawnOpts :: [start_spawn_option()],
577+
SpawnOpts :: [spawn_option()],
572578
Mon :: reference(),
573579
Ret :: term() | {error, Reason :: term()}.
574580

575581
start_monitor(M,F,A,Timeout,SpawnOpts) when is_atom(M),
576582
is_atom(F),
577583
is_list(A) ->
578-
?VERIFY_NO_MONITOR_OPT(M, F, A, Timeout, SpawnOpts),
584+
SpawnOpts1 = ensure_spawn_option(monitor, SpawnOpts),
579585
sync_start_monitor(
580-
?MODULE:spawn_opt(M, F, A, [monitor|SpawnOpts]), Timeout).
586+
?MODULE:spawn_opt(M, F, A, SpawnOpts1), Timeout).
581587

582588
sync_start_monitor({Pid, Ref}, Timeout) ->
583589
receive
@@ -587,7 +593,7 @@ sync_start_monitor({Pid, Ref}, Timeout) ->
587593
flush_EXIT(Pid),
588594
self() ! await_DOWN(Pid, Ref),
589595
{Return, Ref};
590-
{'DOWN', Ref, process, Pid, Reason} = Down ->
596+
{_, Ref, process, Pid, Reason} = Down ->
591597
flush_EXIT(Pid),
592598
self() ! Down,
593599
{{error, Reason}, Ref}
@@ -597,6 +603,19 @@ sync_start_monitor({Pid, Ref}, Timeout) ->
597603
{{error, timeout}, Ref}
598604
end.
599605

606+
%% Adds the given Opt atom to the given SpawnOpts list
607+
%% if it is not already contained either in plain form
608+
%% or as the first element of a tuple.
609+
ensure_spawn_option(Opt, [Opt | _] = SpawnOpts) ->
610+
SpawnOpts;
611+
ensure_spawn_option(Opt, [SpawnOpt | _] = SpawnOpts)
612+
when Opt =:= element(1, SpawnOpt) ->
613+
SpawnOpts;
614+
ensure_spawn_option(Opt, [SpawnOpt | SpawnOpts]) ->
615+
[SpawnOpt | ensure_spawn_option(Opt, SpawnOpts)];
616+
ensure_spawn_option(Opt, []) ->
617+
[Opt].
618+
600619

601620
%% We regard the existence of an {'EXIT', Pid, _} message
602621
%% as proof enough that there was a link that fired and
@@ -620,7 +639,7 @@ kill_flush_EXIT(Pid) ->
620639

621640
await_DOWN(Pid, Ref) ->
622641
receive
623-
{'DOWN', Ref, process, Pid, _} = Down ->
642+
{_, Ref, process, Pid, _} = Down ->
624643
Down
625644
end.
626645

lib/stdlib/test/proc_lib_SUITE.erl

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,11 @@
2929
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
3030
init_per_group/2,end_per_group/2,
3131
crash/1, stacktrace/1, sync_start_nolink/1, sync_start_link/1,
32-
sync_start_monitor/1, sync_start_monitor_link/1,
32+
sync_start_monitor/1, sync_start_monitor_link/1, sync_start_monitor_monitor/1,
3333
sync_start_timeout/1, sync_start_link_timeout/1,
3434
sync_start_monitor_link_timeout/1,
3535
spawn_opt/1, sp1/0, sp1_with_label/0, sp2/0, sp3/1, sp4/2,
36-
sp5/1, sp6/1, sp7/1, sp8/1, sp9/1, sp10/1,
36+
sp5/1, sp6/1, sp7_link/1, sp7_monitor/1, sp8/1, sp9/1, sp10/1,
3737
'\x{447}'/0, hibernate/1, stop/1, t_format/1, t_format_arbitrary/1]).
3838
-export([ otp_6345/1, init_dont_hang/1]).
3939

@@ -64,7 +64,7 @@ all() ->
6464
groups() ->
6565
[{tickets, [], [otp_6345, init_dont_hang]},
6666
{sync_start, [], [sync_start_nolink, sync_start_link,
67-
sync_start_monitor, sync_start_monitor_link,
67+
sync_start_monitor, sync_start_monitor_link, sync_start_monitor_monitor,
6868
sync_start_timeout, sync_start_link_timeout,
6969
sync_start_monitor_link_timeout]}].
7070

@@ -313,7 +313,7 @@ sync_start_monitor(Config) when is_list(Config) ->
313313
ok.
314314

315315
sync_start_monitor_link(Config) when is_list(Config) ->
316-
_Pid = spawn_link(?MODULE, sp7, [self()]),
316+
_Pid = spawn_link(?MODULE, sp7_link, [self()]),
317317
receive
318318
{sync_started, _} -> ct:fail(async_start)
319319
after 1000 -> ok
@@ -334,6 +334,26 @@ sync_start_monitor_link(Config) when is_list(Config) ->
334334
end,
335335
ok.
336336

337+
sync_start_monitor_monitor(Config) when is_list(Config) ->
338+
_Pid = spawn_link(?MODULE, sp7_monitor, [self()]),
339+
receive
340+
{sync_started, _} -> ct:fail(async_start)
341+
after 1000 -> ok
342+
end,
343+
receive
344+
{Pid2, init} ->
345+
Pid2 ! go_on
346+
end,
347+
receive
348+
{sync_started, _} -> ok
349+
after 1000 -> ct:fail(no_sync_start)
350+
end,
351+
receive received_down -> ok
352+
after 1000 -> ct:fail(no_down)
353+
end,
354+
ok.
355+
356+
337357
sync_start_timeout(Config) when is_list(Config) ->
338358
_Pid = spawn_link(?MODULE, sp8, [self()]),
339359
receive done -> ok end,
@@ -445,7 +465,7 @@ sp6(Tester) ->
445465
Tester ! received_down
446466
end.
447467

448-
sp7(Tester) ->
468+
sp7_link(Tester) ->
449469
process_flag(trap_exit, true),
450470
{Pid, Mon} = proc_lib:start_monitor(?MODULE, sp4, [self(), Tester], infinity, [link]),
451471
Tester ! {sync_started, Pid},
@@ -458,6 +478,15 @@ sp7(Tester) ->
458478
Tester ! received_down
459479
end.
460480

481+
sp7_monitor(Tester) ->
482+
process_flag(trap_exit, true),
483+
{Pid, Mon} = proc_lib:start_monitor(?MODULE, sp4, [self(), Tester], infinity, [{monitor, [{tag, sp7_monitor}]}]),
484+
Tester ! {sync_started, Pid},
485+
receive
486+
{sp7_monitor, Mon, process, Pid, normal} ->
487+
Tester ! received_down
488+
end.
489+
461490
sp8(Tester) ->
462491
process_flag(trap_exit, true),
463492
{error,timeout} = proc_lib:start(?MODULE, sp4, [self(), Tester], 500, [link]),
@@ -622,8 +651,12 @@ hib_receive_messages(N) ->
622651
otp_6345(Config) when is_list(Config) ->
623652
Opts = [link,monitor],
624653
try
625-
blupp = proc_lib:start(?MODULE, otp_6345_init, [self()],
626-
1000, Opts)
654+
proc_lib:start(?MODULE, otp_6345_init, [self()],
655+
1000, Opts)
656+
of
657+
{ok, Pid} ->
658+
exit(Pid, kill),
659+
ct:fail(monitor_option_accepted)
627660
catch
628661
error:badarg -> ok
629662
end.

0 commit comments

Comments
 (0)