Current section

Files

Jump to
vix lib vix target_pipe.ex
Raw

lib/vix/target_pipe.ex

defmodule Vix.TargetPipe do
use GenServer
require Logger
alias Vix.Nif
alias __MODULE__
@moduledoc false
defstruct [:fd, :pending, :task_result, :task_pid]
defmodule Pending do
@moduledoc false
defstruct size: nil, client_pid: nil
end
@default_buffer_size 65_535
def new(vips_image, suffix) do
GenServer.start_link(__MODULE__, %{image: vips_image, suffix: suffix})
end
def read(process, max_size \\ @default_buffer_size)
when is_integer(max_size) and max_size > 0 do
GenServer.call(process, {:read, max_size}, :infinity)
end
def stop(pid) do
GenServer.stop(pid)
end
# Server
def init(%{image: image, suffix: suffix}) do
Process.flag(:trap_exit, true)
{:ok, nil, {:continue, %{image: image, suffix: suffix}}}
end
def handle_continue(%{image: image, suffix: suffix}, _) do
case Nif.nif_target_new() do
{:ok, {fd, target}} ->
pid = start_task(image, target, suffix)
{:noreply, %TargetPipe{fd: fd, task_pid: pid, pending: %Pending{}}}
{:error, reason} ->
{:stop, reason, nil}
end
end
def handle_call({:read, size}, from, %TargetPipe{pending: %Pending{client_pid: nil}} = state) do
do_read(%TargetPipe{state | pending: %Pending{size: size, client_pid: from}})
end
def handle_call({:read, _size}, _from, state) do
{:reply, {:error, :pending_read}, state}
end
def handle_info({:select, _read_resource, _ref, :ready_input}, state) do
do_read(state)
end
def handle_info({:EXIT, from, result}, %{task_pid: from} = state) do
do_read(%TargetPipe{state | task_result: result, task_pid: nil})
end
defmacrop eof, do: {:ok, <<>>}
defmacrop eagain, do: {:error, :eagain}
defp do_read(%TargetPipe{task_result: {:error, _reason} = error} = state) do
reply_action(state, error)
end
defp do_read(%TargetPipe{pending: %{size: size}} = state) do
case Nif.nif_read(state.fd, size) do
eof() ->
reply_action(state, :eof)
{:ok, binary} ->
reply_action(state, {:ok, binary})
eagain() ->
noreply_action(state)
{:error, errno} ->
reply_action(state, {:error, errno})
end
end
defp reply_action(%TargetPipe{pending: pending} = state, ret) do
if pending.client_pid do
:ok = GenServer.reply(pending.client_pid, ret)
end
{:noreply, %TargetPipe{state | pending: %Pending{}}}
end
defp noreply_action(state) do
{:noreply, state}
end
defp start_task(image, target, suffix) do
spawn_link(fn ->
result = Nif.nif_image_to_target(image, target, suffix)
Process.exit(self(), result)
end)
end
end