1

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

{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").

并得到预期的响应。当它通过主管启动时,它会挂起并表现出我之前描述的行为。

4

2 回答 2

1

不是答案,而是在这种情况下我会怎么做。我将使用 gen_server:cast 并在 gen_server 中处理超时,在所有工作完成后,我将向请求者发送结果响应。因此,此更改也会影响请求方。

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

于 2012-05-16T02:57:57.020 回答
0

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

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

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

于 2012-05-26T02:37:45.537 回答