Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

change sendfile driver handler to a gen_server

Rather than use a custom message handling loop, make the yaws_sendfile
driver handler a gen_server. Collapse the yaws_sendfile_compat
functionality directly into yaws_sendfile and remove
yaws_sendfile_compat, and change all references to
yaws_sendfile_compat to refer to yaws_sendfile instead.
  • Loading branch information...
commit 6a4c4e7c313ad4e7e2ae2b00b17b8f570f6f7c04 1 parent a425e71
@vinoski vinoski authored
View
4 src/Makefile
@@ -47,7 +47,7 @@ MODULES=yaws \
authmod_gssapi \
yaws_appmod_cgi \
yaws_appmod_fcgi \
- yaws_sendfile yaws_sendfile_compat \
+ yaws_sendfile \
yaws_sup_restarts \
yaws_stats \
yaws_vdir \
@@ -70,7 +70,7 @@ dav:
$(EBIN_FILES) : ../include/yaws.hrl ../include/yaws_api.hrl
-../ebin/yaws_sendfile_compat.$(EMULATOR): yaws_configure.hrl
+../ebin/yaws_sendfile.$(EMULATOR): yaws_configure.hrl
yaws_generated.erl: yaws_generated.template ../vsn.mk
. ../vsn.mk; \
View
222 src/yaws_sendfile.erl
@@ -4,44 +4,37 @@
%%% Created : 9 Nov 2008 by Steve Vinoski <vinoski@ieee.org>
-module(yaws_sendfile).
--export([start_link/0, init/1, stop/0, send/2, send/3, send/4]).
+-author('vinoski@ieee.org').
+
+-export([start_link/0, start/0, stop/0,
+ enabled/0, send/2, send/3, send/4]).
+
+-include("yaws_configure.hrl").
+-include("../include/yaws.hrl").
+
+-ifdef(HAVE_SENDFILE).
+
+-behavior(gen_server).
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
+ code_change/3]).
-include_lib("kernel/include/file.hrl").
start_link() ->
- Shlib = "yaws_sendfile_drv",
- Dir = case yaws_generated:is_local_install() of
- true ->
- filename:dirname(code:which(?MODULE)) ++ "/../priv/lib";
- false ->
- %% ignore dialyzer on this one
- PrivDir = code:priv_dir(yaws),
- filename:join(PrivDir,"lib")
- end,
- case erl_ddll:load_driver(Dir, Shlib) of
- ok -> ok;
- {error, already_loaded} -> ok;
- _ -> exit({error, could_not_load_driver})
- end,
- {ok, spawn_link(?MODULE, init, [Shlib])}.
+ gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
-init(Shlib) ->
- register(?MODULE, self()),
- process_flag(trap_exit, true),
- Port = open_port({spawn, Shlib}, [binary]),
- loop(Port).
+start() ->
+ gen_server:start({local, ?MODULE}, ?MODULE, [], []).
-stop() ->
- ?MODULE ! stop,
- unregister(?MODULE),
- ok.
+enabled() ->
+ true.
send(Out, Filename) ->
send(Out, Filename, 0, 0).
send(Out, Filename, Offset) ->
send(Out, Filename, Offset, 0).
send(Out, Filename, Offset, Count) ->
- Count2 = case Count of
+ Count1 = case Count of
0 ->
case file:read_file_info(Filename) of
{ok, #file_info{size = Size}} ->
@@ -52,63 +45,144 @@ send(Out, Filename, Offset, Count) ->
_ ->
Count
end,
- case Count2 of
+ case Count1 of
{error, _}=Error2 ->
Error2;
_ ->
case prim_inet:getfd(Out) of
- {ok, Socket_fd} ->
- call_port(
- Socket_fd,
- list_to_binary(
- [<<Offset:64, Count2:64, Socket_fd:32>>,
- Filename, <<0:8>>]));
+ {ok, SocketFd} ->
+ Call = list_to_binary(
+ [<<Offset:64, Count1:64, SocketFd:32>>,
+ Filename, <<0:8>>]),
+ gen_server:call(?MODULE, {send, SocketFd, Call}, infinity);
Error3 ->
Error3
end
end.
-call_port(Socket_id, Msg) ->
- ?MODULE ! {call, self(), Socket_id, Msg},
- receive
- {?MODULE, Reply} ->
- Reply
+stop() ->
+ gen_server:cast(?MODULE, stop).
+
+-record(state, {
+ port, % driver port
+ caller_tbl % table mapping socket fd to caller
+ }).
+
+init([]) ->
+ process_flag(trap_exit, true),
+ Shlib = "yaws_sendfile_drv",
+ Dir = case yaws_generated:is_local_install() of
+ true ->
+ filename:dirname(code:which(?MODULE)) ++ "/../priv/lib";
+ false ->
+ %% ignore dialyzer on this one
+ PrivDir = code:priv_dir(yaws),
+ filename:join(PrivDir,"lib")
+ end,
+ case erl_ddll:load_driver(Dir, Shlib) of
+ ok -> ok;
+ {error, already_loaded} -> ok;
+ _ -> exit({error, "could not load driver" ++ Shlib})
+ end,
+ Port = open_port({spawn, Shlib}, [binary]),
+ CallerTable = ets:new(yaws_sendfile, []),
+ {ok, #state{port = Port, caller_tbl = CallerTable}}.
+
+handle_call({send, SocketFd, Msg}, From, State) ->
+ true = erlang:port_command(State#state.port, Msg),
+ true = ets:insert(State#state.caller_tbl, {SocketFd, From}),
+ {noreply, State};
+handle_call(_Req, _From, State) ->
+ {reply, ok, State}.
+
+handle_info({_, {data, <<Cnt:64, SocketFd:32, Res:8, Err/binary>>}}, State) ->
+ Reply = case Res of
+ 1 ->
+ {ok, Cnt};
+ 0 ->
+ {error,
+ list_to_atom(
+ lists:takewhile(fun(El) -> El =/= 0 end,
+ binary_to_list(Err)))}
+ end,
+ CallerTable = State#state.caller_tbl,
+ [{SocketFd, From}] = ets:lookup(CallerTable, SocketFd),
+ gen_server:reply(From, Reply),
+ ets:delete(CallerTable, SocketFd),
+ {noreply, State};
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+handle_cast(stop, State) ->
+ {stop, State};
+handle_cast(_, State) ->
+ {noreply, State}.
+
+terminate(_Reason, #state{port = Port}=State) ->
+ erlang:port_close(Port),
+ receive {'EXIT', Port, _Reason} -> ok
+ after 0 -> ok
+ end,
+ {noreply, State#state{port = undefined}}.
+
+code_change(_OldVsn, Data, _Extra) ->
+ {ok, Data}.
+
+-else.
+
+enabled() ->
+ false.
+start_link() ->
+ ignore.
+start() ->
+ ignore.
+stop() ->
+ ok.
+send(Out, Filename) ->
+ send(Out, Filename, 0, all).
+send(Out, Filename, Offset) ->
+ send(Out, Filename, Offset, all).
+send(Out, Filename, Offset, Count) ->
+ compat_send(Out, Filename, Offset, Count).
+
+compat_send(Out, Filename, Offset, Count) ->
+ case file:open(Filename, [read, binary, raw]) of
+ {ok, Fd} ->
+ file:position(Fd, {bof, Offset}),
+ ChunkSize = (get(gc))#gconf.large_file_chunk_size,
+ Ret = loop_send(Fd, ChunkSize, file:read(Fd, ChunkSize), Out, Count),
+ file:close(Fd),
+ Ret;
+ Err ->
+ Err
end.
-loop(Port) ->
- receive
- {call, Caller, Id, Msg} ->
- put(Id, Caller),
- erlang:port_command(Port, Msg),
- loop(Port);
- {Port, {data, <<Cnt:64, Id:32, Res:8, Err/binary>>}} ->
- Response = case Res of
- 1 ->
- {ok, Cnt};
- 0 ->
- {error,
- list_to_atom(
- lists:takewhile(fun(El) -> El =/= 0 end,
- binary_to_list(Err)))}
- end,
- Caller = erase(Id),
- Caller ! {?MODULE, Response},
- loop(Port);
- stop ->
- erlang:port_close(Port),
- receive {'EXIT', Port, _Reason} -> ok
- after 0 -> ok
+loop_send(Fd, ChunkSize, {ok, Bin}, Out, all) ->
+ case gen_tcp:send(Out, Bin) of
+ ok ->
+ loop_send(Fd, ChunkSize, file:read(Fd, ChunkSize), Out, all);
+ Err ->
+ Err
+ end;
+loop_send(_Fd, _ChunkSize, eof, _Out, _) ->
+ ok;
+loop_send(Fd, ChunkSize, {ok, Bin}, Out, Count) ->
+ Sz = size(Bin),
+ if Sz < Count ->
+ case gen_tcp:send(Out, Bin) of
+ ok ->
+ loop_send(Fd, ChunkSize, file:read(Fd, ChunkSize),
+ Out, Count-Sz);
+ Err ->
+ Err
end;
- {'EXIT', _, shutdown} ->
- erlang:port_close(Port),
- unregister(?MODULE),
- exit(shutdown);
- {'EXIT', Port, Posix_error} ->
- error_logger:format("Fatal error: sendfile port died, error ~p~n",
- [Posix_error]),
- exit(Posix_error);
- {'EXIT', error, Reason} ->
- error_logger:format("Fatal error: sendfile driver failure: ~p~n",
- [Reason]),
- exit(Reason)
- end.
+ Sz == Count ->
+ gen_tcp:send(Out, Bin);
+ Sz > Count ->
+ <<Deliver:Count/binary , _/binary>> = Bin,
+ gen_tcp:send(Out, Deliver)
+ end;
+loop_send(_Fd, _, Err, _,_) ->
+ Err.
+
+-endif.
View
109 src/yaws_sendfile_compat.erl
@@ -1,109 +0,0 @@
-%%% File : yaws_sendfile_compat.erl
-%%% Author : Claes Wikstrom, klacke@hyber.org
-%%% Description : Conditional OS dependent call to sendfile
-%%% Created : Sat Dec 20 20:00:11 CET 2008
-
--module(yaws_sendfile_compat).
--export([start_link/0, init/1, stop/0, send/2, send/3, send/4, enabled/0]).
-
--include_lib("kernel/include/file.hrl").
--include("yaws_configure.hrl").
-
--include("../include/yaws.hrl").
-
-%% will be true for MacOsX, FreeBSD, Linux
--ifdef(HAVE_SENDFILE).
-
-enabled() ->
- true.
-start_link() ->
- yaws_sendfile:start_link().
-stop() ->
- yaws_sendfile:stop().
-init(ShLib) ->
- yaws_sendfile:init(ShLib).
-send(Out, FileName) ->
- case yaws_sendfile:send(Out, FileName) of
- {error, eoverflow} ->
- compat_send(Out, FileName, 0, all);
- Other ->
- Other
- end.
-send(Out, FileName, Offset) ->
- case yaws_sendfile:send(Out, FileName, Offset) of
- {error, eoverflow} ->
- compat_send(Out, FileName, Offset, all);
- Other ->
- Other
- end.
-send(Out, FileName, Offset, Count) ->
- case yaws_sendfile:send(Out, FileName, Offset, Count) of
- {error, eoverflow} ->
- compat_send(Out, FileName, Offset, Count);
- Other ->
- Other
- end.
-
--else.
-
-%% Emulate sendfile, this is true for win32, qnx, solaris. OpenBSD,NetBSD I
-%% still don't know
-
-enabled() ->
- false.
-start_link() ->
- ignore.
-stop() ->
- ok.
-init(_) ->
- ok.
-send(Out, Filename) ->
- send(Out, Filename, 0, all).
-send(Out, Filename, Offset) ->
- send(Out, Filename, Offset, all).
-send(Out, Filename, Offset, Count) ->
- compat_send(Out, Filename, Offset, Count).
-
--endif.
-
-compat_send(Out, Filename, Offset, Count) ->
- case file:open(Filename, [read, binary, raw]) of
- {ok, Fd} ->
- file:position(Fd, {bof, Offset}),
- ChunkSize = (get(gc))#gconf.large_file_chunk_size,
- Ret = loop_send(Fd, ChunkSize, file:read(Fd, ChunkSize), Out, Count),
- file:close(Fd),
- Ret;
- Err ->
- Err
- end.
-
-loop_send(Fd, ChunkSize, {ok, Bin}, Out, all) ->
- case gen_tcp:send(Out, Bin) of
- ok ->
- loop_send(Fd, ChunkSize, file:read(Fd, ChunkSize), Out, all);
- Err ->
- Err
- end;
-loop_send(_Fd, _ChunkSize, eof, _Out, _) ->
- ok;
-loop_send(Fd, ChunkSize, {ok, Bin}, Out, Count) ->
- Sz = size(Bin),
- if Sz < Count ->
- case gen_tcp:send(Out, Bin) of
- ok ->
- loop_send(Fd, ChunkSize, file:read(Fd, ChunkSize),
- Out, Count-Sz);
- Err ->
- Err
- end;
- Sz == Count ->
- gen_tcp:send(Out, Bin);
- Sz > Count ->
- <<Deliver:Count/binary , _/binary>> = Bin,
- gen_tcp:send(Out, Deliver)
- end;
-loop_send(_Fd, _, Err, _,_) ->
- Err.
-
-
View
4 src/yaws_server.erl
@@ -3467,7 +3467,7 @@ deliver_large_file(CliSock, _Req, UT, Range) ->
send_file(CliSock, Path, all, _Priv, no) when is_port(CliSock) ->
?Debug("send_file(~p,~p,no ...)~n", [CliSock, Path]),
- yaws_sendfile_compat:send(CliSock, Path),
+ yaws_sendfile:send(CliSock, Path),
{ok, Size} = yaws:filesize(Path),
yaws_stats:sent(Size);
send_file(CliSock, Path, all, Priv, _Enc) ->
@@ -3477,7 +3477,7 @@ send_file(CliSock, Path, all, Priv, _Enc) ->
send_file(CliSock, Path, {fromto, From, To, _Tot}, undeflated, no)
when is_port(CliSock) ->
Size = To - From + 1,
- yaws_sendfile_compat:send(CliSock, Path, From, Size),
+ yaws_sendfile:send(CliSock, Path, From, Size),
yaws_stats:sent(Size);
send_file(CliSock, Path, {fromto, From, To, _Tot}, undeflated, _Enc) ->
{ok, Fd} = file:open(Path, [raw, binary, read]),
View
6 src/yaws_sup_restarts.erl
@@ -39,10 +39,10 @@ init([]) ->
permanent, 5000, worker, [gen_event]},
- SendFile = case yaws_sendfile_compat:enabled() of
+ SendFile = case yaws_sendfile:enabled() of
true ->
- [{yaws_sendfile,
- {yaws_sendfile_compat, start_link, []},
+ [{yaws_sendfile,
+ {yaws_sendfile, start_link, []},
permanent, 5000, worker, [yaws_sendfile]}];
false ->
[]
Please sign in to comment.
Something went wrong with that request. Please try again.