Почему werl выполняет один и тот же код в одном окне оболочки и зависает в другом?

Я пытаюсь решить упражнение 5-2 из книги O'Reilly Erlang Programming. Моя установка Win10, werl Eshell V7.3. Вот шаги, чтобы воспроизвести мою проблему:

  1. c(frequency).

  2. frequency:start().

  3. CTRL-G -> S -> C -- для переключения на новую оболочку Erlang

  4. frequency:allocate().

  5. frequency:deallocate(10).

Если я опускаю пункт 3, то все идет нормально, но когда я делаю все по вышеуказанной процедуре, то оболочка застревает в строке 30 или 43.

Может кто-нибудь объяснить мне, что я делаю неправильно и как добиться одинакового поведения для этих двух случаев: с одной и с двумя оболочками?

Код модуля (здесь есть два предупреждения, но он все равно компилируется, я пытался разрешить освобождение только при вызове из того же Pid, ​​из которого выполнялось выделение):

-module(frequency).
-export([start/0, stop/0, allocate/0, deallocate/1]).
-export([init/0]).

%% These are the start functions used to crate and
%% initialize the server.

start() ->
  register(frequency, spawn(frequency, init, [])).

init() ->
  Frequencies = {get_frequencies(), []},
  loop(Frequencies).

% Hard Coded
get_frequencies() -> [10, 11, 12, 13, 14, 15].

%% The client Functions

stop()          -> call(stop).
allocate()      -> call(allocate).
deallocate(Freq)-> io:format("Calling deallocate~n",[]), call({deallocate, Freq}).

%% We hide all message passing and the message
%% protocol in a functional interface.

call(Message) ->
  Self = self(),
  io:format("Self: ~w~n", [Self]),
  frequency ! {request, Self, Message},
  receive
    {reply, Reply} -> Reply
  end.

%% The Main Loop

loop(Frequencies) ->
  receive 
    {request, Pid, allocate} ->
      {NewFrequencies, Reply} = allocate(Frequencies, Pid),
      reply(Pid, Reply),
      loop(NewFrequencies);
    {request, Pid2, {deallocate, Freq}} ->
      io:format("Dealocate ~w from pid ~w~n", [Freq, Pid2]),
      NewFrequencies = deallocate(Frequencies, Freq), %, Pid2),
      reply(Pid2, ok),
      loop(NewFrequencies);
    {request, Pid, stop} ->
      reply(Pid, ok)
  end.

reply(Pid, Reply) ->
  Pid ! {reply, Reply}.

%% The Internal Help Functions used to allocate and
%% deallocate frequencies.

allocate({[], Allocated}, _Pid) ->
  {{[], Allocated}, {error, no_frequency}};
allocate({[Freq|Free], Allocated}, Pid) ->
  {{Free, [{Freq, Pid}| Allocated]}, {ok, Freq}}.

deallocate({Free, Allocated}, Freq) -> %, Pid) ->
  Response = lists:keysearch(Freq, 1, Allocated),
  io:format("Response: ~w~n", [Response]),
  case Response of
    {value, {Freq, OPid}} ->
        case OPid of
          Pid ->
            NewAllocated = lists:keydelete(Freq, 1, Allocated),
            io:format("Removed freq~n",[]),
            {[Freq|Free], NewAllocated};
          _OtherPid ->
            io:format("Not allowed to remove freq~n",[]),
            {Free, Allocated}
        end;
    _ -> io:format("Not removed freq~n",[]), {Free, Allocated}
  end.

person Jakub Mitoraj    schedule 15.08.2016    source источник


Ответы (1)


У меня нет полного объяснения проблемы, но она связана с использованием io:format, существует неясная (непонятная для меня :o) концепция лидера группы, которая используется для определения того, на какой оболочке следует использовать io:format. отобразить сообщение.

В общем, не стоит отображать сообщения в серверном цикле, который выполняется в серверном процессе, лучше это делать в интерфейсных функциях, выполняемых в вызывающем процессе.

Я немного изменил ваш код, удалив бесполезную печать и исправил тест pid, и он отлично работает в нескольких оболочках.

-module(freq).
-export([start/0, stop/0, allocate/0, deallocate/1]).
-export([init/0]).

%% These are the start functions used to crate and
%% initialize the server.

start() ->
  register(frequency, spawn(freq, init, [])).

init() ->
  Frequencies = {get_frequencies(), []},
  loop(Frequencies).

% Hard Coded
get_frequencies() -> [10, 11, 12, 13, 14, 15].

%% The client Functions

stop()          -> call(stop).
allocate()      -> call(allocate).
deallocate(Freq)-> io:format("Calling deallocate~n",[]), call({deallocate, Freq}).

%% We hide all message passing and the message
%% protocol in a functional interface.

call(Message) ->
  Self = self(),
  frequency ! {request, Self, Message},
  receive
    {reply, Reply} -> Reply
  end.

%% The Main Loop

loop(Frequencies) ->
  receive 
    {request, Pid, allocate} ->
      {NewFrequencies, Reply} = allocate(Frequencies, Pid),
      reply(Pid, Reply),
      loop(NewFrequencies);
    {request, Pid, {deallocate, Freq}} ->
      {NewFrequencies,Reply} = deallocate(Frequencies, Freq, Pid),
      reply(Pid, Reply),
      loop(NewFrequencies);
    {request, Pid, stop} ->
      reply(Pid, ok)
  end.

reply(Pid, Reply) ->
  Pid ! {reply, Reply}.

%% The Internal Help Functions used to allocate and
%% deallocate frequencies.

allocate({[], Allocated}, _Pid) ->
  {{[], Allocated}, {error, no_frequency}};
allocate({[Freq|Free], Allocated}, Pid) ->
  {{Free, [{Freq, Pid}| Allocated]}, {ok, Freq}}.

deallocate({Free, Allocated}, Freq, Pid) ->
  Response = lists:keysearch(Freq, 1, Allocated),
  case Response of
    {value, {Freq, Pid}} ->
      NewAllocated = lists:keydelete(Freq, 1, Allocated),
      {{[Freq|Free], NewAllocated},removed};
    {value, {Freq, _OtherPid}} ->
      {{Free, Allocated},not_owner};
    _ ->
      {{Free, Allocated},not_found}
  end.
person Pascal    schedule 16.08.2016