处理OTP中的超时

时间:2012-05-15 15:51:18

标签: erlang timeout otp

我已经定义了一个应用程序

{application, ps_barcode,
 [{description, "barcode image generator based on ps-barcode"},
  {vsn, "1.0"},
  {modules, [ps_barcode_app, ps_barcode_supervisor, barcode_data, wand, ps_bc]},
  {registered, [ps_bc, wand, ps_barcode_supervisor]},
  {applications, [kernel, stdlib]},
  {mod, {ps_barcode_app, []}},
  {start_phases, []}]}.

主管init看起来像

init([]) ->
    {ok, {{one_for_one, 3, 10},
      [{tag1, 
        {wand, start, []},
        permanent,
        brutal_kill,
        worker,
        [wand]},
       {tag2,
        {ps_bc, start, []},
        permanent,
        10000,
        worker,
        [ps_bc]}]}}.

这是一个条形码生成器,它使用C组件进行一些图像处理。如果要求处理不存在的文件,或者权限不足,系统会出现错误并重新启动,但是有一个特定错误会导致wand模块超时

GPL Ghostscript 9.04: Unrecoverable error, exit code 1
GPL Ghostscript 9.04: Unrecoverable error, exit code 1
wand.c barcode_to_png 65 Postscript delegate failed `/tmp/tmp.1337.95765.926102': No such file or directory @ error/ps.c/ReadPSImage/827

** exception exit: {timeout,{gen_server,call,
                    [wand,{process_barcode,"/tmp/tmp.1337.95765.926102"}]}}
     in function  gen_server:call/2 (gen_server.erl, line 180)
     in call from ps_bc:generate/3 (ps_bc.erl, line 19)

(Imagemagick错误在那里是不准确的;文件存在,但它是一个带有错误的Postscript文件因此无法解释为正常;我认为这是产生Ghostscript错误并导致程序挂起的原因,但我'我不知道为什么它根本没有返回)。

我遇到的问题是:即使此超时返回错误,wand进程似乎在后台挂起(我结束了这一点,因为对wand的任何进一步调用都会返回另一个超时错误,包括wand:stop由于某种原因)。我不确定要发布多少代码,所以我将它保持在wand模块本身的最低限度。如果我需要发布其他文章,请告诉我。

-module(wand).

-behaviour(gen_server).

-export([start/0, stop/0]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
     terminate/2, code_change/3]).

-export([process/1]).

process(Filename) -> gen_server:call(?MODULE, {process_barcode, Filename}).

handle_call({process_barcode, Filename}, _From, State) ->
    State ! {self(), {command, Filename}},
    receive
      {State, {data, Data}} ->
        {reply, decode(Data), State}
    end;
handle_call({'EXIT', _Port, Reason}, _From, _State) ->
    exit({port_terminated, Reason}).

decode([0]) -> {ok, 0};
decode([1]) -> {error, could_not_read};
decode([2]) -> {error, could_not_write}.

%%%%%%%%%%%%%%%%%%%% generic actions
start() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
stop() -> gen_server:call(?MODULE, stop).

%%%%%%%%%%%%%%%%%%%% gen_server handlers
init([]) -> {ok, open_port({spawn, filename:absname("wand")}, [{packet, 2}])}.
handle_cast(_Msg, State) -> {noreply, State}.
handle_info(_Info, State) -> {noreply, State}.
terminate(_Reason, Port) -> Port ! {self(), close}, ok.
code_change(_OldVsn, State, _Extra) -> {ok, State}.

编辑:忘了提及它可能是相关的;当我通过application:load / application:start运行应用程序时,似乎只会发生挂起。如果我通过

自行测试这个组件
c(wand).
wand:start().
wand:process("/tmp/tmp.malformed-file.ps").

它仍然是错误的,但这个过程真的死了。也就是说,我可以做到

wand:start().
wand:process("/tmp/tmp.existing-well-formed-file.ps").

并获得预期的回复。当它通过主管启动时,它会挂起而展示我之前描述的行为。

2 个答案:

答案 0 :(得分:1)

不是答案,但在这种情况下我会做什么。我将使用gen_server:cast并将在gen_server中处理超时,在完成所有工作后,我将向请求者发送带有结果的响应。所以这种变化也会影响请求方。

但我可能在各方面都错了。

答案 1 :(得分:0)

在处理外部C程序时,似乎使用receive..after而不是普通receive强制执行kill。我不确定为什么其他措施不起作用......

...
receive
  {State, {data, Data}} ->
    {reply, decode(Data), State}
after 3000 ->
  exit(wand_timeout)
end;
...

此外,此时您必须希望合法操作不会超过3000。在这种特殊情况下,这不是问题,但可能是我向C程序添加了更多输出。