Skip to content

Commit

Permalink
whew
Browse files Browse the repository at this point in the history
  • Loading branch information
ConnorRigby committed Aug 2, 2018
1 parent 06bb56b commit bccc4a8
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 43 deletions.
2 changes: 1 addition & 1 deletion .tool-versions
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
erlang 21.0.1
erlang 21.0.4
elixir 1.7.1-otp-21
9 changes: 7 additions & 2 deletions lib/circular_list.ex
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,13 @@ defmodule CircularList do
def update_current(this, fun) do
index = get_index(this)
current_value = at(this, index)
result = fun.(current_value)
%{this | items: Map.put(this.items, index, result)}
if current_value do
result = fun.(current_value)
%{this | items: Map.put(this.items, index, result)}
else
fun.(:noop)
this
end
end

@spec rotate(t) :: t
Expand Down
175 changes: 138 additions & 37 deletions lib/csvm.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,37 @@ defmodule Csvm do
require Logger

@tick_timeout 20
@kinds_that_need_fw [:wait]
@kinds_aloud_while_locked []

defstruct [
:proc_storage,
:hyper_state,
:fw_proc,
:process_io_layer,
:hyper_io_layer,
:tick_timer
]

@opaque job_id :: CircularList.index()

def rpc_request(pid \\ __MODULE__, id \\ -1, %{} = map, fun)
def rpc_request(pid \\ __MODULE__, %{} = map, fun)
when is_function(fun) do
case GenServer.call(pid, {:lookup, id}) do
%FarmProc{} ->
rpc_request(pid, id - 1, map, fun)

nil ->
job = queue(pid, map, -1)
proc = await(pid, job)

case FarmProc.get_status(proc) do
:done ->
apply_callback(fun, [:ok])

:crashed ->
apply_callback(fun, [{:error, FarmProc.get_crash_reason(proc)}])
end
%AST{} = ast = AST.decode(map)
label = ast.args[:label] || raise(ArgumentError)
job = queue(pid, map, -1) |> IO.inspect(label: "JOBID")
proc = await(pid, job)

case FarmProc.get_status(proc) do
:done ->
results = ast(:rpc_ok, %{label: label}, [])
apply_callback(fun, [results])

:crashed ->
message = FarmProc.get_crash_reason(proc)
explanation = ast(:explanation, %{message: message})
results = ast(:rpc_error, %{label: label}, [explanation])
apply_callback(fun, [results])
end
end

Expand Down Expand Up @@ -72,13 +75,17 @@ defmodule Csvm do
%AST{} = ast ->
%Heap{} = heap = AST.slice(ast)
%Address{} = page = addr(page_id)
GenServer.call(pid, {:queue, heap, page})
case GenServer.call(pid, {:queue, heap, page}) do
{:error, :busy} -> queue(pid, map, page_id)
job -> job
end
end
end

@spec await(GenServer.server(), job_id) :: FarmProc.t()
defp await(pid, job_id) do
case GenServer.call(pid, {:lookup, job_id}) do
{:error, :busy} -> await(pid, job_id)
%FarmProc{} = proc ->
case FarmProc.get_status(proc) do
status when status in [:ok, :waiting] ->
Expand All @@ -94,11 +101,6 @@ defmodule Csvm do
end
end

@spec force_cycle(GenServer.server()) :: :ok
def force_cycle(pid \\ __MODULE__) do
GenServer.call(pid, :force_cycle)
end

@doc """
Start a CSVM monitor.
Expand Down Expand Up @@ -128,45 +130,63 @@ defmodule Csvm do
}}
end

def handle_call(:emergency_lock, _from, state) do
def handle_call(:emergency_lock, _from, %Csvm{} = state) do
apply_callback(state.hyper_io_layer, [:emergency_lock])
{:reply, :ok, %{state | hyper_state: :emergency_lock}}
end

def handle_call(:emergency_unlock, _from, state) do
def handle_call(:emergency_unlock, _from, %Csvm{} = state) do
apply_callback(state.hyper_io_layer, [:emergency_unlock])
{:reply, :ok, %{state | hyper_state: :emergency_unlock}}
end

def handle_call(_, _from, {:busy, state}) do
{:reply, {:error, :busy}, {:busy, state}}
end

def handle_call(
{:queue, %Heap{} = heap, %Address{} = page},
_from,
%Csvm{} = state
) do
%FarmProc{} = new_proc = FarmProc.new(state.process_io_layer, page, heap)
:ok = ProcStorage.insert(state.proc_storage, new_proc)
{:reply, ProcStorage.current_index(state.proc_storage), state}
index = ProcStorage.insert(state.proc_storage, new_proc)
{:reply, index, state}
end


def handle_call({:lookup, id}, _from, %Csvm{} = state) do
cleanup = fn(proc, state) ->
ProcStorage.delete(state.proc_storage, id)
if proc.ref == state.fw_proc do
IO.puts "cleaning fw_proc: #{inspect proc.ref}"
{:reply, proc, %{state | fw_proc: nil}}
else
{:reply, proc, state}
end
end

case ProcStorage.lookup(state.proc_storage, id) do
%FarmProc{status: :crashed} = proc ->
ProcStorage.delete(state.proc_storage, id)
{:reply, proc, state}
cleanup.(proc, state)

%FarmProc{status: :done} = proc ->
cleanup.(proc, state)

reply ->
{:reply, reply, state}
end
end

def handle_call(:force_cycle, _, %Csvm{} = state) do
_ = stop_tick(state.tick_timer)
new_timer = start_tick(self(), 0)
{:reply, :ok, %Csvm{state | tick_timer: new_timer}}
def handle_info(:tock, %Csvm{} = state) do
# IO.puts "[info] enter"
pid = self()
ProcStorage.update(state.proc_storage, &do_step(&1, pid, state))
{:noreply, {:busy, state}}
end

def handle_info(:tock, state) do
ProcStorage.update(state.proc_storage, &do_step/1)
def handle_info(%Csvm{} = state, {:busy, _old}) do
# IO.puts "[info] exit"
# make sure to update the timer _AFTER_ we tick.
new_timer = start_tick(self())
{:noreply, %Csvm{state | tick_timer: new_timer}}
Expand All @@ -175,12 +195,93 @@ defmodule Csvm do
defp start_tick(pid, timeout \\ @tick_timeout),
do: Process.send_after(pid, :tock, timeout)

defp stop_tick(timer), do: Process.cancel_timer(timer)
def do_step(:noop, pid, state) do
send(pid, state)
end

@doc false
def do_step(%FarmProc{status: :crashed} = farm_proc), do: farm_proc
def do_step(%FarmProc{status: :crashed} = farm_proc, pid, state) do
IO.puts "crash tick."
send(pid, state)
farm_proc
end

def do_step(%FarmProc{status: :done} = farm_proc, pid, state) do
IO.puts "done tick."
send(pid, state)
farm_proc
end

use Bitwise, only: [bsl: 2]

def do_step(%FarmProc{} = farm_proc, pid, %{fw_proc: nil} = state) do
pc_ptr = FarmProc.get_pc_ptr(farm_proc)
kind = FarmProc.get_kind(farm_proc, pc_ptr)
b0 = (kind in @kinds_aloud_while_locked) |> bit()
b1 = (kind in @kinds_that_need_fw) |> bit()
b2 = (true) |> bit()
b3 = (state.hyper_state == :emergency_lock) |> bit()
bits = bsl(b0, 3) + bsl(b1, 2) + bsl(b2, 1) + b3
if should_step(bits) do
if bool(b1) do
IO.puts "flagging fw_proc: #{inspect farm_proc.ref}"
send(pid, %{state | fw_proc: farm_proc.ref})
else
send(pid, state)
end
actual_step(farm_proc)
else
IO.inspect(bits, base: :binary, label: "not stepping")
IO.inspect(Integer.digits(bits, 2), label: "not stepping")
IO.inspect(bits, label: "not stepping")
send(pid, state)
farm_proc
end
end

def do_step(%FarmProc{} = farm_proc, pid, state) do
pc_ptr = FarmProc.get_pc_ptr(farm_proc)
kind = FarmProc.get_kind(farm_proc, pc_ptr)
b0 = (kind in @kinds_aloud_while_locked) |> bit()
b1 = (kind in @kinds_that_need_fw) |> bit()
b2 = (farm_proc.ref == state.fw_proc) |> bit()
b3 = (state.hyper_state == :emergency_lock) |> bit()
bits = bsl(b0, 3) + bsl(b1, 2) + bsl(b2, 1) + b3
if should_step(bits) do
send(pid, state)
actual_step(farm_proc)
else
IO.inspect(bits, base: :binary, label: "not stepping")
IO.inspect(Integer.digits(bits, 2), label: "not stepping")
IO.inspect(bits, label: "not stepping")
send(pid, state)
farm_proc
end
end

def do_step(%FarmProc{} = farm_proc) do
defp should_step(0b0000), do: true
defp should_step(0b0001), do: false
defp should_step(0b0010), do: true
defp should_step(0b0011), do: false
defp should_step(0b0100), do: false
defp should_step(0b0101), do: false
defp should_step(0b0110), do: true
defp should_step(0b0111), do: false
defp should_step(0b1000), do: true
defp should_step(0b1001), do: true
defp should_step(0b1010), do: true
defp should_step(0b1011), do: true
defp should_step(0b1100), do: false
defp should_step(0b1101), do: false
defp should_step(0b1110), do: true
defp should_step(0b1111), do: true

defp bit(true), do: 1
defp bit(false), do: 0
defp bool(1), do: true
defp bool(0), do: false

defp actual_step(farm_proc) do
try do
FarmProc.step(farm_proc)
rescue
Expand Down
6 changes: 5 additions & 1 deletion lib/csvm/farm_proc.ex
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ defmodule Csvm.FarmProc do
io_result: nil,
crash_reason: nil,
status: :ok,
heap: %{}
heap: %{},
ref: nil

@typedoc "Program counter"
@type heap_address :: Address.t()
Expand All @@ -32,6 +33,7 @@ defmodule Csvm.FarmProc do
@type status_enum :: :ok | :done | :crashed | :waiting

@type t :: %FarmProc{
ref: reference(),
crash_reason: nil | String.t(),
heap: %{Address.t() => Heap.t()},
io_latch: nil | pid,
Expand All @@ -46,6 +48,7 @@ defmodule Csvm.FarmProc do

@typedoc false
@type new :: %Csvm.FarmProc{
ref: reference(),
crash_reason: nil,
heap: %{Address.t() => Heap.t()},
io_latch: nil,
Expand All @@ -64,6 +67,7 @@ defmodule Csvm.FarmProc do
pc = Pointer.new(page, addr(1))

%FarmProc{
ref: make_ref(),
status: :ok,
zero_page: page,
pc: pc,
Expand Down
4 changes: 4 additions & 0 deletions lib/csvm/instruction_set.ex
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,10 @@ defmodule Csvm.InstructionSet do
end
end

def rpc_request(%FarmProc{} = farm_proc) do
sequence(farm_proc)
end

@doc "Execute a sequeence."
@spec sequence(FarmProc.t()) :: FarmProc.t()
def sequence(%FarmProc{} = farm_proc) do
Expand Down
10 changes: 8 additions & 2 deletions lib/csvm/proc_storage.ex
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,15 @@ defmodule Csvm.ProcStorage do
agent
end

@spec insert(proc_storage, FarmProc.t()) :: :ok
@spec insert(proc_storage, FarmProc.t()) :: index
def insert(this, %FarmProc{} = farm_proc) do
Agent.update(this, &CircularList.push(&1, farm_proc))
Agent.get_and_update(this, fn(cl) ->
new_cl = cl
|> CircularList.push(farm_proc)
|> CircularList.rotate()
{CircularList.get_index(new_cl), new_cl}
end)

end

@spec current_index(proc_storage) :: index
Expand Down

0 comments on commit bccc4a8

Please sign in to comment.