Skip to content

Commit

Permalink
Get cxy_fount unit tests to pass again
Browse files Browse the repository at this point in the history
  • Loading branch information
jaynel committed Aug 6, 2016
1 parent 769fbcd commit 5c59ea1
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 53 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ ERLC_OPTS := +debug_info +"{cover_enabled, true}"
TEST_ERLC_OPTS := -I include -I test/epocxy $(ERLC_OPTS)

CT_OPTS := -cover test/epocxy.coverspec
CT_SUITES = cxy_regulator
## batch_feeder ets_buffer cxy_ctl cxy_cache cxy_fount
CT_SUITES = cxy_regulator cxy_fount
## batch_feeder ets_buffer cxy_ctl cxy_cache

DIALYZER_OPTS := -I include -Werror_handling -Wrace_conditions -Wunmatched_returns

Expand Down
45 changes: 32 additions & 13 deletions src/cxy_fount.erl
Original file line number Diff line number Diff line change
Expand Up @@ -701,9 +701,9 @@ reply_pids (Num_Pids, State_Fn, #cf_state{fount_count=FC, slab_size=Slab_Size,

%%% More than 1 pid wanted, more than Slab_Size, see if there are enough to return...
reply_pids (Num_Pids, _State_Fn, #cf_state{fount_count=FC, slab_size=Slab_Size, num_slabs=Num_Slabs} = State)
when Num_Pids > Slab_Size, Num_Pids < (Num_Slabs * Slab_Size) + FC ->
when Num_Pids > Slab_Size, Num_Pids < (Num_Slabs * Slab_Size) + FC ->
Excess = Num_Pids rem Slab_Size,
Slabs_Needed = (Num_Pids - Excess) div Slab_Size,
Slabs_Needed = Num_Pids div Slab_Size,
#cf_state{behaviour=Mod, behaviour_state=Mod_State, fount=Fount_Slab,
reservoir=[#timed_slab{slab=First_Slab} | More_Slabs] = All_Slabs} = State,
#timed_slab{slab=Fount} = Fount_Slab,
Expand All @@ -713,23 +713,42 @@ reply_pids (Num_Pids, _State_Fn, #cf_state{fount_count=FC, slab_size=Slab_Size,
Full_Slabs_Left = Num_Slabs - Slabs_Needed,
{{Pids, Remaining_Fount_Pids}, {Slabs_Requested, Remaining_Slabs}, {New_Num_Slabs, New_Fount_Count}}
= case FC of
Enough when Enough > Excess, Excess > 0 ->

%% Slabs_Needed is more than is available (so Fount must have more than 1 slab in it)...
_Enough when Full_Slabs_Left < 0 ->
Slab_Pids = Num_Pids - FC,
Final_Slabs_Needed = Slab_Pids div Slab_Size,
Final_Excess = Slab_Pids rem Slab_Size,
Final_Slabs_Left = Num_Slabs - Final_Slabs_Needed,
case Final_Excess of
0 when Final_Slabs_Left > 0 -> % Should never be < 0 (= 0 is covered by all pids used up)
done = replace_slabs(State#cf_state.regulator, Mod, Mod_State, 1, Slab_Size), % Fount empty, replace.
{{Fount, []}, lists:split(Final_Slabs_Needed, All_Slabs), {Final_Slabs_Left, 0}};
FE -> % All of the Fount + Final_Excess taken from first slab
{lists:split(FC + Final_Excess, Fount ++ First_Slab), lists:split(Final_Slabs_Needed-1, More_Slabs), {Final_Slabs_Left, Slab_Size-FE}}
end;

%% Take N slabs (no more than present) + Excess from Fount when Fount is bigger than Excess...
Enough when Enough > Excess, Excess > 0 -> % Full_Slabs_Left >= 0
{lists:split(Excess, Fount), lists:split(Slabs_Needed, All_Slabs), {Full_Slabs_Left, FC-Excess}};

%% Take N slabs + Excess from Fount when Excess is exactly Fount size...
Excess when Excess > 0 ->
done = replace_slabs(State#cf_state.regulator, Mod, Mod_State, 1, Slab_Size),
done = replace_slabs(State#cf_state.regulator, Mod, Mod_State, 1, Slab_Size), % Fount empty, replace.
{{Fount, []}, lists:split(Slabs_Needed, All_Slabs), {Full_Slabs_Left, 0}};
Slab_Size when Excess =:= 0 ->
{{Fount, []}, lists:split(Slabs_Needed-1, All_Slabs), {Full_Slabs_Left+1, 0}};
_Not_Enough ->
done = replace_slabs(State#cf_state.regulator, Mod, Mod_State, 1, Slab_Size),

%% Take N slabs when no Excess...
FC when Excess =:= 0 ->
{{[], Fount}, lists:split(Slabs_Needed, All_Slabs), {Full_Slabs_Left, FC}};

%% Excess is bigger than Fount.
_Not_Enough when Excess > FC ->
done = replace_slabs(State#cf_state.regulator, Mod, Mod_State, 1, Slab_Size), % Replace extra slab needed.
{lists:split(Excess, Fount ++ First_Slab), lists:split(Slabs_Needed, More_Slabs), {Full_Slabs_Left-1, FC+Slab_Size-Excess}}
end,
Remaining_Fount = case Remaining_Fount_Pids of
[] -> [];
RFP -> Fount_Slab#timed_slab{slab=RFP}
end,
Remaining_Fount = Fount_Slab#timed_slab{slab=Remaining_Fount_Pids},
Pids_Requested = lists:append([Pids | [S || #timed_slab{slab=S} <- Slabs_Requested]]),
New_State_Fn = case Remaining_Fount =:= [] andalso Remaining_Slabs =:= [] of true -> 'EMPTY'; false -> 'LOW' end,
New_State_Fn = case Remaining_Fount#timed_slab.slab =:= [] andalso Remaining_Slabs =:= [] of true -> 'EMPTY'; false -> 'LOW' end,
New_State = State#cf_state{fount=Remaining_Fount, fount_count=New_Fount_Count, reservoir=Remaining_Slabs, num_slabs=New_Num_Slabs},
case Pids of
[] -> reply({empty_reply, {get_pids, Num_Pids}}, New_State_Fn, New_State);
Expand Down
9 changes: 7 additions & 2 deletions src/cxy_fount_sup.erl
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
-behaviour(supervisor).

%% API
-export([start_link/2, start_link/3, start_link/4, start_link/5,start_link/6, get_regulator/1]).
-export([start_link/2, start_link/3, start_link/4, start_link/5, start_link/6,
get_fount/1, get_regulator/1]).

%% Supervisor callbacks
-export([init/1]).
Expand Down Expand Up @@ -94,7 +95,11 @@ start_link(Fount_Name, Fount_Behaviour, Init_Args, Slab_Size, Reservoir_Depth, N
make_sup_name(Fount_Name) ->
list_to_atom(atom_to_list(Fount_Name) ++ "_sup").

-spec get_regulator(pid()) -> pid().
-spec get_fount (pid()) -> pid().
-spec get_regulator (pid()) -> pid().

get_fount(Fount_Sup) ->
hd([Pid || {cxy_fount, Pid, worker, _Modules} <- supervisor:which_children(Fount_Sup)]).

get_regulator(Fount_Sup) ->
hd([Pid || {cxy_regulator, Pid, worker, _Modules} <- supervisor:which_children(Fount_Sup)]).
Expand Down
70 changes: 35 additions & 35 deletions test/epocxy/cxy_fount_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,9 @@ verify_full_fount(Slab_Size, Depth) ->
ct:comment(Case2), ct:log(Case2),
Full_Fount_Size = Depth * Slab_Size,
Pids1 = ?TM:get_pids(Fount, Full_Fount_Size),
'FULL' = verify_reservoir_is_full(Fount),
'FULL' = verify_reservoir_is_full(Fount, Depth),
Pids2 = ?TM:get_pids(Fount, Full_Fount_Size),
'FULL' = verify_reservoir_is_full(Fount),
'FULL' = verify_reservoir_is_full(Fount, Depth),

Case3 = "Verify that fetches get different pids",
ct:comment(Case3), ct:log(Case3),
Expand All @@ -120,34 +120,30 @@ verify_full_fount(Slab_Size, Depth) ->
true.

start_fount(Behaviour, Slab_Size, Depth) ->
{ok, Fount} = ?TM:start_link(Behaviour, Slab_Size, Depth),
'FULL' = verify_reservoir_is_full(Fount),
{ok, Sup} = cxy_fount_sup:start_link(Behaviour, [none], Slab_Size, Depth),
Fount = cxy_fount_sup:get_fount(Sup),
'FULL' = verify_reservoir_is_full(Fount, Depth),
Fount.

verify_reservoir_is_full(Fount) ->
verify_reservoir_is_full(Fount, 2000, 1).

verify_reservoir_is_full(Fount, Max_Tries, Count) ->
erlang:yield(),
verify_reservoir_is_full(Fount, Num_Of_Spawn_Slices) ->
Time_To_Sleep = (Num_Of_Spawn_Slices + 1) * 100,
timer:sleep(Time_To_Sleep), % 1/10th second per slab
Status = ?TM:get_status(Fount),
case {proplists:get_value(current_state, Status), Count} of
{State, Max_Tries} -> finish_full(Max_Tries, State, Status);
{'FULL', Count} -> finish_full( Count, 'FULL', Status);
_Not_Full_Yet -> verify_reservoir_is_full(Fount, Max_Tries, Count+1)
end.
Final_State = proplists:get_value(current_state, Status),
finish_full(Time_To_Sleep, Final_State, Status).

finish_full(Attempts, Final_State, Status) ->
ct:log("Looped ~p times before reservoir was ~p", [Attempts, Final_State]),
finish_full(Time_Slept, Final_State, Status) ->
ct:log("Slept ~p milliseconds before reservoir was ~p", [Time_Slept, Final_State]),
[FC, Num_Slabs, Max_Slabs, Slab_Size, Pid_Count, Max_Pids]
= [proplists:get_value(P, Status)
|| P <- [fount_count, slab_count, max_slabs, slab_size, pid_count, max_pids]],
Ok_Full_Count = (Max_Pids - Pid_Count) < Slab_Size,
Ok_Slab_Count = (Max_Slabs - 1) =:= Num_Slabs andalso FC > 0,

%% Provide extra data on failure
{true, true, FC, Num_Slabs, Slab_Size, Max_Pids, Final_State, Attempts}
{true, true, FC, Num_Slabs, Slab_Size, Max_Pids, Final_State, Time_Slept}
= {Ok_Full_Count, Ok_Slab_Count, FC, Num_Slabs, Slab_Size,
Max_Pids, Final_State, Attempts},
Max_Pids, Final_State, Time_Slept},
Final_State.


Expand All @@ -168,9 +164,9 @@ check_edge_pid_allocs(_Config) ->
ct:comment(Test_Complete), ct:log(Test_Complete),
ok.

verify_edge(Fount, Alloc_Size) ->
verify_edge(Fount, Alloc_Size, Depth) ->
Pids = ?TM:get_pids(Fount, Alloc_Size),
'FULL' = verify_reservoir_is_full(Fount),
'FULL' = verify_reservoir_is_full(Fount, Depth),
Pids.

verify_edges(Slab_Size, Depth) ->
Expand All @@ -180,17 +176,20 @@ verify_edges(Slab_Size, Depth) ->

Fount = start_fount(cxy_fount_hello_behaviour, Slab_Size, Depth),

Case2 = "Verify " ++ Fount_Dims ++ " fount allocation in slab multiples",
Multiples = [{N, N * Slab_Size} || N <- [1,2,3]],
Case2_Msg = "Verify ~s fount allocation in slab multiples ~p",
Case2 = lists:flatten(io_lib:format(Case2_Msg, [Fount_Dims, Multiples])),
ct:comment(Case2), ct:log(Case2),
Multiples = [N * Slab_Size || N <- [1,2,3]],
[Pids1, Pids2, Pids3]
= [verify_edge(Fount, Alloc_Size) || Alloc_Size <- Multiples],
= [verify_edge(Fount, Alloc_Size, Alloc_Depth)
|| {Alloc_Depth, Alloc_Size} <- Multiples],

%% Deviate from slab modulo arithmetic...
Pids4 = [?TM:get_pid(Fount), ?TM:get_pid(Fount)],

[Pids5, Pids6, Pids7]
= [verify_edge(Fount, Alloc_Size) || Alloc_Size <- Multiples],
= [verify_edge(Fount, Alloc_Size, Alloc_Depth)
|| {Alloc_Depth, Alloc_Size} <- Multiples],

%% Make sure all pids are unique
All_Pids = lists:append([Pids1, Pids2, Pids3, Pids4, Pids5, Pids6, Pids7]),
Expand All @@ -206,21 +205,22 @@ verify_edges(Slab_Size, Depth) ->
Pids9 = ?TM:get_pids(Fount, Get9_Count),
{Get8_Count, Get9_Count} = {length(Pids8), length(Pids9)},
true = sets:is_disjoint(sets:from_list(Pids8), sets:from_list(Pids9)),
'FULL' = verify_reservoir_is_full(Fount),
'FULL' = verify_reservoir_is_full(Fount, 1),

Max_Pids = Slab_Size * Depth,
Get10_Count = min(Max_Pids, round(Slab_Size * 2.4)),
Get11_Count = min(Max_Pids, round(Slab_Size * 1.7)),
Case4 = "Verify " ++ Fount_Dims ++ " allocation in > slab counts ("
++ integer_to_list(Get10_Count) ++ "," ++ integer_to_list(Get11_Count) ++ ")",
ct:comment(Case4), ct:log(Case4),
'FULL' = verify_reservoir_is_full(Fount),
'FULL' = verify_reservoir_is_full(Fount, 1),
Pids10 = ?TM:get_pids(Fount, Get10_Count),
'FULL' = verify_reservoir_is_full(Fount),
'FULL' = verify_reservoir_is_full(Fount, 3),
Pids11 = ?TM:get_pids(Fount, Get11_Count),
{Get10_Count, Get11_Count} = {length(Pids10), length(Pids11)},
{{Get10_Count, Get10_Count}, {Get11_Count, Get11_Count}}
= {{Get10_Count, length(Pids10)}, {Get11_Count, length(Pids11)}},
true = sets:is_disjoint(sets:from_list(Pids10), sets:from_list(Pids11)),
'FULL' = verify_reservoir_is_full(Fount),
'FULL' = verify_reservoir_is_full(Fount, 2),

Test_Complete = "Fount " ++ Fount_Dims ++ " pid allocation verified",
ct:comment(Test_Complete), ct:log(Test_Complete),
Expand Down Expand Up @@ -265,18 +265,18 @@ verify_pids(Fount, Num_Pids, Slab_Size, Depth, Task_Or_Get) ->
case Num_Pids of
Num_Pids when Num_Pids > Slab_Size * Depth ->
[] = Pids,
'FULL' = verify_reservoir_is_full(Fount);
'FULL' = verify_reservoir_is_full(Fount, 1);
Num_Pids when Num_Pids > Slab_Size * (Depth - 1) ->
case length(Pids) of
0 -> 'FULL' = verify_reservoir_is_full(Fount);
0 -> 'FULL' = verify_reservoir_is_full(Fount, 1);
Num_Pids ->
Num_Pids = length(Pids),
_ = unlink_workers(Pids, Task_Or_Get),
'FULL' = verify_reservoir_is_full(Fount)
'FULL' = verify_reservoir_is_full(Fount, Num_Pids div Slab_Size)
end;
Num_Pids ->
_ = unlink_workers(Pids, Task_Or_Get),
'FULL' = verify_reservoir_is_full(Fount)
'FULL' = verify_reservoir_is_full(Fount, Num_Pids div Slab_Size)
end.

unlink_workers(Pids, Task_Or_Get) ->
Expand Down Expand Up @@ -337,8 +337,8 @@ report_speed(_Config) ->

lists:foreach(
fun({Slab_Size, Num_Slabs}) ->
{ok, Fount} = ?TM:start_link(cxy_fount_hello_behaviour, Slab_Size, Num_Slabs),
'FULL' = verify_reservoir_is_full(Fount), % Give it a chance to fill up
Fount = start_fount(cxy_fount_hello_behaviour, Slab_Size, Num_Slabs),
'FULL' = verify_reservoir_is_full(Fount, Num_Slabs), % Give it a chance to fill up
ct:log("Spawn rate per process with ~p pids for ~p slabs: ~p microseconds",
[Slab_Size, Num_Slabs, cxy_fount:get_spawn_rate_per_process(Fount)]),
ct:log("Spawn rate per slab with ~p pids for ~p slabs: ~p microseconds",
Expand Down
2 changes: 1 addition & 1 deletion test/epocxy/cxy_fount_hello_behaviour.erl
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,6 @@ say_to(Worker, Msg) ->
Now1 = now(),
Worker ! {Ref, self(), Msg},
%% now() is used to guarantee monotonic increasing time
receive {Ref, goodbye, Now2} -> true = Now1 < Now2
receive {Ref, goodbye, Now2, {elapsed, _Elapsed}} -> true = Now1 < Now2
after 1000 -> throw(say_hello_timeout)
end.

0 comments on commit 5c59ea1

Please sign in to comment.