Setting this timer while it is running will restart it with
the new time-out value. Therefore it is possible to cancel
- this timeout by setting it to infinity.
+ this time-out by setting it to infinity.
@@ -1130,7 +1144,7 @@ handle_event(_, _, State, Data) ->
Date: Fri, 21 Oct 2016 23:36:26 +0200
Subject: Optimize event timeout
Do not start an event timer unless there are no enqueued events.
---
lib/stdlib/doc/src/gen_statem.xml | 5 +-
lib/stdlib/src/gen_statem.erl | 116 ++++++++++++++++++++++++--------------
2 files changed, 77 insertions(+), 44 deletions(-)
(limited to 'lib/stdlib')
diff --git a/lib/stdlib/doc/src/gen_statem.xml b/lib/stdlib/doc/src/gen_statem.xml
index 567130875a..fd498ee82e 100644
--- a/lib/stdlib/doc/src/gen_statem.xml
+++ b/lib/stdlib/doc/src/gen_statem.xml
@@ -773,8 +773,9 @@ handle_event(_, _, State, Data) ->
after this time (in milliseconds) unless another
event arrives or has arrived
in which case this time-out is cancelled.
- Note that a retried, inserted or state time-out zero
- events counts as arrived.
+ Note that a retried or inserted event counts as arrived.
+ So does a state time-out zero event, if it was generated
+ before this timer is requested.
If the value is infinity, no timer is started, as
diff --git a/lib/stdlib/src/gen_statem.erl b/lib/stdlib/src/gen_statem.erl
index c81916197c..c9f8ec1881 100644
--- a/lib/stdlib/src/gen_statem.erl
+++ b/lib/stdlib/src/gen_statem.erl
@@ -1001,7 +1001,7 @@ loop_event_result(
{sys_debug(Debug, S, State, {consume,Event,State}),
P_0}
end,
- {Events_1,NewP,{TimerRefs,TimerTypes}} =
+ {Events_1,NewP,{TimerRefs_1,TimerTypes_1}} =
%% Move all postponed events to queue and cancel the
%% state timeout if the state changes
if
@@ -1012,17 +1012,16 @@ loop_event_result(
cancel_timer_by_type(
state_timeout, TimerRefs_0, TimerTypes_0)}
end,
- {NewTimerRefs,NewTimerTypes,TimeoutEvents} =
- %% Stop and start timers
- handle_timers(TimerRefs, TimerTypes, TimeoutsR),
- %% Place next events first in reversed queue
- NewEventsR = lists:reverse(Events_1, NextEventsR),
- %% Append timeout zero events
- NewEvents =
- lists:reverse(
- NewEventsR,
- process_timeout_events(TimeoutEvents, NewEventsR)),
- %%
+ {TimerRefs_2,TimerTypes_2,TimeoutEvents} =
+ %% Stop and start timers non-event timers
+ parse_timers(TimerRefs_1, TimerTypes_1, TimeoutsR),
+ %% Place next events last in reversed queue
+ Events_2R = lists:reverse(Events_1, NextEventsR),
+ %% Enqueue immediate timeout events and start event timer
+ {NewTimerRefs,NewTimerTypes,Events_3R} =
+ process_timeout_events(
+ TimerRefs_2, TimerTypes_2, TimeoutEvents, Events_2R),
+ NewEvents = lists:reverse(Events_3R),
loop_events(
Parent, NewDebug, S, NewTimerRefs, NewTimerTypes,
NewEvents, Hibernate, NextState, NewData, NewP).
@@ -1356,55 +1355,88 @@ parse_actions(
%% Stop and start timers as well as create timeout zero events
+%% and pending event timer
%%
-handle_timers(TimerRefs, TimerTypes, TimeoutsR) ->
- Seen = #{},
- TimeoutEvents = [],
- handle_timers(TimerRefs, TimerTypes, TimeoutsR, Seen, TimeoutEvents).
+%% Stop and start timers non-event timers
+parse_timers(TimerRefs, TimerTypes, TimeoutsR) ->
+ parse_timers(TimerRefs, TimerTypes, TimeoutsR, #{}, []).
%%
-handle_timers(TimerRefs, TimerTypes, [], _Seen, TimeoutEvents) ->
+parse_timers(TimerRefs, TimerTypes, [], _Seen, TimeoutEvents) ->
{TimerRefs,TimerTypes,TimeoutEvents};
-handle_timers(TimerRefs, TimerTypes, [Timeout|TimeoutsR], Seen, TimeoutEvents) ->
+parse_timers(
+ TimerRefs, TimerTypes, [Timeout|TimeoutsR], Seen, TimeoutEvents) ->
{TimerType,Time,TimerMsg} = Timeout,
case Seen of
#{TimerType := _} ->
- handle_timers(
+ %% Type seen before - ignore
+ parse_timers(
TimerRefs, TimerTypes, TimeoutsR, Seen, TimeoutEvents);
#{} ->
+ %% Unseen type - handle
NewSeen = Seen#{TimerType => true},
+ %% Cancel any running timer
{NewTimerRefs,NewTimerTypes} =
cancel_timer_by_type(TimerType, TimerRefs, TimerTypes),
- case Time of
- 0 ->
- TimeoutEvent = {TimerType,TimerMsg},
- handle_timers(
- NewTimerRefs, NewTimerTypes,
- TimeoutsR, NewSeen, [TimeoutEvent|TimeoutEvents]);
- infinity ->
+ if
+ Time =:= infinity ->
%% Ignore - timer will never fire
- handle_timers(
- NewTimerRefs, NewTimerTypes,
- TimeoutsR, NewSeen, TimeoutEvents);
- _ ->
+ parse_timers(
+ NewTimerRefs, NewTimerTypes, TimeoutsR,
+ NewSeen, TimeoutEvents);
+ TimerType =:= timeout ->
+ %% Handle event timer later
+ parse_timers(
+ NewTimerRefs, NewTimerTypes, TimeoutsR,
+ NewSeen, [Timeout|TimeoutEvents]);
+ Time =:= 0 ->
+ %% Handle zero time timeouts later
+ TimeoutEvent = {TimerType,TimerMsg},
+ parse_timers(
+ NewTimerRefs, NewTimerTypes, TimeoutsR,
+ NewSeen, [TimeoutEvent|TimeoutEvents]);
+ true ->
+ %% Start a new timer
TimerRef = erlang:start_timer(Time, self(), TimerMsg),
- handle_timers(
+ parse_timers(
NewTimerRefs#{TimerRef => TimerType},
NewTimerTypes#{TimerType => TimerRef},
TimeoutsR, NewSeen, TimeoutEvents)
end
end.
-
-%% Keep an event timeout event if it is the only event so far
-process_timeout_events([], _Es) ->
- [];
-process_timeout_events([{timeout,_} = TimeoutEvent|TimeoutEvents], []) ->
- [TimeoutEvent|process_timeout_events(TimeoutEvents, [TimeoutEvent])];
-process_timeout_events([{timeout,_}|TimeoutEvents], Es) ->
- %% Ignore event timeout since there are other events
- process_timeout_events(TimeoutEvents, Es);
-process_timeout_events([TimeoutEvent|TimeoutEvents], Es) ->
- [TimeoutEvent|process_timeout_events(TimeoutEvents, [TimeoutEvent|Es])].
+%% Enqueue immediate timeout events and start event timer
+process_timeout_events(TimerRefs, TimerTypes, [], EventsR) ->
+ {TimerRefs, TimerTypes, EventsR};
+process_timeout_events(
+ TimerRefs, TimerTypes,
+ [{timeout,0,TimerMsg}|TimeoutEvents], []) ->
+ %% No enqueued events - insert a timeout zero event
+ TimeoutEvent = {timeout,TimerMsg},
+ process_timeout_events(
+ TimerRefs, TimerTypes,
+ TimeoutEvents, [TimeoutEvent]);
+process_timeout_events(
+ TimerRefs, TimerTypes,
+ [{timeout,Time,TimerMsg}], []) ->
+ %% No enqueued events - start event timer
+ TimerRef = erlang:start_timer(Time, self(), TimerMsg),
+ process_timeout_events(
+ TimerRefs#{TimerRef => timeout}, TimerTypes#{timeout => TimerRef},
+ [], []);
+process_timeout_events(
+ TimerRefs, TimerTypes,
+ [{timeout,_Time,_TimerMsg}|TimeoutEvents], EventsR) ->
+ %% There will be some other event so optimize by not starting
+ %% an event timer to just have to cancel it again
+ process_timeout_events(
+ TimerRefs, TimerTypes,
+ TimeoutEvents, EventsR);
+process_timeout_events(
+ TimerRefs, TimerTypes,
+ [{_TimeoutType,_TimeoutMsg} = TimeoutEvent|TimeoutEvents], EventsR) ->
+ process_timeout_events(
+ TimerRefs, TimerTypes,
+ TimeoutEvents, [TimeoutEvent|EventsR]).
--
cgit v1.2.3
From 167c57b1472746d01ff10f759e18e17bf5fb4fb6 Mon Sep 17 00:00:00 2001
From: Raimo Niskanen
Date: Wed, 26 Oct 2016 10:38:13 +0200
Subject: Log terminate to sys debug
---
lib/stdlib/src/gen_statem.erl | 28 ++++++++++++++++++----------
1 file changed, 18 insertions(+), 10 deletions(-)
(limited to 'lib/stdlib')
diff --git a/lib/stdlib/src/gen_statem.erl b/lib/stdlib/src/gen_statem.erl
index c9f8ec1881..018aca90e6 100644
--- a/lib/stdlib/src/gen_statem.erl
+++ b/lib/stdlib/src/gen_statem.erl
@@ -749,6 +749,10 @@ print_event(Dev, {out,Reply,{To,_Tag}}, {Name,State}) ->
io:format(
Dev, "*DBG* ~p send ~p to ~p from state ~p~n",
[Name,Reply,To,State]);
+print_event(Dev, {terminate,Reason}, {Name,State}) ->
+ io:format(
+ Dev, "*DBG* ~p terminate ~p in state ~p~n",
+ [Name,Reason,State]);
print_event(Dev, {Tag,Event,NextState}, {Name,State}) ->
StateString =
case NextState of
@@ -1497,16 +1501,20 @@ terminate(
sys:print_log(Debug),
erlang:raise(C, R, ST)
end,
- case Reason of
- normal -> ok;
- shutdown -> ok;
- {shutdown,_} -> ok;
- _ ->
- error_info(
- Class, Reason, Stacktrace, S, Q, P,
- format_status(terminate, get(), S)),
- sys:print_log(Debug)
- end,
+ _ =
+ case Reason of
+ normal ->
+ sys_debug(Debug, S, State, {terminate,Reason});
+ shutdown ->
+ sys_debug(Debug, S, State, {terminate,Reason});
+ {shutdown,_} ->
+ sys_debug(Debug, S, State, {terminate,Reason});
+ _ ->
+ error_info(
+ Class, Reason, Stacktrace, S, Q, P,
+ format_status(terminate, get(), S)),
+ sys:print_log(Debug)
+ end,
case Stacktrace of
[] ->
erlang:Class(Reason);
--
cgit v1.2.3