11
11
code_change /3 ,
12
12
terminate /2 ]).
13
13
14
- % % public api
14
+ -record (state , {
15
+ pool ,
16
+ listen_opts ,
17
+ pool_opts ,
18
+ socket ,
19
+ mref
20
+ }).
15
21
22
+ % % public api
16
23
start_link (Pool , ListenOpts , AcceptorOpts ) ->
17
24
gen_server :start_link (? MODULE , [Pool , ListenOpts , AcceptorOpts ], []).
18
25
19
26
% % gen_server api
20
27
21
28
init ([Pool , ListenOpts , PoolOpts ]) ->
29
+ {ok , # state {pool = Pool , pool_opts = PoolOpts , listen_opts = ListenOpts }, 0 }.
30
+
31
+ handle_call (Req , _ , State ) ->
32
+ {stop , {bad_call , Req }, State }.
33
+
34
+ handle_cast (Req , State ) ->
35
+ {stop , {bad_cast , Req }, State }.
36
+ handle_info (timeout , State ) ->
37
+ case start_listener (State ) of
38
+ {ok , {Socket , MRef }} ->
39
+ {noreply , State # state {socket = Socket , mref = MRef }};
40
+ _ ->
41
+ erlang :send_after (5000 , self (), timeout ),
42
+ {noreply , State }
43
+ end ;
44
+ handle_info ({'DOWN' , MRef , port , Socket , _Reason }, # state {mref = MRef , socket = Socket } = State ) ->
45
+ catch gen_tcp :close (Socket ),
46
+ erlang :send_after (5000 , self (), timeout ),
47
+ {noreply , State };
48
+ handle_info (_Msg , State ) ->
49
+ {noreply , State }.
50
+
51
+ code_change (_ , State , _ ) ->
52
+ {ok , State }.
53
+
54
+ terminate (_Reason , {Socket , MRef }) ->
55
+ % % Socket may already be down but need to ensure it is closed to avoid
56
+ % % eaddrinuse error on restart
57
+ % % this takes care of that, unless of course this process is killed...
58
+ case demonitor (MRef , [flush , info ]) of
59
+ true -> gen_tcp :close (Socket );
60
+ false -> ok
61
+ end .
62
+
63
+ % % ------------------------------------------------------------------
64
+ % % Internal functions
65
+ % % ------------------------------------------------------------------
66
+ start_listener (# state {
67
+ pool = Pool ,
68
+ listen_opts = ListenOpts ,
69
+ pool_opts = PoolOpts } = _State ) ->
22
70
Port = maps :get (port , ListenOpts , 8080 ),
23
71
IPAddress = maps :get (ip , ListenOpts , {0 , 0 , 0 , 0 }),
24
72
AcceptorPoolSize = maps :get (size , PoolOpts , 10 ),
@@ -27,8 +75,7 @@ init([Pool, ListenOpts, PoolOpts]) ->
27
75
{reuseaddr , true },
28
76
{backlog , 32768 },
29
77
{keepalive , true }]),
30
- % % Trapping exit so can close socket in terminate/2
31
- _ = process_flag (trap_exit , true ),
78
+
32
79
Opts = [{active , false }, {mode , binary }, {packet , raw }, {ip , IPAddress } | SocketOpts ],
33
80
case gen_tcp :listen (Port , Opts ) of
34
81
{ok , Socket } ->
@@ -78,30 +125,7 @@ init([Pool, ListenOpts, PoolOpts]) ->
78
125
socket_not_found ->
79
126
noop
80
127
end ,
81
- {stop , eaddrinuse };
128
+ {error , eaddrinuse };
82
129
{error , Reason } ->
83
- {stop , Reason }
84
- end .
85
-
86
- handle_call (Req , _ , State ) ->
87
- {stop , {bad_call , Req }, State }.
88
-
89
- handle_cast (Req , State ) ->
90
- {stop , {bad_cast , Req }, State }.
91
-
92
- handle_info ({'DOWN' , MRef , port , Socket , Reason }, {Socket , MRef } = State ) ->
93
- {stop , Reason , State };
94
- handle_info (_ , State ) ->
95
- {noreply , State }.
96
-
97
- code_change (_ , State , _ ) ->
98
- {ok , State }.
99
-
100
- terminate (_Reason , {Socket , MRef }) ->
101
- % % Socket may already be down but need to ensure it is closed to avoid
102
- % % eaddrinuse error on restart
103
- % % this takes care of that, unless of course this process is killed...
104
- case demonitor (MRef , [flush , info ]) of
105
- true -> gen_tcp :close (Socket );
106
- false -> ok
130
+ {error , Reason }
107
131
end .
0 commit comments