From 17cba4b82b9b483725312f616a9539230ece3b7e Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Fri, 21 Nov 2014 00:12:12 +0100 Subject: [PATCH 01/70] Changed egol_cell to spawn a process to collect from its neighbours --- src/egol_cell.erl | 162 +++++++++++++++++++++++++++++++--------------- 1 file changed, 111 insertions(+), 51 deletions(-) diff --git a/src/egol_cell.erl b/src/egol_cell.erl index 10a82f3..e70f37a 100644 --- a/src/egol_cell.erl +++ b/src/egol_cell.erl @@ -13,12 +13,15 @@ pause/1, step/1]). +-type mode() :: 'run' | 'step'. -record(state, { xy, dim, content=0 :: 0..1, time=0, + collector :: pid(), + mode=step :: mode(), future= [] :: [{pid(), pos_integer()}], history=[], neighbours}). @@ -74,87 +77,144 @@ cmd_sync(To, Cmd) -> %% states init(#state{xy=XY}=State) -> reg(XY), - idle(State). + loop(State). -idle(State) -> +loop(#state{xy=XY, time=T, content=Content, collector=Collector, + future=Future, history=History}=State) -> receive {set, NewContent} -> - idle(State#state{content=NewContent}); + loop(State#state{content=NewContent}); {From, {get, Time}} -> case content_at(Time, State) of future -> - idle(State#state{future=[{From, Time} | State#state.future]}); + loop(State#state{future=[{From, Time} | State#state.future]}); C -> From ! {cell_content, C}, - idle(State) + loop(State) end; {From, history} -> #state{history=History} = State, From ! History, - idle(State); + loop(State); {From, time} -> From ! State#state.time, - idle(State); + loop(State); run -> - running(State); + case is_collector_running(State) of + true -> + loop(State#state{mode=run}); + false -> + NextState = start_collector(State), + loop(NextState#state{mode=run}) + end; step -> - NewState=run_step(State), - idle(NewState); + case is_collector_running(State) of + true -> + loop(State#state{mode=step}); + false -> + NewState = start_collector(State), + loop(NewState#state{mode=step}) + end; pause -> - idle(State) + loop(State#state{mode=step}); + {Collector, {next_content, NextContent}} -> + NewFuture = process_future(XY, T+1, NextContent, Future), + lager:info("Cell ~p changing to ~p for time ~p", [XY, NextContent, T+1]), + NextState = State#state{content=NextContent, + time=T+1, + history=[{T, Content}|History], + future=NewFuture}, + case State#state.mode of + step -> + loop(NextState); + run -> + loop(start_collector(NextState)) + end end. -running(#state{time=T, neighbours=Neighbours}=State) -> - query_neighbours(T, Neighbours), - NewState = collecting(State, 0, neighbours_at(T, Neighbours)), - receive - pause -> - idle(NewState); - step -> - NextState = collecting(NewState, 0, Neighbours), - idle(NextState) - after - 0 -> - running(NewState) - end. +is_collector_running(#state{collector=Collector}) -> + is_pid(Collector) andalso is_process_alive(Collector). -run_step(#state{time=T, neighbours=Neighbours}=State) -> - query_neighbours(T, Neighbours), - collecting(State, 0, neighbours_at(T, Neighbours)). - -collecting(#state{xy=XY, content=Content, time=T, history=History, future=Future}=State, NeighbourCount, []) -> - NextContent = next_content(Content, NeighbourCount), - NewFuture = process_future(XY, T+1, NextContent, Future), - lager:info("Cell ~p changing to ~p for time ~p", [XY, NextContent, T+1]), - State#state{content=NextContent, - time=T+1, - history=[{T, Content}|History], - future=NewFuture}; -collecting(#state{}=State, NeighbourCount, WaitingOn) -> +start_collector(#state{time=T, neighbours=Neighbours, content=Content}=State) -> + Cell = self(), + Collector = spawn( fun () -> + collector_init(T, Neighbours, Cell, Content) + end ), + State#state{collector=Collector}. + +collector_init(Time, Neighbours, Cell, Content) -> + query_neighbours(Time, Neighbours), + collector_loop(neighbours_at(Time, Neighbours), 0, Cell, Content). + +collector_loop([], NeighbourCount, Cell, Content) -> + Cell ! {self(), {next_content, next_content(Content, NeighbourCount)}}; +collector_loop(WaitingOn, NeighbourCount, Cell, Content) -> receive - {From, {get, Time}} -> - case content_at(Time, State) of - future -> - collecting(State#state{future=[{From, Time}|State#state.future]}, - NeighbourCount, WaitingOn); - C -> - From ! {cell_content, C}, - collecting(State, NeighbourCount, WaitingOn) - end; {cell_content, {{{_,_},_}=XYatT, NeighbourContent}} -> case lists:member(XYatT, WaitingOn) of true -> - collecting(State, NeighbourCount + NeighbourContent, lists:delete(XYatT, WaitingOn)); + collector_loop(lists:delete(XYatT, WaitingOn), + NeighbourCount + NeighbourContent, + Cell, Content); false %% ignore messages we are not waiting for -> - collecting(State, NeighbourCount, WaitingOn) + collector_loop(WaitingOn, NeighbourCount, Cell, Content) end; - {set, NewContent} %% fun stuff can happen if you change the state while running... - -> - collecting(State#state{content=NewContent}, NeighbourCount, WaitingOn) + {set, NewContent} -> %% this could be fun... + collector_loop(WaitingOn, NeighbourCount, Cell, NewContent) end. +%% running(#state{time=T, neighbours=Neighbours}=State) -> +%% query_neighbours(T, Neighbours), +%% NewState = collecting(State, 0, neighbours_at(T, Neighbours)), +%% receive +%% pause -> +%% loop(NewState); +%% step -> +%% NextState = collecting(NewState, 0, Neighbours), +%% loop(NextState) +%% after +%% 0 -> +%% running(NewState) +%% end. + +%% run_step(#state{time=T, neighbours=Neighbours}=State) -> +%% query_neighbours(T, Neighbours), +%% collecting(State, 0, neighbours_at(T, Neighbours)). + +%% collecting(#state{xy=XY, content=Content, time=T, history=History, future=Future}=State, NeighbourCount, []) -> +%% NextContent = next_content(Content, NeighbourCount), +%% NewFuture = process_future(XY, T+1, NextContent, Future), +%% lager:info("Cell ~p changing to ~p for time ~p", [XY, NextContent, T+1]), +%% State#state{content=NextContent, +%% time=T+1, +%% history=[{T, Content}|History], +%% future=NewFuture}; +%% collecting(#state{}=State, NeighbourCount, WaitingOn) -> +%% receive +%% {From, {get, Time}} -> +%% case content_at(Time, State) of +%% future -> +%% collecting(State#state{future=[{From, Time}|State#state.future]}, +%% NeighbourCount, WaitingOn); +%% C -> +%% From ! {cell_content, C}, +%% collecting(State, NeighbourCount, WaitingOn) +%% end; +%% {cell_content, {{{_,_},_}=XYatT, NeighbourContent}} -> +%% case lists:member(XYatT, WaitingOn) of +%% true -> +%% collecting(State, NeighbourCount + NeighbourContent, lists:delete(XYatT, WaitingOn)); +%% false %% ignore messages we are not waiting for +%% -> +%% collecting(State, NeighbourCount, WaitingOn) +%% end; +%% {set, NewContent} %% fun stuff can happen if you change the state while running... +%% -> +%% collecting(State#state{content=NewContent}, NeighbourCount, WaitingOn) +%% end. + process_future(XY, Time, Content, Future) -> {Ready, NewFuture} = lists:partition( fun({_Pid,T}) -> T == Time From 05115743cd2a2f6e7b345abd648d04bb4979292e Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Sun, 30 Nov 2014 23:23:08 +0100 Subject: [PATCH 02/70] Add initial content for cell and run_until feature. --- src/egol.erl | 24 +++++++++++++++++++----- src/egol_cell.erl | 45 +++++++++++++++++++++++++++++++++++++-------- 2 files changed, 56 insertions(+), 13 deletions(-) diff --git a/src/egol.erl b/src/egol.erl index 7adf1eb..2fe6c47 100644 --- a/src/egol.erl +++ b/src/egol.erl @@ -1,10 +1,11 @@ -module(egol). - + -export([start/3, init/3, step/0, run/0, run/1, + run_until/1, pause/0, print/1, print_last/0, @@ -22,15 +23,16 @@ start(N, M, InitialCells) -> init(N, M, InitialCells) -> AllCells = all_cells(N, M), - [egol_cell:start(XY, {N,M}) + [start_cell(XY, {N,M}, lists:member(XY, InitialCells)) || XY <- AllCells ], - timer:sleep(10), - fill_cells(InitialCells), register(?MODULE, self()), loop(#state{size_x=N, size_y=M}). - +start_cell(XY, Dim, true) -> + egol_cell:start(XY, Dim, 1); +start_cell(XY, Dim, false) -> + egol_cell:start(XY, Dim, 0). step() -> ?MODULE ! step. @@ -43,6 +45,9 @@ run(Time) -> timer:sleep(Time), pause(). +run_until(EndTime) -> + ?MODULE ! {run_until, EndTime}. + pause() -> ?MODULE ! pause. @@ -63,6 +68,9 @@ loop(State) -> run -> run_cells(all_cells(State)), loop(State); + {run_until, EndTime} -> + run_cells_until(all_cells(State), EndTime), + loop(State); pause -> pause_cells(all_cells(State)), loop(State); @@ -102,6 +110,12 @@ step_cells(Cells) -> lists:foreach(fun egol_cell:step/1, Cells). run_cells(Cells) -> lists:foreach(fun egol_cell:run/1, Cells). +run_cells_until(Cells, EndTime) -> + lists:foreach(fun (Cell) -> + egol_cell:run_until(Cell, EndTime) + end, + Cells). + pause_cells(Cells) -> lists:foreach(fun egol_cell:pause/1, Cells). diff --git a/src/egol_cell.erl b/src/egol_cell.erl index e70f37a..ddc4b4b 100644 --- a/src/egol_cell.erl +++ b/src/egol_cell.erl @@ -2,7 +2,7 @@ -compile([{parse_transform, lager_transform}]). --export([start/2, +-export([start/3, init/1]). -export([set/2, get/2, @@ -10,6 +10,7 @@ history_sync/1, time_sync/1, run/1, + run_until/2, pause/1, step/1]). @@ -20,17 +21,19 @@ dim, content=0 :: 0..1, time=0, - collector :: pid(), + collector :: pid() | 'undefined', + pacer :: pid() | 'undefined', mode=step :: mode(), future= [] :: [{pid(), pos_integer()}], history=[], neighbours}). -start({X,Y}=XY, {DimX, DimY}=Dim) when X < DimX; - 0 =< X; - Y < DimY; - 0 =< Y -> - spawn(?MODULE, init, [#state{xy=XY, dim=Dim, content=0, +start({X,Y}=XY, {DimX, DimY}=Dim, InitialContent) + when X < DimX; + 0 =< X; + Y < DimY; + 0 =< Y -> + spawn(?MODULE, init, [#state{xy=XY, dim=Dim, content=InitialContent, neighbours=neighbours(XY, Dim)}]). @@ -52,6 +55,9 @@ time_sync(To) -> run(To) -> cmd(To, run). +run_until(To, EndTime) -> + cmd(To, {run_until, EndTime}). + pause(To) -> cmd(To, pause). step(To) -> cmd(To, step). @@ -116,6 +122,19 @@ loop(#state{xy=XY, time=T, content=Content, collector=Collector, NewState = start_collector(State), loop(NewState#state{mode=step}) end; + {run_until, EndTime} -> + case is_collector_running(State) of + true -> + loop(State#state{mode={run_until, EndTime}}); + false -> + case T < EndTime of + true -> + NewState = start_collector(State), + loop(NewState#state{mode={run_until, EndTime}}); + false -> + loop(State) + end + end; pause -> loop(State#state{mode=step}); {Collector, {next_content, NextContent}} -> @@ -129,13 +148,23 @@ loop(#state{xy=XY, time=T, content=Content, collector=Collector, step -> loop(NextState); run -> - loop(start_collector(NextState)) + loop(start_collector(NextState)); + {run_until, EndTime} -> + case NextState#state.time < EndTime of + true -> + loop(start_collector(NextState)); + false -> + loop(NextState#state{mode=step}) + end end end. is_collector_running(#state{collector=Collector}) -> is_pid(Collector) andalso is_process_alive(Collector). +is_pacer_running(#state{pacer=Pacer}) -> + is_pid(Pacer) andalso is_process_alive(Pacer). + start_collector(#state{time=T, neighbours=Neighbours, content=Content}=State) -> Cell = self(), Collector = spawn( fun () -> From 66ed85fe7e13d74389cfc2048997f33a9c01b4ec Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Sun, 30 Nov 2014 23:24:23 +0100 Subject: [PATCH 03/70] Removed dead code. --- src/egol_cell.erl | 49 ----------------------------------------------- 1 file changed, 49 deletions(-) diff --git a/src/egol_cell.erl b/src/egol_cell.erl index ddc4b4b..3267b73 100644 --- a/src/egol_cell.erl +++ b/src/egol_cell.erl @@ -194,55 +194,6 @@ collector_loop(WaitingOn, NeighbourCount, Cell, Content) -> collector_loop(WaitingOn, NeighbourCount, Cell, NewContent) end. -%% running(#state{time=T, neighbours=Neighbours}=State) -> -%% query_neighbours(T, Neighbours), -%% NewState = collecting(State, 0, neighbours_at(T, Neighbours)), -%% receive -%% pause -> -%% loop(NewState); -%% step -> -%% NextState = collecting(NewState, 0, Neighbours), -%% loop(NextState) -%% after -%% 0 -> -%% running(NewState) -%% end. - -%% run_step(#state{time=T, neighbours=Neighbours}=State) -> -%% query_neighbours(T, Neighbours), -%% collecting(State, 0, neighbours_at(T, Neighbours)). - -%% collecting(#state{xy=XY, content=Content, time=T, history=History, future=Future}=State, NeighbourCount, []) -> -%% NextContent = next_content(Content, NeighbourCount), -%% NewFuture = process_future(XY, T+1, NextContent, Future), -%% lager:info("Cell ~p changing to ~p for time ~p", [XY, NextContent, T+1]), -%% State#state{content=NextContent, -%% time=T+1, -%% history=[{T, Content}|History], -%% future=NewFuture}; -%% collecting(#state{}=State, NeighbourCount, WaitingOn) -> -%% receive -%% {From, {get, Time}} -> -%% case content_at(Time, State) of -%% future -> -%% collecting(State#state{future=[{From, Time}|State#state.future]}, -%% NeighbourCount, WaitingOn); -%% C -> -%% From ! {cell_content, C}, -%% collecting(State, NeighbourCount, WaitingOn) -%% end; -%% {cell_content, {{{_,_},_}=XYatT, NeighbourContent}} -> -%% case lists:member(XYatT, WaitingOn) of -%% true -> -%% collecting(State, NeighbourCount + NeighbourContent, lists:delete(XYatT, WaitingOn)); -%% false %% ignore messages we are not waiting for -%% -> -%% collecting(State, NeighbourCount, WaitingOn) -%% end; -%% {set, NewContent} %% fun stuff can happen if you change the state while running... -%% -> -%% collecting(State#state{content=NewContent}, NeighbourCount, WaitingOn) -%% end. process_future(XY, Time, Content, Future) -> {Ready, NewFuture} = lists:partition( fun({_Pid,T}) -> From 07a23ec198cbe2c1be3e9b3e9050078d41fafad7 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Mon, 1 Dec 2014 09:58:55 +0100 Subject: [PATCH 04/70] Add ability to kill cells by coordinates. --- src/egol.erl | 9 ++++++++- src/egol_cell.erl | 11 ++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/egol.erl b/src/egol.erl index 2fe6c47..df35740 100644 --- a/src/egol.erl +++ b/src/egol.erl @@ -7,16 +7,20 @@ run/1, run_until/1, pause/0, + kill/1, print/1, print_last/0, print_lag/0]). -export([test/1]). +-type cell_coordinates() :: {integer(), integer()}. -record(state, {size_x, - size_y}). + size_y, + cells :: [{pid(), reference(), cell_coordinates()}] + }). start(N, M, InitialCells) -> spawn(?MODULE, init, [N, M, InitialCells]). @@ -51,6 +55,9 @@ run_until(EndTime) -> pause() -> ?MODULE ! pause. +kill(XY) -> + egol_cell:kill(XY). + print(T) -> ?MODULE ! {print, T}. diff --git a/src/egol_cell.erl b/src/egol_cell.erl index 3267b73..2a7f8e0 100644 --- a/src/egol_cell.erl +++ b/src/egol_cell.erl @@ -3,7 +3,8 @@ -compile([{parse_transform, lager_transform}]). -export([start/3, - init/1]). + init/1, + kill/1]). -export([set/2, get/2, get_sync/2, @@ -37,6 +38,14 @@ start({X,Y}=XY, {DimX, DimY}=Dim, InitialContent) neighbours=neighbours(XY, Dim)}]). +kill(XY) -> + case gproc:where({n, l, XY}) of + undefined -> + ok; + Pid when is_pid(Pid) -> + exit(Pid, kill) + end. + set(To, State) -> cmd(To, {set, State}). From e9a7f0f5e142bff4da782e7102fee7d512458598 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Mon, 1 Dec 2014 10:55:08 +0100 Subject: [PATCH 05/70] Add egol:mode/0 and egol:max_time/0. --- src/egol.erl | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/src/egol.erl b/src/egol.erl index df35740..64099fc 100644 --- a/src/egol.erl +++ b/src/egol.erl @@ -6,6 +6,8 @@ run/0, run/1, run_until/1, + max_time/0, + mode/0, pause/0, kill/1, print/1, @@ -14,12 +16,10 @@ -export([test/1]). --type cell_coordinates() :: {integer(), integer()}. - -record(state, {size_x, size_y, - cells :: [{pid(), reference(), cell_coordinates()}] + mode }). start(N, M, InitialCells) -> @@ -52,6 +52,22 @@ run(Time) -> run_until(EndTime) -> ?MODULE ! {run_until, EndTime}. +max_time() -> + ?MODULE ! {max_time, self()}, + receive + MaxTime -> + MaxTime + end. + +mode() -> + ?MODULE ! {mode, self()}, + receive + Mode -> + Mode + end. + + + pause() -> ?MODULE ! pause. @@ -84,6 +100,12 @@ loop(State) -> {print,T} -> print(State#state.size_x, State#state.size_y, T), loop(State); + {mode, From} -> + From ! State#state.mode, + loop(State); + {max_time, From} -> + From ! maximum_time(State), + loop(State); print_last -> MinTime = minimum_time(State), io:format("Time is ~p.~n", [MinTime]), @@ -95,6 +117,15 @@ loop(State) -> end. +maximum_time(State) -> + AllCellPids = [ egol_cell:where(Cell) + || Cell <- all_cells(State) ], + LiveCells = lists:filter( fun erlang:is_pid/1, AllCellPids), + AllTimes = [ egol_cell:time_sync(Cell) + || Cell <- LiveCells ], + lists:max(AllTimes). + + minimum_time(State) -> Times = all_times(State), lists:min(Times). From 10edcdc2afccfb3327dca0b6431374d12ee3478c Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Mon, 1 Dec 2014 11:38:05 +0100 Subject: [PATCH 06/70] Add supervisor and manager process. --- src/egol.erl | 23 ++++++++++------- src/egol_cell.erl | 20 ++++++++++++--- src/egol_cell_mgr.erl | 57 +++++++++++++++++++++++++++++++++++++++++++ src/egol_cell_sup.erl | 45 ++++++++++++++++++++++++++++++++++ 4 files changed, 133 insertions(+), 12 deletions(-) create mode 100644 src/egol_cell_mgr.erl create mode 100644 src/egol_cell_sup.erl diff --git a/src/egol.erl b/src/egol.erl index 64099fc..b685bbd 100644 --- a/src/egol.erl +++ b/src/egol.erl @@ -27,16 +27,20 @@ start(N, M, InitialCells) -> init(N, M, InitialCells) -> AllCells = all_cells(N, M), - [start_cell(XY, {N,M}, lists:member(XY, InitialCells)) - || XY <- AllCells ], + Cells = [{start_cell(XY, {N,M}, lists:member(XY, InitialCells)), XY} + || XY <- AllCells ], + egol_cell_mgr:start(Cells), register(?MODULE, self()), loop(#state{size_x=N, - size_y=M}). + size_y=M, + mode=step}). start_cell(XY, Dim, true) -> - egol_cell:start(XY, Dim, 1); + {ok, Pid} = egol_cell_sup:start_cell(XY, Dim, 1), + Pid; start_cell(XY, Dim, false) -> - egol_cell:start(XY, Dim, 0). + {ok, Pid} = egol_cell_sup:start_cell(XY, Dim, 0), + Pid. step() -> ?MODULE ! step. @@ -87,16 +91,16 @@ loop(State) -> receive step -> step_cells(all_cells(State)), - loop(State); + loop(State#state{mode=run}); run -> run_cells(all_cells(State)), - loop(State); + loop(State#state{mode=run}); {run_until, EndTime} -> run_cells_until(all_cells(State), EndTime), - loop(State); + loop(State#state{mode={run_until,EndTime}}); pause -> pause_cells(all_cells(State)), - loop(State); + loop(State#state{mode=step}); {print,T} -> print(State#state.size_x, State#state.size_y, T), loop(State); @@ -234,6 +238,7 @@ test(1) -> test(2) -> [{0,0}, {1,0}, {2,0}, {2,1},{1,2}]; test(3) -> + egol_cell_sup:start_link(), start(8,8,test(2)). diff --git a/src/egol_cell.erl b/src/egol_cell.erl index 2a7f8e0..2210d24 100644 --- a/src/egol_cell.erl +++ b/src/egol_cell.erl @@ -3,8 +3,10 @@ -compile([{parse_transform, lager_transform}]). -export([start/3, + start_link/3, init/1, - kill/1]). + kill/1, + where/1]). -export([set/2, get/2, get_sync/2, @@ -37,9 +39,21 @@ start({X,Y}=XY, {DimX, DimY}=Dim, InitialContent) spawn(?MODULE, init, [#state{xy=XY, dim=Dim, content=InitialContent, neighbours=neighbours(XY, Dim)}]). +start_link({X,Y}=XY, {DimX, DimY}=Dim, InitialContent) + when X < DimX; + 0 =< X; + Y < DimY; + 0 =< Y -> + Pid = spawn_link(?MODULE, init, [#state{xy=XY, dim=Dim, content=InitialContent, + neighbours=neighbours(XY, Dim)}]), + {ok, Pid}. + + +where(XY) -> + gproc:where(cell_name(XY)). kill(XY) -> - case gproc:where({n, l, XY}) of + case where(XY) of undefined -> ok; Pid when is_pid(Pid) -> @@ -76,7 +90,7 @@ step(To) -> cmd(To, step). cmd(To, Cmd) when is_pid(To) -> To ! Cmd; cmd(To, Cmd) when is_tuple(To) -> - cmd( gproc:where(cell_name(To)), Cmd ); + cmd( where(To), Cmd ); cmd(To, Cmd) -> lager:error("Incorrect To:~p with Cmd:~p", [To, Cmd]). diff --git a/src/egol_cell_mgr.erl b/src/egol_cell_mgr.erl new file mode 100644 index 0000000..ae41617 --- /dev/null +++ b/src/egol_cell_mgr.erl @@ -0,0 +1,57 @@ +-module(egol_cell_mgr). + +-export([start/1, + init/1]). + + +-type cell_coordinates() :: {integer(), integer()}. + + +-record(state, + {cells :: [{pid(), reference(), cell_coordinates()}] + }). + +start(Cells) -> + spawn(?MODULE, init, [Cells]). + + + +init(Cells) -> + MyCells = [ begin + Ref = erlang:monitor(process, Pid), + {Pid, Ref, XY} + end + || {Pid, XY} <- Cells ], + loop(#state{cells=MyCells}). + +loop(#state{cells=Cells}=State) -> + receive + {'DOWN', Ref, process, _Pid, _Info} -> + {value, {_,_, XY}, Rest} = lists:keytake(Ref, 2, Cells), + NewPid = await_restart(XY), + NewRef = erlang:monitor(process, NewPid), + NextState = State#state{cells=[{NewPid, NewRef, XY}|Rest]}, + spawn ( fun () -> pacer(NewPid) end ), + loop(NextState) + end. + +pacer(Pid) -> + case egol:mode() of + step -> + EndTime = egol:max_time(), + egol_cell:run_until(Pid, EndTime); + {run_until, EndTime} -> + egol_cell:run_until(Pid, EndTime); + run -> + egol_cell:run(Pid) + end. + +await_restart(XY) -> + timer:sleep(10), + case egol_cell:where(XY) of + undefined -> + await_restart(XY); + Pid -> + Pid + end. + diff --git a/src/egol_cell_sup.erl b/src/egol_cell_sup.erl new file mode 100644 index 0000000..05bbd06 --- /dev/null +++ b/src/egol_cell_sup.erl @@ -0,0 +1,45 @@ +-module(egol_cell_sup). + +-behaviour(supervisor). + +%% API +-export([start_link/0, + start_cell/3]). + +%% Supervisor callbacks +-export([init/1]). + +-define(SERVER, ?MODULE). + +%%%=================================================================== +%%% API functions +%%%=================================================================== + +start_link() -> + supervisor:start_link({local, ?SERVER}, ?MODULE, []). + +start_cell(XY, Dim, InitialContent) -> + supervisor:start_child(?SERVER, [XY, Dim, InitialContent]). + +%%%=================================================================== +%%% Supervisor callbacks +%%%=================================================================== + +init([]) -> + RestartStrategy = simple_one_for_one, + MaxRestarts = 1000, + MaxSecondsBetweenRestarts = 3600, + + SupFlags = {RestartStrategy, MaxRestarts, MaxSecondsBetweenRestarts}, + + Restart = permanent, + Shutdown = 2000, + Type = worker, + + AChild = {egol_cell, {egol_cell, start_link, []}, + Restart, Shutdown, Type, [egol_cell]}, + + {ok, {SupFlags, [AChild]}}. + + + From 1d33955f6dc32e20b42b68fced46f37b6f694b38 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Mon, 1 Dec 2014 12:11:22 +0100 Subject: [PATCH 07/70] Added extra test. NOTE: when running freely there will be deadlocks, so more cleanup is needed. --- src/egol.erl | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/egol.erl b/src/egol.erl index b685bbd..b2b00bf 100644 --- a/src/egol.erl +++ b/src/egol.erl @@ -91,7 +91,7 @@ loop(State) -> receive step -> step_cells(all_cells(State)), - loop(State#state{mode=run}); + loop(State#state{mode=step}); run -> run_cells(all_cells(State)), loop(State#state{mode=run}); @@ -239,7 +239,13 @@ test(2) -> [{0,0}, {1,0}, {2,0}, {2,1},{1,2}]; test(3) -> egol_cell_sup:start_link(), - start(8,8,test(2)). + start(8,8,test(2)); +test(4) -> + test(3), + timer:sleep(50), + run(200), + kill({0,0}), + run(200). From 68b3d9d02643e826fea18473ab1cafd2cddebcba Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Sun, 7 Dec 2014 13:40:00 +0000 Subject: [PATCH 08/70] Removed dead function is_pacer_running. --- src/egol_cell.erl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/egol_cell.erl b/src/egol_cell.erl index 2210d24..ac789ac 100644 --- a/src/egol_cell.erl +++ b/src/egol_cell.erl @@ -185,8 +185,6 @@ loop(#state{xy=XY, time=T, content=Content, collector=Collector, is_collector_running(#state{collector=Collector}) -> is_pid(Collector) andalso is_process_alive(Collector). -is_pacer_running(#state{pacer=Pacer}) -> - is_pid(Pacer) andalso is_process_alive(Pacer). start_collector(#state{time=T, neighbours=Neighbours, content=Content}=State) -> Cell = self(), From d3d3813c5e3c9fac174428234026f68352aed65e Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Sun, 7 Dec 2014 13:40:24 +0000 Subject: [PATCH 09/70] Add test for NDC London 2014 talk example. --- src/egol.erl | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/egol.erl b/src/egol.erl index b2b00bf..4da6691 100644 --- a/src/egol.erl +++ b/src/egol.erl @@ -245,7 +245,26 @@ test(4) -> timer:sleep(50), run(200), kill({0,0}), - run(200). + run(200); +test(5) -> + test(3), + timer:sleep(50), + run(), + timer:sleep(200), + kill({1,1}), + timer:sleep(200), + pause(), + [ kill(C) + || C <- [{0,2}, {1,2}, {2,2}, + {0,1}, {1,1}, {2,1}, + {0,0}, {1,0}, {2,0}]]; +test(6) -> + InitialCells = [{2,3}, {2,5}, + {3,2}, {3,3}, {3,4}, + {4,1}, {4,5}], + egol_cell_sup:start_link(), + start(7,6, InitialCells). + From cb3918a53f31e980abaca6647cef460c329c1b39 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Sun, 7 Dec 2014 13:42:02 +0000 Subject: [PATCH 10/70] Add readme to explain what the repo is about. --- README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..9249f34 --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +This repo originates from this article (link to NDC article) and the associated talk +at NDC London 2014 (link to slideshare). + +Conway's Game of Life cell automaton is used to show how to think like an Erlanger. + +The principle of using asynchronous message passing and thinking in terms of +protocols describing the interactions between processes is applicable to a number of +settings, not just Erlang, but the overlay of using Erlang supervision is much easier +and straightforward to do in Erlang. + +The contents of the repo has moved on from the initial version used for the article +and the talk at NDC - even the talk code was improved from the article's - but I have +tried to tag relevant revisions to make it easier for you to cherry pick a particular +stage in the evolution of the code. + +The wiki for this repo contains a more in-depth explanation of the thinking process +and the tools used than what you will find here with the code. + +Notable tags: +* ndc1 - tag for the NDC article's code. + +Notable branches: +* permanent_collector - instead of spawning a new collector for each time step, the + collector process is re-used. It saves so little resources (5%) that it is not + worth the hassle to do it. + From 4ffb292cc2be39dc8ca470d3d7dcacd14237def9 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Mon, 8 Dec 2014 22:40:15 +0100 Subject: [PATCH 11/70] egol_cell changed to gen_server --- src/egol.erl | 11 +- src/egol_cell.erl | 264 +++++++++++++++++++++++++++------------------- 2 files changed, 161 insertions(+), 114 deletions(-) diff --git a/src/egol.erl b/src/egol.erl index 4da6691..3ad2235 100644 --- a/src/egol.erl +++ b/src/egol.erl @@ -125,7 +125,7 @@ maximum_time(State) -> AllCellPids = [ egol_cell:where(Cell) || Cell <- all_cells(State) ], LiveCells = lists:filter( fun erlang:is_pid/1, AllCellPids), - AllTimes = [ egol_cell:time_sync(Cell) + AllTimes = [ egol_cell:time(Cell) || Cell <- LiveCells ], lists:max(AllTimes). @@ -139,7 +139,7 @@ all_times(State) -> [ T || {_, T} <- Tagged ]. all_times_tagged(State) -> - [ {XY, egol_cell:time_sync(XY)} + [ {XY, egol_cell:time(XY)} || XY <- all_cells(State) ]. @@ -224,11 +224,8 @@ cells_at(Time, Cells) -> [ {C, Time} || C <- Cells ]. query_cells(CellsAtT) -> - [ begin - {_, C} = egol_cell:get_sync(XY, T), - {XY, C} - end - || {XY, T} <- CellsAtT ]. + [ {XY, egol_cell:get(XY, T)} + || {XY, T} <- CellsAtT]. cell_content(XY, Board) -> proplists:get_value(XY, Board). diff --git a/src/egol_cell.erl b/src/egol_cell.erl index ac789ac..d867a2d 100644 --- a/src/egol_cell.erl +++ b/src/egol_cell.erl @@ -2,20 +2,36 @@ -compile([{parse_transform, lager_transform}]). +-behaviour(gen_server). + -export([start/3, start_link/3, - init/1, kill/1, where/1]). + +%% gen_server callbacks +-export([init/1, + handle_call/3, + handle_cast/2, + handle_info/2, + terminate/2, + code_change/3]). + + -export([set/2, get/2, - get_sync/2, - history_sync/1, - time_sync/1, + query_content/2, + history/1, + time/1, run/1, run_until/2, pause/1, step/1]). + +-type cell_content() :: 0 | 1. +-type cell_name() :: {integer(), integer()}. +-type time() :: integer(). + -type mode() :: 'run' | 'step'. @@ -31,22 +47,26 @@ history=[], neighbours}). +%% @todo use via naming with gproc start({X,Y}=XY, {DimX, DimY}=Dim, InitialContent) when X < DimX; 0 =< X; Y < DimY; 0 =< Y -> - spawn(?MODULE, init, [#state{xy=XY, dim=Dim, content=InitialContent, - neighbours=neighbours(XY, Dim)}]). + gen_server:start(?MODULE, + #state{xy=XY, dim=Dim, content=InitialContent, + neighbours=neighbours(XY, Dim)}, + []). start_link({X,Y}=XY, {DimX, DimY}=Dim, InitialContent) when X < DimX; 0 =< X; Y < DimY; 0 =< Y -> - Pid = spawn_link(?MODULE, init, [#state{xy=XY, dim=Dim, content=InitialContent, - neighbours=neighbours(XY, Dim)}]), - {ok, Pid}. + gen_server:start_link(?MODULE, + #state{xy=XY, dim=Dim, content=InitialContent, + neighbours=neighbours(XY, Dim)}, + []). where(XY) -> @@ -60,128 +80,158 @@ kill(XY) -> exit(Pid, kill) end. -set(To, State) -> - cmd(To, {set, State}). +set(Cell, Content) -> + cast(Cell, {set, Content}). -get(To, Time) -> - cmd(To, {self(), {get, Time}}). +%% ask the cell to send a message back with its contents at the given Time. +query_content(Cell, Time) -> + cast(Cell, {query_content, Time, self()}). -get_sync(To, Time) -> - {cell_content, C} = cmd_sync(To, {get, Time}), - C. +-spec get(pid()|cell_name(), time()) -> cell_content(). +get(Cell, Time) -> + call(Cell, {get, Time}). -history_sync(To) -> - cmd_sync(To, history). -time_sync(To) -> - cmd_sync(To, time). +history(Cell) -> + call(Cell, history). -run(To) -> cmd(To, run). +time(Cell) -> + call(Cell, time). -run_until(To, EndTime) -> - cmd(To, {run_until, EndTime}). -pause(To) -> cmd(To, pause). +run(Cell) -> + cast(Cell, run). -step(To) -> cmd(To, step). +run_until(Cell, EndTime) -> + cast(Cell, {run_until, EndTime}). +pause(Cell) -> + cast(Cell, pause). + +step(Cell) -> + cast(Cell, step). + + + +call(Cell, Cmd) -> + gen_server:call(cell_pid(Cell), Cmd). + +cast(Cell, Cmd) -> + gen_server:cast(cell_pid(Cell), Cmd). + +cell_pid({_,_}=XY) -> + where(XY); +cell_pid(Pid) when is_pid(Pid) -> + Pid. -cmd(To, Cmd) when is_pid(To) -> - To ! Cmd; -cmd(To, Cmd) when is_tuple(To) -> - cmd( where(To), Cmd ); -cmd(To, Cmd) -> - lager:error("Incorrect To:~p with Cmd:~p", [To, Cmd]). -cmd_sync(To, Cmd) -> - cmd(To, {self(), Cmd}), - receive - Res -> - Res - end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% states init(#state{xy=XY}=State) -> reg(XY), - loop(State). - - -loop(#state{xy=XY, time=T, content=Content, collector=Collector, - future=Future, history=History}=State) -> - receive - {set, NewContent} -> - loop(State#state{content=NewContent}); - {From, {get, Time}} -> - case content_at(Time, State) of - future -> - loop(State#state{future=[{From, Time} | State#state.future]}); - C -> - From ! {cell_content, C}, - loop(State) - end; - {From, history} -> - #state{history=History} = State, - From ! History, - loop(State); - {From, time} -> - From ! State#state.time, - loop(State); - run -> - case is_collector_running(State) of + {ok, State}. + + +handle_cast({set, NewContent}, State) -> + {noreply, State#state{content=NewContent}}; +handle_cast({From, {get, Time}}, State) -> + case content_at(Time, State) of + future -> + {noreply, State#state{future=[{From, Time} | State#state.future]}}; + C -> + From ! {cell_content, C}, + {noreply, State} + end; +handle_cast({query_content, Time, From}, State) -> + case content_at(Time, State) of + future -> + {noreply, State#state{future=[{From, Time} | State#state.future]}}; + C -> + From ! {cell_content, C}, + {noreply, State} + end; +handle_cast(run, State) -> + case is_collector_running(State) of + true -> + {noreply, State#state{mode=run}}; + false -> + NextState = start_collector(State), + {noreply, NextState#state{mode=run}} + end; +handle_cast(step, State) -> + case is_collector_running(State) of + true -> + {noreply, State#state{mode=step}}; + false -> + NewState = start_collector(State), + {noreply, NewState#state{mode=step}} + end; +handle_cast({run_until, EndTime}, + #state{time=T}=State) -> + case is_collector_running(State) of + true -> + {noreply, State#state{mode={run_until, EndTime}}}; + false -> + case T < EndTime of true -> - loop(State#state{mode=run}); + NewState = start_collector(State), + {noreply, NewState#state{mode={run_until, EndTime}}}; false -> - NextState = start_collector(State), - loop(NextState#state{mode=run}) - end; + {noreply, State} + end + end; +handle_cast(pause, State) -> + {noreply, State#state{mode=step}}. + +handle_call(history, _From, State) -> + {reply, State#state.history, State}; +handle_call(time, _From, State) -> + {reply, State#state.time, State}; +handle_call({get, Time}, _From, State) -> + case content_at(Time, State) of + future -> + {reply, future, State}; + {_, C} -> + {reply, C, State} + end. + + + +handle_info({Collector, {next_content, NextContent}}, + #state{collector=Collector, + future=Future, xy=XY, time=T, + content=Content, history=History}=State) -> + NewFuture = process_future(XY, T+1, NextContent, Future), + lager:info("Cell ~p changing to ~p for time ~p", [XY, NextContent, T+1]), + NextState = State#state{content=NextContent, + time=T+1, + history=[{T, Content}|History], + future=NewFuture}, + case State#state.mode of step -> - case is_collector_running(State) of - true -> - loop(State#state{mode=step}); - false -> - NewState = start_collector(State), - loop(NewState#state{mode=step}) - end; + {noreply, NextState}; + run -> + {noreply, start_collector(NextState)}; {run_until, EndTime} -> - case is_collector_running(State) of + case NextState#state.time < EndTime of true -> - loop(State#state{mode={run_until, EndTime}}); + {noreply, start_collector(NextState)}; false -> - case T < EndTime of - true -> - NewState = start_collector(State), - loop(NewState#state{mode={run_until, EndTime}}); - false -> - loop(State) - end - end; - pause -> - loop(State#state{mode=step}); - {Collector, {next_content, NextContent}} -> - NewFuture = process_future(XY, T+1, NextContent, Future), - lager:info("Cell ~p changing to ~p for time ~p", [XY, NextContent, T+1]), - NextState = State#state{content=NextContent, - time=T+1, - history=[{T, Content}|History], - future=NewFuture}, - case State#state.mode of - step -> - loop(NextState); - run -> - loop(start_collector(NextState)); - {run_until, EndTime} -> - case NextState#state.time < EndTime of - true -> - loop(start_collector(NextState)); - false -> - loop(NextState#state{mode=step}) - end + {noreply, NextState#state{mode=step}} end end. +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + + + is_collector_running(#state{collector=Collector}) -> is_pid(Collector) andalso is_process_alive(Collector). @@ -229,9 +279,9 @@ process_future(XY, Time, Content, Future) -> query_neighbours(T, Neighbours) -> lists:foreach( fun(N) -> - get(N, T) - end, - Neighbours). + query_content(N, T) + end, + Neighbours). next_content(1, 2) -> 1; next_content(_, 3) -> 1; From 4cca3cc32ca56a9036b2c4fc3c5a2bc4e6623e66 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Mon, 8 Dec 2014 23:02:59 +0100 Subject: [PATCH 12/70] egol is now a gen_server. --- src/egol.erl | 130 +++++++++++++++++++++++++--------------------- src/egol_cell.erl | 1 - 2 files changed, 72 insertions(+), 59 deletions(-) diff --git a/src/egol.erl b/src/egol.erl index 3ad2235..64247e3 100644 --- a/src/egol.erl +++ b/src/egol.erl @@ -1,7 +1,10 @@ -module(egol). +-behaviour(gen_server). + + + -export([start/3, - init/3, step/0, run/0, run/1, @@ -14,26 +17,37 @@ print_last/0, print_lag/0]). +%% gen_server callbacks +-export([init/1, + handle_call/3, + handle_cast/2, + handle_info/2, + terminate/2, + code_change/3]). + -export([test/1]). + + -record(state, {size_x, size_y, mode }). +-define(SERVER, ?MODULE). + start(N, M, InitialCells) -> - spawn(?MODULE, init, [N, M, InitialCells]). + gen_server:start({local, ?SERVER}, ?MODULE, [N, M, InitialCells], []). -init(N, M, InitialCells) -> +init([N, M, InitialCells]) -> AllCells = all_cells(N, M), Cells = [{start_cell(XY, {N,M}, lists:member(XY, InitialCells)), XY} || XY <- AllCells ], egol_cell_mgr:start(Cells), - register(?MODULE, self()), - loop(#state{size_x=N, + {ok, #state{size_x=N, size_y=M, - mode=step}). + mode=step}}. start_cell(XY, Dim, true) -> {ok, Pid} = egol_cell_sup:start_cell(XY, Dim, 1), @@ -43,10 +57,10 @@ start_cell(XY, Dim, false) -> Pid. step() -> - ?MODULE ! step. + gen_server:cast(?SERVER, step). run() -> - ?MODULE ! run. + gen_server:cast(?SERVER, run). run(Time) -> run(), @@ -54,71 +68,71 @@ run(Time) -> pause(). run_until(EndTime) -> - ?MODULE ! {run_until, EndTime}. + gen_server:cast(?SERVER,{run_until, EndTime}). max_time() -> - ?MODULE ! {max_time, self()}, - receive - MaxTime -> - MaxTime - end. + gen_server:call(?SERVER, max_time). mode() -> - ?MODULE ! {mode, self()}, - receive - Mode -> - Mode - end. + gen_server:call(?SERVER, mode). - - pause() -> - ?MODULE ! pause. + gen_server:cast(?SERVER, pause). kill(XY) -> egol_cell:kill(XY). print(T) -> - ?MODULE ! {print, T}. + gen_server:cast(?SERVER, {print, T}). print_last() -> - ?MODULE ! print_last. + gen_server:cast(?SERVER, print_last). print_lag() -> - ?MODULE ! print_lag. - -loop(State) -> - receive - step -> - step_cells(all_cells(State)), - loop(State#state{mode=step}); - run -> - run_cells(all_cells(State)), - loop(State#state{mode=run}); - {run_until, EndTime} -> - run_cells_until(all_cells(State), EndTime), - loop(State#state{mode={run_until,EndTime}}); - pause -> - pause_cells(all_cells(State)), - loop(State#state{mode=step}); - {print,T} -> - print(State#state.size_x, State#state.size_y, T), - loop(State); - {mode, From} -> - From ! State#state.mode, - loop(State); - {max_time, From} -> - From ! maximum_time(State), - loop(State); - print_last -> - MinTime = minimum_time(State), - io:format("Time is ~p.~n", [MinTime]), - print(State#state.size_x, State#state.size_y, MinTime), - loop(State); - print_lag -> - print_lag(State), - loop(State) - end. + gen_server:cast(?SERVER, print_lag). + + +handle_cast(step, State) -> + step_cells(all_cells(State)), + {noreply, State#state{mode=step}}; +handle_cast(run, State) -> + run_cells(all_cells(State)), + {noreply, State#state{mode=run}}; +handle_cast({run_until, EndTime}, State) -> + run_cells_until(all_cells(State), EndTime), + {noreply, State#state{mode={run_until,EndTime}}}; +handle_cast(pause, State) -> + pause_cells(all_cells(State)), + {noreply, State#state{mode=step}}; +handle_cast({print,T}, State) -> + print(State#state.size_x, State#state.size_y, T), + {noreply, State}; +handle_cast(print_last, State) -> + MinTime = minimum_time(State), + io:format("Time is ~p.~n", [MinTime]), + print(State#state.size_x, State#state.size_y, MinTime), + {noreply, State}; +handle_cast(print_lag, State) -> + print_lag(State), + {noreply, State}. + + +handle_call(mode, _From, State) -> + {reply, State#state.mode, State}; +handle_call(max_time, _From, State) -> + {reply, maximum_time(State), State}. + + +handle_info(_Msg, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + + maximum_time(State) -> diff --git a/src/egol_cell.erl b/src/egol_cell.erl index d867a2d..7c87109 100644 --- a/src/egol_cell.erl +++ b/src/egol_cell.erl @@ -129,7 +129,6 @@ cell_pid(Pid) when is_pid(Pid) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% states init(#state{xy=XY}=State) -> reg(XY), {ok, State}. From be319d2cf93a191d272be806cbceaafb6ac97dde Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Tue, 9 Dec 2014 09:52:32 +0100 Subject: [PATCH 13/70] Test: starting a cell works --- eqc_test/Makefile | 8 +++ eqc_test/egol_eqc.erl | 98 +++++++++++++++++++++++++++++++++++++ eqc_test/egol_eqc_SUITE.erl | 11 +++++ eqc_test/test.config | 14 ++++++ 4 files changed, 131 insertions(+) create mode 100644 eqc_test/Makefile create mode 100644 eqc_test/egol_eqc.erl create mode 100644 eqc_test/egol_eqc_SUITE.erl create mode 100644 eqc_test/test.config diff --git a/eqc_test/Makefile b/eqc_test/Makefile new file mode 100644 index 0000000..b565955 --- /dev/null +++ b/eqc_test/Makefile @@ -0,0 +1,8 @@ + + + +console: beams + erl -pa ../ebin ../deps/*/ebin . -config test.config + +beams: + erlc *.erl diff --git a/eqc_test/egol_eqc.erl b/eqc_test/egol_eqc.erl new file mode 100644 index 0000000..0edff88 --- /dev/null +++ b/eqc_test/egol_eqc.erl @@ -0,0 +1,98 @@ +-module(egol_eqc). + +-compile(export_all). + +-include_lib("eqc/include/eqc.hrl"). +-include_lib("eqc/include/eqc_component.hrl"). + +-record(state, + { cell, + id, + dim, + content, + time=0, + neighbour_contents = [] + }). + + +api_spec() -> + #api_spec{ + language = erlang, + modules = [ + %% #api_module{ + %% name = gproc_ps, + %% functions = [ #api_fun{ name = subscribe, arity = 2}, + %% #api_fun{ name = publish, arity = 3} + %% ] + %% } + ]}. + + + +initial_state() -> + #state{}. + +%% @doc Default generated property +-spec prop_cell() -> eqc:property(). +prop_cell() -> + ?SETUP(fun() -> + %% setup mocking here + eqc_mocking:start_mocking(api_spec()), + fun() -> application:stop(gproc) end %% Teardown function + end, + ?FORALL(Cmds, commands(?MODULE), + begin + start(), + {H, S, Res} = run_commands(?MODULE,Cmds), + stop(S), + pretty_commands(?MODULE, Cmds, {H, S, Res}, + Res == ok) + end)). + + +start() -> + application:start(gproc), + egol_cell_sup:start_link(), + ok. + +stop(S) -> + egol_cell:kill(S#state.id), + ok. + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% cell + +cell(XY, Dim, Content) -> + {ok, Pid} = egol_cell_sup:start_cell(XY, Dim, Content), + Pid. + +cell_args(_S) -> + ?LET({CellId, Dim}, cell_id_and_dim(), + [CellId, Dim, content()]). + +cell_next(S, Pid, [CellId, Dim, Content]) -> + S#state{cell=Pid, id=CellId, dim=Dim, content=Content}. + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% GENERATORS +cell_id_and_dim() -> + ?LET({DimX, DimY}, {dim(),dim()}, + ?LET({X,Y}, {coord(DimX), coord(DimY)}, + {{X,Y},{DimX,DimY}})). + +dim() -> + ?SUCHTHAT(N, nat(), N>2). + +coord(Max) -> + choose(0, Max-1). + +pos_int() -> + ?LET(N, nat(), N+1). + +content() -> + choose(0,1). diff --git a/eqc_test/egol_eqc_SUITE.erl b/eqc_test/egol_eqc_SUITE.erl new file mode 100644 index 0000000..e77392c --- /dev/null +++ b/eqc_test/egol_eqc_SUITE.erl @@ -0,0 +1,11 @@ +-module(egol_eqc_SUITE). + +-compile(export_all). + +-include_lib("eqc/include/eqc_ct.hrl"). + +all() -> [check_prop_cell]. + +check_prop_cell(_) -> + ?quickcheck((egol_eqc:prop_cell())). + diff --git a/eqc_test/test.config b/eqc_test/test.config new file mode 100644 index 0000000..2d51983 --- /dev/null +++ b/eqc_test/test.config @@ -0,0 +1,14 @@ +[{lager, + [{handlers, + [{lager_console_backend, [critical, true]}, + {lager_file_backend, + [{"log/error.log", error, 10485760, "$D0", 5}, + {"log/console.log", info, 10485760, "$D0", 5}, + {"log/debug.log", debug, 10485760, "$D0", 5} + ]} + + ]} + ]}, + {sasl, + [{sasl_error_logger, false}]} +]. From 3203fe3ea72f338ec09d146dfebd7a45a9702e6c Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Tue, 9 Dec 2014 13:52:54 +0100 Subject: [PATCH 14/70] NOT WORKING. Weird issue with eqc and step cmd in egol_cell. --- eqc_test/egol_eqc.erl | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/eqc_test/egol_eqc.erl b/eqc_test/egol_eqc.erl index 0edff88..61e1ce9 100644 --- a/eqc_test/egol_eqc.erl +++ b/eqc_test/egol_eqc.erl @@ -19,12 +19,11 @@ api_spec() -> #api_spec{ language = erlang, modules = [ - %% #api_module{ - %% name = gproc_ps, - %% functions = [ #api_fun{ name = subscribe, arity = 2}, - %% #api_fun{ name = publish, arity = 3} - %% ] - %% } + #api_module{ + name = egol_cell, + functions = [ #api_fun{ name = query_content, arity = 2} + ] + } ]}. @@ -73,10 +72,36 @@ cell_args(_S) -> ?LET({CellId, Dim}, cell_id_and_dim(), [CellId, Dim, content()]). +cell_pre(S, _) -> + S#state.cell == undefined. + cell_next(S, Pid, [CellId, Dim, Content]) -> S#state{cell=Pid, id=CellId, dim=Dim, content=Content}. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% step + +step(XY) -> + egol_cell:step(XY). + +step_args(S) -> + [S#state.id]. + +step_pre(S, [XY]) -> + S#state.id /= undefined andalso + S#state.id == XY. + +step_callouts(S, [_XY]) -> + ?PAR(lists:duplicate(8, ?CALLOUT(egol_cell, query_content, [?WILDCARD, S#state.time], ok))). + +step_return(_S, _) -> + ok. + +step_next(S, _Res, _Args) -> + S. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% GENERATORS From e0b0e8d24ea5f84db8f715b55afb8b70d02c3964 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Tue, 9 Dec 2014 16:36:41 +0100 Subject: [PATCH 15/70] Remove set from collector process. --- src/egol_cell.erl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/egol_cell.erl b/src/egol_cell.erl index 7c87109..a682333 100644 --- a/src/egol_cell.erl +++ b/src/egol_cell.erl @@ -259,9 +259,7 @@ collector_loop(WaitingOn, NeighbourCount, Cell, Content) -> false %% ignore messages we are not waiting for -> collector_loop(WaitingOn, NeighbourCount, Cell, Content) - end; - {set, NewContent} -> %% this could be fun... - collector_loop(WaitingOn, NeighbourCount, Cell, NewContent) + end end. From dd555a00e93e35cb1a178a409be6a1eb01896655 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Tue, 9 Dec 2014 20:25:50 +0100 Subject: [PATCH 16/70] Add egol_proctol module. One step works in test. * Moved query_content to egol_protcol together with query_response to ease testing. --- eqc_test/egol_eqc.erl | 37 ++++++++++++++++++++++++------------- src/egol_cell.erl | 23 ++++++++++------------- src/egol_protocol.erl | 18 ++++++++++++++++++ 3 files changed, 52 insertions(+), 26 deletions(-) create mode 100644 src/egol_protocol.erl diff --git a/eqc_test/egol_eqc.erl b/eqc_test/egol_eqc.erl index 61e1ce9..e2947a9 100644 --- a/eqc_test/egol_eqc.erl +++ b/eqc_test/egol_eqc.erl @@ -7,6 +7,7 @@ -record(state, { cell, + started=false, id, dim, content, @@ -20,10 +21,16 @@ api_spec() -> language = erlang, modules = [ #api_module{ - name = egol_cell, - functions = [ #api_fun{ name = query_content, arity = 2} + name = egol_protocol, + functions = [ #api_fun{ name = query_content, arity = 2}, + #api_fun{ name = query_response, arity = 2} ] - } + }%, + %% #api_module{ + %% name = gproc, + %% functions = [ #api_fun{ name = where, arity=1}, + %% #api_fun{ name = reg, arity=1} ] + %% } ]}. @@ -37,7 +44,8 @@ prop_cell() -> ?SETUP(fun() -> %% setup mocking here eqc_mocking:start_mocking(api_spec()), - fun() -> application:stop(gproc) end %% Teardown function +%% fun() -> ok end + fun() -> application:stop(gproc) end %% Teardown function end, ?FORALL(Cmds, commands(?MODULE), begin @@ -55,7 +63,8 @@ start() -> ok. stop(S) -> - egol_cell:kill(S#state.id), + catch exit(whereis(egol_cell_sup), normal), +%% egol_cell:kill(S#state.id), ok. @@ -76,24 +85,26 @@ cell_pre(S, _) -> S#state.cell == undefined. cell_next(S, Pid, [CellId, Dim, Content]) -> - S#state{cell=Pid, id=CellId, dim=Dim, content=Content}. + S#state{cell=Pid, id=CellId, dim=Dim, content=Content, started=true}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% step -step(XY) -> - egol_cell:step(XY). +step(Pid) -> + Res = egol_cell:step(Pid), + timer:sleep(50), + Res. step_args(S) -> - [S#state.id]. + [S#state.cell]. -step_pre(S, [XY]) -> - S#state.id /= undefined andalso - S#state.id == XY. +step_pre(S, [Pid]) -> + S#state.cell /= undefined andalso + S#state.cell == Pid. step_callouts(S, [_XY]) -> - ?PAR(lists:duplicate(8, ?CALLOUT(egol_cell, query_content, [?WILDCARD, S#state.time], ok))). + ?PAR(lists:duplicate(8, ?CALLOUT(egol_protocol, query_content, [?WILDCARD, S#state.time], ok))). step_return(_S, _) -> ok. diff --git a/src/egol_cell.erl b/src/egol_cell.erl index a682333..baff10b 100644 --- a/src/egol_cell.erl +++ b/src/egol_cell.erl @@ -20,7 +20,6 @@ -export([set/2, get/2, - query_content/2, history/1, time/1, run/1, @@ -83,9 +82,6 @@ kill(XY) -> set(Cell, Content) -> cast(Cell, {set, Content}). -%% ask the cell to send a message back with its contents at the given Time. -query_content(Cell, Time) -> - cast(Cell, {query_content, Time, self()}). -spec get(pid()|cell_name(), time()) -> cell_content(). get(Cell, Time) -> @@ -144,14 +140,6 @@ handle_cast({From, {get, Time}}, State) -> From ! {cell_content, C}, {noreply, State} end; -handle_cast({query_content, Time, From}, State) -> - case content_at(Time, State) of - future -> - {noreply, State#state{future=[{From, Time} | State#state.future]}}; - C -> - From ! {cell_content, C}, - {noreply, State} - end; handle_cast(run, State) -> case is_collector_running(State) of true -> @@ -221,8 +209,17 @@ handle_info({Collector, {next_content, NextContent}}, false -> {noreply, NextState#state{mode=step}} end + end; +handle_info({query_content, Time, From}, State) -> + case content_at(Time, State) of + future -> + {noreply, State#state{future=[{From, Time} | State#state.future]}}; + C -> + egol_protocol:query_response(From, {cell_content, C}), + {noreply, State} end. + terminate(_Reason, _State) -> ok. @@ -276,7 +273,7 @@ process_future(XY, Time, Content, Future) -> query_neighbours(T, Neighbours) -> lists:foreach( fun(N) -> - query_content(N, T) + egol_protocol:query_content(N, T) end, Neighbours). diff --git a/src/egol_protocol.erl b/src/egol_protocol.erl new file mode 100644 index 0000000..72cc686 --- /dev/null +++ b/src/egol_protocol.erl @@ -0,0 +1,18 @@ +%%% @doc The intra-cell communication is placed in a module so we can mock it when +%%% testing. + +-module(egol_protocol). + +-export([query_content/2, + query_response/2]). + + + + +query_content(XY, Time) -> + Pid = egol_cell:where(XY), + Pid ! {query_content, Time, self()}. + + +query_response(Pid, Resp) -> + Pid ! Resp. From d1d0c13ad59fc3a24a6fd5af8a7787fae88ab8b1 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Tue, 9 Dec 2014 20:33:46 +0100 Subject: [PATCH 17/70] Remove started field for egol_eqc starte. --- eqc_test/egol_eqc.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/eqc_test/egol_eqc.erl b/eqc_test/egol_eqc.erl index e2947a9..57eff23 100644 --- a/eqc_test/egol_eqc.erl +++ b/eqc_test/egol_eqc.erl @@ -7,7 +7,6 @@ -record(state, { cell, - started=false, id, dim, content, @@ -85,7 +84,7 @@ cell_pre(S, _) -> S#state.cell == undefined. cell_next(S, Pid, [CellId, Dim, Content]) -> - S#state{cell=Pid, id=CellId, dim=Dim, content=Content, started=true}. + S#state{cell=Pid, id=CellId, dim=Dim, content=Content}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% From fd47d059de700e49586d9487e5c4f967bf79c5b3 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Thu, 11 Dec 2014 13:21:13 +0100 Subject: [PATCH 18/70] Created target for adding vm.args to the release. --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index 86f0e80..163c2d7 100644 --- a/Makefile +++ b/Makefile @@ -15,5 +15,7 @@ ERLC_COMPILE_OPTS= +'{parse_transform, lager_transform}' ERLC_OPTS= $(ERLC_COMPILE_OPTS) +debug_info TEST_ERLC_OPTS= $(ERLC_COMPILE_OPTS) +release: + relx --vm_args "./config/vm.args" From 17e67dc78a93e2927e1de2ccf352fd5ed4ed8a28 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Thu, 11 Dec 2014 13:21:55 +0100 Subject: [PATCH 19/70] Created array based solution to GOL. --- src/array2.erl | 18 ++++++++ src/egol_array.erl | 110 +++++++++++++++++++++++++++++++++++++++++++++ src/egol_util.erl | 28 ++++++++++++ 3 files changed, 156 insertions(+) create mode 100644 src/array2.erl create mode 100644 src/egol_array.erl create mode 100644 src/egol_util.erl diff --git a/src/array2.erl b/src/array2.erl new file mode 100644 index 0000000..9f02fda --- /dev/null +++ b/src/array2.erl @@ -0,0 +1,18 @@ +-module( array2 ). + +-export([create/1, + get/2, + set/3]). + +create({X, Y}) -> + array:new( [{size, X}, {default, array:new( [{size, Y}, {default, 0}] )}] ). + +get( {X, Y}, Array ) -> + array:get( Y, array:get(X, Array) ). + +set({X, Y}, Value, Array ) -> + Y_array = array:get( X, Array ), + New_y_array = array:set( Y, Value, Y_array ), + array:set( X, New_y_array, Array ). + + diff --git a/src/egol_array.erl b/src/egol_array.erl new file mode 100644 index 0000000..9dc1f82 --- /dev/null +++ b/src/egol_array.erl @@ -0,0 +1,110 @@ +-module(egol_array). + +-export([start/2, + run_until/2, + run/2, + print/1]). + +-export([test/1]). + +-export([get/2]). + + +-record(board, + { matrix, + dim, + time=0}). + +start(Dim, InitialCells) -> + fill_cells(#board{ matrix = array2:create(Dim), dim = Dim}, + InitialCells). + +run(Board, Time) -> + Pid = running(Board), + timer:sleep(Time), + Pid ! {stop, self()}, + receive + Res -> + Res + end. + +running(Board) -> + spawn(fun() -> running_loop(Board) end). + +running_loop(Board) -> + receive + {stop, To} -> + To ! Board + after + 0 -> + running_loop(step(Board)) + end. + + + +run_until(Board, T) -> + run_until(Board, 0, T+1). + +run_until(Board, T, T) -> + Board; +run_until(Board, T, EndTime) -> + run_until(step(Board), T+1, EndTime). + +set_cell(#board{matrix=M}=Board, XY, Content) -> + Board#board{ matrix= array2:set(XY, Content, M)}. + +fill_cells(Board, []) -> + Board; +fill_cells(Board, [XY|Rest]) -> + NewBoard = set_cell(Board, XY, 1), + fill_cells(NewBoard, Rest). + +get(#board{matrix=M}, XY) -> + array2:get(XY, M). + +step(#board{matrix=M, dim=Dim, time=T}) -> + NewBoard = start(Dim, []), + evolve_board(NewBoard#board{time=T}, {0,0}, Dim, M). + + +evolve_board(#board{time=T}=Board, {0,Y}, {_DimX, DimY}, _M) when Y==DimY -> + Board#board{time=T+1}; +evolve_board(#board{matrix=NewM}=Board, {X,Y}=XY, {DimX, DimY}=Dim, M) when X + Content = compute_cell(XY, Dim, M), + NewBoard = Board#board{ matrix=array2:set(XY, Content, NewM)}, + evolve_board(NewBoard, {X+1,Y}, Dim, M); +evolve_board(Board, {X,Y}, {DimX, _}=Dim, M) when X==DimX -> + evolve_board(Board, {0, Y+1}, Dim, M). + +compute_cell(XY, Dim, M) -> + Neighbours = egol_util:neighbours(XY, Dim), + egol_util:next_content(array2:get(XY,M), + neighbour_count(M, Neighbours, 0)). + +neighbour_count(_M, [], NC) -> + NC; +neighbour_count(M, [XY|Rest], NC) -> + neighbour_count(M, Rest, NC + array2:get(XY, M)). + + +print(#board{matrix=M, dim={DimX, DimY}, time=T}) -> + io:format("Time: ~p~n", [T]), + print(M, DimY-1, DimX). + +print(M, 0, DimX) -> + print_row(M, 0, DimX); +print(M, Y, DimX) -> + print_row(M, Y, DimX), + print(M, Y-1, DimX). + +print_row(M, Y, DimX) -> + [ egol_util:format_cell_value(array2:get({X,Y}, M)) + || X <- lists:seq(0,DimX-1) ], + io:format("~n"). + +test(1) -> + start({8,8}, test(2)); +test(2) -> + [{0,0}, {1,0}, {2,0}, {2,1},{1,2}]; +test(N) -> + start({N,N}, test(2)). diff --git a/src/egol_util.erl b/src/egol_util.erl new file mode 100644 index 0000000..18494b4 --- /dev/null +++ b/src/egol_util.erl @@ -0,0 +1,28 @@ +-module(egol_util). + +-export([neighbours/2]). +-export([neighbours_at/2]). + +-export([next_content/2]). + +-export([format_cell_value/1]). + + + +neighbours({X,Y}, {DimX, DimY}) -> + [ {Xa rem DimX, Ya rem DimY} || + Xa <- lists:seq(X-1+DimX, X+1+DimX), + Ya <- lists:seq(Y-1+DimY, Y+1+DimY), + {Xa,Ya} /= {X+DimX,Y+DimY}]. + +neighbours_at(T, Neighbours) -> + [ {N, T} || N <- Neighbours ]. + + +next_content(1, 2) -> 1; +next_content(_, 3) -> 1; +next_content(_, _) -> 0. + + +format_cell_value(0) -> io:format("."); +format_cell_value(1) -> io:format("*"). \ No newline at end of file From 7b3d1e716f99db31df69bd3810cc958232e95a3a Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Thu, 11 Dec 2014 13:30:29 +0100 Subject: [PATCH 20/70] Add flood_query_response command. --- eqc_test/egol_eqc.erl | 177 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 161 insertions(+), 16 deletions(-) diff --git a/eqc_test/egol_eqc.erl b/eqc_test/egol_eqc.erl index 57eff23..6ce244e 100644 --- a/eqc_test/egol_eqc.erl +++ b/eqc_test/egol_eqc.erl @@ -11,7 +11,9 @@ dim, content, time=0, - neighbour_contents = [] + collector, + waiting_on = undefined, + neighbour_count = 0 }). @@ -47,13 +49,17 @@ prop_cell() -> fun() -> application:stop(gproc) end %% Teardown function end, ?FORALL(Cmds, commands(?MODULE), + % ?IMPLIES(length(Cmds)>20, begin start(), {H, S, Res} = run_commands(?MODULE,Cmds), stop(S), pretty_commands(?MODULE, Cmds, {H, S, Res}, - Res == ok) - end)). + aggregate(command_names(Cmds), + Res == ok)) + end)) +%) +. start() -> @@ -63,17 +69,23 @@ start() -> stop(S) -> catch exit(whereis(egol_cell_sup), normal), +%% application:stop(gproc), %% egol_cell:kill(S#state.id), ok. +weight(_S, get) -> 1; +weight(_S, cell) -> 1; +weight(_S, flood_requery_response) -> 20; +weight(_S, _) -> 4. + -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% cell +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% cell(XY, Dim, Content) -> {ok, Pid} = egol_cell_sup:start_cell(XY, Dim, Content), + io:format("cell STARTED~n"), Pid. cell_args(_S) -> @@ -88,29 +100,151 @@ cell_next(S, Pid, [CellId, Dim, Content]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% step - -step(Pid) -> +step(Pid, Id, Time) -> + case gproc:where(collector_name(Id, Time)) of + undefined -> + ok; + OldCollector -> + io:format("killed old collector~n"), + exit(OldCollector, kill) + end, Res = egol_cell:step(Pid), - timer:sleep(50), - Res. + timer:sleep(100), + gproc:await(collector_name(Id, Time),100), + case gproc:where(collector_name(Id, Time)) of + undefined -> + io:format("BUMMER!!! step has NOT started collector~n"); + Collector -> + io:format("collector started after step:~p~n",[Collector]), + Collector + end. +%% Res. + +collector_name(Id, Time) -> {n,l,{collector, Id, Time}}. step_args(S) -> - [S#state.cell]. + [S#state.cell, S#state.id, S#state.time]. -step_pre(S, [Pid]) -> +step_pre(S, [Pid, _, _]) -> S#state.cell /= undefined andalso - S#state.cell == Pid. + S#state.cell == Pid andalso + S#state.waiting_on == undefined. -step_callouts(S, [_XY]) -> +step_callouts(S, [_Pid, _XY, _Time ]) -> ?PAR(lists:duplicate(8, ?CALLOUT(egol_protocol, query_content, [?WILDCARD, S#state.time], ok))). step_return(_S, _) -> ok. -step_next(S, _Res, _Args) -> - S. +step_next(S, Res, _Args) -> + S#state{waiting_on = egol_util:neighbours_at(S#state.time, + egol_util:neighbours(S#state.id, S#state.dim)), + collector=Res}. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +get(Pid, Time) -> + Res = egol_cell:get(Pid, Time), + io:format("get works~n"), + Res. + +get_args(S) -> + [S#state.cell, S#state.time]. + +get_pre(S, _) -> + S#state.cell /= undefined. + +get_post(S, [_Pid, _Time], Res) -> + eq(Res, S#state.content). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +query_response(Pid, XY, Time, Content) -> +%% egol_protocol:query_response(Pid, {cell_content, {XY, Time}, Content}). + Pid ! {cell_content, {{XY, Time}, Content}}. + +query_response_args(#state{id=undefined}) -> + [undefined, undefined, undefined, undefined]; +query_response_args(#state{cell=Cell, waiting_on=undefined}) when is_pid(Cell) -> + [undefined, undefined, undefined, undefined]; +query_response_args(S) -> + [S#state.collector, neighbour(S), S#state.time, content()]; + +query_response_args(S) -> + try +%% {Collector,_} = gproc:await({n,l,{collector, S#state.id, S#state.time}},100), + Collector = gproc:where(collector_name(S#state.id, S#state.time)), + case Collector of + undefined -> + io:format("undefined collector~n"); + _ -> + io:format("collector is running~n") + end, + + [Collector, neighbour(S), S#state.time, content()] + catch + _:_ -> + [undefined, undefined, undefined, undefined] + end. + +query_response_pre(#state{waiting_on=undefined}, _ ) -> false; +query_response_pre(S, [Collector, Neighbour, Time, _] ) -> + Collector /= undefined andalso + S#state.waiting_on /= [] andalso + lists:member({Neighbour, Time}, S#state.waiting_on) andalso + Time == S#state.time. + +query_response_next(S, _Res, [_, Neighbour, Time, Content]) -> + NC = S#state.neighbour_count + Content, + case lists:delete({Neighbour, Time}, S#state.waiting_on) of + [] -> + timer:sleep(50), + io:format("got ALL neighbour contents~n"), + S#state{content=next_content(S#state.content, NC), + time=Time+1, + waiting_on=undefined, + collector=undefined, + neighbour_count=0}; + Wait -> + S#state{waiting_on = Wait, neighbour_count = NC} + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +flood_query_response(Pid, Cells) -> + lists:foreach( fun(CC) -> + Pid ! CC + end, + Cells). + +cell_contents(CellsAtT) -> + [ cell_content(CellAtT, content()) + || CellAtT <- CellsAtT ]. + +cell_content(CellAtT, Content) -> + {cell_content, {CellAtT, Content}}. + +flood_query_response_args(#state{waiting_on=[_]}) -> + [undefined, undefined]; +flood_query_response_args(#state{waiting_on=Wait}=S) when Wait /= [], + Wait /= undefined -> + ?LET(CellContents, cell_contents(tl(Wait)), + [S#state.collector, CellContents]); +flood_query_response_args(_) -> + [undefined, undefined]. + +flood_query_response_pre(_, [undefined, _]) -> false; +flood_query_response_pre(#state{waiting_on=[_]}, _) -> false; +flood_query_response_pre(#state{waiting_on=Wait}, _) when Wait /= [], + Wait /= undefined -> + true; +flood_query_response_pre(_,_) -> + false. + +flood_query_response_next(S, _Res, [_Pid, CellContents]) -> + Contents = [ C || {cell_content, {_, C}} <- CellContents], + NC = lists:sum(Contents), + S#state{waiting_on=[hd(S#state.waiting_on)], + neighbour_count=S#state.neighbour_count+NC}. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -131,3 +265,14 @@ pos_int() -> content() -> choose(0,1). + +neighbour(S) -> + oneof(egol_util:neighbours(S#state.id, S#state.dim)). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% HELPERS + +next_content(0,3) -> 1; +next_content(C, N) when N==2; N==3 -> C; +next_content(_,_) -> 0. + From 233475e51c425d9d199469ccb5205072eb496391 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Thu, 11 Dec 2014 13:31:00 +0100 Subject: [PATCH 21/70] minor tweaks to egol_util --- src/egol.erl | 15 +++++++++------ src/egol_cell.erl | 35 +++++++++++------------------------ 2 files changed, 20 insertions(+), 30 deletions(-) diff --git a/src/egol.erl b/src/egol.erl index 64247e3..f64fdb4 100644 --- a/src/egol.erl +++ b/src/egol.erl @@ -188,7 +188,7 @@ print(N, M, Time) -> print_row(K, N, Board) -> Values = [ cell_content({N1,K}, Board) || N1 <- lists:seq(0, N-1) ], - [format_cell_value(Value) + [egol_util:format_cell_value(Value) || Value <- Values], io:format("~n"). @@ -220,10 +220,6 @@ format_lag(N) when N < 10 -> io_lib:format("~p", [N]); format_lag(_) -> "+". - - -format_cell_value(0) -> io:format("."); -format_cell_value(1) -> io:format("*"). all_cells(N, M) -> @@ -251,6 +247,9 @@ test(2) -> test(3) -> egol_cell_sup:start_link(), start(8,8,test(2)); +test(100) -> + egol_cell_sup:start_link(), + start(100,100,test(2)); test(4) -> test(3), timer:sleep(50), @@ -274,7 +273,11 @@ test(6) -> {3,2}, {3,3}, {3,4}, {4,1}, {4,5}], egol_cell_sup:start_link(), - start(7,6, InitialCells). + start(7,6, InitialCells); +test(N) -> + egol_cell_sup:start_link(), + start(N,N,test(2)). + diff --git a/src/egol_cell.erl b/src/egol_cell.erl index baff10b..6955981 100644 --- a/src/egol_cell.erl +++ b/src/egol_cell.erl @@ -54,7 +54,7 @@ start({X,Y}=XY, {DimX, DimY}=Dim, InitialContent) 0 =< Y -> gen_server:start(?MODULE, #state{xy=XY, dim=Dim, content=InitialContent, - neighbours=neighbours(XY, Dim)}, + neighbours=egol_util:neighbours(XY, Dim)}, []). start_link({X,Y}=XY, {DimX, DimY}=Dim, InitialContent) @@ -64,7 +64,7 @@ start_link({X,Y}=XY, {DimX, DimY}=Dim, InitialContent) 0 =< Y -> gen_server:start_link(?MODULE, #state{xy=XY, dim=Dim, content=InitialContent, - neighbours=neighbours(XY, Dim)}, + neighbours=egol_util:neighbours(XY, Dim)}, []). @@ -192,7 +192,7 @@ handle_info({Collector, {next_content, NextContent}}, future=Future, xy=XY, time=T, content=Content, history=History}=State) -> NewFuture = process_future(XY, T+1, NextContent, Future), - lager:info("Cell ~p changing to ~p for time ~p", [XY, NextContent, T+1]), +%% lager:info("Cell ~p changing to ~p for time ~p", [XY, NextContent, T+1]), NextState = State#state{content=NextContent, time=T+1, history=[{T, Content}|History], @@ -232,19 +232,21 @@ is_collector_running(#state{collector=Collector}) -> is_pid(Collector) andalso is_process_alive(Collector). -start_collector(#state{time=T, neighbours=Neighbours, content=Content}=State) -> +start_collector(#state{time=T, neighbours=Neighbours, + content=Content, xy=XY}=State) -> Cell = self(), - Collector = spawn( fun () -> - collector_init(T, Neighbours, Cell, Content) + Collector = spawn_link( fun () -> + collector_init(XY, T, Neighbours, Cell, Content) end ), State#state{collector=Collector}. -collector_init(Time, Neighbours, Cell, Content) -> +collector_init(XY, Time, Neighbours, Cell, Content) -> + gproc:reg({n, l, {collector, XY, Time}}), query_neighbours(Time, Neighbours), - collector_loop(neighbours_at(Time, Neighbours), 0, Cell, Content). + collector_loop(egol_util:neighbours_at(Time, Neighbours), 0, Cell, Content). collector_loop([], NeighbourCount, Cell, Content) -> - Cell ! {self(), {next_content, next_content(Content, NeighbourCount)}}; + Cell ! {self(), {next_content, egol_util:next_content(Content, NeighbourCount)}}; collector_loop(WaitingOn, NeighbourCount, Cell, Content) -> receive {cell_content, {{{_,_},_}=XYatT, NeighbourContent}} -> @@ -276,10 +278,6 @@ query_neighbours(T, Neighbours) -> egol_protocol:query_content(N, T) end, Neighbours). - -next_content(1, 2) -> 1; -next_content(_, 3) -> 1; -next_content(_, _) -> 0. @@ -295,14 +293,3 @@ reg(XY) -> gproc:reg(cell_name(XY)). cell_name(XY) -> {n,l,XY}. - - - -neighbours({X,Y}, {DimX, DimY}) -> - [ {Xa rem DimX, Ya rem DimY} || - Xa <- lists:seq(X-1+DimX, X+1+DimX), - Ya <- lists:seq(Y-1+DimY, Y+1+DimY), - {Xa,Ya} /= {X+DimX,Y+DimY}]. - -neighbours_at(T, Neighbours) -> - [ {N, T} || N <- Neighbours ]. From 56b8c7301d55091b0b0e4cb9dda5e6bb2bb6aeb3 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Thu, 11 Dec 2014 13:31:49 +0100 Subject: [PATCH 22/70] config dir added --- config/sys.config | 8 ++++++++ config/vm.args | 1 + 2 files changed, 9 insertions(+) create mode 100644 config/sys.config create mode 100644 config/vm.args diff --git a/config/sys.config b/config/sys.config new file mode 100644 index 0000000..5378b87 --- /dev/null +++ b/config/sys.config @@ -0,0 +1,8 @@ +[{lager, [ + {handlers, [ + {lager_console_backend, info}, + {lager_file_backend, [{file, "log/error.log"}, {level, error}]}, + {lager_file_backend, [{file, "log/console.log"}, {level, info}]} + ]} + ]} +]. diff --git a/config/vm.args b/config/vm.args new file mode 100644 index 0000000..db0ee31 --- /dev/null +++ b/config/vm.args @@ -0,0 +1 @@ ++P 10000000 From 22ff6470f39109824e06ce864f2af78f6123fcf1 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Thu, 11 Dec 2014 15:59:42 +0100 Subject: [PATCH 23/70] Removed gproc. --- Makefile | 9 ++-- src/egol.app.src | 2 +- src/egol.erl | 10 ++-- src/egol_cell.erl | 12 ++--- src/egol_cell_mgr.erl | 120 +++++++++++++++++++++++++++++++----------- src/egol_cell_sup.erl | 4 +- src/egol_protocol.erl | 4 +- 7 files changed, 112 insertions(+), 49 deletions(-) diff --git a/Makefile b/Makefile index 163c2d7..0ae732c 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,11 @@ PROJECT = egol -DEPS = edown gen_leader gproc lager +#DEPS = edown gen_leader gproc lager +DEPS = lager -dep_gproc = git git://github.com/uwiger/gproc 0.3.1 -dep_edown = git https://github.com/esl/edown.git master -dep_gen_leader = git https://github.com/abecciu/gen_leader_revival.git master +#dep_gproc = git git://github.com/uwiger/gproc 0.3.1 +#dep_edown = git https://github.com/esl/edown.git master +#dep_gen_leader = git https://github.com/abecciu/gen_leader_revival.git master include erlang.mk diff --git a/src/egol.app.src b/src/egol.app.src index f16066e..760fe75 100644 --- a/src/egol.app.src +++ b/src/egol.app.src @@ -3,5 +3,5 @@ {vsn,"0.0.1"}, {modules,[egol]}, {registered,[]}, - {applications,[kernel, stdlib, gproc, lager]}, + {applications,[kernel, stdlib, lager]}, {start_phases,[]}]}. diff --git a/src/egol.erl b/src/egol.erl index f64fdb4..c6a029c 100644 --- a/src/egol.erl +++ b/src/egol.erl @@ -42,9 +42,9 @@ start(N, M, InitialCells) -> init([N, M, InitialCells]) -> AllCells = all_cells(N, M), - Cells = [{start_cell(XY, {N,M}, lists:member(XY, InitialCells)), XY} - || XY <- AllCells ], - egol_cell_mgr:start(Cells), + egol_cell_mgr:start(), + [{start_cell(XY, {N,M}, lists:member(XY, InitialCells)), XY} + || XY <- AllCells ], {ok, #state{size_x=N, size_y=M, mode=step}}. @@ -274,10 +274,12 @@ test(6) -> {4,1}, {4,5}], egol_cell_sup:start_link(), start(7,6, InitialCells); +test(simple) -> + egol_cell_sup:start_link(), + start(4,4,test(2)); test(N) -> egol_cell_sup:start_link(), start(N,N,test(2)). - diff --git a/src/egol_cell.erl b/src/egol_cell.erl index 6955981..c04b53e 100644 --- a/src/egol_cell.erl +++ b/src/egol_cell.erl @@ -46,7 +46,6 @@ history=[], neighbours}). -%% @todo use via naming with gproc start({X,Y}=XY, {DimX, DimY}=Dim, InitialContent) when X < DimX; 0 =< X; @@ -69,7 +68,7 @@ start_link({X,Y}=XY, {DimX, DimY}=Dim, InitialContent) where(XY) -> - gproc:where(cell_name(XY)). + egol_cell_mgr:lookup(XY). kill(XY) -> case where(XY) of @@ -117,7 +116,7 @@ cast(Cell, Cmd) -> gen_server:cast(cell_pid(Cell), Cmd). cell_pid({_,_}=XY) -> - where(XY); + egol_cell_mgr:lookup(XY); cell_pid(Pid) when is_pid(Pid) -> Pid. @@ -126,7 +125,6 @@ cell_pid(Pid) when is_pid(Pid) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% init(#state{xy=XY}=State) -> - reg(XY), {ok, State}. @@ -192,7 +190,7 @@ handle_info({Collector, {next_content, NextContent}}, future=Future, xy=XY, time=T, content=Content, history=History}=State) -> NewFuture = process_future(XY, T+1, NextContent, Future), -%% lager:info("Cell ~p changing to ~p for time ~p", [XY, NextContent, T+1]), + lager:debug("Cell ~p changing to ~p for time ~p", [XY, NextContent, T+1]), NextState = State#state{content=NextContent, time=T+1, history=[{T, Content}|History], @@ -234,6 +232,7 @@ is_collector_running(#state{collector=Collector}) -> start_collector(#state{time=T, neighbours=Neighbours, content=Content, xy=XY}=State) -> + lager:debug("start_collector: neighbours=~p~n", [Neighbours]), Cell = self(), Collector = spawn_link( fun () -> collector_init(XY, T, Neighbours, Cell, Content) @@ -241,7 +240,6 @@ start_collector(#state{time=T, neighbours=Neighbours, State#state{collector=Collector}. collector_init(XY, Time, Neighbours, Cell, Content) -> - gproc:reg({n, l, {collector, XY, Time}}), query_neighbours(Time, Neighbours), collector_loop(egol_util:neighbours_at(Time, Neighbours), 0, Cell, Content). @@ -289,7 +287,5 @@ content_at(Time, #state{xy=XY, history=History}) when is_integer(Time), Time >= {_, Content} = lists:keyfind(Time, 1, History), {{XY, Time}, Content}. -reg(XY) -> - gproc:reg(cell_name(XY)). cell_name(XY) -> {n,l,XY}. diff --git a/src/egol_cell_mgr.erl b/src/egol_cell_mgr.erl index ae41617..c562be1 100644 --- a/src/egol_cell_mgr.erl +++ b/src/egol_cell_mgr.erl @@ -1,39 +1,99 @@ -module(egol_cell_mgr). --export([start/1, - init/1]). +-compile([{parse_transform, lager_transform}]). + +-behaviour(gen_server). + + +-export([start/0]). + +-export([reg/2, + lookup/1]). + + +%% gen_server callbacks +-export([init/1, + handle_call/3, + handle_cast/2, + handle_info/2, + terminate/2, + code_change/3]). + -type cell_coordinates() :: {integer(), integer()}. -record(state, - {cells :: [{pid(), reference(), cell_coordinates()}] + {cells =[] :: [{pid(), reference(), cell_coordinates()}] }). -start(Cells) -> - spawn(?MODULE, init, [Cells]). +start() -> + gen_server:start({local,?MODULE}, ?MODULE, [], []). +reg(XY, Pid) when is_pid(Pid) -> + gen_server:cast(?MODULE, {reg, XY, Pid}). -init(Cells) -> - MyCells = [ begin - Ref = erlang:monitor(process, Pid), - {Pid, Ref, XY} - end - || {Pid, XY} <- Cells ], - loop(#state{cells=MyCells}). +lookup(XY) -> + gen_server:call(?MODULE, {lookup, XY}). -loop(#state{cells=Cells}=State) -> - receive - {'DOWN', Ref, process, _Pid, _Info} -> - {value, {_,_, XY}, Rest} = lists:keytake(Ref, 2, Cells), - NewPid = await_restart(XY), - NewRef = erlang:monitor(process, NewPid), - NextState = State#state{cells=[{NewPid, NewRef, XY}|Rest]}, - spawn ( fun () -> pacer(NewPid) end ), - loop(NextState) - end. +init([]) -> + {ok, #state{}}. + +handle_call({lookup, XY}, _From, #state{cells=Cells}=State) -> + Reply = + case lists:keyfind(XY, 3, Cells) of + false -> + undefined; %% @todo queue until registered + {Pid, _Ref, XY} -> + Pid + end, + {reply, Reply, State}. + +handle_cast({reg, XY, Pid}, #state{cells=Cells}=State) -> + NewRef = erlang:monitor(process, Pid), + NextState = State#state{cells=[{Pid, NewRef, XY}|Cells]}, + spawn ( fun () -> pacer(Pid) end ), + {noreply, NextState}. + +handle_info({'DOWN', Ref, process, _Pid, _Info}, + #state{cells=Cells}=State) -> + Rest = lists:keydelete(Ref, 2, Cells), + {noreply, State#state{cells=Rest}}. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + + + +%% loop(#state{cells=Cells}=State) -> +%% receive +%% {'DOWN', Ref, process, _Pid, _Info} -> +%% Rest = lists:keydelete(Ref, 2, Cells), +%% loop(State#state{cells=Rest}); +%% %% NewPid = await_restart(XY), +%% %% NewRef = erlang:monitor(process, NewPid), +%% %% NextState = State#state{cells=[{NewPid, NewRef, XY}|Rest]}, +%% %% spawn ( fun () -> pacer(NewPid) end ), +%% %% loop(NextState) +%% {reg, XY, Pid} -> +%% NewRef = erlang:monitor(process, Pid), +%% NextState = State#state{cells=[{Pid, NewRef, XY}|Cells]}, +%% spawn ( fun () -> pacer(Pid) end ), +%% loop(NextState); +%% {lookup, XY, From} -> +%% case lists:keyfind(XY, 3, Cells) of +%% false -> +%% From ! undefined; %% @todo queue until registered +%% {Pid, _Ref, XY} -> +%% From ! Pid +%% end, +%% loop(State) +%% end. pacer(Pid) -> case egol:mode() of @@ -46,12 +106,12 @@ pacer(Pid) -> egol_cell:run(Pid) end. -await_restart(XY) -> - timer:sleep(10), - case egol_cell:where(XY) of - undefined -> - await_restart(XY); - Pid -> - Pid - end. +%% await_restart(XY) -> +%% timer:sleep(10), +%% case egol_cell:where(XY) of +%% undefined -> +%% await_restart(XY); +%% Pid -> +%% Pid +%% end. diff --git a/src/egol_cell_sup.erl b/src/egol_cell_sup.erl index 05bbd06..a1e3b8a 100644 --- a/src/egol_cell_sup.erl +++ b/src/egol_cell_sup.erl @@ -19,7 +19,9 @@ start_link() -> supervisor:start_link({local, ?SERVER}, ?MODULE, []). start_cell(XY, Dim, InitialContent) -> - supervisor:start_child(?SERVER, [XY, Dim, InitialContent]). + {ok, Pid} = Res = supervisor:start_child(?SERVER, [XY, Dim, InitialContent]), + egol_cell_mgr:reg(XY, Pid), + Res. %%%=================================================================== %%% Supervisor callbacks diff --git a/src/egol_protocol.erl b/src/egol_protocol.erl index 72cc686..6f20920 100644 --- a/src/egol_protocol.erl +++ b/src/egol_protocol.erl @@ -3,6 +3,8 @@ -module(egol_protocol). +-compile([{parse_transform, lager_transform}]). + -export([query_content/2, query_response/2]). @@ -10,7 +12,7 @@ query_content(XY, Time) -> - Pid = egol_cell:where(XY), + Pid = egol_cell_mgr:lookup(XY), Pid ! {query_content, Time, self()}. From ed747414cdf89fb39f2293430acc5360cff42769 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Fri, 12 Dec 2014 09:00:59 +0100 Subject: [PATCH 24/70] Fully removed gproc. * use ets to lookup pids. * ets table to record time of each process. --- relx.config | 3 +- src/egol.erl | 72 +++++++++++++++++++---------------- src/egol_cell.erl | 9 ++--- src/egol_cell_mgr.erl | 88 ++++++++++++++++++------------------------- src/egol_util.erl | 30 ++++++++++++++- 5 files changed, 108 insertions(+), 94 deletions(-) diff --git a/relx.config b/relx.config index 79af9f9..5c5393c 100644 --- a/relx.config +++ b/relx.config @@ -18,6 +18,5 @@ {release, {egol, "0.0.1"}, [egol, sasl, lager, - mnesia, - gproc + mnesia ]}. diff --git a/src/egol.erl b/src/egol.erl index c6a029c..fc382ad 100644 --- a/src/egol.erl +++ b/src/egol.erl @@ -9,6 +9,7 @@ run/0, run/1, run_until/1, + minmax/0, max_time/0, mode/0, pause/0, @@ -25,7 +26,8 @@ terminate/2, code_change/3]). --export([test/1]). +-export([test/1, + debug/0]). @@ -37,17 +39,21 @@ -define(SERVER, ?MODULE). + + start(N, M, InitialCells) -> gen_server:start({local, ?SERVER}, ?MODULE, [N, M, InitialCells], []). init([N, M, InitialCells]) -> - AllCells = all_cells(N, M), + egol_time:init(), egol_cell_mgr:start(), - [{start_cell(XY, {N,M}, lists:member(XY, InitialCells)), XY} - || XY <- AllCells ], + gen_server:cast(self(), {start_cells, N, M, InitialCells}), {ok, #state{size_x=N, size_y=M, - mode=step}}. + mode=not_started}}. + +debug() -> + lager:set_loglevel(lager_console_backend, debug). start_cell(XY, Dim, true) -> {ok, Pid} = egol_cell_sup:start_cell(XY, Dim, 1), @@ -70,8 +76,17 @@ run(Time) -> run_until(EndTime) -> gen_server:cast(?SERVER,{run_until, EndTime}). +minmax() -> + egol_time:minmax(). + max_time() -> - gen_server:call(?SERVER, max_time). + {_Min, Max} = egol_time:minmax(), + Max. + +min_time() -> + {Min, _Max} = egol_time:minmax(), + Min. + mode() -> gen_server:call(?SERVER, mode). @@ -91,7 +106,12 @@ print_last() -> print_lag() -> gen_server:cast(?SERVER, print_lag). - +handle_cast({start_cells, N, M, InitialCells}, State) -> + EgolPid = self(), + spawn(fun() -> start_cells(N, M, InitialCells, EgolPid) end), + {noreply, State}; +handle_cast(init_done, State) -> + {noreply, State#state{mode=step}}; handle_cast(step, State) -> step_cells(all_cells(State)), {noreply, State#state{mode=step}}; @@ -108,7 +128,7 @@ handle_cast({print,T}, State) -> print(State#state.size_x, State#state.size_y, T), {noreply, State}; handle_cast(print_last, State) -> - MinTime = minimum_time(State), + MinTime = min_time(), %minimum_time(State), io:format("Time is ~p.~n", [MinTime]), print(State#state.size_x, State#state.size_y, MinTime), {noreply, State}; @@ -116,12 +136,15 @@ handle_cast(print_lag, State) -> print_lag(State), {noreply, State}. +start_cells(N, M, InitialCells, EgolPid) -> + AllCells = all_cells(N, M), + [{start_cell(XY, {N,M}, lists:member(XY, InitialCells)), XY} + || XY <- AllCells], + gen_server:cast(EgolPid, init_done). -handle_call(mode, _From, State) -> - {reply, State#state.mode, State}; -handle_call(max_time, _From, State) -> - {reply, maximum_time(State), State}. +handle_call(mode, _From, State) -> + {reply, State#state.mode, State}. handle_info(_Msg, State) -> {noreply, State}. @@ -132,22 +155,8 @@ terminate(_Reason, _State) -> code_change(_OldVsn, State, _Extra) -> {ok, State}. - - - -maximum_time(State) -> - AllCellPids = [ egol_cell:where(Cell) - || Cell <- all_cells(State) ], - LiveCells = lists:filter( fun erlang:is_pid/1, AllCellPids), - AllTimes = [ egol_cell:time(Cell) - || Cell <- LiveCells ], - lists:max(AllTimes). -minimum_time(State) -> - Times = all_times(State), - lists:min(Times). - all_times(State) -> Tagged = all_times_tagged(State), [ T || {_, T} <- Tagged ]. @@ -157,11 +166,6 @@ all_times_tagged(State) -> || XY <- all_cells(State) ]. -fill_cells(Cells) -> - [ egol_cell:set(Cell, 1) - || Cell <- Cells]. - - step_cells(Cells) -> lists:foreach(fun egol_cell:step/1, Cells). run_cells(Cells) -> lists:foreach(fun egol_cell:run/1, Cells). @@ -223,9 +227,11 @@ format_lag(_) -> all_cells(N, M) -> + Xs = egol_util:shuffle(lists:seq(0, N - 1)), + Ys = egol_util:shuffle(lists:seq(0, M - 1)), [ {X, Y} - || X <- lists:seq(0, N-1), - Y <- lists:seq(0, M-1) ]. + || X <- Xs, + Y <- Ys]. all_cells(#state{size_x=N, size_y=M}) -> all_cells(N, M). diff --git a/src/egol_cell.erl b/src/egol_cell.erl index c04b53e..1a48eb0 100644 --- a/src/egol_cell.erl +++ b/src/egol_cell.erl @@ -125,6 +125,7 @@ cell_pid(Pid) when is_pid(Pid) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% init(#state{xy=XY}=State) -> + egol_time:set(XY, 0), {ok, State}. @@ -191,6 +192,7 @@ handle_info({Collector, {next_content, NextContent}}, content=Content, history=History}=State) -> NewFuture = process_future(XY, T+1, NextContent, Future), lager:debug("Cell ~p changing to ~p for time ~p", [XY, NextContent, T+1]), + egol_time:set(XY, T+1), NextState = State#state{content=NextContent, time=T+1, history=[{T, Content}|History], @@ -218,14 +220,13 @@ handle_info({query_content, Time, From}, State) -> end. -terminate(_Reason, _State) -> +terminate(_Reason, State) -> + egol_time:clear(State#state.xy), ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. - - is_collector_running(#state{collector=Collector}) -> is_pid(Collector) andalso is_process_alive(Collector). @@ -287,5 +288,3 @@ content_at(Time, #state{xy=XY, history=History}) when is_integer(Time), Time >= {_, Content} = lists:keyfind(Time, 1, History), {{XY, Time}, Content}. - -cell_name(XY) -> {n,l,XY}. diff --git a/src/egol_cell_mgr.erl b/src/egol_cell_mgr.erl index c562be1..29b7b2e 100644 --- a/src/egol_cell_mgr.erl +++ b/src/egol_cell_mgr.erl @@ -10,6 +10,7 @@ -export([reg/2, lookup/1]). +-export([count/0]). %% gen_server callbacks -export([init/1, @@ -25,7 +26,7 @@ -record(state, - {cells =[] :: [{pid(), reference(), cell_coordinates()}] + { monitors %%:: map {reference(), cell_coordinates()} }). start() -> @@ -36,67 +37,58 @@ reg(XY, Pid) when is_pid(Pid) -> gen_server:cast(?MODULE, {reg, XY, Pid}). lookup(XY) -> - gen_server:call(?MODULE, {lookup, XY}). + case ets:lookup(mgr_xy, XY) of + [] -> + undefined; + [{XY, Pid}] -> + Pid + end. + +count() -> + gen_server:call(?MODULE, count). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% init([]) -> - {ok, #state{}}. - -handle_call({lookup, XY}, _From, #state{cells=Cells}=State) -> - Reply = - case lists:keyfind(XY, 3, Cells) of - false -> - undefined; %% @todo queue until registered - {Pid, _Ref, XY} -> - Pid - end, - {reply, Reply, State}. - -handle_cast({reg, XY, Pid}, #state{cells=Cells}=State) -> + ets:new(mgr_xy, [named_table, + {read_concurrency, true}]), + {ok, #state{monitors=gb_trees:empty()}}. + +handle_call(count, _From, State) -> + C1 = ets:size(mgr_xy), + C2 = gb_trees:size(State#state.monitors), + {reply, {C1,C2}, State}. + +handle_cast({reg, XY, Pid}, + #state{monitors=Monitors}=State) -> + lager:debug("mgr reg ~p ~p", [XY, Pid]), NewRef = erlang:monitor(process, Pid), - NextState = State#state{cells=[{Pid, NewRef, XY}|Cells]}, + ets:insert(mgr_xy, {XY, Pid}), + NextState = State#state{monitors=gb_trees:enter(NewRef, XY, Monitors)}, spawn ( fun () -> pacer(Pid) end ), {noreply, NextState}. handle_info({'DOWN', Ref, process, _Pid, _Info}, - #state{cells=Cells}=State) -> - Rest = lists:keydelete(Ref, 2, Cells), - {noreply, State#state{cells=Rest}}. + #state{cells=Cells, monitors=Monitors}=State) -> + {value, XY} = gb_trees:lookup(Ref, Monitors), + ets:delete(mgr_xy, XY), + {noreply, State#state{%cells=gb_trees:delete(XY, Cells), + monitors=gb_trees:delete(Ref, Monitors)} + }. terminate(_Reason, _State) -> - ok. + ets:delete(mgr_xy). code_change(_OldVsn, State, _Extra) -> {ok, State}. -%% loop(#state{cells=Cells}=State) -> -%% receive -%% {'DOWN', Ref, process, _Pid, _Info} -> -%% Rest = lists:keydelete(Ref, 2, Cells), -%% loop(State#state{cells=Rest}); -%% %% NewPid = await_restart(XY), -%% %% NewRef = erlang:monitor(process, NewPid), -%% %% NextState = State#state{cells=[{NewPid, NewRef, XY}|Rest]}, -%% %% spawn ( fun () -> pacer(NewPid) end ), -%% %% loop(NextState) -%% {reg, XY, Pid} -> -%% NewRef = erlang:monitor(process, Pid), -%% NextState = State#state{cells=[{Pid, NewRef, XY}|Cells]}, -%% spawn ( fun () -> pacer(Pid) end ), -%% loop(NextState); -%% {lookup, XY, From} -> -%% case lists:keyfind(XY, 3, Cells) of -%% false -> -%% From ! undefined; %% @todo queue until registered -%% {Pid, _Ref, XY} -> -%% From ! Pid -%% end, -%% loop(State) -%% end. pacer(Pid) -> case egol:mode() of + not_started -> + lager:debug("pacer with mode not_started"), + ok; step -> EndTime = egol:max_time(), egol_cell:run_until(Pid, EndTime); @@ -106,12 +98,4 @@ pacer(Pid) -> egol_cell:run(Pid) end. -%% await_restart(XY) -> -%% timer:sleep(10), -%% case egol_cell:where(XY) of -%% undefined -> -%% await_restart(XY); -%% Pid -> -%% Pid -%% end. diff --git a/src/egol_util.erl b/src/egol_util.erl index 18494b4..928c60a 100644 --- a/src/egol_util.erl +++ b/src/egol_util.erl @@ -6,7 +6,7 @@ -export([next_content/2]). -export([format_cell_value/1]). - +-export([shuffle/1]). neighbours({X,Y}, {DimX, DimY}) -> @@ -25,4 +25,30 @@ next_content(_, _) -> 0. format_cell_value(0) -> io:format("."); -format_cell_value(1) -> io:format("*"). \ No newline at end of file +format_cell_value(1) -> io:format("*"). + +%% from https://erlangcentral.org/wiki/index.php?title=RandomShuffle +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% shuffle(List1) -> List2 +%% Takes a list and randomly shuffles it. Relies on random:uniform +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +shuffle(List) -> +%% Determine the log n portion then randomize the list. + randomize(round(math:log(length(List)) + 0.5), List). + +randomize(1, List) -> + randomize(List); +randomize(T, List) -> + lists:foldl(fun(_E, Acc) -> + randomize(Acc) + end, randomize(List), lists:seq(1, (T - 1))). + +randomize(List) -> + D = lists:map(fun(A) -> + {random:uniform(), A} + end, List), + {_, D1} = lists:unzip(lists:keysort(1, D)), + D1. From ef032247274346a4bc1341ec1c151f8383319114 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Fri, 12 Dec 2014 09:17:32 +0100 Subject: [PATCH 25/70] Clean-up cell_mgr * Removed dead code from pre-ets solution --- src/egol.erl | 4 ++-- src/egol_cell.erl | 6 +++--- src/egol_cell_mgr.erl | 6 ++---- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/egol.erl b/src/egol.erl index fc382ad..2f37431 100644 --- a/src/egol.erl +++ b/src/egol.erl @@ -128,7 +128,7 @@ handle_cast({print,T}, State) -> print(State#state.size_x, State#state.size_y, T), {noreply, State}; handle_cast(print_last, State) -> - MinTime = min_time(), %minimum_time(State), + MinTime = min_time(), io:format("Time is ~p.~n", [MinTime]), print(State#state.size_x, State#state.size_y, MinTime), {noreply, State}; @@ -140,7 +140,7 @@ start_cells(N, M, InitialCells, EgolPid) -> AllCells = all_cells(N, M), [{start_cell(XY, {N,M}, lists:member(XY, InitialCells)), XY} || XY <- AllCells], - gen_server:cast(EgolPid, init_done). + gen_server:cast(EgolPid, init_done). handle_call(mode, _From, State) -> diff --git a/src/egol_cell.erl b/src/egol_cell.erl index 1a48eb0..b4f965b 100644 --- a/src/egol_cell.erl +++ b/src/egol_cell.erl @@ -232,15 +232,15 @@ is_collector_running(#state{collector=Collector}) -> start_collector(#state{time=T, neighbours=Neighbours, - content=Content, xy=XY}=State) -> + content=Content}=State) -> lager:debug("start_collector: neighbours=~p~n", [Neighbours]), Cell = self(), Collector = spawn_link( fun () -> - collector_init(XY, T, Neighbours, Cell, Content) + collector_init(T, Neighbours, Cell, Content) end ), State#state{collector=Collector}. -collector_init(XY, Time, Neighbours, Cell, Content) -> +collector_init(Time, Neighbours, Cell, Content) -> query_neighbours(Time, Neighbours), collector_loop(egol_util:neighbours_at(Time, Neighbours), 0, Cell, Content). diff --git a/src/egol_cell_mgr.erl b/src/egol_cell_mgr.erl index 29b7b2e..fcbb496 100644 --- a/src/egol_cell_mgr.erl +++ b/src/egol_cell_mgr.erl @@ -68,12 +68,10 @@ handle_cast({reg, XY, Pid}, {noreply, NextState}. handle_info({'DOWN', Ref, process, _Pid, _Info}, - #state{cells=Cells, monitors=Monitors}=State) -> + #state{monitors=Monitors}=State) -> {value, XY} = gb_trees:lookup(Ref, Monitors), ets:delete(mgr_xy, XY), - {noreply, State#state{%cells=gb_trees:delete(XY, Cells), - monitors=gb_trees:delete(Ref, Monitors)} - }. + {noreply, State#state{monitors=gb_trees:delete(Ref, Monitors)}}. terminate(_Reason, _State) -> ets:delete(mgr_xy). From 0bdbc84c94cf9b8e7e5145ff93e5fbc168cefc0a Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Fri, 12 Dec 2014 19:19:55 +0100 Subject: [PATCH 26/70] make release is smarter --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 0ae732c..9a49fe8 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ ERLC_COMPILE_OPTS= +'{parse_transform, lager_transform}' ERLC_OPTS= $(ERLC_COMPILE_OPTS) +debug_info TEST_ERLC_OPTS= $(ERLC_COMPILE_OPTS) -release: +release: app relx --vm_args "./config/vm.args" From d9862c78470a9c531fdccd6d8f6709de5f157562 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Fri, 12 Dec 2014 19:21:37 +0100 Subject: [PATCH 27/70] Minor clean-ups in egol and egol_cell_mgr --- src/egol.erl | 16 ++++++++-------- src/egol_cell_mgr.erl | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/egol.erl b/src/egol.erl index 2f37431..710e108 100644 --- a/src/egol.erl +++ b/src/egol.erl @@ -77,15 +77,13 @@ run_until(EndTime) -> gen_server:cast(?SERVER,{run_until, EndTime}). minmax() -> - egol_time:minmax(). + {egol_time:min(), egol_time:max()}. max_time() -> - {_Min, Max} = egol_time:minmax(), - Max. + egol_time:max(). min_time() -> - {Min, _Max} = egol_time:minmax(), - Min. + egol_time:min(). mode() -> @@ -138,8 +136,10 @@ handle_cast(print_lag, State) -> start_cells(N, M, InitialCells, EgolPid) -> AllCells = all_cells(N, M), - [{start_cell(XY, {N,M}, lists:member(XY, InitialCells)), XY} - || XY <- AllCells], + lists:foreach( fun(XY) -> + start_cell(XY, {N,M}, lists:member(XY, InitialCells)) + end, + AllCells ), gen_server:cast(EgolPid, init_done). @@ -149,7 +149,7 @@ handle_call(mode, _From, State) -> handle_info(_Msg, State) -> {noreply, State}. -terminate(_Reason, _State) -> +terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> diff --git a/src/egol_cell_mgr.erl b/src/egol_cell_mgr.erl index fcbb496..a83cbe7 100644 --- a/src/egol_cell_mgr.erl +++ b/src/egol_cell_mgr.erl @@ -60,7 +60,7 @@ handle_call(count, _From, State) -> handle_cast({reg, XY, Pid}, #state{monitors=Monitors}=State) -> - lager:debug("mgr reg ~p ~p", [XY, Pid]), + %% lager:debug("mgr reg ~p ~p", [XY, Pid]), NewRef = erlang:monitor(process, Pid), ets:insert(mgr_xy, {XY, Pid}), NextState = State#state{monitors=gb_trees:enter(NewRef, XY, Monitors)}, @@ -85,7 +85,7 @@ code_change(_OldVsn, State, _Extra) -> pacer(Pid) -> case egol:mode() of not_started -> - lager:debug("pacer with mode not_started"), + %% lager:debug("pacer with mode not_started"), ok; step -> EndTime = egol:max_time(), From d2432d16c9b7ce19f7d0d129322864532f31b690 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Fri, 12 Dec 2014 19:29:52 +0100 Subject: [PATCH 28/70] egol_time --- src/egol_time.erl | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 src/egol_time.erl diff --git a/src/egol_time.erl b/src/egol_time.erl new file mode 100644 index 0000000..7f2d7e1 --- /dev/null +++ b/src/egol_time.erl @@ -0,0 +1,44 @@ +-module(egol_time). + +-export([init/0, + set/2, + min/0, + max/0, + clear/1]). + + +init() -> + ets:new(egol_time, [named_table, public]), + ets:insert(egol_time, [{max,0}]). + +set(XY, Time) -> + ets:insert(egol_time, {XY, Time}), + case Time > max() of + true -> + ets:insert(egol_time, {max, Time}); + false -> + ok + end. + + + +max() -> + [{max, Max}] = ets:lookup(egol_time, max), + Max. + +clear(XY) -> + ets:delete(egol_time, XY). + +min() -> + case ets:first(egol_time) of + '$end_of_table' -> + 0; + First -> + InitialMin = ets:lookup_element(egol_time, First, 2), + ets:foldr( fun({_,T}, Min) -> + min(T, Min) + end, + InitialMin, + egol_time) + end. + From 973088b608b775a7db2889da85d50009d3fabe2e Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Fri, 12 Dec 2014 20:09:19 +0100 Subject: [PATCH 29/70] Removed gproc from egol_eqc --- eqc_test/egol_eqc.erl | 43 ++++++++++++++++++++++++------------------- src/egol_cell.erl | 13 ++++++++++++- src/egol_time.erl | 4 ++++ 3 files changed, 40 insertions(+), 20 deletions(-) diff --git a/eqc_test/egol_eqc.erl b/eqc_test/egol_eqc.erl index 6ce244e..dfa6669 100644 --- a/eqc_test/egol_eqc.erl +++ b/eqc_test/egol_eqc.erl @@ -45,8 +45,8 @@ prop_cell() -> ?SETUP(fun() -> %% setup mocking here eqc_mocking:start_mocking(api_spec()), -%% fun() -> ok end - fun() -> application:stop(gproc) end %% Teardown function + fun() -> ok end +%% fun() -> application:stop(gproc) end %% Teardown function end, ?FORALL(Cmds, commands(?MODULE), % ?IMPLIES(length(Cmds)>20, @@ -63,12 +63,15 @@ prop_cell() -> start() -> - application:start(gproc), + egol_time:init(), + egol_cell_mgr:start(), egol_cell_sup:start_link(), ok. stop(S) -> catch exit(whereis(egol_cell_sup), normal), + catch exit(whereis(egol_cell_mgr), normal), + egol_time:stop(), %% application:stop(gproc), %% egol_cell:kill(S#state.id), ok. @@ -101,23 +104,25 @@ cell_next(S, Pid, [CellId, Dim, Content]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% step(Pid, Id, Time) -> - case gproc:where(collector_name(Id, Time)) of - undefined -> - ok; - OldCollector -> - io:format("killed old collector~n"), - exit(OldCollector, kill) - end, - Res = egol_cell:step(Pid), + %% case gproc:where(collector_name(Id, Time)) of + %% undefined -> + %% ok; + %% OldCollector -> + %% io:format("killed old collector~n"), + %% exit(OldCollector, kill) + %% end, + CollectorPid = egol_cell:step(Pid), timer:sleep(100), - gproc:await(collector_name(Id, Time),100), - case gproc:where(collector_name(Id, Time)) of - undefined -> - io:format("BUMMER!!! step has NOT started collector~n"); - Collector -> - io:format("collector started after step:~p~n",[Collector]), - Collector - end. + CollectorPid. + %% timer:sleep(100), + %% gproc:await(collector_name(Id, Time),100), + %% case gproc:where(collector_name(Id, Time)) of + %% undefined -> + %% io:format("BUMMER!!! step has NOT started collector~n"); + %% Collector -> + %% io:format("collector started after step:~p~n",[Collector]), + %% Collector + %% end. %% Res. collector_name(Id, Time) -> {n,l,{collector, Id, Time}}. diff --git a/src/egol_cell.erl b/src/egol_cell.erl index b4f965b..9579788 100644 --- a/src/egol_cell.erl +++ b/src/egol_cell.erl @@ -105,7 +105,7 @@ pause(Cell) -> cast(Cell, pause). step(Cell) -> - cast(Cell, step). + call(Cell, step). @@ -182,8 +182,19 @@ handle_call({get, Time}, _From, State) -> {reply, future, State}; {_, C} -> {reply, C, State} + end; +handle_call(step, _From, State) -> + case is_collector_running(State) of + true -> + Reply = State#state.collector, + {reply, Reply, State#state{mode=step}}; + false -> + NewState = start_collector(State), + Reply = NewState#state.collector, + {reply, Reply, NewState#state{mode=step}} end. + handle_info({Collector, {next_content, NextContent}}, diff --git a/src/egol_time.erl b/src/egol_time.erl index 7f2d7e1..46ffeba 100644 --- a/src/egol_time.erl +++ b/src/egol_time.erl @@ -1,6 +1,7 @@ -module(egol_time). -export([init/0, + stop/0, set/2, min/0, max/0, @@ -11,6 +12,9 @@ init() -> ets:new(egol_time, [named_table, public]), ets:insert(egol_time, [{max,0}]). +stop() -> + ets:delete(egol_time). + set(XY, Time) -> ets:insert(egol_time, {XY, Time}), case Time > max() of From 41ccfe6a41b9ce0ad8169ff8c1e9b07a331c817d Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Fri, 12 Dec 2014 20:21:42 +0100 Subject: [PATCH 30/70] Removed all dead code for gproc --- eqc_test/egol_eqc.erl | 55 ++++--------------------------------------- 1 file changed, 5 insertions(+), 50 deletions(-) diff --git a/eqc_test/egol_eqc.erl b/eqc_test/egol_eqc.erl index dfa6669..79356a8 100644 --- a/eqc_test/egol_eqc.erl +++ b/eqc_test/egol_eqc.erl @@ -26,12 +26,7 @@ api_spec() -> functions = [ #api_fun{ name = query_content, arity = 2}, #api_fun{ name = query_response, arity = 2} ] - }%, - %% #api_module{ - %% name = gproc, - %% functions = [ #api_fun{ name = where, arity=1}, - %% #api_fun{ name = reg, arity=1} ] - %% } + } ]}. @@ -46,7 +41,6 @@ prop_cell() -> %% setup mocking here eqc_mocking:start_mocking(api_spec()), fun() -> ok end -%% fun() -> application:stop(gproc) end %% Teardown function end, ?FORALL(Cmds, commands(?MODULE), % ?IMPLIES(length(Cmds)>20, @@ -72,8 +66,6 @@ stop(S) -> catch exit(whereis(egol_cell_sup), normal), catch exit(whereis(egol_cell_mgr), normal), egol_time:stop(), -%% application:stop(gproc), -%% egol_cell:kill(S#state.id), ok. @@ -88,7 +80,7 @@ weight(_S, _) -> 4. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% cell(XY, Dim, Content) -> {ok, Pid} = egol_cell_sup:start_cell(XY, Dim, Content), - io:format("cell STARTED~n"), +% io:format("cell STARTED~n"), Pid. cell_args(_S) -> @@ -104,28 +96,9 @@ cell_next(S, Pid, [CellId, Dim, Content]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% step(Pid, Id, Time) -> - %% case gproc:where(collector_name(Id, Time)) of - %% undefined -> - %% ok; - %% OldCollector -> - %% io:format("killed old collector~n"), - %% exit(OldCollector, kill) - %% end, CollectorPid = egol_cell:step(Pid), timer:sleep(100), CollectorPid. - %% timer:sleep(100), - %% gproc:await(collector_name(Id, Time),100), - %% case gproc:where(collector_name(Id, Time)) of - %% undefined -> - %% io:format("BUMMER!!! step has NOT started collector~n"); - %% Collector -> - %% io:format("collector started after step:~p~n",[Collector]), - %% Collector - %% end. -%% Res. - -collector_name(Id, Time) -> {n,l,{collector, Id, Time}}. step_args(S) -> [S#state.cell, S#state.id, S#state.time]. @@ -142,7 +115,7 @@ step_return(_S, _) -> ok. step_next(S, Res, _Args) -> - + io:format("step_next time:~p~n", [S#state.time]), S#state{waiting_on = egol_util:neighbours_at(S#state.time, egol_util:neighbours(S#state.id, S#state.dim)), collector=Res}. @@ -150,7 +123,7 @@ step_next(S, Res, _Args) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% get(Pid, Time) -> Res = egol_cell:get(Pid, Time), - io:format("get works~n"), +% io:format("get works~n"), Res. get_args(S) -> @@ -164,7 +137,6 @@ get_post(S, [_Pid, _Time], Res) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% query_response(Pid, XY, Time, Content) -> -%% egol_protocol:query_response(Pid, {cell_content, {XY, Time}, Content}). Pid ! {cell_content, {{XY, Time}, Content}}. query_response_args(#state{id=undefined}) -> @@ -172,24 +144,7 @@ query_response_args(#state{id=undefined}) -> query_response_args(#state{cell=Cell, waiting_on=undefined}) when is_pid(Cell) -> [undefined, undefined, undefined, undefined]; query_response_args(S) -> - [S#state.collector, neighbour(S), S#state.time, content()]; - -query_response_args(S) -> - try -%% {Collector,_} = gproc:await({n,l,{collector, S#state.id, S#state.time}},100), - Collector = gproc:where(collector_name(S#state.id, S#state.time)), - case Collector of - undefined -> - io:format("undefined collector~n"); - _ -> - io:format("collector is running~n") - end, - - [Collector, neighbour(S), S#state.time, content()] - catch - _:_ -> - [undefined, undefined, undefined, undefined] - end. + [S#state.collector, neighbour(S), S#state.time, content()]. query_response_pre(#state{waiting_on=undefined}, _ ) -> false; query_response_pre(S, [Collector, Neighbour, Time, _] ) -> From 109ef30881169f6a8106c29198048a6ebc742865 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Sun, 14 Dec 2014 20:50:07 +0100 Subject: [PATCH 31/70] Add egol_cell:collector/1 for testing purposes. * Made egol_cell:step/1 a cast again. --- eqc_test/egol_eqc.erl | 4 ++-- src/egol_cell.erl | 28 ++++++++++++++++++---------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/eqc_test/egol_eqc.erl b/eqc_test/egol_eqc.erl index 79356a8..ff0fe1a 100644 --- a/eqc_test/egol_eqc.erl +++ b/eqc_test/egol_eqc.erl @@ -96,9 +96,9 @@ cell_next(S, Pid, [CellId, Dim, Content]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% step(Pid, Id, Time) -> - CollectorPid = egol_cell:step(Pid), + egol_cell:step(Pid), timer:sleep(100), - CollectorPid. + egol_cell:collector(Pid). step_args(S) -> [S#state.cell, S#state.id, S#state.time]. diff --git a/src/egol_cell.erl b/src/egol_cell.erl index 9579788..abbdb30 100644 --- a/src/egol_cell.erl +++ b/src/egol_cell.erl @@ -27,6 +27,9 @@ pause/1, step/1]). +%% for testing only!! +-export([collector/1]). + -type cell_content() :: 0 | 1. -type cell_name() :: {integer(), integer()}. -type time() :: integer(). @@ -105,9 +108,10 @@ pause(Cell) -> cast(Cell, pause). step(Cell) -> - call(Cell, step). - + cast(Cell, step). +collector(Cell) -> + call(Cell, collector). call(Cell, Cmd) -> gen_server:call(cell_pid(Cell), Cmd). @@ -170,7 +174,15 @@ handle_cast({run_until, EndTime}, end end; handle_cast(pause, State) -> - {noreply, State#state{mode=step}}. + {noreply, State#state{mode=step}}; +handle_cast(step, State) -> + case is_collector_running(State) of + true -> + {noreply, State#state{mode=step}}; + false -> + NewState = start_collector(State), + {noreply, NewState#state{mode=step}} + end. handle_call(history, _From, State) -> {reply, State#state.history, State}; @@ -183,18 +195,14 @@ handle_call({get, Time}, _From, State) -> {_, C} -> {reply, C, State} end; -handle_call(step, _From, State) -> +handle_call(collector, _From, State) -> case is_collector_running(State) of true -> - Reply = State#state.collector, - {reply, Reply, State#state{mode=step}}; + {reply, State#state.collector, State}; false -> - NewState = start_collector(State), - Reply = NewState#state.collector, - {reply, Reply, NewState#state{mode=step}} + {reply, undefined, State} end. - handle_info({Collector, {next_content, NextContent}}, From 71b439b6397add923dee0fd3c70e1a68881fcc95 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Sun, 14 Dec 2014 21:26:14 +0100 Subject: [PATCH 32/70] Add complete_step to ease modelling of restarting cells. --- eqc_test/egol_eqc.erl | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/eqc_test/egol_eqc.erl b/eqc_test/egol_eqc.erl index ff0fe1a..5db0528 100644 --- a/eqc_test/egol_eqc.erl +++ b/eqc_test/egol_eqc.erl @@ -62,7 +62,7 @@ start() -> egol_cell_sup:start_link(), ok. -stop(S) -> +stop(_S) -> catch exit(whereis(egol_cell_sup), normal), catch exit(whereis(egol_cell_mgr), normal), egol_time:stop(), @@ -95,7 +95,7 @@ cell_next(S, Pid, [CellId, Dim, Content]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -step(Pid, Id, Time) -> +step(Pid, _Id, _Time) -> egol_cell:step(Pid), timer:sleep(100), egol_cell:collector(Pid). @@ -115,7 +115,7 @@ step_return(_S, _) -> ok. step_next(S, Res, _Args) -> - io:format("step_next time:~p~n", [S#state.time]), +% io:format("step_next time:~p~n", [S#state.time]), S#state{waiting_on = egol_util:neighbours_at(S#state.time, egol_util:neighbours(S#state.id, S#state.dim)), collector=Res}. @@ -158,7 +158,7 @@ query_response_next(S, _Res, [_, Neighbour, Time, Content]) -> case lists:delete({Neighbour, Time}, S#state.waiting_on) of [] -> timer:sleep(50), - io:format("got ALL neighbour contents~n"), + % io:format("got ALL neighbour contents~n"), S#state{content=next_content(S#state.content, NC), time=Time+1, waiting_on=undefined, @@ -169,11 +169,11 @@ query_response_next(S, _Res, [_, Neighbour, Time, Content]) -> end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -flood_query_response(Pid, Cells) -> +flood_query_response(Pid, CellContents) -> lists:foreach( fun(CC) -> Pid ! CC end, - Cells). + CellContents). cell_contents(CellsAtT) -> [ cell_content(CellAtT, content()) @@ -205,6 +205,29 @@ flood_query_response_next(S, _Res, [_Pid, CellContents]) -> S#state{waiting_on=[hd(S#state.waiting_on)], neighbour_count=S#state.neighbour_count+NC}. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +complete_step(Pid, CellContents) -> + flood_query_response(Pid, tl(CellContents)), + {cell_content, {{XY, Time}, Content}} = hd(CellContents), + query_response(Pid, XY, Time, Content). + +complete_step_args(#state{waiting_on=Wait}=S) when is_list(Wait) andalso + length(Wait) > 1 -> + [S#state.collector, cell_contents(Wait)]; +complete_step_args(_) -> + [undefined, undefined]. + +complete_step_pre(#state{waiting_on=Wait}) when is_list(Wait) andalso + length(Wait) > 1 -> + true; +complete_step_pre(_) -> + false. + +complete_step_next(S, Res, [Pid, CellContents]) -> + S2 = flood_query_response_next(S, Res, [Pid, tl(CellContents)]), + {cell_content, {{XY, Time}, Content}} = hd(CellContents), + query_response_next(S2, Res, [Pid, XY, Time, Content]). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% From 2f7487b487a3bbc39fb33167d4dedfb59c15e78f Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Sun, 14 Dec 2014 22:16:49 +0100 Subject: [PATCH 33/70] Made egol_cell_mgr carry mode. --- src/egol.erl | 3 +++ src/egol_cell_mgr.erl | 35 ++++++++++++++++++----------------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/egol.erl b/src/egol.erl index 710e108..425dd48 100644 --- a/src/egol.erl +++ b/src/egol.erl @@ -111,12 +111,15 @@ handle_cast({start_cells, N, M, InitialCells}, State) -> handle_cast(init_done, State) -> {noreply, State#state{mode=step}}; handle_cast(step, State) -> + egol_cell_mgr:set_mode(step), step_cells(all_cells(State)), {noreply, State#state{mode=step}}; handle_cast(run, State) -> + egol_cell_mgr:set_mode(Run), run_cells(all_cells(State)), {noreply, State#state{mode=run}}; handle_cast({run_until, EndTime}, State) -> + egol_cell_mgr:set_mode({run_until, EndTime}), run_cells_until(all_cells(State), EndTime), {noreply, State#state{mode={run_until,EndTime}}}; handle_cast(pause, State) -> diff --git a/src/egol_cell_mgr.erl b/src/egol_cell_mgr.erl index a83cbe7..784f610 100644 --- a/src/egol_cell_mgr.erl +++ b/src/egol_cell_mgr.erl @@ -8,7 +8,8 @@ -export([start/0]). -export([reg/2, - lookup/1]). + lookup/1, + set_mode/1]). -export([count/0]). @@ -26,7 +27,8 @@ -record(state, - { monitors %%:: map {reference(), cell_coordinates()} + { mode = step, + monitors %%:: map {reference(), cell_coordinates()} }). start() -> @@ -36,6 +38,9 @@ start() -> reg(XY, Pid) when is_pid(Pid) -> gen_server:cast(?MODULE, {reg, XY, Pid}). +set_mode(Mode) -> + gen_server:cast(?MODULE, {set_mode, Mode}). + lookup(XY) -> case ets:lookup(mgr_xy, XY) of [] -> @@ -64,8 +69,10 @@ handle_cast({reg, XY, Pid}, NewRef = erlang:monitor(process, Pid), ets:insert(mgr_xy, {XY, Pid}), NextState = State#state{monitors=gb_trees:enter(NewRef, XY, Monitors)}, - spawn ( fun () -> pacer(Pid) end ), - {noreply, NextState}. + spawn ( fun () -> pacer(Pid, State#state.mode) end ), + {noreply, NextState}; +handle_cast({set_mode, Mode}, State) -> + {noreply, State#state{mode=Mode}}. handle_info({'DOWN', Ref, process, _Pid, _Info}, #state{monitors=Monitors}=State) -> @@ -82,18 +89,12 @@ code_change(_OldVsn, State, _Extra) -> -pacer(Pid) -> - case egol:mode() of - not_started -> - %% lager:debug("pacer with mode not_started"), - ok; - step -> - EndTime = egol:max_time(), - egol_cell:run_until(Pid, EndTime); - {run_until, EndTime} -> - egol_cell:run_until(Pid, EndTime); - run -> - egol_cell:run(Pid) - end. +pacer(Pid, step) -> + EndTime = egol_time:max(), + egol_cell:run_until(Pid, EndTime); +pacer(Pid, {run_until, EndTime}) -> + egol_cell:run_until(Pid, EndTime); +pacer(Pid, run) -> + egol_cell:run(Pid). From 6b2b326c5ced23d716931447fb825148d29d9431 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Sun, 14 Dec 2014 22:17:45 +0100 Subject: [PATCH 34/70] Refactored query_response args. * Not a flat list, but {{XY, T}, C} --- eqc_test/egol_eqc.erl | 44 +++++++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/eqc_test/egol_eqc.erl b/eqc_test/egol_eqc.erl index 5db0528..ef967d4 100644 --- a/eqc_test/egol_eqc.erl +++ b/eqc_test/egol_eqc.erl @@ -90,6 +90,7 @@ cell_args(_S) -> cell_pre(S, _) -> S#state.cell == undefined. + cell_next(S, Pid, [CellId, Dim, Content]) -> S#state{cell=Pid, id=CellId, dim=Dim, content=Content}. @@ -136,24 +137,24 @@ get_post(S, [_Pid, _Time], Res) -> eq(Res, S#state.content). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -query_response(Pid, XY, Time, Content) -> - Pid ! {cell_content, {{XY, Time}, Content}}. +query_response(Pid, Resp) -> + send_query_response(Pid, Resp). query_response_args(#state{id=undefined}) -> - [undefined, undefined, undefined, undefined]; + [undefined, undefined]; query_response_args(#state{cell=Cell, waiting_on=undefined}) when is_pid(Cell) -> - [undefined, undefined, undefined, undefined]; + [undefined, undefined]; query_response_args(S) -> - [S#state.collector, neighbour(S), S#state.time, content()]. + [S#state.collector, {{neighbour(S), S#state.time}, content()}]. query_response_pre(#state{waiting_on=undefined}, _ ) -> false; -query_response_pre(S, [Collector, Neighbour, Time, _] ) -> +query_response_pre(S, [Collector, {{Neighbour, Time}, _}] ) -> Collector /= undefined andalso S#state.waiting_on /= [] andalso lists:member({Neighbour, Time}, S#state.waiting_on) andalso Time == S#state.time. -query_response_next(S, _Res, [_, Neighbour, Time, Content]) -> +query_response_next(S, _Res, [_, {{Neighbour, Time}, Content}]) -> NC = S#state.neighbour_count + Content, case lists:delete({Neighbour, Time}, S#state.waiting_on) of [] -> @@ -171,7 +172,7 @@ query_response_next(S, _Res, [_, Neighbour, Time, Content]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% flood_query_response(Pid, CellContents) -> lists:foreach( fun(CC) -> - Pid ! CC + send_query_response(Pid, CC) end, CellContents). @@ -180,7 +181,7 @@ cell_contents(CellsAtT) -> || CellAtT <- CellsAtT ]. cell_content(CellAtT, Content) -> - {cell_content, {CellAtT, Content}}. + {CellAtT, Content}. flood_query_response_args(#state{waiting_on=[_]}) -> [undefined, undefined]; @@ -200,7 +201,7 @@ flood_query_response_pre(_,_) -> false. flood_query_response_next(S, _Res, [_Pid, CellContents]) -> - Contents = [ C || {cell_content, {_, C}} <- CellContents], + Contents = [ C || {_, C} <- CellContents], NC = lists:sum(Contents), S#state{waiting_on=[hd(S#state.waiting_on)], neighbour_count=S#state.neighbour_count+NC}. @@ -208,8 +209,7 @@ flood_query_response_next(S, _Res, [_Pid, CellContents]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% complete_step(Pid, CellContents) -> flood_query_response(Pid, tl(CellContents)), - {cell_content, {{XY, Time}, Content}} = hd(CellContents), - query_response(Pid, XY, Time, Content). + query_response(Pid, hd(CellContents)). complete_step_args(#state{waiting_on=Wait}=S) when is_list(Wait) andalso length(Wait) > 1 -> @@ -225,9 +225,23 @@ complete_step_pre(_) -> complete_step_next(S, Res, [Pid, CellContents]) -> S2 = flood_query_response_next(S, Res, [Pid, tl(CellContents)]), - {cell_content, {{XY, Time}, Content}} = hd(CellContents), - query_response_next(S2, Res, [Pid, XY, Time, Content]). + query_response_next(S2, Res, [Pid, hd(CellContents)]). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% kill(Id, EndTime, NeighbourHistory) -> +%% egol_cell:kill(Id), +%% timer:sleep(100), +%% Pid = egol_cell_mgr:lookup(Id), +%% Collector = egol_cell:collector(Pid), +%% drive_collector(Pid, Collector, 0, EndTime, NeighbourHistory), +%% Pid. + +%% %% go forward until the cell has progressed to max time +%% drive_collector(_Pid, _Collector, T, T, NeighbourHistory) -> +%% ; +%% drive_collector(Pid, Collector, T, EndTime, NeighbourHistory) -> + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -259,3 +273,5 @@ next_content(0,3) -> 1; next_content(C, N) when N==2; N==3 -> C; next_content(_,_) -> 0. +send_query_response(Pid, {{_XY,_T}, _C}=Resp) -> + Pid ! {cell_content, Resp}. From 7664477f30985c0270eff04b4e47b263cb3b515f Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Sun, 14 Dec 2014 22:18:32 +0100 Subject: [PATCH 35/70] Fix variable to atom for run mode. --- src/egol.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/egol.erl b/src/egol.erl index 425dd48..291c215 100644 --- a/src/egol.erl +++ b/src/egol.erl @@ -115,7 +115,7 @@ handle_cast(step, State) -> step_cells(all_cells(State)), {noreply, State#state{mode=step}}; handle_cast(run, State) -> - egol_cell_mgr:set_mode(Run), + egol_cell_mgr:set_mode(run), run_cells(all_cells(State)), {noreply, State#state{mode=run}}; handle_cast({run_until, EndTime}, State) -> From acc7be0991b49f1e4b5951f2c964edf8892b9f2d Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Sun, 14 Dec 2014 22:24:08 +0100 Subject: [PATCH 36/70] egol_eqc: carry neighbour history in model --- eqc_test/egol_eqc.erl | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/eqc_test/egol_eqc.erl b/eqc_test/egol_eqc.erl index ef967d4..81a2c3d 100644 --- a/eqc_test/egol_eqc.erl +++ b/eqc_test/egol_eqc.erl @@ -13,7 +13,8 @@ time=0, collector, waiting_on = undefined, - neighbour_count = 0 + neighbour_count = 0, + neighbour_history = [] }). @@ -154,7 +155,7 @@ query_response_pre(S, [Collector, {{Neighbour, Time}, _}] ) -> lists:member({Neighbour, Time}, S#state.waiting_on) andalso Time == S#state.time. -query_response_next(S, _Res, [_, {{Neighbour, Time}, Content}]) -> +query_response_next(S, _Res, [_, {{Neighbour, Time}, Content}=Resp]) -> NC = S#state.neighbour_count + Content, case lists:delete({Neighbour, Time}, S#state.waiting_on) of [] -> @@ -164,9 +165,11 @@ query_response_next(S, _Res, [_, {{Neighbour, Time}, Content}]) -> time=Time+1, waiting_on=undefined, collector=undefined, - neighbour_count=0}; + neighbour_count=0, + neighbour_history=S#state.neighbour_history ++ [Resp]}; Wait -> - S#state{waiting_on = Wait, neighbour_count = NC} + S#state{waiting_on = Wait, neighbour_count = NC, + neighbour_history=S#state.neighbour_history ++ [Resp]} end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -204,7 +207,8 @@ flood_query_response_next(S, _Res, [_Pid, CellContents]) -> Contents = [ C || {_, C} <- CellContents], NC = lists:sum(Contents), S#state{waiting_on=[hd(S#state.waiting_on)], - neighbour_count=S#state.neighbour_count+NC}. + neighbour_count=S#state.neighbour_count+NC, + neighbour_history=S#state.neighbour_history ++ CellContents}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% complete_step(Pid, CellContents) -> From 04ae03d6cb70a1f83594317726cb9df08060cca3 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Mon, 15 Dec 2014 08:50:49 +0100 Subject: [PATCH 37/70] Added egol_sup to start everything. --- src/egol.erl | 12 +++++------- src/egol_cell.erl | 1 + src/egol_cell_mgr.erl | 8 ++++---- src/egol_cell_sup.erl | 2 +- src/egol_sup.erl | 35 +++++++++++++++++++++++++++++++++++ 5 files changed, 46 insertions(+), 12 deletions(-) create mode 100644 src/egol_sup.erl diff --git a/src/egol.erl b/src/egol.erl index 291c215..228e73f 100644 --- a/src/egol.erl +++ b/src/egol.erl @@ -45,8 +45,6 @@ start(N, M, InitialCells) -> gen_server:start({local, ?SERVER}, ?MODULE, [N, M, InitialCells], []). init([N, M, InitialCells]) -> - egol_time:init(), - egol_cell_mgr:start(), gen_server:cast(self(), {start_cells, N, M, InitialCells}), {ok, #state{size_x=N, size_y=M, @@ -254,10 +252,10 @@ test(1) -> test(2) -> [{0,0}, {1,0}, {2,0}, {2,1},{1,2}]; test(3) -> - egol_cell_sup:start_link(), + egol_sup:start_link(), start(8,8,test(2)); test(100) -> - egol_cell_sup:start_link(), + egol__sup:start_link(), start(100,100,test(2)); test(4) -> test(3), @@ -281,13 +279,13 @@ test(6) -> InitialCells = [{2,3}, {2,5}, {3,2}, {3,3}, {3,4}, {4,1}, {4,5}], - egol_cell_sup:start_link(), + egol_sup:start_link(), start(7,6, InitialCells); test(simple) -> - egol_cell_sup:start_link(), + egol_sup:start_link(), start(4,4,test(2)); test(N) -> - egol_cell_sup:start_link(), + egol_sup:start_link(), start(N,N,test(2)). diff --git a/src/egol_cell.erl b/src/egol_cell.erl index abbdb30..5c31b48 100644 --- a/src/egol_cell.erl +++ b/src/egol_cell.erl @@ -130,6 +130,7 @@ cell_pid(Pid) when is_pid(Pid) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% init(#state{xy=XY}=State) -> egol_time:set(XY, 0), + egol_cell_mgr:reg(XY, self()), {ok, State}. diff --git a/src/egol_cell_mgr.erl b/src/egol_cell_mgr.erl index 784f610..01c77c2 100644 --- a/src/egol_cell_mgr.erl +++ b/src/egol_cell_mgr.erl @@ -5,7 +5,7 @@ -behaviour(gen_server). --export([start/0]). +-export([start_link/0]). -export([reg/2, lookup/1, @@ -31,8 +31,8 @@ monitors %%:: map {reference(), cell_coordinates()} }). -start() -> - gen_server:start({local,?MODULE}, ?MODULE, [], []). +start_link() -> + gen_server:start_link({local,?MODULE}, ?MODULE, [], []). reg(XY, Pid) when is_pid(Pid) -> @@ -65,7 +65,7 @@ handle_call(count, _From, State) -> handle_cast({reg, XY, Pid}, #state{monitors=Monitors}=State) -> - %% lager:debug("mgr reg ~p ~p", [XY, Pid]), + lager:debug("mgr reg ~p ~p", [XY, Pid]), NewRef = erlang:monitor(process, Pid), ets:insert(mgr_xy, {XY, Pid}), NextState = State#state{monitors=gb_trees:enter(NewRef, XY, Monitors)}, diff --git a/src/egol_cell_sup.erl b/src/egol_cell_sup.erl index a1e3b8a..a05e329 100644 --- a/src/egol_cell_sup.erl +++ b/src/egol_cell_sup.erl @@ -20,7 +20,7 @@ start_link() -> start_cell(XY, Dim, InitialContent) -> {ok, Pid} = Res = supervisor:start_child(?SERVER, [XY, Dim, InitialContent]), - egol_cell_mgr:reg(XY, Pid), +%% egol_cell_mgr:reg(XY, Pid), Res. %%%=================================================================== diff --git a/src/egol_sup.erl b/src/egol_sup.erl new file mode 100644 index 0000000..d693f83 --- /dev/null +++ b/src/egol_sup.erl @@ -0,0 +1,35 @@ +-module(egol_sup). + +-behaviour(supervisor). + +-export([start_link/0]). + +-export([init/1]). + + +-define(SERVER, ?MODULE). + +start_link() -> + egol_time:init(), + supervisor:start_link({local, ?SERVER}, ?MODULE, []). + + +init([]) -> + RestartStrategy = one_for_all, + MaxRestarts = 1000, + MaxSecondsBetweenRestarts = 3600, + + SupFlags = {RestartStrategy, MaxRestarts, MaxSecondsBetweenRestarts}, + + Restart = permanent, + Shutdown = 2000, + + %% Time = {egol_time, {egol_time, init, []}, + %% Restart, Shutdown, worker, [egol_time]}, + CellSup= {egol_cell_sup, {egol_cell_sup, start_link, []}, + Restart, Shutdown, supervisor, [egol_cell_sup]}, + MgrSup = {egol_cell_mgr, {egol_cell_mgr, start_link, []}, + Restart, Shutdown, worker, [egol_cell_mgr]}, + + + {ok, {SupFlags, [MgrSup, CellSup]}}. From e0e2755827ab29df7cde822da3492fa02c4aee37 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Mon, 15 Dec 2014 09:18:25 +0100 Subject: [PATCH 38/70] Got the start-up for egol_eqc working. --- eqc_test/egol_eqc.erl | 180 ++++++++++++++++++++++++++++++++---------- 1 file changed, 137 insertions(+), 43 deletions(-) diff --git a/eqc_test/egol_eqc.erl b/eqc_test/egol_eqc.erl index 81a2c3d..6d601d1 100644 --- a/eqc_test/egol_eqc.erl +++ b/eqc_test/egol_eqc.erl @@ -11,10 +11,12 @@ dim, content, time=0, + steps=0, collector, waiting_on = undefined, neighbour_count = 0, - neighbour_history = [] + neighbour_history = [], + has_killed = false }). @@ -38,9 +40,10 @@ initial_state() -> %% @doc Default generated property -spec prop_cell() -> eqc:property(). prop_cell() -> - ?SETUP(fun() -> + ?SETUP( %%fun my_setup/0, +fun() -> %% setup mocking here - eqc_mocking:start_mocking(api_spec()), + eqc_mocking:start_mocking(api_spec()), fun() -> ok end end, ?FORALL(Cmds, commands(?MODULE), @@ -56,21 +59,41 @@ prop_cell() -> %) . +my_setup() -> + start(), + fun() -> stop end. + start() -> - egol_time:init(), - egol_cell_mgr:start(), - egol_cell_sup:start_link(), + catch egol_time:stop(), + egol_sup:start_link(), ok. -stop(_S) -> - catch exit(whereis(egol_cell_sup), normal), - catch exit(whereis(egol_cell_mgr), normal), - egol_time:stop(), +stop() -> + print_regs(), + catch exit(whereis(egol_sup), normal), + catch egol_time:stop(), + eqc_mocking:stop_mocking(), + timer:sleep(100), ok. +print_regs() -> + try + CellSup = whereis(cell_sup), + MgrSup = whereis(mgr_sup), + io:format("CellSup: ~p - MgrSup: ~p~n", [CellSup, MgrSup]) + catch + _:_ -> + io:format("unable to print regs~n") + end. + +stop(S) -> + catch exit(S#state.cell, normal), + catch exit(S#state.collector, normal), + timer:sleep(100). +weight(_S, kill) -> 0; weight(_S, get) -> 1; weight(_S, cell) -> 1; weight(_S, flood_requery_response) -> 20; @@ -81,7 +104,7 @@ weight(_S, _) -> 4. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% cell(XY, Dim, Content) -> {ok, Pid} = egol_cell_sup:start_cell(XY, Dim, Content), -% io:format("cell STARTED~n"), + io:format("cell STARTED ~p (~p/~p)~n", [Pid, XY, Dim]), Pid. cell_args(_S) -> @@ -91,54 +114,67 @@ cell_args(_S) -> cell_pre(S, _) -> S#state.cell == undefined. +cell_post(_S, [_, _, _], Res) -> + is_pid(Res) and erlang:is_process_alive(Res). + cell_next(S, Pid, [CellId, Dim, Content]) -> S#state{cell=Pid, id=CellId, dim=Dim, content=Content}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -step(Pid, _Id, _Time) -> - egol_cell:step(Pid), +step(Pid, Id, _Time) -> + io:format("step for ~p/~p~n", [Pid, Id]), + egol_cell:step(Id), + Res = egol_cell:collector(Id), timer:sleep(100), - egol_cell:collector(Pid). + Res. step_args(S) -> [S#state.cell, S#state.id, S#state.time]. -step_pre(S, [Pid, _, _]) -> - S#state.cell /= undefined andalso - S#state.cell == Pid andalso +step_pre(S, [Pid, Id, _]) -> + %% io:format("step_pre cell:~p, pid:~p waiting_on:~p~n", + %% [S#state.cell, Pid, S#state.waiting_on]), + S#state.id /= undefined andalso +%% S#state.cell == Pid andalso + S#state.id == Id andalso S#state.waiting_on == undefined. step_callouts(S, [_Pid, _XY, _Time ]) -> - ?PAR(lists:duplicate(8, ?CALLOUT(egol_protocol, query_content, [?WILDCARD, S#state.time], ok))). + step_callouts_for_time(S#state.time). -step_return(_S, _) -> - ok. +step_callouts_for_time(Time) -> + ?PAR(lists:duplicate(8, ?CALLOUT(egol_protocol, query_content, [?WILDCARD, Time], ok))). + +%% step_return(_S, _) -> +%% ok. step_next(S, Res, _Args) -> % io:format("step_next time:~p~n", [S#state.time]), S#state{waiting_on = egol_util:neighbours_at(S#state.time, egol_util:neighbours(S#state.id, S#state.dim)), - collector=Res}. + collector=Res, + steps=S#state.steps+1}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -get(Pid, Time) -> - Res = egol_cell:get(Pid, Time), +get(Id, Time) -> + Res = egol_cell:get(Id, Time), % io:format("get works~n"), Res. get_args(S) -> - [S#state.cell, S#state.time]. + [S#state.id, S#state.time]. get_pre(S, _) -> - S#state.cell /= undefined. + S#state.id /= undefined. -get_post(S, [_Pid, _Time], Res) -> - eq(Res, S#state.content). +get_post(S, [_Id, _Time], Res) -> + Res == S#state.content. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% query_response(Pid, Resp) -> + io:format("query_response(~p)~n", [Pid]), send_query_response(Pid, Resp). query_response_args(#state{id=undefined}) -> @@ -174,6 +210,7 @@ query_response_next(S, _Res, [_, {{Neighbour, Time}, Content}=Resp]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% flood_query_response(Pid, CellContents) -> + io:format("flood_query_response(~p)~n", [Pid]), lists:foreach( fun(CC) -> send_query_response(Pid, CC) end, @@ -212,6 +249,7 @@ flood_query_response_next(S, _Res, [_Pid, CellContents]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% complete_step(Pid, CellContents) -> + io:format("complete_step(~p)~n", [Pid]), flood_query_response(Pid, tl(CellContents)), query_response(Pid, hd(CellContents)). @@ -221,10 +259,10 @@ complete_step_args(#state{waiting_on=Wait}=S) when is_list(Wait) andalso complete_step_args(_) -> [undefined, undefined]. -complete_step_pre(#state{waiting_on=Wait}) when is_list(Wait) andalso - length(Wait) > 1 -> +complete_step_pre(#state{waiting_on=Wait}, [_,_]) when is_list(Wait) andalso + length(Wait) > 1 -> true; -complete_step_pre(_) -> +complete_step_pre(_, [_,_]) -> false. complete_step_next(S, Res, [Pid, CellContents]) -> @@ -233,19 +271,75 @@ complete_step_next(S, Res, [Pid, CellContents]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% kill(Id, EndTime, NeighbourHistory) -> -%% egol_cell:kill(Id), -%% timer:sleep(100), -%% Pid = egol_cell_mgr:lookup(Id), -%% Collector = egol_cell:collector(Pid), -%% drive_collector(Pid, Collector, 0, EndTime, NeighbourHistory), -%% Pid. - -%% %% go forward until the cell has progressed to max time -%% drive_collector(_Pid, _Collector, T, T, NeighbourHistory) -> -%% ; -%% drive_collector(Pid, Collector, T, EndTime, NeighbourHistory) -> - +kill(Id, 0, _) -> + try + egol_cell:kill(Id), + timer:sleep(500), + Pid = egol_cell_mgr:lookup(Id), + Collector = undefined, + {Pid, Collector} + catch + E:M -> + io:format("kill exception ~p:~p ~p~n", [E, M, erlang:get_stacktrace()]) + end; +kill(Id, EndTime, NeighbourHistory) -> + try + egol_cell:kill(Id), + timer:sleep(500), + Pid = egol_cell_mgr:lookup(Id), + io:format("kill new_pid:~p~n", [Pid]), + Collector = egol_cell:collector(Pid), + LastCollector = drive_collector(Pid, Collector, 0, EndTime, NeighbourHistory), + {Pid, LastCollector} + catch + E:M -> + io:format("kill exception ~p:~p ~p~n", [E, M, erlang:get_stacktrace()]) + end. + +%% go forward until the cell has progressed to max time +drive_collector(_Pid, Collector, T, T, _NeighbourHistory) -> + Collector; +%%send_all_query_responses(Collector, NeighbourHistory); +drive_collector(Pid, Collector, T, EndTime, NeighbourHistory) -> + %% @todo consider filtering out the CellContents not for this time step, though + %% they should do no harm. + send_all_query_responses(Collector, NeighbourHistory), + timer:sleep(100), + NewCollector = egol_cell:collector(Pid), + drive_collector(Pid, NewCollector, T+1, EndTime, NeighbourHistory). + +send_all_query_responses(Collector, NeighbourHistory) -> + lists:foreach(fun(CellContent) -> + send_query_response(Collector, CellContent) + end, + NeighbourHistory). + +kill_args(#state{id=Id, time=Time, neighbour_history=NH}) -> + [Id, Time, NH]. + +kill_pre(S, [Id, _, _]) -> + S#state.cell /= undefined andalso + S#state.id == Id andalso not(S#state.has_killed). + + +kill_callouts(_S, [_Id, 0, _]) -> + ?EMPTY; +kill_callouts(_S, [_Id, EndTime, _NH]) -> + ?SEQ( [ step_callouts_for_time(T) + || T <- lists:seq(0, EndTime-1) ] ). + +kill_post(#state{time=0}, [_Id, _EndTime, _NH], {_Cell, undefined}) -> + true; +kill_post(#state{time=0}, [_Id, _EndTime, _NH], {_, _}) -> + false; +kill_post(_S, [_Id, _EndTime, _NH], {_Cell, undefined}) -> + false; +kill_post(_S, [_Id, _EndTime, _NH], {_Cell, _Collector}) -> + true. + +kill_next(S, {Pid, Collector}, [_Id, _EndTime, _NH]) -> + S#state{cell=Pid, collector=Collector, has_killed=true}. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% From 35c321e110c25b3c5285f1dea6f081fe41047497 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Mon, 15 Dec 2014 10:03:19 +0100 Subject: [PATCH 39/70] kill without future works --- eqc_test/egol_eqc.erl | 83 ++++++++++++++++++++++++++----------------- 1 file changed, 51 insertions(+), 32 deletions(-) diff --git a/eqc_test/egol_eqc.erl b/eqc_test/egol_eqc.erl index 6d601d1..2a0bb04 100644 --- a/eqc_test/egol_eqc.erl +++ b/eqc_test/egol_eqc.erl @@ -70,7 +70,7 @@ start() -> ok. stop() -> - print_regs(), +%% print_regs(), catch exit(whereis(egol_sup), normal), catch egol_time:stop(), eqc_mocking:stop_mocking(), @@ -93,7 +93,7 @@ stop(S) -> timer:sleep(100). -weight(_S, kill) -> 0; +%%weight(_S, kill) -> 0; weight(_S, get) -> 1; weight(_S, cell) -> 1; weight(_S, flood_requery_response) -> 20; @@ -104,7 +104,7 @@ weight(_S, _) -> 4. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% cell(XY, Dim, Content) -> {ok, Pid} = egol_cell_sup:start_cell(XY, Dim, Content), - io:format("cell STARTED ~p (~p/~p)~n", [Pid, XY, Dim]), + %io:format("cell STARTED ~p (~p/~p)~n", [Pid, XY, Dim]), Pid. cell_args(_S) -> @@ -124,7 +124,7 @@ cell_next(S, Pid, [CellId, Dim, Content]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% step(Pid, Id, _Time) -> - io:format("step for ~p/~p~n", [Pid, Id]), + %io:format("step for ~p/~p~n", [Pid, Id]), egol_cell:step(Id), Res = egol_cell:collector(Id), timer:sleep(100), @@ -134,7 +134,7 @@ step_args(S) -> [S#state.cell, S#state.id, S#state.time]. step_pre(S, [Pid, Id, _]) -> - %% io:format("step_pre cell:~p, pid:~p waiting_on:~p~n", + %% %io:format("step_pre cell:~p, pid:~p waiting_on:~p~n", %% [S#state.cell, Pid, S#state.waiting_on]), S#state.id /= undefined andalso %% S#state.cell == Pid andalso @@ -151,7 +151,7 @@ step_callouts_for_time(Time) -> %% ok. step_next(S, Res, _Args) -> -% io:format("step_next time:~p~n", [S#state.time]), +% %io:format("step_next time:~p~n", [S#state.time]), S#state{waiting_on = egol_util:neighbours_at(S#state.time, egol_util:neighbours(S#state.id, S#state.dim)), collector=Res, @@ -160,21 +160,23 @@ step_next(S, Res, _Args) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% get(Id, Time) -> Res = egol_cell:get(Id, Time), -% io:format("get works~n"), +% %io:format("get works~n"), Res. get_args(S) -> [S#state.id, S#state.time]. -get_pre(S, _) -> - S#state.id /= undefined. +get_pre(S, [Id, Time]) -> + S#state.id /= undefined andalso + S#state.id == Id andalso + S#state.time == Time. get_post(S, [_Id, _Time], Res) -> Res == S#state.content. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% query_response(Pid, Resp) -> - io:format("query_response(~p)~n", [Pid]), + %io:format("query_response(~p)~n", [Pid]), send_query_response(Pid, Resp). query_response_args(#state{id=undefined}) -> @@ -196,7 +198,7 @@ query_response_next(S, _Res, [_, {{Neighbour, Time}, Content}=Resp]) -> case lists:delete({Neighbour, Time}, S#state.waiting_on) of [] -> timer:sleep(50), - % io:format("got ALL neighbour contents~n"), + % %io:format("got ALL neighbour contents~n"), S#state{content=next_content(S#state.content, NC), time=Time+1, waiting_on=undefined, @@ -210,7 +212,7 @@ query_response_next(S, _Res, [_, {{Neighbour, Time}, Content}=Resp]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% flood_query_response(Pid, CellContents) -> - io:format("flood_query_response(~p)~n", [Pid]), + %io:format("flood_query_response(~p)~n", [Pid]), lists:foreach( fun(CC) -> send_query_response(Pid, CC) end, @@ -249,7 +251,7 @@ flood_query_response_next(S, _Res, [_Pid, CellContents]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% complete_step(Pid, CellContents) -> - io:format("complete_step(~p)~n", [Pid]), + %io:format("complete_step(~p)~n", [Pid]), flood_query_response(Pid, tl(CellContents)), query_response(Pid, hd(CellContents)). @@ -277,7 +279,8 @@ kill(Id, 0, _) -> timer:sleep(500), Pid = egol_cell_mgr:lookup(Id), Collector = undefined, - {Pid, Collector} + %{Pid, Collector} + Pid catch E:M -> io:format("kill exception ~p:~p ~p~n", [E, M, erlang:get_stacktrace()]) @@ -285,12 +288,14 @@ kill(Id, 0, _) -> kill(Id, EndTime, NeighbourHistory) -> try egol_cell:kill(Id), - timer:sleep(500), + timer:sleep(100), Pid = egol_cell_mgr:lookup(Id), - io:format("kill new_pid:~p~n", [Pid]), + %io:format("kill new_pid:~p~n", [Pid]), Collector = egol_cell:collector(Pid), - LastCollector = drive_collector(Pid, Collector, 0, EndTime, NeighbourHistory), - {Pid, LastCollector} + %io:format("new collector: ~p~n", [Collector]), + drive_collector(Pid, Collector, 0, EndTime, NeighbourHistory), +% {Pid, LastCollector} + Pid catch E:M -> io:format("kill exception ~p:~p ~p~n", [E, M, erlang:get_stacktrace()]) @@ -317,9 +322,10 @@ send_all_query_responses(Collector, NeighbourHistory) -> kill_args(#state{id=Id, time=Time, neighbour_history=NH}) -> [Id, Time, NH]. -kill_pre(S, [Id, _, _]) -> +kill_pre(S, [Id, EndTime, _]) -> S#state.cell /= undefined andalso - S#state.id == Id andalso not(S#state.has_killed). + S#state.id == Id andalso not(S#state.has_killed) andalso + S#state.time == EndTime. kill_callouts(_S, [_Id, 0, _]) -> @@ -328,17 +334,24 @@ kill_callouts(_S, [_Id, EndTime, _NH]) -> ?SEQ( [ step_callouts_for_time(T) || T <- lists:seq(0, EndTime-1) ] ). -kill_post(#state{time=0}, [_Id, _EndTime, _NH], {_Cell, undefined}) -> - true; -kill_post(#state{time=0}, [_Id, _EndTime, _NH], {_, _}) -> - false; -kill_post(_S, [_Id, _EndTime, _NH], {_Cell, undefined}) -> - false; -kill_post(_S, [_Id, _EndTime, _NH], {_Cell, _Collector}) -> - true. - -kill_next(S, {Pid, Collector}, [_Id, _EndTime, _NH]) -> - S#state{cell=Pid, collector=Collector, has_killed=true}. +%% kill_post(#state{time=0}, [_Id, _EndTime, _NH], {_Cell, undefined}) -> +%% true; +%% kill_post(#state{time=0}, [_Id, _EndTime, _NH], {_, _}) -> +%% false; +%% kill_post(_S, [_Id, _EndTime, _NH], {_Cell, undefined}) -> +%% false; +%% kill_post(_S, [_Id, _EndTime, _NH], {_Cell, _Collector}) -> +%% true. + + +%% after a kill the cell will run up to the EndTime and then await a step before it +%% can continue since it has not received a step command to drive it forward, so +%% waiting_on has to be undefined. +kill_next(S, Pid, %{Pid, Collector}, + [_Id, EndTime, _NH]) -> + S#state{cell=Pid, collector=undefined, + has_killed=true, + waiting_on=undefined}. @@ -372,4 +385,10 @@ next_content(C, N) when N==2; N==3 -> C; next_content(_,_) -> 0. send_query_response(Pid, {{_XY,_T}, _C}=Resp) -> - Pid ! {cell_content, Resp}. + try + Pid ! {cell_content, Resp} + catch + _:_ -> + io:format("send_query_response (~p, ~p) failed~n", + [Pid, Resp]) + end. From 3747c74b4033292893a091db7f35a5df1d0d8b95 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Mon, 15 Dec 2014 11:39:22 +0100 Subject: [PATCH 40/70] Remove flood_query_response and complete_step * Make the test model simpler. --- eqc_test/egol_eqc.erl | 94 +++++++------------------------------------ 1 file changed, 14 insertions(+), 80 deletions(-) diff --git a/eqc_test/egol_eqc.erl b/eqc_test/egol_eqc.erl index 2a0bb04..76e43b5 100644 --- a/eqc_test/egol_eqc.erl +++ b/eqc_test/egol_eqc.erl @@ -15,8 +15,7 @@ collector, waiting_on = undefined, neighbour_count = 0, - neighbour_history = [], - has_killed = false + neighbour_history = [] }). @@ -94,10 +93,14 @@ stop(S) -> %%weight(_S, kill) -> 0; +weight(#state{id=undefined}, Cmd) when Cmd /= cell -> 0; +weight(#state{waiting_on=W}, step) when is_list(W) -> 0; weight(_S, get) -> 1; weight(_S, cell) -> 1; -weight(_S, flood_requery_response) -> 20; -weight(_S, _) -> 4. +weight(_S, requery_response) -> 20; +%weight(_S, complete_step) -> 0; +weight(_S, kill) -> 3; +weight(_S, _) -> 2. @@ -197,8 +200,8 @@ query_response_next(S, _Res, [_, {{Neighbour, Time}, Content}=Resp]) -> NC = S#state.neighbour_count + Content, case lists:delete({Neighbour, Time}, S#state.waiting_on) of [] -> - timer:sleep(50), - % %io:format("got ALL neighbour contents~n"), + timer:sleep(100), + io:format("got ALL neighbour contents=~p~n",[NC]), S#state{content=next_content(S#state.content, NC), time=Time+1, waiting_on=undefined, @@ -210,67 +213,6 @@ query_response_next(S, _Res, [_, {{Neighbour, Time}, Content}=Resp]) -> neighbour_history=S#state.neighbour_history ++ [Resp]} end. -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -flood_query_response(Pid, CellContents) -> - %io:format("flood_query_response(~p)~n", [Pid]), - lists:foreach( fun(CC) -> - send_query_response(Pid, CC) - end, - CellContents). - -cell_contents(CellsAtT) -> - [ cell_content(CellAtT, content()) - || CellAtT <- CellsAtT ]. - -cell_content(CellAtT, Content) -> - {CellAtT, Content}. - -flood_query_response_args(#state{waiting_on=[_]}) -> - [undefined, undefined]; -flood_query_response_args(#state{waiting_on=Wait}=S) when Wait /= [], - Wait /= undefined -> - ?LET(CellContents, cell_contents(tl(Wait)), - [S#state.collector, CellContents]); -flood_query_response_args(_) -> - [undefined, undefined]. - -flood_query_response_pre(_, [undefined, _]) -> false; -flood_query_response_pre(#state{waiting_on=[_]}, _) -> false; -flood_query_response_pre(#state{waiting_on=Wait}, _) when Wait /= [], - Wait /= undefined -> - true; -flood_query_response_pre(_,_) -> - false. - -flood_query_response_next(S, _Res, [_Pid, CellContents]) -> - Contents = [ C || {_, C} <- CellContents], - NC = lists:sum(Contents), - S#state{waiting_on=[hd(S#state.waiting_on)], - neighbour_count=S#state.neighbour_count+NC, - neighbour_history=S#state.neighbour_history ++ CellContents}. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -complete_step(Pid, CellContents) -> - %io:format("complete_step(~p)~n", [Pid]), - flood_query_response(Pid, tl(CellContents)), - query_response(Pid, hd(CellContents)). - -complete_step_args(#state{waiting_on=Wait}=S) when is_list(Wait) andalso - length(Wait) > 1 -> - [S#state.collector, cell_contents(Wait)]; -complete_step_args(_) -> - [undefined, undefined]. - -complete_step_pre(#state{waiting_on=Wait}, [_,_]) when is_list(Wait) andalso - length(Wait) > 1 -> - true; -complete_step_pre(_, [_,_]) -> - false. - -complete_step_next(S, Res, [Pid, CellContents]) -> - S2 = flood_query_response_next(S, Res, [Pid, tl(CellContents)]), - query_response_next(S2, Res, [Pid, hd(CellContents)]). - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% kill(Id, 0, _) -> @@ -308,7 +250,10 @@ drive_collector(_Pid, Collector, T, T, _NeighbourHistory) -> drive_collector(Pid, Collector, T, EndTime, NeighbourHistory) -> %% @todo consider filtering out the CellContents not for this time step, though %% they should do no harm. - send_all_query_responses(Collector, NeighbourHistory), + ResponsesForTime = [ R + || {{_,RTime},_}=R <- NeighbourHistory, + RTime == T ], + send_all_query_responses(Collector, ResponsesForTime), timer:sleep(100), NewCollector = egol_cell:collector(Pid), drive_collector(Pid, NewCollector, T+1, EndTime, NeighbourHistory). @@ -324,7 +269,7 @@ kill_args(#state{id=Id, time=Time, neighbour_history=NH}) -> kill_pre(S, [Id, EndTime, _]) -> S#state.cell /= undefined andalso - S#state.id == Id andalso not(S#state.has_killed) andalso + S#state.id == Id andalso S#state.time == EndTime. @@ -334,23 +279,12 @@ kill_callouts(_S, [_Id, EndTime, _NH]) -> ?SEQ( [ step_callouts_for_time(T) || T <- lists:seq(0, EndTime-1) ] ). -%% kill_post(#state{time=0}, [_Id, _EndTime, _NH], {_Cell, undefined}) -> -%% true; -%% kill_post(#state{time=0}, [_Id, _EndTime, _NH], {_, _}) -> -%% false; -%% kill_post(_S, [_Id, _EndTime, _NH], {_Cell, undefined}) -> -%% false; -%% kill_post(_S, [_Id, _EndTime, _NH], {_Cell, _Collector}) -> -%% true. - - %% after a kill the cell will run up to the EndTime and then await a step before it %% can continue since it has not received a step command to drive it forward, so %% waiting_on has to be undefined. kill_next(S, Pid, %{Pid, Collector}, [_Id, EndTime, _NH]) -> S#state{cell=Pid, collector=undefined, - has_killed=true, waiting_on=undefined}. From f253f4e94f89b83c9b2443884c15ed8aa012ea07 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Mon, 15 Dec 2014 13:16:32 +0100 Subject: [PATCH 41/70] Changed to only one kill per test case Avoids supervisor restart. --- eqc_test/egol_eqc.erl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/eqc_test/egol_eqc.erl b/eqc_test/egol_eqc.erl index 76e43b5..3fd9c94 100644 --- a/eqc_test/egol_eqc.erl +++ b/eqc_test/egol_eqc.erl @@ -15,7 +15,8 @@ collector, waiting_on = undefined, neighbour_count = 0, - neighbour_history = [] + neighbour_history = [], + kill_count=0 }). @@ -270,7 +271,8 @@ kill_args(#state{id=Id, time=Time, neighbour_history=NH}) -> kill_pre(S, [Id, EndTime, _]) -> S#state.cell /= undefined andalso S#state.id == Id andalso - S#state.time == EndTime. + S#state.time == EndTime andalso + S#state.kill_count<1. kill_callouts(_S, [_Id, 0, _]) -> @@ -285,7 +287,8 @@ kill_callouts(_S, [_Id, EndTime, _NH]) -> kill_next(S, Pid, %{Pid, Collector}, [_Id, EndTime, _NH]) -> S#state{cell=Pid, collector=undefined, - waiting_on=undefined}. + waiting_on=undefined, + kill_count=S#state.kill_count+1}. From af07000afaf641afb18cb184c84d305273c266e5 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Mon, 15 Dec 2014 18:56:14 +0100 Subject: [PATCH 42/70] egol_eqc speed_up and some tweaks to egol_cell --- eqc_test/egol_eqc.erl | 129 +++++++++++++++++++++++++++++++----------- src/egol_cell.erl | 16 +++++- 2 files changed, 111 insertions(+), 34 deletions(-) diff --git a/eqc_test/egol_eqc.erl b/eqc_test/egol_eqc.erl index 3fd9c94..c2f3f02 100644 --- a/eqc_test/egol_eqc.erl +++ b/eqc_test/egol_eqc.erl @@ -92,15 +92,28 @@ stop(S) -> catch exit(S#state.collector, normal), timer:sleep(100). +%% is_process_dead(S#state.cell), +%% is_process_dead(S#state.collector). + +%% is_process_dead(undefinded) -> +%% ok; +%% is_process_dead(Pid) -> +%% case is_process_alive(Pid) of +%% true -> +%% timer:sleep(1), +%% is_process_dead(Pid); +%% false -> +%% ok +%% end. %%weight(_S, kill) -> 0; weight(#state{id=undefined}, Cmd) when Cmd /= cell -> 0; weight(#state{waiting_on=W}, step) when is_list(W) -> 0; weight(_S, get) -> 1; weight(_S, cell) -> 1; -weight(_S, requery_response) -> 20; +weight(_S, requery_response) -> 25; %weight(_S, complete_step) -> 0; -weight(_S, kill) -> 3; +weight(_S, kill) -> 1; weight(_S, _) -> 2. @@ -130,16 +143,35 @@ cell_next(S, Pid, [CellId, Dim, Content]) -> step(Pid, Id, _Time) -> %io:format("step for ~p/~p~n", [Pid, Id]), egol_cell:step(Id), - Res = egol_cell:collector(Id), - timer:sleep(100), +% Res = egol_cell:collector(Id), + Res = await_defined_collector(Id), + %io:format("defined collector: ~p~n", [Res]), + sync_collector(Res), Res. +sync_collector(Pid) -> + Ref = make_ref(), + Pid ! {sync_collector, self(), Ref}, + receive + Ref -> + timer:sleep(10) + end. + +await_defined_collector(Id) -> + case egol_cell:collector(Id) of + undefined -> + timer:sleep(5), + await_defined_collector(Id); + Pid -> + Pid + end. + step_args(S) -> [S#state.cell, S#state.id, S#state.time]. step_pre(S, [Pid, Id, _]) -> - %% %io:format("step_pre cell:~p, pid:~p waiting_on:~p~n", - %% [S#state.cell, Pid, S#state.waiting_on]), + %io:format("step_pre cell:~p, pid:~p waiting_on:~p~n", +% [S#state.cell, Pid, S#state.waiting_on]), S#state.id /= undefined andalso %% S#state.cell == Pid andalso S#state.id == Id andalso @@ -155,7 +187,7 @@ step_callouts_for_time(Time) -> %% ok. step_next(S, Res, _Args) -> -% %io:format("step_next time:~p~n", [S#state.time]), + %io:format("step_next time:~p~n", [S#state.time]), S#state{waiting_on = egol_util:neighbours_at(S#state.time, egol_util:neighbours(S#state.id, S#state.dim)), collector=Res, @@ -164,7 +196,7 @@ step_next(S, Res, _Args) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% get(Id, Time) -> Res = egol_cell:get(Id, Time), -% %io:format("get works~n"), +% %%io:format("get works~n"), Res. get_args(S) -> @@ -179,30 +211,51 @@ get_post(S, [_Id, _Time], Res) -> Res == S#state.content. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -query_response(Pid, Resp) -> - %io:format("query_response(~p)~n", [Pid]), - send_query_response(Pid, Resp). +query_response(Pid, Resp, continue) -> + %io:format("query_response(~p, ~p)~n", [Pid, Resp]), + send_query_response(Pid, Resp); +query_response(Pid, Resp, {Id, AwaitTime}) -> + %io:format("query_response(~p, ~p)~n", [Pid, Resp]), + send_query_response(Pid, Resp), + %io:format("going to await_time_change(~p,~p)~n", [Id, AwaitTime]), + await_time_change(Id, AwaitTime). query_response_args(#state{id=undefined}) -> [undefined, undefined]; query_response_args(#state{cell=Cell, waiting_on=undefined}) when is_pid(Cell) -> [undefined, undefined]; query_response_args(S) -> - [S#state.collector, {{neighbour(S), S#state.time}, content()}]. + [S#state.collector, {{neighbour(S), S#state.time}, content()}, + await_time(S)]. + +await_time(#state{waiting_on=[_]}=S) -> + {S#state.id, S#state.time+1}; +await_time(_S) -> + continue. + +await_time_change(Id, AwaitTime) -> + case egol_cell:time(Id) of + AwaitTime -> + ok; + _ -> + %io:format("await_time_change pid ~p/~p~n", +% [egol_cell_mgr:lookup(Id), egol_cell:collector(Id)]), + timer:sleep(5), + await_time_change(Id, AwaitTime) + end. query_response_pre(#state{waiting_on=undefined}, _ ) -> false; -query_response_pre(S, [Collector, {{Neighbour, Time}, _}] ) -> +query_response_pre(S, [Collector, {{Neighbour, Time}, _}, _] ) -> Collector /= undefined andalso S#state.waiting_on /= [] andalso lists:member({Neighbour, Time}, S#state.waiting_on) andalso Time == S#state.time. -query_response_next(S, _Res, [_, {{Neighbour, Time}, Content}=Resp]) -> +query_response_next(S, _Res, [_, {{Neighbour, Time}, Content}=Resp, _]) -> NC = S#state.neighbour_count + Content, case lists:delete({Neighbour, Time}, S#state.waiting_on) of [] -> - timer:sleep(100), - io:format("got ALL neighbour contents=~p~n",[NC]), +% io:format("got ALL neighbour contents=~p~n",[NC]), S#state{content=next_content(S#state.content, NC), time=Time+1, waiting_on=undefined, @@ -215,49 +268,61 @@ query_response_next(S, _Res, [_, {{Neighbour, Time}, Content}=Resp]) -> end. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% kill(Id, 0, _) -> try + OldPid = egol_cell_mgr:lookup(Id), + %io:format("killing ~p~n", [OldPid]), egol_cell:kill(Id), - timer:sleep(500), - Pid = egol_cell_mgr:lookup(Id), - Collector = undefined, - %{Pid, Collector} - Pid + NewPid = await_new_cell_pid(OldPid, Id), + %io:format("started ~p~n", [NewPid]), + NewPid catch E:M -> io:format("kill exception ~p:~p ~p~n", [E, M, erlang:get_stacktrace()]) end; kill(Id, EndTime, NeighbourHistory) -> try + OldPid = egol_cell_mgr:lookup(Id), egol_cell:kill(Id), - timer:sleep(100), - Pid = egol_cell_mgr:lookup(Id), - %io:format("kill new_pid:~p~n", [Pid]), + Pid = await_new_cell_pid(OldPid, Id), + %%io:format("kill new_pid:~p~n", [Pid]), Collector = egol_cell:collector(Pid), - %io:format("new collector: ~p~n", [Collector]), - drive_collector(Pid, Collector, 0, EndTime, NeighbourHistory), -% {Pid, LastCollector} + %%io:format("new collector: ~p~n", [Collector]), + drive_collector(Pid, Id, Collector, 0, EndTime, NeighbourHistory), Pid catch E:M -> io:format("kill exception ~p:~p ~p~n", [E, M, erlang:get_stacktrace()]) end. +await_new_cell_pid(OldPid, Id) -> + case egol_cell_mgr:lookup(Id) of + NewPid when is_pid(NewPid) andalso + NewPid /= OldPid -> + NewPid; + _ -> + timer:sleep(5), + await_new_cell_pid(OldPid, Id) + end. + + %% go forward until the cell has progressed to max time -drive_collector(_Pid, Collector, T, T, _NeighbourHistory) -> +drive_collector(_Pid, _Id, Collector, T, T, _NeighbourHistory) -> Collector; %%send_all_query_responses(Collector, NeighbourHistory); -drive_collector(Pid, Collector, T, EndTime, NeighbourHistory) -> +drive_collector(Pid, Id, Collector, T, EndTime, NeighbourHistory) -> %% @todo consider filtering out the CellContents not for this time step, though %% they should do no harm. ResponsesForTime = [ R || {{_,RTime},_}=R <- NeighbourHistory, RTime == T ], send_all_query_responses(Collector, ResponsesForTime), - timer:sleep(100), + await_time_change(Id, T), NewCollector = egol_cell:collector(Pid), - drive_collector(Pid, NewCollector, T+1, EndTime, NeighbourHistory). + drive_collector(Pid, Id, NewCollector, T+1, EndTime, NeighbourHistory). send_all_query_responses(Collector, NeighbourHistory) -> lists:foreach(fun(CellContent) -> @@ -285,7 +350,7 @@ kill_callouts(_S, [_Id, EndTime, _NH]) -> %% can continue since it has not received a step command to drive it forward, so %% waiting_on has to be undefined. kill_next(S, Pid, %{Pid, Collector}, - [_Id, EndTime, _NH]) -> + [_Id, _EndTime, _NH]) -> S#state{cell=Pid, collector=undefined, waiting_on=undefined, kill_count=S#state.kill_count+1}. diff --git a/src/egol_cell.erl b/src/egol_cell.erl index 5c31b48..61f6620 100644 --- a/src/egol_cell.erl +++ b/src/egol_cell.erl @@ -258,6 +258,7 @@ start_collector(#state{time=T, neighbours=Neighbours, Collector = spawn_link( fun () -> collector_init(T, Neighbours, Cell, Content) end ), +%% io:format("start_collector new pid ~p~n", [Collector]), State#state{collector=Collector}. collector_init(Time, Neighbours, Cell, Content) -> @@ -268,7 +269,9 @@ collector_loop([], NeighbourCount, Cell, Content) -> Cell ! {self(), {next_content, egol_util:next_content(Content, NeighbourCount)}}; collector_loop(WaitingOn, NeighbourCount, Cell, Content) -> receive - {cell_content, {{{_,_},_}=XYatT, NeighbourContent}} -> + {cell_content, {{{_,_},_}=XYatT, NeighbourContent}} = Msg -> + %% io:format("collector ~p got cell_content ~p - ~p~n", + %% [self(), XYatT, NeighbourContent]), case lists:member(XYatT, WaitingOn) of true -> collector_loop(lists:delete(XYatT, WaitingOn), @@ -276,8 +279,17 @@ collector_loop(WaitingOn, NeighbourCount, Cell, Content) -> Cell, Content); false %% ignore messages we are not waiting for -> + %% io:format("collector got wrong message ~p~n", + %% [Msg]), collector_loop(WaitingOn, NeighbourCount, Cell, Content) - end + end; + {sync_collector, From, Ref} -> %% WARNING: only for eqc testing + From ! Ref, + collector_loop(WaitingOn, NeighbourCount, Cell, Content); + Garbage -> + io:format("collector got GARBAGE ~p~n", + [Garbage]), + exit(collector_got_garbage) end. From 0622a492d3d11a0ea494f1342f29886ff05a82b6 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Mon, 15 Dec 2014 19:12:12 +0100 Subject: [PATCH 43/70] egol_eqc:query_content added --- eqc_test/egol_eqc.erl | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/eqc_test/egol_eqc.erl b/eqc_test/egol_eqc.erl index c2f3f02..9260aae 100644 --- a/eqc_test/egol_eqc.erl +++ b/eqc_test/egol_eqc.erl @@ -267,8 +267,26 @@ query_response_next(S, _Res, [_, {{Neighbour, Time}, Content}=Resp, _]) -> neighbour_history=S#state.neighbour_history ++ [Resp]} end. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +query_content(Id, Time) -> + send_query_content(Id, Time), + timer:sleep(20), + ok. + +query_content_args(S) -> + [S#state.id, S#state.time]. +query_content_pre(S, [Id, _Time]) -> + S#state.id /= undefined andalso + S#state.id == Id. + +query_content_callouts(S, [Id, Time]) -> + CellContent = {cell_content, {{Id, Time}, S#state.content}}, + ?CALLOUT(egol_protocol, query_response, [?WILDCARD, CellContent], ok). + +query_content_next(S, _Res, [_Id, _Time]) -> + S. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% kill(Id, 0, _) -> @@ -394,3 +412,7 @@ send_query_response(Pid, {{_XY,_T}, _C}=Resp) -> io:format("send_query_response (~p, ~p) failed~n", [Pid, Resp]) end. + +send_query_content(Id, Time) -> + Pid = egol_cell_mgr:lookup(Id), + Pid ! {query_content, Time, self()}. From ebedfc19219cb12ed0dec58984c636d4b7e5ae29 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Thu, 18 Dec 2014 16:36:45 +0000 Subject: [PATCH 44/70] corrected replay of neighbour_history --- .gitignore | 2 +- eqc_test/egol_eqc.erl | 150 ++++++++++++++++++++++++++++++------------ 2 files changed, 108 insertions(+), 44 deletions(-) diff --git a/.gitignore b/.gitignore index 8e46d5a..7e2ec34 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,6 @@ deps *.plt erl_crash.dump ebin -rel/example_project +_rel/* .concrete/DEV_MODE .rebar diff --git a/eqc_test/egol_eqc.erl b/eqc_test/egol_eqc.erl index 9260aae..c3b7441 100644 --- a/eqc_test/egol_eqc.erl +++ b/eqc_test/egol_eqc.erl @@ -16,7 +16,8 @@ waiting_on = undefined, neighbour_count = 0, neighbour_history = [], - kill_count=0 + kill_count=0, + pending_query_content=[] }). @@ -111,7 +112,8 @@ weight(#state{id=undefined}, Cmd) when Cmd /= cell -> 0; weight(#state{waiting_on=W}, step) when is_list(W) -> 0; weight(_S, get) -> 1; weight(_S, cell) -> 1; -weight(_S, requery_response) -> 25; +weight(_S, query_response) -> 25; +weight(#state{waiting_on=[_]}, last_query_response) -> 100; %weight(_S, complete_step) -> 0; weight(_S, kill) -> 1; weight(_S, _) -> 2. @@ -211,74 +213,108 @@ get_post(S, [_Id, _Time], Res) -> Res == S#state.content. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -query_response(Pid, Resp, continue) -> - %io:format("query_response(~p, ~p)~n", [Pid, Resp]), - send_query_response(Pid, Resp); -query_response(Pid, Resp, {Id, AwaitTime}) -> - %io:format("query_response(~p, ~p)~n", [Pid, Resp]), - send_query_response(Pid, Resp), - %io:format("going to await_time_change(~p,~p)~n", [Id, AwaitTime]), - await_time_change(Id, AwaitTime). +query_response(CollectorPid, Resp) -> + io:format("query_response(~p, ~p)~n", [CollectorPid, Resp]), + send_query_response(CollectorPid, Resp). query_response_args(#state{id=undefined}) -> [undefined, undefined]; query_response_args(#state{cell=Cell, waiting_on=undefined}) when is_pid(Cell) -> [undefined, undefined]; query_response_args(S) -> - [S#state.collector, {{neighbour(S), S#state.time}, content()}, - await_time(S)]. + [S#state.collector, neighbour_response(S)]. + +neighbour_response(S) -> + %%io:format("neighbour_response: ~p~n", [S]), + ?LET(N, neighbour(S), + case lists:keyfind({N, S#state.time}, 1, S#state.neighbour_history) of + false -> + {{N, S#state.time}, content()}; + Resp -> + Resp + end). + +query_response_pre(_S, [undefined, undefined]) -> false; +query_response_pre(#state{waiting_on=undefined}, _ ) -> false; +query_response_pre(S, [Collector, {{Neighbour, Time}, _}] ) -> + Collector /= undefined andalso + S#state.waiting_on /= [] andalso + lists:member({Neighbour, Time}, S#state.waiting_on) andalso + Time == S#state.time. + +query_response_next(S, _Res, [_, {{Neighbour, Time}, Content}=Resp]) -> + NC = S#state.neighbour_count + Content, + Wait = lists:delete({Neighbour, Time}, S#state.waiting_on), + S#state{waiting_on = Wait, neighbour_count = NC, + neighbour_history=S#state.neighbour_history ++ [Resp]}. -await_time(#state{waiting_on=[_]}=S) -> - {S#state.id, S#state.time+1}; -await_time(_S) -> - continue. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +last_query_response(CollectorPid, Resp, {Id, AwaitTime}) -> + send_query_response(CollectorPid, Resp), + await_time_change(Id, AwaitTime). await_time_change(Id, AwaitTime) -> case egol_cell:time(Id) of AwaitTime -> ok; _ -> - %io:format("await_time_change pid ~p/~p~n", -% [egol_cell_mgr:lookup(Id), egol_cell:collector(Id)]), + io:format("await_time_change id ~p~n", [Id]), + io:format("await_time_change pid ~p/~p~n", + [egol_cell_mgr:lookup(Id), egol_cell:collector(Id)]), timer:sleep(5), await_time_change(Id, AwaitTime) end. -query_response_pre(#state{waiting_on=undefined}, _ ) -> false; -query_response_pre(S, [Collector, {{Neighbour, Time}, _}, _] ) -> - Collector /= undefined andalso - S#state.waiting_on /= [] andalso - lists:member({Neighbour, Time}, S#state.waiting_on) andalso - Time == S#state.time. -query_response_next(S, _Res, [_, {{Neighbour, Time}, Content}=Resp, _]) -> +last_query_response_args(#state{id=undefined}) -> + [undefined]; +last_query_response_args(#state{waiting_on=undefined}) -> + [undefined]; +last_query_response_args(#state{waiting_on=W}) when length(W) /= 1 -> + [undefined]; +last_query_response_args(S) -> + [S#state.collector, {hd(S#state.waiting_on), content()}, {S#state.id, S#state.time+1}]. + +cell_content(#state{id=Id, time=Time, content=Content}) -> + {{Id, Time}, Content}. + +last_query_response_pre(_, [undefined]) -> false; +last_query_response_pre(S, [_CollectorPid, _Resp, {AwaitId, _AwaitTime}]) -> + length(S#state.waiting_on)==1 andalso + S#state.id == AwaitId. + +last_query_response_callouts(#state{pending_query_content=[]}, _) -> + ?EMPTY; +last_query_response_callouts(S, _) -> + ?PAR([cell_content_msg(NeighbourId, NeighbourTime, S#state.content) + || {NeighbourId, NeighbourTime} <- S#state.pending_query_content]). + +last_query_response_next(S, _Res, [_CollectorPid, {_, Content}=Resp, _AwaitIdTime]) -> NC = S#state.neighbour_count + Content, - case lists:delete({Neighbour, Time}, S#state.waiting_on) of - [] -> -% io:format("got ALL neighbour contents=~p~n",[NC]), - S#state{content=next_content(S#state.content, NC), - time=Time+1, - waiting_on=undefined, - collector=undefined, - neighbour_count=0, - neighbour_history=S#state.neighbour_history ++ [Resp]}; - Wait -> - S#state{waiting_on = Wait, neighbour_count = NC, - neighbour_history=S#state.neighbour_history ++ [Resp]} - end. + io:format("last_query_response_next NC=~p~n", [NC]), + S#state{pending_query_content=[], + content=next_content(S#state.content, NC), + time=S#state.time+1, + waiting_on=undefined, + collector=undefined, + neighbour_count=0, + neighbour_history=S#state.neighbour_history ++ [Resp]}. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% query_content(Id, Time) -> send_query_content(Id, Time), - timer:sleep(20), + timer:sleep(50), ok. query_content_args(S) -> [S#state.id, S#state.time]. -query_content_pre(S, [Id, _Time]) -> +query_content_pre(S, [Id, Time]) -> S#state.id /= undefined andalso - S#state.id == Id. + S#state.id == Id andalso + S#state.time == Time. query_content_callouts(S, [Id, Time]) -> @@ -288,6 +324,33 @@ query_content_callouts(S, [Id, Time]) -> query_content_next(S, _Res, [_Id, _Time]) -> S. + +cell_content_msg(Id, Time, Content) -> + {cell_content, {{Id, Time}, Content}}. + +cell_content_msg(S) -> + cell_content_msg(S#state.id, S#state.time, S#state.content). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% query_future(Id, _RequestId, Time) -> +%% send_query_content(Id, Time), +%% ok. + +%% query_future_args(S) -> +%% Pending = [ N || {N,_} <- S#state.pending_query_content ], +%% ?LET(Neighbour, oneof(egol_util:neighbours(S#state.id, S#state.dim) -- Pending), +%% [S#state.id, Neighbour, S#state.time+1]). + +%% query_future_pre(S, [Id, _NeighbourId, Time]) -> +%% S#state.id /= undefined andalso +%% S#state.id == Id andalso +%% S#state.time == (Time+1). + +%% query_future_next(S, _Res, [_Id, NeighbourId, Time]) -> +%% S#state{pending_query_content= +%% S#state.pending_query_content++[{NeighbourId, Time}]}. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% kill(Id, 0, _) -> try @@ -371,6 +434,7 @@ kill_next(S, Pid, %{Pid, Collector}, [_Id, _EndTime, _NH]) -> S#state{cell=Pid, collector=undefined, waiting_on=undefined, + neighbour_count=0, kill_count=S#state.kill_count+1}. @@ -404,9 +468,9 @@ next_content(0,3) -> 1; next_content(C, N) when N==2; N==3 -> C; next_content(_,_) -> 0. -send_query_response(Pid, {{_XY,_T}, _C}=Resp) -> +send_query_response(Pid, {{XY,T}, C}=Resp) -> try - Pid ! {cell_content, Resp} + Pid ! cell_content_msg(XY,T,C) catch _:_ -> io:format("send_query_response (~p, ~p) failed~n", From 5cfe3ed86f35a608170f39e76d247c7e770e6065 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Wed, 28 Jan 2015 17:05:29 +0000 Subject: [PATCH 45/70] Removed cell and collector pids usage --- eqc_test/egol_eqc.erl | 186 +++++++++++++++++++----------------------- 1 file changed, 85 insertions(+), 101 deletions(-) diff --git a/eqc_test/egol_eqc.erl b/eqc_test/egol_eqc.erl index c3b7441..80beed4 100644 --- a/eqc_test/egol_eqc.erl +++ b/eqc_test/egol_eqc.erl @@ -6,13 +6,11 @@ -include_lib("eqc/include/eqc_component.hrl"). -record(state, - { cell, - id, + { id, dim, content, time=0, steps=0, - collector, waiting_on = undefined, neighbour_count = 0, neighbour_history = [], @@ -27,7 +25,7 @@ api_spec() -> modules = [ #api_module{ name = egol_protocol, - functions = [ #api_fun{ name = query_content, arity = 2}, + functions = [ #api_fun{ name = query_content, arity = 3}, #api_fun{ name = query_response, arity = 2} ] } @@ -89,23 +87,9 @@ print_regs() -> end. stop(S) -> - catch exit(S#state.cell, normal), - catch exit(S#state.collector, normal), + catch egol_cell:kill(S#state.id), timer:sleep(100). -%% is_process_dead(S#state.cell), -%% is_process_dead(S#state.collector). - -%% is_process_dead(undefinded) -> -%% ok; -%% is_process_dead(Pid) -> -%% case is_process_alive(Pid) of -%% true -> -%% timer:sleep(1), -%% is_process_dead(Pid); -%% false -> -%% ok -%% end. %%weight(_S, kill) -> 0; weight(#state{id=undefined}, Cmd) when Cmd /= cell -> 0; @@ -114,7 +98,6 @@ weight(_S, get) -> 1; weight(_S, cell) -> 1; weight(_S, query_response) -> 25; weight(#state{waiting_on=[_]}, last_query_response) -> 100; -%weight(_S, complete_step) -> 0; weight(_S, kill) -> 1; weight(_S, _) -> 2. @@ -131,68 +114,58 @@ cell_args(_S) -> [CellId, Dim, content()]). cell_pre(S, _) -> - S#state.cell == undefined. + S#state.id == undefined. cell_post(_S, [_, _, _], Res) -> is_pid(Res) and erlang:is_process_alive(Res). -cell_next(S, Pid, [CellId, Dim, Content]) -> - S#state{cell=Pid, id=CellId, dim=Dim, content=Content}. +cell_next(S, _Pid, [CellId, Dim, Content]) -> + S#state{id=CellId, dim=Dim, content=Content}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -step(Pid, Id, _Time) -> +step(Id, _Dim, _Time) -> %io:format("step for ~p/~p~n", [Pid, Id]), egol_cell:step(Id), -% Res = egol_cell:collector(Id), - Res = await_defined_collector(Id), - %io:format("defined collector: ~p~n", [Res]), - sync_collector(Res), - Res. + await_collecting_status(Id). -sync_collector(Pid) -> - Ref = make_ref(), - Pid ! {sync_collector, self(), Ref}, - receive - Ref -> - timer:sleep(10) - end. -await_defined_collector(Id) -> - case egol_cell:collector(Id) of +await_collecting_status(Id) -> + case egol_cell:collecting_status(Id) of undefined -> timer:sleep(5), - await_defined_collector(Id); - Pid -> - Pid + await_collecting_status(Id); + _ -> + ok end. + + step_args(S) -> - [S#state.cell, S#state.id, S#state.time]. + [S#state.id, S#state.dim, S#state.time]. -step_pre(S, [Pid, Id, _]) -> +step_pre(S, [Id, Dim, Time]) -> %io:format("step_pre cell:~p, pid:~p waiting_on:~p~n", % [S#state.cell, Pid, S#state.waiting_on]), S#state.id /= undefined andalso -%% S#state.cell == Pid andalso S#state.id == Id andalso + S#state.dim == Dim andalso + S#state.time == Time andalso S#state.waiting_on == undefined. -step_callouts(S, [_Pid, _XY, _Time ]) -> - step_callouts_for_time(S#state.time). +step_callouts(_S, [XY, Dim, Time ]) -> + step_callouts_for_time(Time, egol_util:neighbours(XY, Dim), XY). -step_callouts_for_time(Time) -> - ?PAR(lists:duplicate(8, ?CALLOUT(egol_protocol, query_content, [?WILDCARD, Time], ok))). +step_callouts_for_time(Time, Neighbours, XY) -> + ?PAR(lists:duplicate(8, ?CALLOUT(egol_protocol, query_content, [?WILDCARD, Time, XY], ok))). + %% ?PAR([?CALLOUT(egol_protocol, query_content, [N, Time, XY], ok) + %% || N <- Neighbours]). -%% step_return(_S, _) -> -%% ok. - -step_next(S, Res, _Args) -> +step_next(S, _Res, _Args) -> %io:format("step_next time:~p~n", [S#state.time]), S#state{waiting_on = egol_util:neighbours_at(S#state.time, egol_util:neighbours(S#state.id, S#state.dim)), - collector=Res, steps=S#state.steps+1}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -213,16 +186,16 @@ get_post(S, [_Id, _Time], Res) -> Res == S#state.content. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -query_response(CollectorPid, Resp) -> - io:format("query_response(~p, ~p)~n", [CollectorPid, Resp]), - send_query_response(CollectorPid, Resp). +query_response(Id, Resp) -> + io:format("query_response(~p, ~p)~n", [Id, Resp]), + send_query_response(Id, Resp). query_response_args(#state{id=undefined}) -> [undefined, undefined]; -query_response_args(#state{cell=Cell, waiting_on=undefined}) when is_pid(Cell) -> +query_response_args(#state{waiting_on=undefined}) -> [undefined, undefined]; query_response_args(S) -> - [S#state.collector, neighbour_response(S)]. + [S#state.id, neighbour_response(S)]. neighbour_response(S) -> %%io:format("neighbour_response: ~p~n", [S]), @@ -236,8 +209,8 @@ neighbour_response(S) -> query_response_pre(_S, [undefined, undefined]) -> false; query_response_pre(#state{waiting_on=undefined}, _ ) -> false; -query_response_pre(S, [Collector, {{Neighbour, Time}, _}] ) -> - Collector /= undefined andalso +query_response_pre(S, [Id, {{Neighbour, Time}, _}] ) -> + Id /= undefined andalso S#state.waiting_on /= [] andalso lists:member({Neighbour, Time}, S#state.waiting_on) andalso Time == S#state.time. @@ -249,8 +222,9 @@ query_response_next(S, _Res, [_, {{Neighbour, Time}, Content}=Resp]) -> neighbour_history=S#state.neighbour_history ++ [Resp]}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -last_query_response(CollectorPid, Resp, {Id, AwaitTime}) -> - send_query_response(CollectorPid, Resp), +last_query_response(Resp, {Id, AwaitTime}) -> + send_query_response(Id, Resp), + io:format("calling await_time_change(~p, ~p)~n", [Id, AwaitTime]), await_time_change(Id, AwaitTime). await_time_change(Id, AwaitTime) -> @@ -258,9 +232,9 @@ await_time_change(Id, AwaitTime) -> AwaitTime -> ok; _ -> - io:format("await_time_change id ~p~n", [Id]), - io:format("await_time_change pid ~p/~p~n", - [egol_cell_mgr:lookup(Id), egol_cell:collector(Id)]), + %% io:format("await_time_change id ~p~n", [Id]), + %% io:format("await_time_change pid ~p/~p~n", + %% [egol_cell_mgr:lookup(Id), egol_cell:collector(Id)]), timer:sleep(5), await_time_change(Id, AwaitTime) end. @@ -273,30 +247,32 @@ last_query_response_args(#state{waiting_on=undefined}) -> last_query_response_args(#state{waiting_on=W}) when length(W) /= 1 -> [undefined]; last_query_response_args(S) -> - [S#state.collector, {hd(S#state.waiting_on), content()}, {S#state.id, S#state.time+1}]. + [{hd(S#state.waiting_on), content()}, {S#state.id, S#state.time+1}]. cell_content(#state{id=Id, time=Time, content=Content}) -> {{Id, Time}, Content}. last_query_response_pre(_, [undefined]) -> false; -last_query_response_pre(S, [_CollectorPid, _Resp, {AwaitId, _AwaitTime}]) -> +last_query_response_pre(S, [_Resp, {AwaitId, _AwaitTime}]) -> length(S#state.waiting_on)==1 andalso S#state.id == AwaitId. last_query_response_callouts(#state{pending_query_content=[]}, _) -> ?EMPTY; -last_query_response_callouts(S, _) -> - ?PAR([cell_content_msg(NeighbourId, NeighbourTime, S#state.content) - || {NeighbourId, NeighbourTime} <- S#state.pending_query_content]). +last_query_response_callouts(S, [{_, Content}, _]) -> + NC = S#state.neighbour_count + Content, + NextContent = next_content(S#state.content, NC), + ?PAR([?CALLOUT(egol_protocol, query_response, + [?WILDCARD, cell_content_msg(S#state.id, NeighbourTime, NextContent)], ok) + || {_NeighbourId, NeighbourTime} <- S#state.pending_query_content]). -last_query_response_next(S, _Res, [_CollectorPid, {_, Content}=Resp, _AwaitIdTime]) -> +last_query_response_next(S, _Res, [{_, Content}=Resp, _AwaitIdTime]) -> NC = S#state.neighbour_count + Content, io:format("last_query_response_next NC=~p~n", [NC]), S#state{pending_query_content=[], content=next_content(S#state.content, NC), time=S#state.time+1, waiting_on=undefined, - collector=undefined, neighbour_count=0, neighbour_history=S#state.neighbour_history ++ [Resp]}. @@ -332,23 +308,29 @@ cell_content_msg(S) -> cell_content_msg(S#state.id, S#state.time, S#state.content). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% query_future(Id, _RequestId, Time) -> -%% send_query_content(Id, Time), -%% ok. - -%% query_future_args(S) -> -%% Pending = [ N || {N,_} <- S#state.pending_query_content ], -%% ?LET(Neighbour, oneof(egol_util:neighbours(S#state.id, S#state.dim) -- Pending), -%% [S#state.id, Neighbour, S#state.time+1]). +query_future(Id, _RequestId, Time) -> + send_query_content(Id, Time), + ok. -%% query_future_pre(S, [Id, _NeighbourId, Time]) -> -%% S#state.id /= undefined andalso -%% S#state.id == Id andalso -%% S#state.time == (Time+1). +query_future_args(#state{id=undefined}) -> + [undefined]; +query_future_args(S) -> +% io:format("query_future_args called~n"), + Pending = [ N || {N,_} <- S#state.pending_query_content ], +% io:format("pending: ~p~n", [Pending]), + ?LET(Neighbour, oneof(egol_util:neighbours(S#state.id, S#state.dim) -- Pending), + [S#state.id, Neighbour, S#state.time+1]). + +query_future_pre(_S, [undefined]) -> false; +query_future_pre(S, [Id, _NeighbourId, Time]) -> + io:format("query_future_pre ~p - ~p at ~p for ~p~n", [Id, Time, S#state.time, S#state.id]), + S#state.id /= undefined andalso + S#state.id == Id andalso + (S#state.time+1) == Time. -%% query_future_next(S, _Res, [_Id, NeighbourId, Time]) -> -%% S#state{pending_query_content= -%% S#state.pending_query_content++[{NeighbourId, Time}]}. +query_future_next(S, _Res, [_Id, NeighbourId, Time]) -> + S#state{pending_query_content= + S#state.pending_query_content++[{NeighbourId, Time}]}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -373,6 +355,7 @@ kill(Id, EndTime, NeighbourHistory) -> Collector = egol_cell:collector(Pid), %%io:format("new collector: ~p~n", [Collector]), drive_collector(Pid, Id, Collector, 0, EndTime, NeighbourHistory), + timer:sleep(50), Pid catch E:M -> @@ -395,11 +378,11 @@ drive_collector(_Pid, _Id, Collector, T, T, _NeighbourHistory) -> Collector; %%send_all_query_responses(Collector, NeighbourHistory); drive_collector(Pid, Id, Collector, T, EndTime, NeighbourHistory) -> - %% @todo consider filtering out the CellContents not for this time step, though - %% they should do no harm. + io:format("drive_collector ~p~n", [NeighbourHistory]), ResponsesForTime = [ R || {{_,RTime},_}=R <- NeighbourHistory, RTime == T ], + io:format("drive_collector ~p~n", [ResponsesForTime]), send_all_query_responses(Collector, ResponsesForTime), await_time_change(Id, T), NewCollector = egol_cell:collector(Pid), @@ -415,7 +398,7 @@ kill_args(#state{id=Id, time=Time, neighbour_history=NH}) -> [Id, Time, NH]. kill_pre(S, [Id, EndTime, _]) -> - S#state.cell /= undefined andalso + S#state.id /= undefined andalso S#state.id == Id andalso S#state.time == EndTime andalso S#state.kill_count<1. @@ -423,17 +406,16 @@ kill_pre(S, [Id, EndTime, _]) -> kill_callouts(_S, [_Id, 0, _]) -> ?EMPTY; -kill_callouts(_S, [_Id, EndTime, _NH]) -> - ?SEQ( [ step_callouts_for_time(T) +kill_callouts(_S, [Id, EndTime, _NH]) -> + Neighbours = egol_util:neighbours(Id), + ?SEQ( [ step_callouts_for_time(T, Neighbours, Id) || T <- lists:seq(0, EndTime-1) ] ). %% after a kill the cell will run up to the EndTime and then await a step before it %% can continue since it has not received a step command to drive it forward, so %% waiting_on has to be undefined. -kill_next(S, Pid, %{Pid, Collector}, - [_Id, _EndTime, _NH]) -> - S#state{cell=Pid, collector=undefined, - waiting_on=undefined, +kill_next(S, _Pid, [_Id, _EndTime, _NH]) -> + S#state{waiting_on=undefined, neighbour_count=0, kill_count=S#state.kill_count+1}. @@ -468,15 +450,17 @@ next_content(0,3) -> 1; next_content(C, N) when N==2; N==3 -> C; next_content(_,_) -> 0. -send_query_response(Pid, {{XY,T}, C}=Resp) -> +send_query_response(Id, {{XY,T}, C}=Resp) -> try - Pid ! cell_content_msg(XY,T,C) +%% Pid ! cell_content_msg(XY,T,C) + egol_cell:query_response(Id, cell_content_msg(XY, T, C)) catch _:_ -> io:format("send_query_response (~p, ~p) failed~n", - [Pid, Resp]) + [Id, Resp]) end. send_query_content(Id, Time) -> - Pid = egol_cell_mgr:lookup(Id), - Pid ! {query_content, Time, self()}. + %% Pid = egol_cell_mgr:lookup(Id), + %% Pid ! {query_content, Time, self()}. + egol_cell:query_content(Id, Time, na). From ccbe04f2912a76e56a18fe4502f18a34f83d0d52 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Wed, 28 Jan 2015 17:06:37 +0000 Subject: [PATCH 46/70] Changed to use collecting_status for egol_cell * Add query_response/2 and query_response/3 to egol_cel and modified egol_procotol accordingly. --- src/egol_cell.erl | 80 ++++++++++++++++++++++++++----------------- src/egol_protocol.erl | 15 ++++---- 2 files changed, 57 insertions(+), 38 deletions(-) diff --git a/src/egol_cell.erl b/src/egol_cell.erl index 61f6620..ba8b359 100644 --- a/src/egol_cell.erl +++ b/src/egol_cell.erl @@ -25,10 +25,12 @@ run/1, run_until/2, pause/1, - step/1]). + step/1, + query_response/2, + query_content/3]). -%% for testing only!! --export([collector/1]). +%% for testing and debugging only!! +-export([collecting_status/1]). -type cell_content() :: 0 | 1. -type cell_name() :: {integer(), integer()}. @@ -110,8 +112,14 @@ pause(Cell) -> step(Cell) -> cast(Cell, step). -collector(Cell) -> - call(Cell, collector). +collecting_status(Cell) -> + call(Cell, collecting_status). + +query_response(Cell, Resp) -> + cast(Cell, Resp). + +query_content(Cell, Time, FromXY) -> + cast(Cell, {query_content, Time, FromXY}). call(Cell, Cmd) -> gen_server:call(cell_pid(Cell), Cmd). @@ -176,14 +184,22 @@ handle_cast({run_until, EndTime}, end; handle_cast(pause, State) -> {noreply, State#state{mode=step}}; -handle_cast(step, State) -> +handle_cast({query_content, Time, From}, State) -> + case content_at(Time, State) of + future -> + {noreply, State#state{future=[{From, Time} | State#state.future]}}; + C -> + egol_protocol:query_response(From, {cell_content, C}), + {noreply, State} + end; +handle_cast(Resp, State) -> case is_collector_running(State) of true -> - {noreply, State#state{mode=step}}; + State#state.collector ! Resp; false -> - NewState = start_collector(State), - {noreply, NewState#state{mode=step}} - end. + ok + end, + {noreply, State}. handle_call(history, _From, State) -> {reply, State#state.history, State}; @@ -196,10 +212,11 @@ handle_call({get, Time}, _From, State) -> {_, C} -> {reply, C, State} end; -handle_call(collector, _From, State) -> +handle_call(collecting_status, From, State) -> case is_collector_running(State) of true -> - {reply, State#state.collector, State}; + State#state.collector ! {status, From}, + {noreply, State}; false -> {reply, undefined, State} end. @@ -229,15 +246,16 @@ handle_info({Collector, {next_content, NextContent}}, false -> {noreply, NextState#state{mode=step}} end - end; -handle_info({query_content, Time, From}, State) -> - case content_at(Time, State) of - future -> - {noreply, State#state{future=[{From, Time} | State#state.future]}}; - C -> - egol_protocol:query_response(From, {cell_content, C}), - {noreply, State} end. +%% handle_info({query_content, Time, From}, State) -> +%% case content_at(Time, State) of +%% future -> +%% {noreply, State#state{future=[{From, Time} | State#state.future]}}; +%% C -> +%% egol_protocol:query_response(From, {cell_content, C}), +%% {noreply, State} +%% end. + terminate(_Reason, State) -> @@ -252,17 +270,17 @@ is_collector_running(#state{collector=Collector}) -> start_collector(#state{time=T, neighbours=Neighbours, - content=Content}=State) -> + content=Content, xy=XY}=State) -> lager:debug("start_collector: neighbours=~p~n", [Neighbours]), Cell = self(), Collector = spawn_link( fun () -> - collector_init(T, Neighbours, Cell, Content) + collector_init(T, Neighbours, Cell, XY, Content) end ), %% io:format("start_collector new pid ~p~n", [Collector]), State#state{collector=Collector}. -collector_init(Time, Neighbours, Cell, Content) -> - query_neighbours(Time, Neighbours), +collector_init(Time, Neighbours, Cell, XY, Content) -> + query_neighbours(XY, Time, Neighbours), collector_loop(egol_util:neighbours_at(Time, Neighbours), 0, Cell, Content). collector_loop([], NeighbourCount, Cell, Content) -> @@ -283,8 +301,8 @@ collector_loop(WaitingOn, NeighbourCount, Cell, Content) -> %% [Msg]), collector_loop(WaitingOn, NeighbourCount, Cell, Content) end; - {sync_collector, From, Ref} -> %% WARNING: only for eqc testing - From ! Ref, + {status, From} -> + gen_server:reply(From, {WaitingOn, NeighbourCount}), collector_loop(WaitingOn, NeighbourCount, Cell, Content); Garbage -> io:format("collector got GARBAGE ~p~n", @@ -294,19 +312,19 @@ collector_loop(WaitingOn, NeighbourCount, Cell, Content) -> process_future(XY, Time, Content, Future) -> - {Ready, NewFuture} = lists:partition( fun({_Pid,T}) -> + {Ready, NewFuture} = lists:partition( fun({_FromXY,T}) -> T == Time end, Future), - lists:foreach( fun({Pid,_}) -> - Pid ! {cell_content, {{XY,Time}, Content}} + lists:foreach( fun({FromXY,_}) -> + egol_protocol:query_response(FromXY, {cell_content, {{XY,Time}, Content}}) end, Ready), NewFuture. -query_neighbours(T, Neighbours) -> +query_neighbours(XY, T, Neighbours) -> lists:foreach( fun(N) -> - egol_protocol:query_content(N, T) + egol_protocol:query_content(N, T, XY) end, Neighbours). diff --git a/src/egol_protocol.erl b/src/egol_protocol.erl index 6f20920..5aa035a 100644 --- a/src/egol_protocol.erl +++ b/src/egol_protocol.erl @@ -5,16 +5,17 @@ -compile([{parse_transform, lager_transform}]). --export([query_content/2, +-export([query_content/3, query_response/2]). -query_content(XY, Time) -> - Pid = egol_cell_mgr:lookup(XY), - Pid ! {query_content, Time, self()}. +query_content(XY, Time, FromXY) -> + %% Pid = egol_cell_mgr:lookup(XY), + %% Pid ! {query_content, Time, self()}. + egol_cell:query_content(XY, Time, FromXY). - -query_response(Pid, Resp) -> - Pid ! Resp. +query_response(Cell, Resp) -> + %% Pid ! Resp. + egol_cell:query_response(Cell, Resp). From 5785090627331045e810a45cf1813dc759481075 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Thu, 29 Jan 2015 14:05:43 +0000 Subject: [PATCH 47/70] Changed the restart interval to allow testing --- src/egol_cell_sup.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/egol_cell_sup.erl b/src/egol_cell_sup.erl index a05e329..5408547 100644 --- a/src/egol_cell_sup.erl +++ b/src/egol_cell_sup.erl @@ -30,7 +30,7 @@ start_cell(XY, Dim, InitialContent) -> init([]) -> RestartStrategy = simple_one_for_one, MaxRestarts = 1000, - MaxSecondsBetweenRestarts = 3600, + MaxSecondsBetweenRestarts = 1, SupFlags = {RestartStrategy, MaxRestarts, MaxSecondsBetweenRestarts}, From 23e5dd2d87214294ed4bdc6cb44c8dddb7bf78bf Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Thu, 29 Jan 2015 14:06:16 +0000 Subject: [PATCH 48/70] Exported types and nicer kill function --- src/egol_cell.erl | 59 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 14 deletions(-) diff --git a/src/egol_cell.erl b/src/egol_cell.erl index ba8b359..e0f353b 100644 --- a/src/egol_cell.erl +++ b/src/egol_cell.erl @@ -36,6 +36,7 @@ -type cell_name() :: {integer(), integer()}. -type time() :: integer(). +-export_type([cell_content/0, cell_name/0, time/0]). -type mode() :: 'run' | 'step'. @@ -75,13 +76,16 @@ start_link({X,Y}=XY, {DimX, DimY}=Dim, InitialContent) where(XY) -> egol_cell_mgr:lookup(XY). +%% kill(XY) -> +%% case where(XY) of +%% undefined -> +%% ok; +%% Pid when is_pid(Pid) -> +%% exit(Pid, stop) +%% end. + kill(XY) -> - case where(XY) of - undefined -> - ok; - Pid when is_pid(Pid) -> - exit(Pid, kill) - end. + cast(XY, stop). set(Cell, Content) -> cast(Cell, {set, Content}). @@ -185,13 +189,16 @@ handle_cast({run_until, EndTime}, handle_cast(pause, State) -> {noreply, State#state{mode=step}}; handle_cast({query_content, Time, From}, State) -> - case content_at(Time, State) of + case content_at(Time, State) of future -> {noreply, State#state{future=[{From, Time} | State#state.future]}}; C -> + %%io:format("query_content ~p ~p ~p~n", [Time, From, State]), egol_protocol:query_response(From, {cell_content, C}), {noreply, State} end; +handle_cast(stop, State) -> + {stop, normal, State}; handle_cast(Resp, State) -> case is_collector_running(State) of true -> @@ -231,6 +238,7 @@ handle_info({Collector, {next_content, NextContent}}, lager:debug("Cell ~p changing to ~p for time ~p", [XY, NextContent, T+1]), egol_time:set(XY, T+1), NextState = State#state{content=NextContent, + collector=undefined, time=T+1, history=[{T, Content}|History], future=NewFuture}, @@ -280,30 +288,43 @@ start_collector(#state{time=T, neighbours=Neighbours, State#state{collector=Collector}. collector_init(Time, Neighbours, Cell, XY, Content) -> + NeighbourRefs = monitor_neighbours(Neighbours), query_neighbours(XY, Time, Neighbours), - collector_loop(egol_util:neighbours_at(Time, Neighbours), 0, Cell, Content). + collector_loop(egol_util:neighbours_at(Time, Neighbours), NeighbourRefs, 0, Cell, Content). -collector_loop([], NeighbourCount, Cell, Content) -> +collector_loop([], _, NeighbourCount, Cell, Content) -> Cell ! {self(), {next_content, egol_util:next_content(Content, NeighbourCount)}}; -collector_loop(WaitingOn, NeighbourCount, Cell, Content) -> +collector_loop(WaitingOn, NeighbourRefs, NeighbourCount, Cell, Content) -> receive - {cell_content, {{{_,_},_}=XYatT, NeighbourContent}} = Msg -> + {cell_content, {{{_,_}=XY,_}=XYatT, NeighbourContent}} -> %% io:format("collector ~p got cell_content ~p - ~p~n", %% [self(), XYatT, NeighbourContent]), case lists:member(XYatT, WaitingOn) of true -> + NewNeighbourRefs = lists:keydelete(XY, 2, NeighbourRefs), collector_loop(lists:delete(XYatT, WaitingOn), + NewNeighbourRefs, NeighbourCount + NeighbourContent, Cell, Content); false %% ignore messages we are not waiting for -> %% io:format("collector got wrong message ~p~n", %% [Msg]), - collector_loop(WaitingOn, NeighbourCount, Cell, Content) + collector_loop(WaitingOn, NeighbourRefs, NeighbourCount, Cell, Content) end; {status, From} -> gen_server:reply(From, {WaitingOn, NeighbourCount}), - collector_loop(WaitingOn, NeighbourCount, Cell, Content); + collector_loop(WaitingOn, NeighbourRefs, NeighbourCount, Cell, Content); + {'DOWN', Ref, process, Pid, Info} -> + case lists:keytake(Ref, 1, NeighbourRefs) of + false -> + io:format("collector got down for unknown process ~p:~p~n", [Pid, Info]), + collector_loop(WaitingOn, NeighbourRefs, NeighbourCount, Cell, Content); + {value, {Ref, NeighbourId}, RestNeighbourRefs} -> + NewRef = monitor_neighbour(NeighbourId), + collector_loop(WaitingOn, [{NewRef, NeighbourId}|RestNeighbourRefs], + NeighbourCount, Cell, Content) + end; Garbage -> io:format("collector got GARBAGE ~p~n", [Garbage]), @@ -328,7 +349,17 @@ query_neighbours(XY, T, Neighbours) -> end, Neighbours). - +monitor_neighbours(Neighbours) -> + lists:map( fun monitor_neighbour/1, Neighbours ). + +monitor_neighbour(N) -> + case egol_cell_mgr:lookup(N) of + undefined -> + timer:sleep(3), + monitor_neighbour(N); + Pid -> + {erlang:monitor(process, Pid), N} + end. content_at(Time, #state{xy=XY, time=Time, content=Content}) -> {{XY,Time}, Content}; From 7f19916bc5917a76afb64b4c817d14bfbfc9d2ea Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Thu, 29 Jan 2015 14:07:04 +0000 Subject: [PATCH 49/70] Hacks to forget old tests. NEEDS CLEAN UP --- eqc_test/egol_eqc.erl | 243 ++++++++++++++++++++++++++++++------------ 1 file changed, 176 insertions(+), 67 deletions(-) diff --git a/eqc_test/egol_eqc.erl b/eqc_test/egol_eqc.erl index 80beed4..7b68f7b 100644 --- a/eqc_test/egol_eqc.erl +++ b/eqc_test/egol_eqc.erl @@ -14,6 +14,7 @@ waiting_on = undefined, neighbour_count = 0, neighbour_history = [], + neighbours = [] :: [{egol_cell:cell_name(), pid()}], kill_count=0, pending_query_content=[] }). @@ -26,9 +27,17 @@ api_spec() -> #api_module{ name = egol_protocol, functions = [ #api_fun{ name = query_content, arity = 3}, - #api_fun{ name = query_response, arity = 2} + #api_fun{ name = query_response, arity = 2} ] } +%% , +%% #api_module{ +%% name = egol_time, +%% functions = [ #api_fun{ name = max, arity = 0}, +%% #api_fun{ name = set, arity = 2}, +%% #api_fun{ name = clear, arity = 1}, +%% #api_fun{ name = init, arity = 0}] +%% } ]}. @@ -42,12 +51,14 @@ prop_cell() -> ?SETUP( %%fun my_setup/0, fun() -> %% setup mocking here - eqc_mocking:start_mocking(api_spec()), + %eqc_mocking:start_mocking(api_spec()), + ok, fun() -> ok end end, ?FORALL(Cmds, commands(?MODULE), % ?IMPLIES(length(Cmds)>20, begin +%% print("Cmds: ~p~n", [Cmds]), start(), {H, S, Res} = run_commands(?MODULE,Cmds), stop(S), @@ -64,35 +75,83 @@ my_setup() -> start() -> + eqc_mocking:start_mocking(api_spec()), catch egol_time:stop(), egol_sup:start_link(), + await_egol_cell_sup(), ok. +await_egol_cell_sup() -> + case whereis(egol_cell_sup) of + undefined -> + timer:sleep(5), + await_egol_cell_sup(); + _ -> + ok + end. + stop() -> %% print_regs(), - catch exit(whereis(egol_sup), normal), + %catch exit(whereis(egol_sup), shutdown), catch egol_time:stop(), eqc_mocking:stop_mocking(), timer:sleep(100), ok. +stop_egol_sup() -> + Pid = whereis(egol_sup), + Ref = monitor(process, Pid), + exit(Pid, shutdown), + receive + {'DOWN', Ref, process, Pid, _Reason} -> + ok + after 1000 -> + error(exit_timeout) + end. + + + + print_regs() -> try CellSup = whereis(cell_sup), MgrSup = whereis(mgr_sup), - io:format("CellSup: ~p - MgrSup: ~p~n", [CellSup, MgrSup]) + print("CellSup: ~p - MgrSup: ~p~n", [CellSup, MgrSup]) catch _:_ -> - io:format("unable to print regs~n") + print("unable to print regs~n") end. stop(S) -> - catch egol_cell:kill(S#state.id), - timer:sleep(100). + eqc_mocking:stop_mocking(), + case egol_cell_mgr:lookup(S#state.id) of + undefined -> + ok; + Pid -> + egol_cell:kill(S#state.id), + await_death(Pid) + end, +% stop_egol_sup(). +% timer:sleep(100). + ok. +await_death(Pid) -> + case erlang:is_process_alive(Pid) of + true -> + timer:sleep(5), + await_death(Pid); + false -> + ok + end. %%weight(_S, kill) -> 0; weight(#state{id=undefined}, Cmd) when Cmd /= cell -> 0; +%% weight(#state{id=undefined}, cell) -> 1; +%% weight(#state{}, query_content) -> 1; +%% weight(_, query_future) -> 1; +%% weight(_, _) -> 0; + +%% weight(#state{waiting_on=W}, step) when is_list(W) -> 0; weight(_S, get) -> 1; weight(_S, cell) -> 1; @@ -106,8 +165,25 @@ weight(_S, _) -> 2. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% cell(XY, Dim, Content) -> {ok, Pid} = egol_cell_sup:start_cell(XY, Dim, Content), - %io:format("cell STARTED ~p (~p/~p)~n", [Pid, XY, Dim]), - Pid. + print("cell STARTED ~p (~p/~p)~n", [Pid, XY, Dim]), + Neighbours = start_neighbours(XY, Dim), + {Pid, Neighbours}. + +start_neighbours(XY, Dim) -> + Nids = egol_util:neighbours(XY, Dim), + [ start_neighbour(Nid) || Nid <- Nids ]. + +start_neighbour(Nid) -> + Pid = spawn_link( fun dummy_neigbour_loop/0 ), + egol_time:set(Nid, 0), + egol_cell_mgr:reg(Nid, Pid), + {Nid, Pid}. + +dummy_neigbour_loop() -> + receive + _ -> dummy_neigbour_loop() + end. + cell_args(_S) -> ?LET({CellId, Dim}, cell_id_and_dim(), @@ -116,17 +192,16 @@ cell_args(_S) -> cell_pre(S, _) -> S#state.id == undefined. -cell_post(_S, [_, _, _], Res) -> - is_pid(Res) and erlang:is_process_alive(Res). +cell_post(_S, [_, _, _], {Pid, _Neighbours}) -> + is_pid(Pid) and erlang:is_process_alive(Pid). - -cell_next(S, _Pid, [CellId, Dim, Content]) -> - S#state{id=CellId, dim=Dim, content=Content}. +cell_next(S, {_Pid, Neighbours}, [CellId, Dim, Content]) -> + S#state{id=CellId, dim=Dim, content=Content, neighbours=Neighbours}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% step(Id, _Dim, _Time) -> - %io:format("step for ~p/~p~n", [Pid, Id]), + print("step for ~p~n", [Id]), egol_cell:step(Id), await_collecting_status(Id). @@ -146,7 +221,7 @@ step_args(S) -> [S#state.id, S#state.dim, S#state.time]. step_pre(S, [Id, Dim, Time]) -> - %io:format("step_pre cell:~p, pid:~p waiting_on:~p~n", + %print("step_pre cell:~p, pid:~p waiting_on:~p~n", % [S#state.cell, Pid, S#state.waiting_on]), S#state.id /= undefined andalso S#state.id == Id andalso @@ -157,13 +232,15 @@ step_pre(S, [Id, Dim, Time]) -> step_callouts(_S, [XY, Dim, Time ]) -> step_callouts_for_time(Time, egol_util:neighbours(XY, Dim), XY). -step_callouts_for_time(Time, Neighbours, XY) -> - ?PAR(lists:duplicate(8, ?CALLOUT(egol_protocol, query_content, [?WILDCARD, Time, XY], ok))). +step_callouts_for_time(Time, _Neighbours, XY) -> + Queries = lists:duplicate(8, ?CALLOUT(egol_protocol, query_content, [?WILDCARD, Time, XY], ok)), +%% Monitorings = lists:duplicate(8, ?CALLOUT(egol_protocol, lookup, [?WILDCARD], self())), + ?PAR(Queries).%++Monitorings). %% ?PAR([?CALLOUT(egol_protocol, query_content, [N, Time, XY], ok) %% || N <- Neighbours]). step_next(S, _Res, _Args) -> - %io:format("step_next time:~p~n", [S#state.time]), + %print("step_next time:~p~n", [S#state.time]), S#state{waiting_on = egol_util:neighbours_at(S#state.time, egol_util:neighbours(S#state.id, S#state.dim)), steps=S#state.steps+1}. @@ -171,7 +248,7 @@ step_next(S, _Res, _Args) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% get(Id, Time) -> Res = egol_cell:get(Id, Time), -% %%io:format("get works~n"), +% %%print("get works~n"), Res. get_args(S) -> @@ -187,7 +264,7 @@ get_post(S, [_Id, _Time], Res) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% query_response(Id, Resp) -> - io:format("query_response(~p, ~p)~n", [Id, Resp]), + print("query_response(~p, ~p)~n", [Id, Resp]), send_query_response(Id, Resp). query_response_args(#state{id=undefined}) -> @@ -198,7 +275,7 @@ query_response_args(S) -> [S#state.id, neighbour_response(S)]. neighbour_response(S) -> - %%io:format("neighbour_response: ~p~n", [S]), + %%print("neighbour_response: ~p~n", [S]), ?LET(N, neighbour(S), case lists:keyfind({N, S#state.time}, 1, S#state.neighbour_history) of false -> @@ -208,7 +285,8 @@ neighbour_response(S) -> end). query_response_pre(_S, [undefined, undefined]) -> false; -query_response_pre(#state{waiting_on=undefined}, _ ) -> false; +query_response_pre(#state{waiting_on=undefined}, _ ) -> false; +query_response_pre(#state{waiting_on=W}, _ ) when length(W) =< 1 -> false; query_response_pre(S, [Id, {{Neighbour, Time}, _}] ) -> Id /= undefined andalso S#state.waiting_on /= [] andalso @@ -223,17 +301,19 @@ query_response_next(S, _Res, [_, {{Neighbour, Time}, Content}=Resp]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% last_query_response(Resp, {Id, AwaitTime}) -> + print("last_query_response(~p, {~p, ~p})~n", [Resp, Id, AwaitTime]), send_query_response(Id, Resp), - io:format("calling await_time_change(~p, ~p)~n", [Id, AwaitTime]), + print("calling await_time_change(~p, ~p)~n", [Id, AwaitTime]), await_time_change(Id, AwaitTime). await_time_change(Id, AwaitTime) -> case egol_cell:time(Id) of AwaitTime -> + ok; _ -> - %% io:format("await_time_change id ~p~n", [Id]), - %% io:format("await_time_change pid ~p/~p~n", + %% print("await_time_change id ~p~n", [Id]), + %% print("await_time_change pid ~p/~p~n", %% [egol_cell_mgr:lookup(Id), egol_cell:collector(Id)]), timer:sleep(5), await_time_change(Id, AwaitTime) @@ -268,7 +348,7 @@ last_query_response_callouts(S, [{_, Content}, _]) -> last_query_response_next(S, _Res, [{_, Content}=Resp, _AwaitIdTime]) -> NC = S#state.neighbour_count + Content, - io:format("last_query_response_next NC=~p~n", [NC]), + print("last_query_response_next NC=~p~n", [NC]), S#state{pending_query_content=[], content=next_content(S#state.content, NC), time=S#state.time+1, @@ -280,6 +360,7 @@ last_query_response_next(S, _Res, [{_, Content}=Resp, _AwaitIdTime]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% query_content(Id, Time) -> + print("query_content(~p,~p)~n", [Id, Time]), send_query_content(Id, Time), timer:sleep(50), ok. @@ -308,22 +389,23 @@ cell_content_msg(S) -> cell_content_msg(S#state.id, S#state.time, S#state.content). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -query_future(Id, _RequestId, Time) -> +query_future(Id, RequestId, Time) -> + print("query_future(~p, ~p, ~p)~n", [Id, RequestId, Time]), send_query_content(Id, Time), ok. query_future_args(#state{id=undefined}) -> [undefined]; query_future_args(S) -> -% io:format("query_future_args called~n"), - Pending = [ N || {N,_} <- S#state.pending_query_content ], -% io:format("pending: ~p~n", [Pending]), - ?LET(Neighbour, oneof(egol_util:neighbours(S#state.id, S#state.dim) -- Pending), +% print("query_future_args called~n"), +% Pending = [ N || {N,_} <- S#state.pending_query_content ], +% print("pending: ~p~n", [Pending]), + ?LET(Neighbour, oneof(egol_util:neighbours(S#state.id, S#state.dim)), [S#state.id, Neighbour, S#state.time+1]). query_future_pre(_S, [undefined]) -> false; query_future_pre(S, [Id, _NeighbourId, Time]) -> - io:format("query_future_pre ~p - ~p at ~p for ~p~n", [Id, Time, S#state.time, S#state.id]), + %%print("query_future_pre ~p - ~p at ~p for ~p~n", [Id, Time, S#state.time, S#state.id]), S#state.id /= undefined andalso S#state.id == Id andalso (S#state.time+1) == Time. @@ -334,34 +416,45 @@ query_future_next(S, _Res, [_Id, NeighbourId, Time]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -kill(Id, 0, _) -> +kill(Id, _Dim, 0, _, PendingQueryContent) -> + print("kill(~p, _, 0, _, ~p)~n", [Id, PendingQueryContent]), try OldPid = egol_cell_mgr:lookup(Id), - %io:format("killing ~p~n", [OldPid]), + %print("killing ~p~n", [OldPid]), egol_cell:kill(Id), - NewPid = await_new_cell_pid(OldPid, Id), - %io:format("started ~p~n", [NewPid]), - NewPid + await_new_cell_pid(OldPid, Id), + %print("started ~p~n", [NewPid]), + resend_pending_query_content(Id, PendingQueryContent), + ok catch E:M -> - io:format("kill exception ~p:~p ~p~n", [E, M, erlang:get_stacktrace()]) + print("kill exception ~p:~p ~p~n", [E, M, erlang:get_stacktrace()]) end; -kill(Id, EndTime, NeighbourHistory) -> +kill(Id, _Dim, EndTime, NeighbourHistory, PendingQueryContent) -> + print("kill(~p, _, ~p, ~p, ~p)~n", [Id, EndTime, NeighbourHistory, PendingQueryContent]), try OldPid = egol_cell_mgr:lookup(Id), egol_cell:kill(Id), - Pid = await_new_cell_pid(OldPid, Id), - %%io:format("kill new_pid:~p~n", [Pid]), - Collector = egol_cell:collector(Pid), - %%io:format("new collector: ~p~n", [Collector]), - drive_collector(Pid, Id, Collector, 0, EndTime, NeighbourHistory), + await_new_cell_pid(OldPid, Id), + resend_pending_query_content(Id, PendingQueryContent), + %%print("kill new_pid:~p~n", [Pid]), + _ = egol_cell:collecting_status(Id), + %%print("new collector: ~p~n", [Collector]), + drive_collector(Id, 0, EndTime, NeighbourHistory), timer:sleep(50), - Pid + ok catch E:M -> - io:format("kill exception ~p:~p ~p~n", [E, M, erlang:get_stacktrace()]) + print("kill exception ~p:~p ~p~n", [E, M, erlang:get_stacktrace()]) end. +resend_pending_query_content(Id, PendingQueryContent) -> + %print("started ~p~n", [NewPid]), + lists:foreach(fun({_NeighbourId, Time}) -> + send_query_content(Id, Time) + end, + PendingQueryContent). + await_new_cell_pid(OldPid, Id) -> case egol_cell_mgr:lookup(Id) of NewPid when is_pid(NewPid) andalso @@ -374,48 +467,55 @@ await_new_cell_pid(OldPid, Id) -> %% go forward until the cell has progressed to max time -drive_collector(_Pid, _Id, Collector, T, T, _NeighbourHistory) -> - Collector; +drive_collector(_Id, T, T, _NeighbourHistory) -> + ok; %%send_all_query_responses(Collector, NeighbourHistory); -drive_collector(Pid, Id, Collector, T, EndTime, NeighbourHistory) -> - io:format("drive_collector ~p~n", [NeighbourHistory]), +drive_collector(Id, T, EndTime, NeighbourHistory) -> + print("drive_collector ~p~n", [NeighbourHistory]), ResponsesForTime = [ R || {{_,RTime},_}=R <- NeighbourHistory, RTime == T ], - io:format("drive_collector ~p~n", [ResponsesForTime]), - send_all_query_responses(Collector, ResponsesForTime), + print("drive_collector ~p~n", [ResponsesForTime]), + send_all_query_responses(Id, ResponsesForTime), await_time_change(Id, T), - NewCollector = egol_cell:collector(Pid), - drive_collector(Pid, Id, NewCollector, T+1, EndTime, NeighbourHistory). + _ = egol_cell:collecting_status(Id), + drive_collector(Id, T+1, EndTime, NeighbourHistory). -send_all_query_responses(Collector, NeighbourHistory) -> +send_all_query_responses(Id, NeighbourHistory) -> lists:foreach(fun(CellContent) -> - send_query_response(Collector, CellContent) + send_query_response(Id, CellContent) end, NeighbourHistory). -kill_args(#state{id=Id, time=Time, neighbour_history=NH}) -> - [Id, Time, NH]. +kill_args(#state{id=Id, dim=Dim, time=Time, + neighbour_history=NH, pending_query_content=PendingQueryContent}) -> + [Id, Dim, Time, NH, PendingQueryContent]. -kill_pre(S, [Id, EndTime, _]) -> +kill_pre(S, [Id, _Dim, EndTime, _, _]) -> S#state.id /= undefined andalso S#state.id == Id andalso S#state.time == EndTime andalso S#state.kill_count<1. -kill_callouts(_S, [_Id, 0, _]) -> +kill_callouts(_S, [_Id, _Dim, 0, _, _]) -> ?EMPTY; -kill_callouts(_S, [Id, EndTime, _NH]) -> - Neighbours = egol_util:neighbours(Id), - ?SEQ( [ step_callouts_for_time(T, Neighbours, Id) - || T <- lists:seq(0, EndTime-1) ] ). +kill_callouts(S, [Id, Dim, EndTime, _NH, _]) -> + Neighbours = egol_util:neighbours(Id, Dim), + StepCallouts = ?SEQ( [ step_callouts_for_time(T, Neighbours, Id) + || T <- lists:seq(0, EndTime-1) ] ), + %% PendingQueryResponses = ?PAR( [?CALLOUT( egol_protocol, query_response, + %% [?WILDCARD, cell_content_msg(S#state.id, NeighbourTime, ?WILDCARD)], ok) + %% || {_NeighbourId, NeighbourTime} <- S#state.pending_query_content ]), + %% ?PAR(StepCallouts, PendingQueryResponses). + StepCallouts. %% after a kill the cell will run up to the EndTime and then await a step before it %% can continue since it has not received a step command to drive it forward, so %% waiting_on has to be undefined. -kill_next(S, _Pid, [_Id, _EndTime, _NH]) -> +kill_next(S, _Pid, [_Id, _Dim, _EndTime, _NH, _]) -> S#state{waiting_on=undefined, +% pending_query_content = [], neighbour_count=0, kill_count=S#state.kill_count+1}. @@ -456,7 +556,7 @@ send_query_response(Id, {{XY,T}, C}=Resp) -> egol_cell:query_response(Id, cell_content_msg(XY, T, C)) catch _:_ -> - io:format("send_query_response (~p, ~p) failed~n", + print("send_query_response (~p, ~p) failed~n", [Id, Resp]) end. @@ -464,3 +564,12 @@ send_query_content(Id, Time) -> %% Pid = egol_cell_mgr:lookup(Id), %% Pid ! {query_content, Time, self()}. egol_cell:query_content(Id, Time, na). + + +print(Str) -> + io:format(Str). +% ok. + +print(Str, Args) -> + io:format(Str, Args). + %ok. From 3a43f94ffbdbcf23fd2266727aae203cb2002368 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Mon, 2 Feb 2015 22:10:45 +0100 Subject: [PATCH 50/70] Ask restarted neighbour about content again. --- src/egol_cell.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/egol_cell.erl b/src/egol_cell.erl index e0f353b..5d20f6d 100644 --- a/src/egol_cell.erl +++ b/src/egol_cell.erl @@ -322,6 +322,7 @@ collector_loop(WaitingOn, NeighbourRefs, NeighbourCount, Cell, Content) -> collector_loop(WaitingOn, NeighbourRefs, NeighbourCount, Cell, Content); {value, {Ref, NeighbourId}, RestNeighbourRefs} -> NewRef = monitor_neighbour(NeighbourId), + egol_protocol:query_content(NeighbourId, T, Id), collector_loop(WaitingOn, [{NewRef, NeighbourId}|RestNeighbourRefs], NeighbourCount, Cell, Content) end; From 5bd9c3252cf07f932de9e6f3658a607d101f7029 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Mon, 2 Feb 2015 22:11:22 +0100 Subject: [PATCH 51/70] WIP kill_neighbour stash --- eqc_test/egol_eqc.erl | 61 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 12 deletions(-) diff --git a/eqc_test/egol_eqc.erl b/eqc_test/egol_eqc.erl index 7b68f7b..86b2e96 100644 --- a/eqc_test/egol_eqc.erl +++ b/eqc_test/egol_eqc.erl @@ -78,7 +78,7 @@ start() -> eqc_mocking:start_mocking(api_spec()), catch egol_time:stop(), egol_sup:start_link(), - await_egol_cell_sup(), + %% await_egol_cell_sup(), ok. await_egol_cell_sup() -> @@ -161,13 +161,17 @@ weight(_S, kill) -> 1; weight(_S, _) -> 2. +command_precondition_common(S, Cmd) -> + S#state.id /= undefined orelse Cmd == cell. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% cell(XY, Dim, Content) -> - {ok, Pid} = egol_cell_sup:start_cell(XY, Dim, Content), - print("cell STARTED ~p (~p/~p)~n", [Pid, XY, Dim]), + {ok, _Pid} = egol_cell_sup:start_cell(XY, Dim, Content), + print("cell STARTED (~p/~p)~n", [XY, Dim]), Neighbours = start_neighbours(XY, Dim), - {Pid, Neighbours}. + print("cell neighbours: ~p~n", [Neighbours]), + Neighbours, + ok. start_neighbours(XY, Dim) -> Nids = egol_util:neighbours(XY, Dim), @@ -177,7 +181,8 @@ start_neighbour(Nid) -> Pid = spawn_link( fun dummy_neigbour_loop/0 ), egol_time:set(Nid, 0), egol_cell_mgr:reg(Nid, Pid), - {Nid, Pid}. + %%{Nid, Pid}, + ok. dummy_neigbour_loop() -> receive @@ -189,14 +194,16 @@ cell_args(_S) -> ?LET({CellId, Dim}, cell_id_and_dim(), [CellId, Dim, content()]). -cell_pre(S, _) -> +cell_pre(S) -> S#state.id == undefined. -cell_post(_S, [_, _, _], {Pid, _Neighbours}) -> - is_pid(Pid) and erlang:is_process_alive(Pid). +cell_post(_S, [_, _, _], Neighbours) -> + print("cell_post Neighbours: ~p~n", [Neighbours]), + true. +%% is_pid(Pid) and erlang:is_process_alive(Pid). -cell_next(S, {_Pid, Neighbours}, [CellId, Dim, Content]) -> - S#state{id=CellId, dim=Dim, content=Content, neighbours=Neighbours}. +cell_next(S, _Res, [CellId, Dim, Content]) -> + S#state{id=CellId, dim=Dim, content=Content}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -309,7 +316,6 @@ last_query_response(Resp, {Id, AwaitTime}) -> await_time_change(Id, AwaitTime) -> case egol_cell:time(Id) of AwaitTime -> - ok; _ -> %% print("await_time_change id ~p~n", [Id]), @@ -500,7 +506,7 @@ kill_pre(S, [Id, _Dim, EndTime, _, _]) -> kill_callouts(_S, [_Id, _Dim, 0, _, _]) -> ?EMPTY; -kill_callouts(S, [Id, Dim, EndTime, _NH, _]) -> +kill_callouts(_S, [Id, Dim, EndTime, _NH, _]) -> Neighbours = egol_util:neighbours(Id, Dim), StepCallouts = ?SEQ( [ step_callouts_for_time(T, Neighbours, Id) || T <- lists:seq(0, EndTime-1) ] ), @@ -519,7 +525,38 @@ kill_next(S, _Pid, [_Id, _Dim, _EndTime, _NH, _]) -> neighbour_count=0, kill_count=S#state.kill_count+1}. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +kill_neighbour(Nid) -> + Pid = egol_cell_mgr:lookup(Nid), + exit(Pid, stop), + start_neighbour(Nid). + +%% kill_neighbour_args(#state{waiting_on=undefined}) -> +%% [undefined]; +%% kill_neighbour_args(#state{neighbours=1}) -> +%% [undefined]; +kill_neighbour_args(S) -> + %% Nids = [ Nid || {Nid, _T} <- S#state.waiting_on ], + %% io:format("kill_neighbours_args: ~p in ~p~n", [Nids, S#state.neighbours]), + %% ?LET(Nid, oneof(Nids), + %% lists:keyfind(Nid, 1, S#state.neighbours)). + [neighbour(S)]. + +kill_neighbour_pre(#state{waiting_on=undefined}) -> false; +kill_neighbour_pre(_S) -> true. + +kill_neighbour_callouts(S, [{Nid, _Pid}]) -> + Nids = [ N || {N, _T} <- S#state.waiting_on ], + case lists:member(Nid, Nids) of + true -> + ?CALLOUT(egol_protocol, query_content, [?WILDCARD, S#state.time, S#state.id], ok); + false -> + ?EMPTY + end. +%% kill_neighbour_next(S, _Res, _Args) -> +%% S. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% GENERATORS From ecc451e864b6f50e6ba6b064d5d9bb65458bd015 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Wed, 4 Feb 2015 11:10:29 +0100 Subject: [PATCH 52/70] Everything but kill is working Need to figure out how to ensure that all processes have been stopped between tests. --- eqc_test/egol_eqc.erl | 235 ++++++++++++++++++++++++------------------ src/egol_cell.erl | 90 +++++++++++++--- src/egol_cell_sup.erl | 2 +- src/egol_sup.erl | 5 + 4 files changed, 218 insertions(+), 114 deletions(-) diff --git a/eqc_test/egol_eqc.erl b/eqc_test/egol_eqc.erl index 86b2e96..5a5233c 100644 --- a/eqc_test/egol_eqc.erl +++ b/eqc_test/egol_eqc.erl @@ -14,7 +14,7 @@ waiting_on = undefined, neighbour_count = 0, neighbour_history = [], - neighbours = [] :: [{egol_cell:cell_name(), pid()}], + neighbours = [] :: [pid()], kill_count=0, pending_query_content=[] }). @@ -51,104 +51,119 @@ prop_cell() -> ?SETUP( %%fun my_setup/0, fun() -> %% setup mocking here - %eqc_mocking:start_mocking(api_spec()), + %% eqc_mocking:start_mocking(api_spec()), + egol_sup:start_link(), ok, - fun() -> ok end + fun() -> stop() end end, - ?FORALL(Cmds, commands(?MODULE), + with_parameter(default_process, worker, %% THis is the dirty trick + ?FORALL(Cmds, commands(?MODULE), % ?IMPLIES(length(Cmds)>20, begin %% print("Cmds: ~p~n", [Cmds]), start(), + eqc_mocking:start_mocking(api_spec()), {H, S, Res} = run_commands(?MODULE,Cmds), stop(S), pretty_commands(?MODULE, Cmds, {H, S, Res}, aggregate(command_names(Cmds), Res == ok)) end)) +) %) . -my_setup() -> - start(), - fun() -> stop end. +%% my_setup() -> +%% start(), +%% fun() -> stop() end. start() -> eqc_mocking:start_mocking(api_spec()), catch egol_time:stop(), - egol_sup:start_link(), - %% await_egol_cell_sup(), + egol_time:init(), + %%egol_sup:start_link(), ok. -await_egol_cell_sup() -> - case whereis(egol_cell_sup) of - undefined -> - timer:sleep(5), - await_egol_cell_sup(); - _ -> - ok - end. stop() -> -%% print_regs(), + %% print_regs(), %catch exit(whereis(egol_sup), shutdown), catch egol_time:stop(), - eqc_mocking:stop_mocking(), + catch eqc_mocking:stop_mocking(), + stop_egol_sup(), timer:sleep(100), ok. stop_egol_sup() -> - Pid = whereis(egol_sup), - Ref = monitor(process, Pid), - exit(Pid, shutdown), - receive - {'DOWN', Ref, process, Pid, _Reason} -> - ok - after 1000 -> - error(exit_timeout) + case whereis(egol_sup) of + undefined -> + ok; + Pid -> + Ref = monitor(process, Pid), + catch exit(Pid, shutdown), + receive + {'DOWN', Ref, process, Pid, _Reason} -> + ok + after 1000 -> + print("egol_sup not going down~n"), + error(exit_timeout) + end end. - - - -print_regs() -> - try - CellSup = whereis(cell_sup), - MgrSup = whereis(mgr_sup), - print("CellSup: ~p - MgrSup: ~p~n", [CellSup, MgrSup]) - catch - _:_ -> - print("unable to print regs~n") - end. stop(S) -> eqc_mocking:stop_mocking(), - case egol_cell_mgr:lookup(S#state.id) of - undefined -> - ok; - Pid -> - egol_cell:kill(S#state.id), - await_death(Pid) - end, -% stop_egol_sup(). -% timer:sleep(100). + catch case egol_cell_mgr:lookup(S#state.id) of + undefined -> + ok; + Pid -> + Ref = monitor(process, Pid), + CollectorPid = egol_cell:stop(S#state.id), + await_death(Pid, Ref), + print("CollectorPid: ~p~n", [CollectorPid]), + exit(CollectorPid, stop), + await_collector_death(CollectorPid), + print("collector is dead~n") + end, + timer:sleep(50), + [ catch exit(NPid, stop) || NPid <- S#state.neighbours ], + %%stop_egol_sup(), + %% timer:sleep(100), ok. -await_death(Pid) -> - case erlang:is_process_alive(Pid) of +await_death(Pid, Ref) -> + %% case erlang:is_process_alive(Pid) of + %% true -> + %% print("await death~n"), + %% timer:sleep(5), + %% await_death(Pid); + %% false -> + %% ok + %% end. + receive + {'DOWN', Ref, process, Pid, _Reason} -> + print("await death DONE~n") + end. + +await_collector_death(Pid) when is_pid(Pid) -> + case is_process_alive(Pid) of true -> timer:sleep(5), - await_death(Pid); + await_collector_death(Pid); false -> ok - end. + end; +await_collector_death(_) -> + true. + %%weight(_S, kill) -> 0; -weight(#state{id=undefined}, Cmd) when Cmd /= cell -> 0; +%% weight(#state{id=undefined}, Cmd) when Cmd /= cell -> 0; %% weight(#state{id=undefined}, cell) -> 1; -%% weight(#state{}, query_content) -> 1; +%% weight(#state{}, query_content) -> 1; %% weight(_, query_future) -> 1; +%% weight(_, step) -> 1; %% weight(_, _) -> 0; %% @@ -169,26 +184,39 @@ cell(XY, Dim, Content) -> {ok, _Pid} = egol_cell_sup:start_cell(XY, Dim, Content), print("cell STARTED (~p/~p)~n", [XY, Dim]), Neighbours = start_neighbours(XY, Dim), - print("cell neighbours: ~p~n", [Neighbours]), - Neighbours, - ok. +% monitor_dummies(Neighbours), + %% print("cell neighbours: ~p~n", [Neighbours]), + Neighbours. start_neighbours(XY, Dim) -> Nids = egol_util:neighbours(XY, Dim), [ start_neighbour(Nid) || Nid <- Nids ]. start_neighbour(Nid) -> - Pid = spawn_link( fun dummy_neigbour_loop/0 ), - egol_time:set(Nid, 0), + Pid = spawn( fun dummy_neigbour_loop/0 ), egol_cell_mgr:reg(Nid, Pid), + print("start_neighbour(~p): ~p~n", [Nid, Pid]), %%{Nid, Pid}, - ok. + Pid. dummy_neigbour_loop() -> receive _ -> dummy_neigbour_loop() end. +%% monitor_dummies(Ns) -> +%% [ monitor(process, N) || N <- Ns ], +%% spawn ( fun() -> monitor_loop() end). + +%% monitor_loop() -> +%% receive +%% Msg -> +%% print("monitor_loop got ~p~n", [Msg]), +%% monitor_loop() +%% end. + + + cell_args(_S) -> ?LET({CellId, Dim}, cell_id_and_dim(), @@ -197,13 +225,13 @@ cell_args(_S) -> cell_pre(S) -> S#state.id == undefined. -cell_post(_S, [_, _, _], Neighbours) -> - print("cell_post Neighbours: ~p~n", [Neighbours]), +cell_post(_S, [_, _, _], _Neighbours) -> +%% print("cell_post Neighbours: ~p~n", [Neighbours]), true. %% is_pid(Pid) and erlang:is_process_alive(Pid). -cell_next(S, _Res, [CellId, Dim, Content]) -> - S#state{id=CellId, dim=Dim, content=Content}. +cell_next(S, Res, [CellId, Dim, Content]) -> + S#state{id=CellId, dim=Dim, content=Content, neighbours=Res}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -248,8 +276,7 @@ step_callouts_for_time(Time, _Neighbours, XY) -> step_next(S, _Res, _Args) -> %print("step_next time:~p~n", [S#state.time]), - S#state{waiting_on = egol_util:neighbours_at(S#state.time, - egol_util:neighbours(S#state.id, S#state.dim)), + S#state{waiting_on = egol_util:neighbours(S#state.id, S#state.dim), steps=S#state.steps+1}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -274,10 +301,6 @@ query_response(Id, Resp) -> print("query_response(~p, ~p)~n", [Id, Resp]), send_query_response(Id, Resp). -query_response_args(#state{id=undefined}) -> - [undefined, undefined]; -query_response_args(#state{waiting_on=undefined}) -> - [undefined, undefined]; query_response_args(S) -> [S#state.id, neighbour_response(S)]. @@ -291,18 +314,23 @@ neighbour_response(S) -> Resp end). -query_response_pre(_S, [undefined, undefined]) -> false; -query_response_pre(#state{waiting_on=undefined}, _ ) -> false; -query_response_pre(#state{waiting_on=W}, _ ) when length(W) =< 1 -> false; +query_response_pre(#state{waiting_on=undefined}) -> false; +query_response_pre(#state{waiting_on=W}) when length (W) =< 1 -> false; +query_response_pre(_S) -> true. + + +%% query_response_pre(_S, [undefined, undefined]) -> false; +%% query_response_pre(#state{waiting_on=undefined}, _ ) -> false; +%% query_response_pre(#state{waiting_on=W}, _ ) when length(W) =< 1 -> false; query_response_pre(S, [Id, {{Neighbour, Time}, _}] ) -> Id /= undefined andalso S#state.waiting_on /= [] andalso - lists:member({Neighbour, Time}, S#state.waiting_on) andalso + lists:member(Neighbour, S#state.waiting_on) andalso Time == S#state.time. -query_response_next(S, _Res, [_, {{Neighbour, Time}, Content}=Resp]) -> +query_response_next(S, _Res, [_, {{Neighbour, _Time}, Content}=Resp]) -> NC = S#state.neighbour_count + Content, - Wait = lists:delete({Neighbour, Time}, S#state.waiting_on), + Wait = lists:delete(Neighbour, S#state.waiting_on), S#state{waiting_on = Wait, neighbour_count = NC, neighbour_history=S#state.neighbour_history ++ [Resp]}. @@ -326,22 +354,16 @@ await_time_change(Id, AwaitTime) -> end. -last_query_response_args(#state{id=undefined}) -> - [undefined]; -last_query_response_args(#state{waiting_on=undefined}) -> - [undefined]; -last_query_response_args(#state{waiting_on=W}) when length(W) /= 1 -> - [undefined]; last_query_response_args(S) -> - [{hd(S#state.waiting_on), content()}, {S#state.id, S#state.time+1}]. + [{{hd(S#state.waiting_on), S#state.time}, content()}, {S#state.id, S#state.time+1}]. cell_content(#state{id=Id, time=Time, content=Content}) -> {{Id, Time}, Content}. -last_query_response_pre(_, [undefined]) -> false; -last_query_response_pre(S, [_Resp, {AwaitId, _AwaitTime}]) -> - length(S#state.waiting_on)==1 andalso - S#state.id == AwaitId. +last_query_response_pre(#state{waiting_on=[_]}) -> true; +last_query_response_pre(_S) -> false. + + last_query_response_callouts(#state{pending_query_content=[]}, _) -> ?EMPTY; @@ -430,7 +452,7 @@ kill(Id, _Dim, 0, _, PendingQueryContent) -> egol_cell:kill(Id), await_new_cell_pid(OldPid, Id), %print("started ~p~n", [NewPid]), - resend_pending_query_content(Id, PendingQueryContent), + %resend_pending_query_content(Id, PendingQueryContent), ok catch E:M -> @@ -442,7 +464,7 @@ kill(Id, _Dim, EndTime, NeighbourHistory, PendingQueryContent) -> OldPid = egol_cell_mgr:lookup(Id), egol_cell:kill(Id), await_new_cell_pid(OldPid, Id), - resend_pending_query_content(Id, PendingQueryContent), +% resend_pending_query_content(Id, PendingQueryContent), %%print("kill new_pid:~p~n", [Pid]), _ = egol_cell:collecting_status(Id), %%print("new collector: ~p~n", [Collector]), @@ -521,15 +543,30 @@ kill_callouts(_S, [Id, Dim, EndTime, _NH, _]) -> %% waiting_on has to be undefined. kill_next(S, _Pid, [_Id, _Dim, _EndTime, _NH, _]) -> S#state{waiting_on=undefined, -% pending_query_content = [], + pending_query_content = [], neighbour_count=0, kill_count=S#state.kill_count+1}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -kill_neighbour(Nid) -> +kill_neighbour(Nid, Id) -> Pid = egol_cell_mgr:lookup(Nid), + print("kill_neighbour(~p,~p) : ~p~n", [Nid, Id, Pid]), + ensure_death(Pid), + print("kill_neighbour(~p) ~p done~n", [Nid, Pid]), + NewPid = start_neighbour(Nid), + timer:sleep(200), + Status = egol_cell:collecting_status(Id), + print("kill_neighbour status: ~p~n", [Status]), + timer:sleep(200), + NewPid. + +ensure_death(Pid) -> + Ref = monitor(process, Pid), exit(Pid, stop), - start_neighbour(Nid). + receive + {'DOWN', Ref, process, Pid, _Reason} -> ok + end. + %% kill_neighbour_args(#state{waiting_on=undefined}) -> %% [undefined]; @@ -540,22 +577,22 @@ kill_neighbour_args(S) -> %% io:format("kill_neighbours_args: ~p in ~p~n", [Nids, S#state.neighbours]), %% ?LET(Nid, oneof(Nids), %% lists:keyfind(Nid, 1, S#state.neighbours)). - [neighbour(S)]. + [neighbour(S), S#state.id]. kill_neighbour_pre(#state{waiting_on=undefined}) -> false; -kill_neighbour_pre(_S) -> true. +kill_neighbour_pre(#state{waiting_on=W}) -> W /= []. -kill_neighbour_callouts(S, [{Nid, _Pid}]) -> - Nids = [ N || {N, _T} <- S#state.waiting_on ], - case lists:member(Nid, Nids) of +kill_neighbour_callouts(S, [Nid, _Id]) -> +% Nids = [ N || {N, _T} <- S#state.waiting_on ], + case lists:member(Nid, S#state.waiting_on) of true -> ?CALLOUT(egol_protocol, query_content, [?WILDCARD, S#state.time, S#state.id], ok); false -> ?EMPTY end. -%% kill_neighbour_next(S, _Res, _Args) -> -%% S. +kill_neighbour_next(S, NewPid, _Args) -> + S#state{neighbours = [NewPid|S#state.neighbours]}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/src/egol_cell.erl b/src/egol_cell.erl index 5d20f6d..d4be64e 100644 --- a/src/egol_cell.erl +++ b/src/egol_cell.erl @@ -85,7 +85,10 @@ where(XY) -> %% end. kill(XY) -> - cast(XY, stop). + call(XY, kill). + +stop(XY) -> + call(XY, stop). set(Cell, Content) -> cast(Cell, {set, Content}). @@ -199,6 +202,8 @@ handle_cast({query_content, Time, From}, State) -> end; handle_cast(stop, State) -> {stop, normal, State}; +handle_cast(kill, State) -> + {stop, kill, State}; handle_cast(Resp, State) -> case is_collector_running(State) of true -> @@ -226,7 +231,24 @@ handle_call(collecting_status, From, State) -> {noreply, State}; false -> {reply, undefined, State} - end. + end; +handle_call(stop, _From, State) -> + case is_pid(State#state.collector) of + true -> + State#state.collector ! stop; + false -> + ok + end, + {stop, normal, State#state.collector, State}; +handle_call(kill, _From, State) -> + case is_pid(State#state.collector) of + true -> + State#state.collector ! stop; + false -> + ok + end, + {stop, kill, State#state.collector, State}. + @@ -268,8 +290,22 @@ handle_info({Collector, {next_content, NextContent}}, terminate(_Reason, State) -> egol_time:clear(State#state.xy), +% stop_collector(State#state.collector), ok. +%% stop_collector(Collector) when is_pid(Collector) -> +%% case is_process_alive(Collector) of +%% true -> +%% io:format("killing collector ~p~n", [Collector]), +%% exit(Collector, normal), +%% timer:sleep(5), +%% stop_collector(Collector); +%% false -> +%% ok +%% end; +%% stop_collector(_) -> +%% ok. + code_change(_OldVsn, State, _Extra) -> {ok, State}. @@ -290,11 +326,11 @@ start_collector(#state{time=T, neighbours=Neighbours, collector_init(Time, Neighbours, Cell, XY, Content) -> NeighbourRefs = monitor_neighbours(Neighbours), query_neighbours(XY, Time, Neighbours), - collector_loop(egol_util:neighbours_at(Time, Neighbours), NeighbourRefs, 0, Cell, Content). + collector_loop(egol_util:neighbours_at(Time, Neighbours), NeighbourRefs, 0, Cell, XY, Content). -collector_loop([], _, NeighbourCount, Cell, Content) -> +collector_loop([], _, NeighbourCount, Cell, _ID, Content) -> Cell ! {self(), {next_content, egol_util:next_content(Content, NeighbourCount)}}; -collector_loop(WaitingOn, NeighbourRefs, NeighbourCount, Cell, Content) -> +collector_loop(WaitingOn, NeighbourRefs, NeighbourCount, Cell, Id, Content) -> receive {cell_content, {{{_,_}=XY,_}=XYatT, NeighbourContent}} -> %% io:format("collector ~p got cell_content ~p - ~p~n", @@ -305,33 +341,59 @@ collector_loop(WaitingOn, NeighbourRefs, NeighbourCount, Cell, Content) -> collector_loop(lists:delete(XYatT, WaitingOn), NewNeighbourRefs, NeighbourCount + NeighbourContent, - Cell, Content); + Cell, Id, Content); false %% ignore messages we are not waiting for -> %% io:format("collector got wrong message ~p~n", %% [Msg]), - collector_loop(WaitingOn, NeighbourRefs, NeighbourCount, Cell, Content) + collector_loop(WaitingOn, NeighbourRefs, NeighbourCount, Cell, Id,Content) end; {status, From} -> gen_server:reply(From, {WaitingOn, NeighbourCount}), - collector_loop(WaitingOn, NeighbourRefs, NeighbourCount, Cell, Content); + collector_loop(WaitingOn, NeighbourRefs, NeighbourCount, Cell, Id, Content); {'DOWN', Ref, process, Pid, Info} -> case lists:keytake(Ref, 1, NeighbourRefs) of false -> - io:format("collector got down for unknown process ~p:~p~n", [Pid, Info]), - collector_loop(WaitingOn, NeighbourRefs, NeighbourCount, Cell, Content); + io:format("collector got down for unmonitored process ~p:~p~n", [Pid, Info]), + collector_loop(WaitingOn, NeighbourRefs, NeighbourCount, Cell, Id, Content); {value, {Ref, NeighbourId}, RestNeighbourRefs} -> - NewRef = monitor_neighbour(NeighbourId), - egol_protocol:query_content(NeighbourId, T, Id), - collector_loop(WaitingOn, [{NewRef, NeighbourId}|RestNeighbourRefs], - NeighbourCount, Cell, Content) + %% NewRef = monitor_neighbour(NeighbourId), + %% {_,Time} = hd(WaitingOn), + %% io:format("collector loop doing a query_content(~p, ~p, ~p)~n", + %% [NeighbourId, Time, Id]), + %% egol_protocol:query_content(NeighbourId, Time, Id), + %% collector_loop(WaitingOn, [{NewRef, NeighbourId}|RestNeighbourRefs], + %% NeighbourCount, Cell, Id, Content) + Self = self(), + spawn(fun() -> monitor_neighbour_loop(NeighbourId, Pid, Self) end), + io:format("collector_loop spawned monitor_neighbour_loop(~p)~n", [NeighbourId]), + collector_loop(WaitingOn, RestNeighbourRefs, NeighbourCount, Cell, Id, Content) end; + {new_neighbour, {N, Pid}} -> + Ref = monitor(process, Pid), + io:format("collector_loop now monitoring new neighbour process for ~p(~p)~n", + [N, Pid]), + {_,Time} = hd(WaitingOn), + egol_protocol:query_content(N, Time, Id), + collector_loop(WaitingOn, [{Ref, N}|NeighbourRefs], NeighbourCount, Cell, Id, Content); + stop -> + exit(normal); Garbage -> io:format("collector got GARBAGE ~p~n", [Garbage]), exit(collector_got_garbage) end. +monitor_neighbour_loop(N, Pid, Collector) -> + case egol_cell_mgr:lookup(N) of + NewPid when is_pid(NewPid) andalso NewPid /= Pid -> + Collector ! {new_neighbour, {N, NewPid}}; + _ -> + timer:sleep(3), + monitor_neighbour_loop(N, Pid, Collector) + end. + + process_future(XY, Time, Content, Future) -> {Ready, NewFuture} = lists:partition( fun({_FromXY,T}) -> diff --git a/src/egol_cell_sup.erl b/src/egol_cell_sup.erl index 5408547..d17b0a0 100644 --- a/src/egol_cell_sup.erl +++ b/src/egol_cell_sup.erl @@ -34,7 +34,7 @@ init([]) -> SupFlags = {RestartStrategy, MaxRestarts, MaxSecondsBetweenRestarts}, - Restart = permanent, + Restart = transient, Shutdown = 2000, Type = worker, diff --git a/src/egol_sup.erl b/src/egol_sup.erl index d693f83..c391d63 100644 --- a/src/egol_sup.erl +++ b/src/egol_sup.erl @@ -3,6 +3,7 @@ -behaviour(supervisor). -export([start_link/0]). +-export([stop/0]). -export([init/1]). @@ -13,6 +14,10 @@ start_link() -> egol_time:init(), supervisor:start_link({local, ?SERVER}, ?MODULE, []). +stop() -> + catch exit(whereis(egol_sup), shutdown), + egol_time:stop(). + init([]) -> RestartStrategy = one_for_all, From 74f774f642e997c5bb4e4e733e01e3c4df0d5311 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Wed, 4 Feb 2015 15:34:33 +0100 Subject: [PATCH 53/70] egol_cell:stop/0 added and monitor loop for neighbours --- src/egol_cell.erl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/egol_cell.erl b/src/egol_cell.erl index d4be64e..6abbbbf 100644 --- a/src/egol_cell.erl +++ b/src/egol_cell.erl @@ -7,6 +7,7 @@ -export([start/3, start_link/3, kill/1, + stop/1, where/1]). %% gen_server callbacks @@ -337,7 +338,8 @@ collector_loop(WaitingOn, NeighbourRefs, NeighbourCount, Cell, Id, Content) -> %% [self(), XYatT, NeighbourContent]), case lists:member(XYatT, WaitingOn) of true -> - NewNeighbourRefs = lists:keydelete(XY, 2, NeighbourRefs), + {value, {Ref, _}, NewNeighbourRefs} = lists:keytake(XY, 2, NeighbourRefs), + demonitor(Ref, [flush]), collector_loop(lists:delete(XYatT, WaitingOn), NewNeighbourRefs, NeighbourCount + NeighbourContent, @@ -365,7 +367,7 @@ collector_loop(WaitingOn, NeighbourRefs, NeighbourCount, Cell, Id, Content) -> %% collector_loop(WaitingOn, [{NewRef, NeighbourId}|RestNeighbourRefs], %% NeighbourCount, Cell, Id, Content) Self = self(), - spawn(fun() -> monitor_neighbour_loop(NeighbourId, Pid, Self) end), + spawn_link(fun() -> monitor_neighbour_loop(NeighbourId, Pid, Self) end), io:format("collector_loop spawned monitor_neighbour_loop(~p)~n", [NeighbourId]), collector_loop(WaitingOn, RestNeighbourRefs, NeighbourCount, Cell, Id, Content) end; @@ -387,6 +389,8 @@ collector_loop(WaitingOn, NeighbourRefs, NeighbourCount, Cell, Id, Content) -> monitor_neighbour_loop(N, Pid, Collector) -> case egol_cell_mgr:lookup(N) of NewPid when is_pid(NewPid) andalso NewPid /= Pid -> + io:format("monitor_neighbour_loop(~p, ~p, ~p) now found ~p~n", + [N, Pid, Collector, NewPid]), Collector ! {new_neighbour, {N, NewPid}}; _ -> timer:sleep(3), From a54bcfb7031310ca9c7286c6170ef7ddfd390848 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Wed, 4 Feb 2015 15:34:50 +0100 Subject: [PATCH 54/70] use spawn_link on pacer process --- src/egol_cell_mgr.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/egol_cell_mgr.erl b/src/egol_cell_mgr.erl index 01c77c2..1db52eb 100644 --- a/src/egol_cell_mgr.erl +++ b/src/egol_cell_mgr.erl @@ -69,7 +69,7 @@ handle_cast({reg, XY, Pid}, NewRef = erlang:monitor(process, Pid), ets:insert(mgr_xy, {XY, Pid}), NextState = State#state{monitors=gb_trees:enter(NewRef, XY, Monitors)}, - spawn ( fun () -> pacer(Pid, State#state.mode) end ), + spawn_link ( fun () -> pacer(Pid, State#state.mode) end ), {noreply, NextState}; handle_cast({set_mode, Mode}, State) -> {noreply, State#state{mode=Mode}}. From 828000ffca7d5bdd581672796f6caed9db24045f Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Wed, 4 Feb 2015 15:35:14 +0100 Subject: [PATCH 55/70] Remove stop function from egol_sup --- src/egol_sup.erl | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/egol_sup.erl b/src/egol_sup.erl index c391d63..d693f83 100644 --- a/src/egol_sup.erl +++ b/src/egol_sup.erl @@ -3,7 +3,6 @@ -behaviour(supervisor). -export([start_link/0]). --export([stop/0]). -export([init/1]). @@ -14,10 +13,6 @@ start_link() -> egol_time:init(), supervisor:start_link({local, ?SERVER}, ?MODULE, []). -stop() -> - catch exit(whereis(egol_sup), shutdown), - egol_time:stop(). - init([]) -> RestartStrategy = one_for_all, From eebf304394a5bbf60bc2a11275a6baf347715bfa Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Wed, 4 Feb 2015 15:36:24 +0100 Subject: [PATCH 56/70] Start egol application - almost working All failing test cases work when running eqc:check/1, so the problem is about spilling over from one test case to another. --- eqc_test/egol_eqc.erl | 44 ++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/eqc_test/egol_eqc.erl b/eqc_test/egol_eqc.erl index 5a5233c..fe2bd45 100644 --- a/eqc_test/egol_eqc.erl +++ b/eqc_test/egol_eqc.erl @@ -56,20 +56,19 @@ fun() -> ok, fun() -> stop() end end, - with_parameter(default_process, worker, %% THis is the dirty trick +% with_parameter(default_process, worker, %% THis is the dirty trick ?FORALL(Cmds, commands(?MODULE), % ?IMPLIES(length(Cmds)>20, begin %% print("Cmds: ~p~n", [Cmds]), start(), - eqc_mocking:start_mocking(api_spec()), {H, S, Res} = run_commands(?MODULE,Cmds), stop(S), pretty_commands(?MODULE, Cmds, {H, S, Res}, aggregate(command_names(Cmds), Res == ok)) end)) -) +%) %) . @@ -79,9 +78,10 @@ fun() -> start() -> + application:start(egol), eqc_mocking:start_mocking(api_spec()), - catch egol_time:stop(), - egol_time:init(), + %% catch egol_time:stop(), + %% egol_time:init(), %%egol_sup:start_link(), ok. @@ -89,10 +89,11 @@ start() -> stop() -> %% print_regs(), %catch exit(whereis(egol_sup), shutdown), - catch egol_time:stop(), +%% catch egol_time:stop(), catch eqc_mocking:stop_mocking(), - stop_egol_sup(), +%% stop_egol_sup(), timer:sleep(100), + application:stop(egol), ok. stop_egol_sup() -> @@ -114,22 +115,23 @@ stop_egol_sup() -> stop(S) -> eqc_mocking:stop_mocking(), - catch case egol_cell_mgr:lookup(S#state.id) of - undefined -> - ok; - Pid -> - Ref = monitor(process, Pid), - CollectorPid = egol_cell:stop(S#state.id), - await_death(Pid, Ref), - print("CollectorPid: ~p~n", [CollectorPid]), - exit(CollectorPid, stop), - await_collector_death(CollectorPid), - print("collector is dead~n") - end, - timer:sleep(50), + %% catch case egol_cell_mgr:lookup(S#state.id) of + %% undefined -> + %% ok; + %% Pid -> + %% Ref = monitor(process, Pid), + %% CollectorPid = egol_cell:stop(S#state.id), + %% await_death(Pid, Ref), + %% print("CollectorPid: ~p~n", [CollectorPid]), + %% exit(CollectorPid, stop), + %% await_collector_death(CollectorPid), + %% print("collector is dead~n") + %% end, + timer:sleep(100), + application:stop(egol), [ catch exit(NPid, stop) || NPid <- S#state.neighbours ], %%stop_egol_sup(), - %% timer:sleep(100), + timer:sleep(100), ok. await_death(Pid, Ref) -> From 46e0c8d76031363c3cb6334c6bfedc73c545247f Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Wed, 4 Feb 2015 21:59:54 +0100 Subject: [PATCH 57/70] Correct variable name for egol_cell_mgr in egol_sup --- src/egol_sup.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/egol_sup.erl b/src/egol_sup.erl index d693f83..a9480d1 100644 --- a/src/egol_sup.erl +++ b/src/egol_sup.erl @@ -28,8 +28,8 @@ init([]) -> %% Restart, Shutdown, worker, [egol_time]}, CellSup= {egol_cell_sup, {egol_cell_sup, start_link, []}, Restart, Shutdown, supervisor, [egol_cell_sup]}, - MgrSup = {egol_cell_mgr, {egol_cell_mgr, start_link, []}, + CellMgr = {egol_cell_mgr, {egol_cell_mgr, start_link, []}, Restart, Shutdown, worker, [egol_cell_mgr]}, - {ok, {SupFlags, [MgrSup, CellSup]}}. + {ok, {SupFlags, [CellMgr, CellSup]}}. From 55cfd20718ba64e40208159dcc2d8892aae6b92e Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Wed, 4 Feb 2015 22:01:55 +0100 Subject: [PATCH 58/70] Trying one_for_one supervisor for cells --- src/egol_cell_sup.erl | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/egol_cell_sup.erl b/src/egol_cell_sup.erl index d17b0a0..63f5f2f 100644 --- a/src/egol_cell_sup.erl +++ b/src/egol_cell_sup.erl @@ -19,7 +19,7 @@ start_link() -> supervisor:start_link({local, ?SERVER}, ?MODULE, []). start_cell(XY, Dim, InitialContent) -> - {ok, Pid} = Res = supervisor:start_child(?SERVER, [XY, Dim, InitialContent]), + {ok, _Pid} = Res = supervisor:start_child(?SERVER, child_spec(XY, Dim, InitialContent)), %% egol_cell_mgr:reg(XY, Pid), Res. @@ -28,20 +28,24 @@ start_cell(XY, Dim, InitialContent) -> %%%=================================================================== init([]) -> - RestartStrategy = simple_one_for_one, + RestartStrategy = one_for_one, MaxRestarts = 1000, MaxSecondsBetweenRestarts = 1, SupFlags = {RestartStrategy, MaxRestarts, MaxSecondsBetweenRestarts}, - Restart = transient, - Shutdown = 2000, - Type = worker, + %% Restart = transient, + %% Shutdown = 10000, + %% Type = worker, - AChild = {egol_cell, {egol_cell, start_link, []}, - Restart, Shutdown, Type, [egol_cell]}, + %% AChild = {egol_cell, {egol_cell, start_link, []}, + %% Restart, Shutdown, Type, [egol_cell]}, - {ok, {SupFlags, [AChild]}}. + {ok, {SupFlags, []}}. +child_spec(XY, Dim, InitialContent) -> + {{egol_cell, XY}, {egol_cell, start_link, [XY, Dim, InitialContent]}, + transient, infinity, worker, [egol_cell]}. + From 3ba9c829f88e5fa0c1a4a873259d67ab91055ba9 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Mon, 9 Feb 2015 11:42:05 +0100 Subject: [PATCH 59/70] Proper app file. --- src/egol.app.src | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/egol.app.src b/src/egol.app.src index 760fe75..7984d1c 100644 --- a/src/egol.app.src +++ b/src/egol.app.src @@ -1,7 +1,10 @@ {application,egol, [{description,"EGOL - Erlang Game Of Life"}, {vsn,"0.0.1"}, - {modules,[egol]}, - {registered,[]}, + {modules,[egol_app, egol_sup, + egol_cell_sup, egol_cell, + egol_cell_mgr]}, + {registered,[egol_sup, egol_cell_sup, egol_cell_mgr]}, {applications,[kernel, stdlib, lager]}, + {mod, {egol_app, []}}, {start_phases,[]}]}. From b7549f67850511f546f88fdf868f0b9116635577 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Mon, 9 Feb 2015 11:42:31 +0100 Subject: [PATCH 60/70] Remove manual start of egol_sup --- src/egol.erl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/egol.erl b/src/egol.erl index 228e73f..56327c6 100644 --- a/src/egol.erl +++ b/src/egol.erl @@ -279,7 +279,6 @@ test(6) -> InitialCells = [{2,3}, {2,5}, {3,2}, {3,3}, {3,4}, {4,1}, {4,5}], - egol_sup:start_link(), start(7,6, InitialCells); test(simple) -> egol_sup:start_link(), From a24ca6499f8d033b1f7f420c3a3c5fbdea359540 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Mon, 9 Feb 2015 11:43:23 +0100 Subject: [PATCH 61/70] Test works fine. Ready to do clean-up. --- eqc_test/egol_eqc.erl | 105 +++++++++++------------------------------- 1 file changed, 26 insertions(+), 79 deletions(-) diff --git a/eqc_test/egol_eqc.erl b/eqc_test/egol_eqc.erl index fe2bd45..f52d0b1 100644 --- a/eqc_test/egol_eqc.erl +++ b/eqc_test/egol_eqc.erl @@ -52,7 +52,6 @@ prop_cell() -> fun() -> %% setup mocking here %% eqc_mocking:start_mocking(api_spec()), - egol_sup:start_link(), ok, fun() -> stop() end end, @@ -78,86 +77,28 @@ fun() -> start() -> - application:start(egol), + {ok, _} = application:ensure_all_started(egol), + print("egol_cell_sup: ~p~n", [sys:get_state(egol_cell_sup)]), eqc_mocking:start_mocking(api_spec()), - %% catch egol_time:stop(), - %% egol_time:init(), - %%egol_sup:start_link(), ok. stop() -> - %% print_regs(), - %catch exit(whereis(egol_sup), shutdown), -%% catch egol_time:stop(), catch eqc_mocking:stop_mocking(), -%% stop_egol_sup(), - timer:sleep(100), - application:stop(egol), +% timer:sleep(100), ok. -stop_egol_sup() -> - case whereis(egol_sup) of - undefined -> - ok; - Pid -> - Ref = monitor(process, Pid), - catch exit(Pid, shutdown), - receive - {'DOWN', Ref, process, Pid, _Reason} -> - ok - after 1000 -> - print("egol_sup not going down~n"), - error(exit_timeout) - end - end. stop(S) -> + print("stop(S)~n"), eqc_mocking:stop_mocking(), - %% catch case egol_cell_mgr:lookup(S#state.id) of - %% undefined -> - %% ok; - %% Pid -> - %% Ref = monitor(process, Pid), - %% CollectorPid = egol_cell:stop(S#state.id), - %% await_death(Pid, Ref), - %% print("CollectorPid: ~p~n", [CollectorPid]), - %% exit(CollectorPid, stop), - %% await_collector_death(CollectorPid), - %% print("collector is dead~n") - %% end, - timer:sleep(100), application:stop(egol), [ catch exit(NPid, stop) || NPid <- S#state.neighbours ], - %%stop_egol_sup(), - timer:sleep(100), +% timer:sleep(100), ok. -await_death(Pid, Ref) -> - %% case erlang:is_process_alive(Pid) of - %% true -> - %% print("await death~n"), - %% timer:sleep(5), - %% await_death(Pid); - %% false -> - %% ok - %% end. - receive - {'DOWN', Ref, process, Pid, _Reason} -> - print("await death DONE~n") - end. -await_collector_death(Pid) when is_pid(Pid) -> - case is_process_alive(Pid) of - true -> - timer:sleep(5), - await_collector_death(Pid); - false -> - ok - end; -await_collector_death(_) -> - true. %%weight(_S, kill) -> 0; @@ -184,8 +125,11 @@ command_precondition_common(S, Cmd) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% cell(XY, Dim, Content) -> {ok, _Pid} = egol_cell_sup:start_cell(XY, Dim, Content), + print("process_count:~p~n", [erlang:system_info(process_count)]), print("cell STARTED (~p/~p)~n", [XY, Dim]), + print("process_count:~p~n", [erlang:system_info(process_count)]), Neighbours = start_neighbours(XY, Dim), + print("process_count:~p~n", [erlang:system_info(process_count)]), % monitor_dummies(Neighbours), %% print("cell neighbours: ~p~n", [Neighbours]), Neighbours. @@ -227,10 +171,10 @@ cell_args(_S) -> cell_pre(S) -> S#state.id == undefined. -cell_post(_S, [_, _, _], _Neighbours) -> +cell_post(_S, [Id, _, _], _Neighbours) -> %% print("cell_post Neighbours: ~p~n", [Neighbours]), - true. -%% is_pid(Pid) and erlang:is_process_alive(Pid). + Pid = egol_cell_mgr:lookup(Id), + is_pid(Pid) and erlang:is_process_alive(Pid). cell_next(S, Res, [CellId, Dim, Content]) -> S#state{id=CellId, dim=Dim, content=Content, neighbours=Res}. @@ -239,7 +183,9 @@ cell_next(S, Res, [CellId, Dim, Content]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% step(Id, _Dim, _Time) -> print("step for ~p~n", [Id]), + print("process_count:~p~n", [erlang:system_info(process_count)]), egol_cell:step(Id), + print("process_count:~p~n", [erlang:system_info(process_count)]), await_collecting_status(Id). @@ -301,6 +247,7 @@ get_post(S, [_Id, _Time], Res) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% query_response(Id, Resp) -> print("query_response(~p, ~p)~n", [Id, Resp]), + print("process_count:~p~n", [erlang:system_info(process_count)]), send_query_response(Id, Resp). query_response_args(S) -> @@ -448,21 +395,21 @@ query_future_next(S, _Res, [_Id, NeighbourId, Time]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% kill(Id, _Dim, 0, _, PendingQueryContent) -> print("kill(~p, _, 0, _, ~p)~n", [Id, PendingQueryContent]), - try + %% try OldPid = egol_cell_mgr:lookup(Id), %print("killing ~p~n", [OldPid]), egol_cell:kill(Id), await_new_cell_pid(OldPid, Id), %print("started ~p~n", [NewPid]), %resend_pending_query_content(Id, PendingQueryContent), - ok - catch - E:M -> - print("kill exception ~p:~p ~p~n", [E, M, erlang:get_stacktrace()]) - end; + ok; + %% catch + %% E:M -> + %% print("kill exception ~p:~p ~p~n", [E, M, erlang:get_stacktrace()]) + %% end; kill(Id, _Dim, EndTime, NeighbourHistory, PendingQueryContent) -> print("kill(~p, _, ~p, ~p, ~p)~n", [Id, EndTime, NeighbourHistory, PendingQueryContent]), - try + %% try OldPid = egol_cell_mgr:lookup(Id), egol_cell:kill(Id), await_new_cell_pid(OldPid, Id), @@ -472,11 +419,11 @@ kill(Id, _Dim, EndTime, NeighbourHistory, PendingQueryContent) -> %%print("new collector: ~p~n", [Collector]), drive_collector(Id, 0, EndTime, NeighbourHistory), timer:sleep(50), - ok - catch - E:M -> - print("kill exception ~p:~p ~p~n", [E, M, erlang:get_stacktrace()]) - end. + ok. + %% catch + %% E:M -> + %% print("kill exception ~p:~p ~p~n", [E, M, erlang:get_stacktrace()]) + %% end. resend_pending_query_content(Id, PendingQueryContent) -> %print("started ~p~n", [NewPid]), From 758e078fea8644d7918909cfb0ebdb3d95dafcdb Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Mon, 9 Feb 2015 12:04:37 +0100 Subject: [PATCH 62/70] Clean-up: commented out io:format TO-DO: turn them into lager calls. --- src/egol_cell.erl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/egol_cell.erl b/src/egol_cell.erl index 6abbbbf..4a4c974 100644 --- a/src/egol_cell.erl +++ b/src/egol_cell.erl @@ -356,7 +356,7 @@ collector_loop(WaitingOn, NeighbourRefs, NeighbourCount, Cell, Id, Content) -> {'DOWN', Ref, process, Pid, Info} -> case lists:keytake(Ref, 1, NeighbourRefs) of false -> - io:format("collector got down for unmonitored process ~p:~p~n", [Pid, Info]), + %% io:format("collector got down for unmonitored process ~p:~p~n", [Pid, Info]), collector_loop(WaitingOn, NeighbourRefs, NeighbourCount, Cell, Id, Content); {value, {Ref, NeighbourId}, RestNeighbourRefs} -> %% NewRef = monitor_neighbour(NeighbourId), @@ -368,13 +368,13 @@ collector_loop(WaitingOn, NeighbourRefs, NeighbourCount, Cell, Id, Content) -> %% NeighbourCount, Cell, Id, Content) Self = self(), spawn_link(fun() -> monitor_neighbour_loop(NeighbourId, Pid, Self) end), - io:format("collector_loop spawned monitor_neighbour_loop(~p)~n", [NeighbourId]), + %% io:format("collector_loop spawned monitor_neighbour_loop(~p)~n", [NeighbourId]), collector_loop(WaitingOn, RestNeighbourRefs, NeighbourCount, Cell, Id, Content) end; {new_neighbour, {N, Pid}} -> Ref = monitor(process, Pid), - io:format("collector_loop now monitoring new neighbour process for ~p(~p)~n", - [N, Pid]), + %% io:format("collector_loop now monitoring new neighbour process for ~p(~p)~n", + %% [N, Pid]), {_,Time} = hd(WaitingOn), egol_protocol:query_content(N, Time, Id), collector_loop(WaitingOn, [{Ref, N}|NeighbourRefs], NeighbourCount, Cell, Id, Content); @@ -389,8 +389,8 @@ collector_loop(WaitingOn, NeighbourRefs, NeighbourCount, Cell, Id, Content) -> monitor_neighbour_loop(N, Pid, Collector) -> case egol_cell_mgr:lookup(N) of NewPid when is_pid(NewPid) andalso NewPid /= Pid -> - io:format("monitor_neighbour_loop(~p, ~p, ~p) now found ~p~n", - [N, Pid, Collector, NewPid]), + %% io:format("monitor_neighbour_loop(~p, ~p, ~p) now found ~p~n", + %% [N, Pid, Collector, NewPid]), Collector ! {new_neighbour, {N, NewPid}}; _ -> timer:sleep(3), From bc32ae9fcae53c69aa5c3953ee55d408f2d25bdd Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Mon, 9 Feb 2015 12:05:06 +0100 Subject: [PATCH 63/70] Clean-up eqc. removed all printouts and dead code --- eqc_test/egol_eqc.erl | 213 +++++++----------------------------------- 1 file changed, 32 insertions(+), 181 deletions(-) diff --git a/eqc_test/egol_eqc.erl b/eqc_test/egol_eqc.erl index f52d0b1..4cb090a 100644 --- a/eqc_test/egol_eqc.erl +++ b/eqc_test/egol_eqc.erl @@ -30,14 +30,6 @@ api_spec() -> #api_fun{ name = query_response, arity = 2} ] } -%% , -%% #api_module{ -%% name = egol_time, -%% functions = [ #api_fun{ name = max, arity = 0}, -%% #api_fun{ name = set, arity = 2}, -%% #api_fun{ name = clear, arity = 1}, -%% #api_fun{ name = init, arity = 0}] -%% } ]}. @@ -48,68 +40,34 @@ initial_state() -> %% @doc Default generated property -spec prop_cell() -> eqc:property(). prop_cell() -> - ?SETUP( %%fun my_setup/0, -fun() -> - %% setup mocking here - %% eqc_mocking:start_mocking(api_spec()), - ok, - fun() -> stop() end - end, -% with_parameter(default_process, worker, %% THis is the dirty trick + ?SETUP( fun my_setup/0, ?FORALL(Cmds, commands(?MODULE), - % ?IMPLIES(length(Cmds)>20, begin -%% print("Cmds: ~p~n", [Cmds]), start(), {H, S, Res} = run_commands(?MODULE,Cmds), stop(S), pretty_commands(?MODULE, Cmds, {H, S, Res}, aggregate(command_names(Cmds), Res == ok)) - end)) -%) -%) -. - -%% my_setup() -> -%% start(), -%% fun() -> stop() end. + end)). + +my_setup() -> + eqc_mocking:start_mocking(api_spec()), + fun eqc_mocking:stop_mocking/0. start() -> {ok, _} = application:ensure_all_started(egol), - print("egol_cell_sup: ~p~n", [sys:get_state(egol_cell_sup)]), - eqc_mocking:start_mocking(api_spec()), - ok. - - -stop() -> - catch eqc_mocking:stop_mocking(), -% timer:sleep(100), ok. - - stop(S) -> - print("stop(S)~n"), - eqc_mocking:stop_mocking(), application:stop(egol), [ catch exit(NPid, stop) || NPid <- S#state.neighbours ], -% timer:sleep(100), ok. -%%weight(_S, kill) -> 0; -%% weight(#state{id=undefined}, Cmd) when Cmd /= cell -> 0; -%% weight(#state{id=undefined}, cell) -> 1; -%% weight(#state{}, query_content) -> 1; -%% weight(_, query_future) -> 1; -%% weight(_, step) -> 1; -%% weight(_, _) -> 0; - -%% weight(#state{waiting_on=W}, step) when is_list(W) -> 0; weight(_S, get) -> 1; weight(_S, cell) -> 1; @@ -125,13 +83,7 @@ command_precondition_common(S, Cmd) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% cell(XY, Dim, Content) -> {ok, _Pid} = egol_cell_sup:start_cell(XY, Dim, Content), - print("process_count:~p~n", [erlang:system_info(process_count)]), - print("cell STARTED (~p/~p)~n", [XY, Dim]), - print("process_count:~p~n", [erlang:system_info(process_count)]), Neighbours = start_neighbours(XY, Dim), - print("process_count:~p~n", [erlang:system_info(process_count)]), -% monitor_dummies(Neighbours), - %% print("cell neighbours: ~p~n", [Neighbours]), Neighbours. start_neighbours(XY, Dim) -> @@ -141,8 +93,6 @@ start_neighbours(XY, Dim) -> start_neighbour(Nid) -> Pid = spawn( fun dummy_neigbour_loop/0 ), egol_cell_mgr:reg(Nid, Pid), - print("start_neighbour(~p): ~p~n", [Nid, Pid]), - %%{Nid, Pid}, Pid. dummy_neigbour_loop() -> @@ -150,16 +100,6 @@ dummy_neigbour_loop() -> _ -> dummy_neigbour_loop() end. -%% monitor_dummies(Ns) -> -%% [ monitor(process, N) || N <- Ns ], -%% spawn ( fun() -> monitor_loop() end). - -%% monitor_loop() -> -%% receive -%% Msg -> -%% print("monitor_loop got ~p~n", [Msg]), -%% monitor_loop() -%% end. @@ -172,7 +112,6 @@ cell_pre(S) -> S#state.id == undefined. cell_post(_S, [Id, _, _], _Neighbours) -> -%% print("cell_post Neighbours: ~p~n", [Neighbours]), Pid = egol_cell_mgr:lookup(Id), is_pid(Pid) and erlang:is_process_alive(Pid). @@ -182,10 +121,7 @@ cell_next(S, Res, [CellId, Dim, Content]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% step(Id, _Dim, _Time) -> - print("step for ~p~n", [Id]), - print("process_count:~p~n", [erlang:system_info(process_count)]), egol_cell:step(Id), - print("process_count:~p~n", [erlang:system_info(process_count)]), await_collecting_status(Id). @@ -204,8 +140,6 @@ step_args(S) -> [S#state.id, S#state.dim, S#state.time]. step_pre(S, [Id, Dim, Time]) -> - %print("step_pre cell:~p, pid:~p waiting_on:~p~n", -% [S#state.cell, Pid, S#state.waiting_on]), S#state.id /= undefined andalso S#state.id == Id andalso S#state.dim == Dim andalso @@ -217,21 +151,15 @@ step_callouts(_S, [XY, Dim, Time ]) -> step_callouts_for_time(Time, _Neighbours, XY) -> Queries = lists:duplicate(8, ?CALLOUT(egol_protocol, query_content, [?WILDCARD, Time, XY], ok)), -%% Monitorings = lists:duplicate(8, ?CALLOUT(egol_protocol, lookup, [?WILDCARD], self())), - ?PAR(Queries).%++Monitorings). - %% ?PAR([?CALLOUT(egol_protocol, query_content, [N, Time, XY], ok) - %% || N <- Neighbours]). + ?PAR(Queries). step_next(S, _Res, _Args) -> - %print("step_next time:~p~n", [S#state.time]), S#state{waiting_on = egol_util:neighbours(S#state.id, S#state.dim), steps=S#state.steps+1}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% get(Id, Time) -> - Res = egol_cell:get(Id, Time), -% %%print("get works~n"), - Res. + egol_cell:get(Id, Time). get_args(S) -> [S#state.id, S#state.time]. @@ -246,15 +174,12 @@ get_post(S, [_Id, _Time], Res) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% query_response(Id, Resp) -> - print("query_response(~p, ~p)~n", [Id, Resp]), - print("process_count:~p~n", [erlang:system_info(process_count)]), send_query_response(Id, Resp). query_response_args(S) -> [S#state.id, neighbour_response(S)]. neighbour_response(S) -> - %%print("neighbour_response: ~p~n", [S]), ?LET(N, neighbour(S), case lists:keyfind({N, S#state.time}, 1, S#state.neighbour_history) of false -> @@ -268,9 +193,6 @@ query_response_pre(#state{waiting_on=W}) when length (W) =< 1 -> false; query_response_pre(_S) -> true. -%% query_response_pre(_S, [undefined, undefined]) -> false; -%% query_response_pre(#state{waiting_on=undefined}, _ ) -> false; -%% query_response_pre(#state{waiting_on=W}, _ ) when length(W) =< 1 -> false; query_response_pre(S, [Id, {{Neighbour, Time}, _}] ) -> Id /= undefined andalso S#state.waiting_on /= [] andalso @@ -285,9 +207,7 @@ query_response_next(S, _Res, [_, {{Neighbour, _Time}, Content}=Resp]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% last_query_response(Resp, {Id, AwaitTime}) -> - print("last_query_response(~p, {~p, ~p})~n", [Resp, Id, AwaitTime]), send_query_response(Id, Resp), - print("calling await_time_change(~p, ~p)~n", [Id, AwaitTime]), await_time_change(Id, AwaitTime). await_time_change(Id, AwaitTime) -> @@ -295,9 +215,6 @@ await_time_change(Id, AwaitTime) -> AwaitTime -> ok; _ -> - %% print("await_time_change id ~p~n", [Id]), - %% print("await_time_change pid ~p/~p~n", - %% [egol_cell_mgr:lookup(Id), egol_cell:collector(Id)]), timer:sleep(5), await_time_change(Id, AwaitTime) end. @@ -325,7 +242,6 @@ last_query_response_callouts(S, [{_, Content}, _]) -> last_query_response_next(S, _Res, [{_, Content}=Resp, _AwaitIdTime]) -> NC = S#state.neighbour_count + Content, - print("last_query_response_next NC=~p~n", [NC]), S#state{pending_query_content=[], content=next_content(S#state.content, NC), time=S#state.time+1, @@ -337,7 +253,6 @@ last_query_response_next(S, _Res, [{_, Content}=Resp, _AwaitIdTime]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% query_content(Id, Time) -> - print("query_content(~p,~p)~n", [Id, Time]), send_query_content(Id, Time), timer:sleep(50), ok. @@ -366,23 +281,18 @@ cell_content_msg(S) -> cell_content_msg(S#state.id, S#state.time, S#state.content). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -query_future(Id, RequestId, Time) -> - print("query_future(~p, ~p, ~p)~n", [Id, RequestId, Time]), +query_future(Id, _RequestId, Time) -> send_query_content(Id, Time), ok. query_future_args(#state{id=undefined}) -> [undefined]; query_future_args(S) -> -% print("query_future_args called~n"), -% Pending = [ N || {N,_} <- S#state.pending_query_content ], -% print("pending: ~p~n", [Pending]), ?LET(Neighbour, oneof(egol_util:neighbours(S#state.id, S#state.dim)), [S#state.id, Neighbour, S#state.time+1]). query_future_pre(_S, [undefined]) -> false; query_future_pre(S, [Id, _NeighbourId, Time]) -> - %%print("query_future_pre ~p - ~p at ~p for ~p~n", [Id, Time, S#state.time, S#state.id]), S#state.id /= undefined andalso S#state.id == Id andalso (S#state.time+1) == Time. @@ -393,44 +303,19 @@ query_future_next(S, _Res, [_Id, NeighbourId, Time]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -kill(Id, _Dim, 0, _, PendingQueryContent) -> - print("kill(~p, _, 0, _, ~p)~n", [Id, PendingQueryContent]), - %% try - OldPid = egol_cell_mgr:lookup(Id), - %print("killing ~p~n", [OldPid]), - egol_cell:kill(Id), - await_new_cell_pid(OldPid, Id), - %print("started ~p~n", [NewPid]), - %resend_pending_query_content(Id, PendingQueryContent), - ok; - %% catch - %% E:M -> - %% print("kill exception ~p:~p ~p~n", [E, M, erlang:get_stacktrace()]) - %% end; -kill(Id, _Dim, EndTime, NeighbourHistory, PendingQueryContent) -> - print("kill(~p, _, ~p, ~p, ~p)~n", [Id, EndTime, NeighbourHistory, PendingQueryContent]), - %% try - OldPid = egol_cell_mgr:lookup(Id), - egol_cell:kill(Id), - await_new_cell_pid(OldPid, Id), -% resend_pending_query_content(Id, PendingQueryContent), - %%print("kill new_pid:~p~n", [Pid]), - _ = egol_cell:collecting_status(Id), - %%print("new collector: ~p~n", [Collector]), - drive_collector(Id, 0, EndTime, NeighbourHistory), - timer:sleep(50), - ok. - %% catch - %% E:M -> - %% print("kill exception ~p:~p ~p~n", [E, M, erlang:get_stacktrace()]) - %% end. - -resend_pending_query_content(Id, PendingQueryContent) -> - %print("started ~p~n", [NewPid]), - lists:foreach(fun({_NeighbourId, Time}) -> - send_query_content(Id, Time) - end, - PendingQueryContent). +kill(Id, _Dim, 0, _) -> + OldPid = egol_cell_mgr:lookup(Id), + egol_cell:kill(Id), + await_new_cell_pid(OldPid, Id), + ok; +kill(Id, _Dim, EndTime, NeighbourHistory) -> + OldPid = egol_cell_mgr:lookup(Id), + egol_cell:kill(Id), + await_new_cell_pid(OldPid, Id), + _ = egol_cell:collecting_status(Id), + drive_collector(Id, 0, EndTime, NeighbourHistory), + timer:sleep(50), + ok. await_new_cell_pid(OldPid, Id) -> case egol_cell_mgr:lookup(Id) of @@ -446,13 +331,10 @@ await_new_cell_pid(OldPid, Id) -> %% go forward until the cell has progressed to max time drive_collector(_Id, T, T, _NeighbourHistory) -> ok; -%%send_all_query_responses(Collector, NeighbourHistory); drive_collector(Id, T, EndTime, NeighbourHistory) -> - print("drive_collector ~p~n", [NeighbourHistory]), ResponsesForTime = [ R || {{_,RTime},_}=R <- NeighbourHistory, RTime == T ], - print("drive_collector ~p~n", [ResponsesForTime]), send_all_query_responses(Id, ResponsesForTime), await_time_change(Id, T), _ = egol_cell:collecting_status(Id), @@ -464,33 +346,25 @@ send_all_query_responses(Id, NeighbourHistory) -> end, NeighbourHistory). -kill_args(#state{id=Id, dim=Dim, time=Time, - neighbour_history=NH, pending_query_content=PendingQueryContent}) -> - [Id, Dim, Time, NH, PendingQueryContent]. +kill_args(#state{id=Id, dim=Dim, time=Time, neighbour_history=NH}) -> + [Id, Dim, Time, NH]. -kill_pre(S, [Id, _Dim, EndTime, _, _]) -> +kill_pre(S, [Id, _Dim, EndTime, _]) -> S#state.id /= undefined andalso S#state.id == Id andalso S#state.time == EndTime andalso S#state.kill_count<1. -kill_callouts(_S, [_Id, _Dim, 0, _, _]) -> +kill_callouts(_S, [_Id, _Dim, 0, _]) -> ?EMPTY; -kill_callouts(_S, [Id, Dim, EndTime, _NH, _]) -> +kill_callouts(_S, [Id, Dim, EndTime, _NH]) -> Neighbours = egol_util:neighbours(Id, Dim), StepCallouts = ?SEQ( [ step_callouts_for_time(T, Neighbours, Id) || T <- lists:seq(0, EndTime-1) ] ), - %% PendingQueryResponses = ?PAR( [?CALLOUT( egol_protocol, query_response, - %% [?WILDCARD, cell_content_msg(S#state.id, NeighbourTime, ?WILDCARD)], ok) - %% || {_NeighbourId, NeighbourTime} <- S#state.pending_query_content ]), - %% ?PAR(StepCallouts, PendingQueryResponses). StepCallouts. -%% after a kill the cell will run up to the EndTime and then await a step before it -%% can continue since it has not received a step command to drive it forward, so -%% waiting_on has to be undefined. -kill_next(S, _Pid, [_Id, _Dim, _EndTime, _NH, _]) -> +kill_next(S, _Pid, [_Id, _Dim, _EndTime, _NH]) -> S#state{waiting_on=undefined, pending_query_content = [], neighbour_count=0, @@ -499,13 +373,10 @@ kill_next(S, _Pid, [_Id, _Dim, _EndTime, _NH, _]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% kill_neighbour(Nid, Id) -> Pid = egol_cell_mgr:lookup(Nid), - print("kill_neighbour(~p,~p) : ~p~n", [Nid, Id, Pid]), ensure_death(Pid), - print("kill_neighbour(~p) ~p done~n", [Nid, Pid]), NewPid = start_neighbour(Nid), timer:sleep(200), - Status = egol_cell:collecting_status(Id), - print("kill_neighbour status: ~p~n", [Status]), + _Status = egol_cell:collecting_status(Id), timer:sleep(200), NewPid. @@ -517,22 +388,13 @@ ensure_death(Pid) -> end. -%% kill_neighbour_args(#state{waiting_on=undefined}) -> -%% [undefined]; -%% kill_neighbour_args(#state{neighbours=1}) -> -%% [undefined]; kill_neighbour_args(S) -> - %% Nids = [ Nid || {Nid, _T} <- S#state.waiting_on ], - %% io:format("kill_neighbours_args: ~p in ~p~n", [Nids, S#state.neighbours]), - %% ?LET(Nid, oneof(Nids), - %% lists:keyfind(Nid, 1, S#state.neighbours)). [neighbour(S), S#state.id]. kill_neighbour_pre(#state{waiting_on=undefined}) -> false; kill_neighbour_pre(#state{waiting_on=W}) -> W /= []. kill_neighbour_callouts(S, [Nid, _Id]) -> -% Nids = [ N || {N, _T} <- S#state.waiting_on ], case lists:member(Nid, S#state.waiting_on) of true -> ?CALLOUT(egol_protocol, query_content, [?WILDCARD, S#state.time, S#state.id], ok); @@ -573,26 +435,15 @@ next_content(0,3) -> 1; next_content(C, N) when N==2; N==3 -> C; next_content(_,_) -> 0. -send_query_response(Id, {{XY,T}, C}=Resp) -> +send_query_response(Id, {{XY,T}, C}) -> try -%% Pid ! cell_content_msg(XY,T,C) egol_cell:query_response(Id, cell_content_msg(XY, T, C)) catch - _:_ -> - print("send_query_response (~p, ~p) failed~n", - [Id, Resp]) + Error:Reason -> + exit({send_query_response, Error, Reason, Id, {XY,T}, C}) end. send_query_content(Id, Time) -> - %% Pid = egol_cell_mgr:lookup(Id), - %% Pid ! {query_content, Time, self()}. egol_cell:query_content(Id, Time, na). -print(Str) -> - io:format(Str). -% ok. - -print(Str, Args) -> - io:format(Str, Args). - %ok. From a007bc27dbb6861479c3b45d21f39a6b8f46211b Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Mon, 9 Feb 2015 16:16:34 +0100 Subject: [PATCH 64/70] Clean-up - spurious last_query_response bug --- eqc_test/egol_eqc.erl | 99 ++++++++++++++++++++++++++----------------- 1 file changed, 61 insertions(+), 38 deletions(-) diff --git a/eqc_test/egol_eqc.erl b/eqc_test/egol_eqc.erl index 4cb090a..1256dcf 100644 --- a/eqc_test/egol_eqc.erl +++ b/eqc_test/egol_eqc.erl @@ -10,7 +10,6 @@ dim, content, time=0, - steps=0, waiting_on = undefined, neighbour_count = 0, neighbour_history = [], @@ -63,6 +62,7 @@ start() -> stop(S) -> application:stop(egol), [ catch exit(NPid, stop) || NPid <- S#state.neighbours ], + timer:sleep(10), ok. @@ -100,10 +100,6 @@ dummy_neigbour_loop() -> _ -> dummy_neigbour_loop() end. - - - - cell_args(_S) -> ?LET({CellId, Dim}, cell_id_and_dim(), [CellId, Dim, content()]). @@ -113,7 +109,7 @@ cell_pre(S) -> cell_post(_S, [Id, _, _], _Neighbours) -> Pid = egol_cell_mgr:lookup(Id), - is_pid(Pid) and erlang:is_process_alive(Pid). + is_pid(Pid) andalso erlang:is_process_alive(Pid). cell_next(S, Res, [CellId, Dim, Content]) -> S#state{id=CellId, dim=Dim, content=Content, neighbours=Res}. @@ -122,40 +118,47 @@ cell_next(S, Res, [CellId, Dim, Content]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% step(Id, _Dim, _Time) -> egol_cell:step(Id), - await_collecting_status(Id). + await_collecting_status(Id, 10). -await_collecting_status(Id) -> +await_collecting_status(_Id, 0) -> + step_error; +await_collecting_status(Id, N) -> case egol_cell:collecting_status(Id) of undefined -> timer:sleep(5), - await_collecting_status(Id); + await_collecting_status(Id, N-1); _ -> - ok + step_success end. - - step_args(S) -> [S#state.id, S#state.dim, S#state.time]. -step_pre(S, [Id, Dim, Time]) -> +step_pre(S) -> S#state.id /= undefined andalso - S#state.id == Id andalso - S#state.dim == Dim andalso - S#state.time == Time andalso S#state.waiting_on == undefined. +step_pre(S, [XY, Dim, Time]) -> + S#state.id == XY andalso + S#state.dim == Dim andalso + S#state.time == Time. + step_callouts(_S, [XY, Dim, Time ]) -> step_callouts_for_time(Time, egol_util:neighbours(XY, Dim), XY). -step_callouts_for_time(Time, _Neighbours, XY) -> +step_callouts_for_time(Time, Neighbours, XY) -> Queries = lists:duplicate(8, ?CALLOUT(egol_protocol, query_content, [?WILDCARD, Time, XY], ok)), + %% Queries = [ ?CALLOUT(egol_protocol, query_content, [N, Time, XY], ok) + %% || N <- Neighbours ], + %% io:format("Queries ~p~n", [Queries]), ?PAR(Queries). +step_post(_S, _Args, Res) -> + Res == step_success. + step_next(S, _Res, _Args) -> - S#state{waiting_on = egol_util:neighbours(S#state.id, S#state.dim), - steps=S#state.steps+1}. + S#state{waiting_on = egol_util:neighbours(S#state.id, S#state.dim)}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% get(Id, Time) -> @@ -164,8 +167,10 @@ get(Id, Time) -> get_args(S) -> [S#state.id, S#state.time]. +get_pre(S) -> + S#state.id /= undefined. + get_pre(S, [Id, Time]) -> - S#state.id /= undefined andalso S#state.id == Id andalso S#state.time == Time. @@ -188,10 +193,9 @@ neighbour_response(S) -> Resp end). -query_response_pre(#state{waiting_on=undefined}) -> false; -query_response_pre(#state{waiting_on=W}) when length (W) =< 1 -> false; -query_response_pre(_S) -> true. - +query_response_pre(S) -> + is_list(S#state.waiting_on) andalso + length(S#state.waiting_on) > 1. query_response_pre(S, [Id, {{Neighbour, Time}, _}] ) -> Id /= undefined andalso @@ -208,15 +212,17 @@ query_response_next(S, _Res, [_, {{Neighbour, _Time}, Content}=Resp]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% last_query_response(Resp, {Id, AwaitTime}) -> send_query_response(Id, Resp), - await_time_change(Id, AwaitTime). + await_time_change(Id, AwaitTime, 50). -await_time_change(Id, AwaitTime) -> +await_time_change(_, _, 0) -> + error; +await_time_change(Id, AwaitTime, N) -> case egol_cell:time(Id) of AwaitTime -> ok; _ -> timer:sleep(5), - await_time_change(Id, AwaitTime) + await_time_change(Id, AwaitTime, N-1) end. @@ -228,9 +234,11 @@ cell_content(#state{id=Id, time=Time, content=Content}) -> last_query_response_pre(#state{waiting_on=[_]}) -> true; last_query_response_pre(_S) -> false. - - - + +last_query_respones_pre(S, [_, {Id, AwaitTime}]) -> + S#state.id == Id andalso + (S#state.time + 1) == AwaitTime. + last_query_response_callouts(#state{pending_query_content=[]}, _) -> ?EMPTY; last_query_response_callouts(S, [{_, Content}, _]) -> @@ -240,6 +248,9 @@ last_query_response_callouts(S, [{_, Content}, _]) -> [?WILDCARD, cell_content_msg(S#state.id, NeighbourTime, NextContent)], ok) || {_NeighbourId, NeighbourTime} <- S#state.pending_query_content]). +last_query_response_post(_S, _Args, Res) -> + Res == ok. + last_query_response_next(S, _Res, [{_, Content}=Resp, _AwaitIdTime]) -> NC = S#state.neighbour_count + Content, S#state{pending_query_content=[], @@ -254,14 +265,16 @@ last_query_response_next(S, _Res, [{_, Content}=Resp, _AwaitIdTime]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% query_content(Id, Time) -> send_query_content(Id, Time), - timer:sleep(50), + _ = egol_cell:collecting_status(Id), ok. query_content_args(S) -> [S#state.id, S#state.time]. +query_content_pre(S) -> + S#state.id /= undefined. + query_content_pre(S, [Id, Time]) -> - S#state.id /= undefined andalso S#state.id == Id andalso S#state.time == Time. @@ -312,6 +325,7 @@ kill(Id, _Dim, EndTime, NeighbourHistory) -> OldPid = egol_cell_mgr:lookup(Id), egol_cell:kill(Id), await_new_cell_pid(OldPid, Id), + timer:sleep(50), _ = egol_cell:collecting_status(Id), drive_collector(Id, 0, EndTime, NeighbourHistory), timer:sleep(50), @@ -336,9 +350,14 @@ drive_collector(Id, T, EndTime, NeighbourHistory) -> || {{_,RTime},_}=R <- NeighbourHistory, RTime == T ], send_all_query_responses(Id, ResponsesForTime), - await_time_change(Id, T), - _ = egol_cell:collecting_status(Id), - drive_collector(Id, T+1, EndTime, NeighbourHistory). + case await_time_change(Id, T, 20) of + ok -> + timer:sleep(50), + _ = egol_cell:collecting_status(Id), + drive_collector(Id, T+1, EndTime, NeighbourHistory); + error -> + error + end. send_all_query_responses(Id, NeighbourHistory) -> lists:foreach(fun(CellContent) -> @@ -349,12 +368,13 @@ send_all_query_responses(Id, NeighbourHistory) -> kill_args(#state{id=Id, dim=Dim, time=Time, neighbour_history=NH}) -> [Id, Dim, Time, NH]. -kill_pre(S, [Id, _Dim, EndTime, _]) -> +kill_pre(S) -> S#state.id /= undefined andalso - S#state.id == Id andalso - S#state.time == EndTime andalso S#state.kill_count<1. +kill_pre(S, [Id, _Dim, EndTime, _]) -> + S#state.id == Id andalso + S#state.time == EndTime. kill_callouts(_S, [_Id, _Dim, 0, _]) -> ?EMPTY; @@ -364,6 +384,9 @@ kill_callouts(_S, [Id, Dim, EndTime, _NH]) -> || T <- lists:seq(0, EndTime-1) ] ), StepCallouts. +kill_post(_S, _Args, Res) -> + Res == ok. + kill_next(S, _Pid, [_Id, _Dim, _EndTime, _NH]) -> S#state{waiting_on=undefined, pending_query_content = [], From 2bc092859e8763b6f299fbf74719edf8a58f88f5 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Mon, 9 Feb 2015 16:36:37 +0100 Subject: [PATCH 65/70] Fix incorrect named last_query_response_pre fun --- eqc_test/egol_eqc.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eqc_test/egol_eqc.erl b/eqc_test/egol_eqc.erl index 1256dcf..2c28bbd 100644 --- a/eqc_test/egol_eqc.erl +++ b/eqc_test/egol_eqc.erl @@ -199,7 +199,7 @@ query_response_pre(S) -> query_response_pre(S, [Id, {{Neighbour, Time}, _}] ) -> Id /= undefined andalso - S#state.waiting_on /= [] andalso + is_list(S#state.waiting_on) andalso lists:member(Neighbour, S#state.waiting_on) andalso Time == S#state.time. @@ -235,7 +235,7 @@ cell_content(#state{id=Id, time=Time, content=Content}) -> last_query_response_pre(#state{waiting_on=[_]}) -> true; last_query_response_pre(_S) -> false. -last_query_respones_pre(S, [_, {Id, AwaitTime}]) -> +last_query_response_pre(S, [_, {Id, AwaitTime}]) -> S#state.id == Id andalso (S#state.time + 1) == AwaitTime. From ac8f9c354f110a568af6811121ce58e5beed9b49 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Mon, 9 Feb 2015 17:25:32 +0100 Subject: [PATCH 66/70] Fix start when clause to be conjunction --- src/egol_cell.erl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/egol_cell.erl b/src/egol_cell.erl index 4a4c974..f3ab401 100644 --- a/src/egol_cell.erl +++ b/src/egol_cell.erl @@ -54,9 +54,9 @@ neighbours}). start({X,Y}=XY, {DimX, DimY}=Dim, InitialContent) - when X < DimX; - 0 =< X; - Y < DimY; + when X < DimX, + 0 =< X, + Y < DimY, 0 =< Y -> gen_server:start(?MODULE, #state{xy=XY, dim=Dim, content=InitialContent, @@ -64,9 +64,9 @@ start({X,Y}=XY, {DimX, DimY}=Dim, InitialContent) []). start_link({X,Y}=XY, {DimX, DimY}=Dim, InitialContent) - when X < DimX; - 0 =< X; - Y < DimY; + when X < DimX, + 0 =< X, + Y < DimY, 0 =< Y -> gen_server:start_link(?MODULE, #state{xy=XY, dim=Dim, content=InitialContent, From afa8954f5ae118ee0331d286fe3fcf8ebf1dc73c Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Tue, 17 Feb 2015 13:45:30 +0000 Subject: [PATCH 67/70] Added apps to allow percept2 to run. --- relx.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/relx.config b/relx.config index 5c5393c..d15e190 100644 --- a/relx.config +++ b/relx.config @@ -18,5 +18,5 @@ {release, {egol, "0.0.1"}, [egol, sasl, lager, - mnesia + mnesia, runtime_tools, inets ]}. From ccc2da28b062144fd2d9968a207abdd067d6041d Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Tue, 17 Feb 2015 13:47:03 +0000 Subject: [PATCH 68/70] Minor clean-up. --- src/egol.erl | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/egol.erl b/src/egol.erl index 56327c6..98fb105 100644 --- a/src/egol.erl +++ b/src/egol.erl @@ -254,9 +254,6 @@ test(2) -> test(3) -> egol_sup:start_link(), start(8,8,test(2)); -test(100) -> - egol__sup:start_link(), - start(100,100,test(2)); test(4) -> test(3), timer:sleep(50), @@ -281,13 +278,7 @@ test(6) -> {4,1}, {4,5}], start(7,6, InitialCells); test(simple) -> - egol_sup:start_link(), start(4,4,test(2)); test(N) -> - egol_sup:start_link(), start(N,N,test(2)). - - - - From e8518e24595eb6899c501012ce62229ea2760105 Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Fri, 10 Apr 2015 13:59:58 +0100 Subject: [PATCH 69/70] await_new_neighbour checked in --- src/egol_cell.erl | 6 +++--- src/egol_cell_mgr.erl | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/egol_cell.erl b/src/egol_cell.erl index f3ab401..b2b37b4 100644 --- a/src/egol_cell.erl +++ b/src/egol_cell.erl @@ -367,7 +367,7 @@ collector_loop(WaitingOn, NeighbourRefs, NeighbourCount, Cell, Id, Content) -> %% collector_loop(WaitingOn, [{NewRef, NeighbourId}|RestNeighbourRefs], %% NeighbourCount, Cell, Id, Content) Self = self(), - spawn_link(fun() -> monitor_neighbour_loop(NeighbourId, Pid, Self) end), + spawn_link(fun() -> await_new_neighbour(NeighbourId, Pid, Self) end), %% io:format("collector_loop spawned monitor_neighbour_loop(~p)~n", [NeighbourId]), collector_loop(WaitingOn, RestNeighbourRefs, NeighbourCount, Cell, Id, Content) end; @@ -386,7 +386,7 @@ collector_loop(WaitingOn, NeighbourRefs, NeighbourCount, Cell, Id, Content) -> exit(collector_got_garbage) end. -monitor_neighbour_loop(N, Pid, Collector) -> +await_new_neighbour(N, Pid, Collector) -> case egol_cell_mgr:lookup(N) of NewPid when is_pid(NewPid) andalso NewPid /= Pid -> %% io:format("monitor_neighbour_loop(~p, ~p, ~p) now found ~p~n", @@ -394,7 +394,7 @@ monitor_neighbour_loop(N, Pid, Collector) -> Collector ! {new_neighbour, {N, NewPid}}; _ -> timer:sleep(3), - monitor_neighbour_loop(N, Pid, Collector) + await_new_neighbour(N, Pid, Collector) end. diff --git a/src/egol_cell_mgr.erl b/src/egol_cell_mgr.erl index 1db52eb..020f741 100644 --- a/src/egol_cell_mgr.erl +++ b/src/egol_cell_mgr.erl @@ -69,7 +69,7 @@ handle_cast({reg, XY, Pid}, NewRef = erlang:monitor(process, Pid), ets:insert(mgr_xy, {XY, Pid}), NextState = State#state{monitors=gb_trees:enter(NewRef, XY, Monitors)}, - spawn_link ( fun () -> pacer(Pid, State#state.mode) end ), + kickoff_cell(Pid, State#state.mode), {noreply, NextState}; handle_cast({set_mode, Mode}, State) -> {noreply, State#state{mode=Mode}}. @@ -89,12 +89,12 @@ code_change(_OldVsn, State, _Extra) -> -pacer(Pid, step) -> +kickoff_cell(Pid, step) -> EndTime = egol_time:max(), egol_cell:run_until(Pid, EndTime); -pacer(Pid, {run_until, EndTime}) -> +kickoff_cell(Pid, {run_until, EndTime}) -> egol_cell:run_until(Pid, EndTime); -pacer(Pid, run) -> +kickoff_cell(Pid, run) -> egol_cell:run(Pid). From 4404895533e377282f0544dd8538adbec18bee2c Mon Sep 17 00:00:00 2001 From: Torben Hoffmann Date: Fri, 10 Apr 2015 14:12:20 +0100 Subject: [PATCH 70/70] Add egol_app to git --- src/egol_app.erl | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/egol_app.erl diff --git a/src/egol_app.erl b/src/egol_app.erl new file mode 100644 index 0000000..9e3187e --- /dev/null +++ b/src/egol_app.erl @@ -0,0 +1,12 @@ +-module(egol_app). + +-behaviour(application). + +-export([start/2, + stop/1]). + +start(_StartType, _StartArgs) -> + egol_sup:start_link(). + +stop(_) -> + ok.