Initial commit
This commit is contained in:
commit
790ecf5446
4
.formatter.exs
Normal file
4
.formatter.exs
Normal file
@ -0,0 +1,4 @@
|
||||
# Used by "mix format"
|
||||
[
|
||||
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
||||
]
|
26
.gitignore
vendored
Normal file
26
.gitignore
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
# The directory Mix will write compiled artifacts to.
|
||||
/_build/
|
||||
|
||||
# If you run "mix test --cover", coverage assets end up here.
|
||||
/cover/
|
||||
|
||||
# The directory Mix downloads your dependencies sources to.
|
||||
/deps/
|
||||
|
||||
# Where third-party dependencies like ExDoc output generated docs.
|
||||
/doc/
|
||||
|
||||
# Ignore .fetch files in case you like to edit your project deps locally.
|
||||
/.fetch
|
||||
|
||||
# If the VM crashes, it generates a dump, let's ignore it too.
|
||||
erl_crash.dump
|
||||
|
||||
# Also ignore archive artifacts (built via "mix archive.build").
|
||||
*.ez
|
||||
|
||||
# Ignore package tarball (built via "mix hex.build").
|
||||
distributed_system_coursework-*.tar
|
||||
|
||||
# Temporary files, for example, from tests.
|
||||
/tmp/
|
95
eager_reliable_broadcast.ex
Normal file
95
eager_reliable_broadcast.ex
Normal file
@ -0,0 +1,95 @@
|
||||
defmodule EagerReliableBroadcast do
|
||||
def start(name, processes) do
|
||||
pid = spawn(EagerReliableBroadcast, :init, [name, processes])
|
||||
# :global.unregister_name(name)
|
||||
case :global.re_register_name(name, pid) do
|
||||
:yes -> pid
|
||||
:no -> :error
|
||||
end
|
||||
IO.puts "registered #{name}"
|
||||
pid
|
||||
end
|
||||
|
||||
# Init event must be the first
|
||||
# one after the component is created
|
||||
def init(name, processes) do
|
||||
state = %{
|
||||
name: name,
|
||||
processes: processes,
|
||||
delivered: %{}, # Use this data structure to remember IDs of the delivered messages
|
||||
seq_no: 0 # Use this variable to remember the last sequence number used to identify a message
|
||||
}
|
||||
run(state)
|
||||
end
|
||||
|
||||
def run(state) do
|
||||
state = receive do
|
||||
# Handle the broadcast request event
|
||||
{:broadcast, m} ->
|
||||
IO.puts("#{inspect state.name}: RB-broadcast: #{inspect m}")
|
||||
# Create a unique message identifier from state.name and state.seqno.
|
||||
# Create a new data message data_msg from the given payload m
|
||||
# the message identifier.
|
||||
# Update the state as necessary
|
||||
data_msg = {:data, state.name, state.seq_no, m}
|
||||
|
||||
# Use the provided beb_broadcast function to propagate data_msg to
|
||||
|
||||
# all process
|
||||
beb_broadcast(data_msg, state.processes)
|
||||
%{state | seq_no: state.seq_no + 1 }
|
||||
|
||||
{:data, proc, seq_no, m} ->
|
||||
if not Map.has_key?(state.delivered, {proc, seq_no, m}) do
|
||||
data_msg = {:data, proc, seq_no, m}
|
||||
beb_broadcast(data_msg, state.processes)
|
||||
unicast({:deliver, proc, m}, state.name)
|
||||
%{state | delivered: Map.put(state.delivered, {proc, seq_no, m}, 1)}
|
||||
else
|
||||
val = Map.get(state.delivered, {proc, seq_no, m})
|
||||
if val < Enum.count(state.processes) do
|
||||
%{state | delivered: Map.put(state.delivered, {proc, seq_no, m}, val + 1)}
|
||||
else
|
||||
%{state | delivered: Map.delete(state.delivered, {proc, seq_no, m})}
|
||||
end
|
||||
end
|
||||
|
||||
# If <proc, seqno> was already delivered, do nothing.
|
||||
# Otherwise, update delivered, generate a deliver event for the
|
||||
# upper layer, and re-broadcast (echo) the received message.
|
||||
# In both cases, do not forget to return the state.
|
||||
|
||||
{:deliver, proc, m} ->
|
||||
# Simulate the deliver indication event
|
||||
IO.puts("#{inspect state.name}: RB-deliver: #{inspect m} from #{inspect proc}")
|
||||
state
|
||||
end
|
||||
run(state)
|
||||
end
|
||||
|
||||
|
||||
defp unicast(m, p) do
|
||||
case :global.whereis_name(p) do
|
||||
pid when is_pid(pid) -> send(pid, m)
|
||||
:undefined -> :ok
|
||||
end
|
||||
end
|
||||
|
||||
defp beb_broadcast(m, dest), do: for p <- dest, do: unicast(m, p)
|
||||
|
||||
# You can use this function to simulate a process failure.
|
||||
# name: the name of this process
|
||||
# proc_to_fail: the name of the failed process
|
||||
# fail_send_to: list of processes proc_to_fail will not be broadcasting messages to
|
||||
# Note that this list must include proc_to_fail.
|
||||
# m and dest are the same as the respective arguments of the normal
|
||||
# beb_broadcast.
|
||||
defp beb_broadcast_with_failures(name, proc_to_fail, fail_send_to, m, dest) do
|
||||
if name == proc_to_fail do
|
||||
for p <- dest, p not in fail_send_to, do: unicast(m, p)
|
||||
else
|
||||
for p <- dest, p != proc_to_fail, do: unicast(m, p)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
28
mix.exs
Normal file
28
mix.exs
Normal file
@ -0,0 +1,28 @@
|
||||
defmodule DistributedSystemCoursework.MixProject do
|
||||
use Mix.Project
|
||||
|
||||
def project do
|
||||
[
|
||||
app: :distributed_system_coursework,
|
||||
version: "0.1.0",
|
||||
elixir: "~> 1.15",
|
||||
start_permanent: Mix.env() == :prod,
|
||||
deps: deps()
|
||||
]
|
||||
end
|
||||
|
||||
# Run "mix help compile.app" to learn about applications.
|
||||
def application do
|
||||
[
|
||||
extra_applications: [:logger]
|
||||
]
|
||||
end
|
||||
|
||||
# Run "mix help deps" to learn about dependencies.
|
||||
defp deps do
|
||||
[
|
||||
# {:dep_from_hexpm, "~> 0.3.0"},
|
||||
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
|
||||
]
|
||||
end
|
||||
end
|
Reference in New Issue
Block a user