1+ % %% -*-mode:erlang;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*-
2+ % %% ex: set ft=erlang fenc=utf-8 sts=4 ts=4 sw=4 et:
3+
4+ -module (gen_rpc_exec_worker_sup ).
5+
6+ % %% Behaviour
7+ -behaviour (supervisor ).
8+
9+ -include (" logger.hrl" ).
10+
11+ % %% API
12+ -export ([start_link /0 , submit /4 ]).
13+
14+ % %% Supervisor callbacks
15+ -export ([init /1 ]).
16+
17+ -define (FIRST_WORKER_INDEX , 1 ).
18+
19+ % %% ===================================================
20+ % %% API
21+ % %% ===================================================
22+ -spec start_link () -> supervisor :startlink_ret ().
23+ start_link () ->
24+ supervisor :start_link ({local , ? MODULE }, ? MODULE , []).
25+
26+ -spec submit (module (), atom (), list (), boolean ()) -> ok .
27+ submit (M , F , A , PreserveOrder ) ->
28+ Name = gen_rpc_exec_worker :worker_name (worker_index (PreserveOrder )),
29+ case gen_rpc_registry :whereis_name (Name ) of
30+ undefined ->
31+ ? log (notice , " event=exec_cast_worker_unavailable, name=~p " , [Name ]),
32+ exec_cast (M , F , A , PreserveOrder );
33+ Pid ->
34+ gen_server :cast (Pid , {exec_cast , M , F , A }),
35+ ok
36+ end .
37+
38+ % %% ===================================================
39+ % %% Supervisor callbacks
40+ % %% ===================================================
41+ init ([]) ->
42+ Children = [worker_spec (Index ) || Index <- lists :seq (1 , worker_count ())],
43+ {ok , {{one_for_one , 15 , 1 }, Children }}.
44+
45+ % %% ===================================================
46+ % %% Internal functions
47+ % %% ===================================================
48+ % %% The `exec_cast/4` function is a fallback that executes the cast directly if the worker is unavailable.
49+ % %% Usually happens when relup failed to start the worker pool.
50+ exec_cast (M , F , A , _PreserveOrder = true ) ->
51+ {Pid , MRef } = erlang :spawn_monitor (M , F , A ),
52+ receive
53+ {'DOWN' , MRef , process , Pid , _ } -> ok
54+ end ;
55+ exec_cast (M , F , A , _PreserveOrder = false ) ->
56+ _ = erlang :spawn (M , F , A ),
57+ ok .
58+
59+ worker_spec (Index ) ->
60+ Name = gen_rpc_exec_worker :worker_name (Index ),
61+ {Name , {gen_rpc_exec_worker , start_link , [Index ]}, permanent , 5000 , worker , [gen_rpc_exec_worker ]}.
62+
63+ worker_count () ->
64+ erlang :system_info (schedulers ).
65+
66+ worker_index (true ) ->
67+ ? FIRST_WORKER_INDEX ;
68+ worker_index (false ) ->
69+ random_worker_index ().
70+
71+ random_worker_index () ->
72+ case worker_count () of
73+ 1 ->
74+ 1 ;
75+ Count ->
76+ erlang :phash2 (make_ref (), Count ) + 1
77+ end .
0 commit comments