Gen_server tanpa pengawasan tidak memanggil penghentian ketika menerima sinyal keluar

gen_server dokumentasi pada Module:terminate panggilan balik mengatakan:

Meskipun proses gen_server bukan bagian dari pohon pengawasan, fungsi ini dipanggil jika menerima pesan 'EXIT' dari induknya. Alasannya sama seperti pada pesan 'EXIT'.

Inilah fungsi handle_info dan terminate saya:

handle_info(UnknownMessage, State) ->
    io:format("Got unknown message: ~p~n", [UnknownMessage]),
    {noreply, State}.

terminate(Reason, State) ->
    io:format("Terminating with reason: ~p~n", [Reason]).

Saya memulai server ini menggunakan gen_server:start. Saya berasumsi ketika saya memanggil erlang:exit(Pid, fuckoff), itu harus memanggil fungsi panggilan balik terminate. Tapi itu menunjukkan:

Got unknown message: {'EXIT',<0.33.0>,fuckoff}

Artinya ia memanggil handle_info. Namun ketika saya menelepon gen_server:stop, semuanya berfungsi seperti yang disebutkan dalam dokumentasi. Saya menelepon gen_server saya dari shell. Bisakah Anda menjelaskan hal ini?

[PEMBARUAN]

Berikut adalah kode sumber fungsi decode_msg di dalam gen_server. Jika menerima pesan 'EXIT', ia harus memanggil fungsi terminate:

decode_msg(Msg, Parent, Name, State, Mod, Time, Debug, Hib) ->
    case Msg of
    {system, From, Req} ->
        sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
                  [Name, State, Mod, Time], Hib);
    {'EXIT', Parent, Reason} ->
        terminate(Reason, Name, Msg, Mod, State, Debug);
    _Msg when Debug =:= [] ->
        handle_msg(Msg, Parent, Name, State, Mod);
    _Msg ->
        Debug1 = sys:handle_debug(Debug, fun print_event/3,
                      Name, {in, Msg}),
        handle_msg(Msg, Parent, Name, State, Mod, Debug1)
end.

Dalam kasus saya, ini tidak memanggil fungsi terminate.

[PEMBARUAN]

Saat saya memulai gen_server menggunakan gen_server:start_link(), mengirimkan sinyal keluar menggunakan erlang:exit(Pid, Reason) akan menghasilkan pemanggilan fungsi panggilan balik terminate yang merupakan perilaku yang diharapkan. Tampaknya ada perbedaan dalam menafsirkan sinyal keluar apakah suatu proses terhubung ke induknya atau tidak.


person Majid Azimi    schedule 10.09.2016    source sumber
comment
Apakah Anda menyetel tanda trap_exit proses ke true di suatu tempat dalam kode?   -  person Hamidreza Soleimani    schedule 25.09.2016
comment
Ya. Di dalam fungsi init.   -  person Majid Azimi    schedule 25.09.2016


Jawaban (2)


Jawaban singkat:

Jika Anda memanggil fungsi exit/2 dari dalam aktor gen_server, fungsi tersebut akan berperilaku seperti yang diharapkan berdasarkan dokumentasi dan panggilan balik terminate/2 akan dipanggil.

Jawaban panjang:

Ketika Anda mengirim pesan keluar dari shell, nilai Parent dari tupel keluar diatur ke id proses shell, sebaliknya ketika Anda memulai proses gen_server dari shell, nilai Parent-nya diatur ke id prosesnya sendiri, bukan shell id proses, oleh karena itu ketika mendapat pesan keluar tidak cocok dengan klausa kedua blok terima di fungsi decode_msg/8 sehingga fungsi terminate/6 tidak dipanggil dan akhirnya cocok dengan klausa berikutnya yaitu memanggil fungsi handle_msg/5.

Rekomendasi:

Untuk memanggil panggilan balik terminate/3 bahkan dengan mengirimkan pesan keluar ke proses gen_server, Anda dapat menjebak pesan keluar di handle_info/2 dan kemudian kembali dengan stop tuple sebagai berikut:

init([]) ->
    process_flag(trap_exit, true),
    {ok, #state{}}.

handle_info({'EXIT', _From, Reason}, State) ->
    io:format("Got exit message: ~p~n", []),
    {stop, Reason, State}.

terminate(Reason, State) ->
    io:format("Terminating with reason: ~p~n", [Reason]),
    %% do cleanup ...
    ok.
person Hamidreza Soleimani    schedule 25.09.2016
comment
Saya mendapatkannya. Saya dapat melihatnya di observer. Mengapa induk disetel ke id prosesnya sendiri ketika saya memulai proses di shell? Ada alasan khusus? - person Majid Azimi; 25.09.2016
comment
@MajidAzimi: Berdasarkan kode, gen:get_parent/0 yang bertanggung jawab untuk menemukan induk dari gen_server mencari item pertama dari kunci $ancestors dalam kamus proses. Karena proses gen_server adalah proses OTP yang dimulai dengan proc_lib, proses tersebut seharusnya memiliki ID proses aktor pemanggil di kamus $ancestors, yang merupakan ID proses shell dalam kasus ini dan menetapkannya sebagai induk, tetapi tidak berfungsi seperti yang diharapkan . Saya perlu lebih banyak waktu untuk mengetahui apakah itu bug atau sesuatu yang lain. - person Hamidreza Soleimani; 25.09.2016
comment
Sejauh yang saya bisa periksa, ketika saya memulai proses OTP menggunakan gen_*:start pid induk disetel ke proses yang baru dibuat itu sendiri. Saat memulai dengan gen_*:start_link, ini disetel ke elemen pertama $ancestors. Tidak masalah jika induk sebenarnya adalah shell atau gen_* lainnya. Dalam kedua kasus $ancestors berisi semua orang tua hingga ke akarnya. gen_*:start_link menggunakan elemen pertama dari $ancestors untuk mendapatkan induk (yang merupakan perilaku yang diharapkan) tetapi untuk beberapa alasan gen_*:start menggunakan pidnya sendiri sebagai induk. - person Majid Azimi; 25.09.2016

Saat Anda memulai gen_server, prosesnya sederhana, jadi, erlang:exit/1 atau erlang:exit/2 berfungsi seperti yang diharapkan.

  • Jika Pid tidak menjebak pintu keluar, Pid sendiri keluar dengan alasan keluar.
  • Jika Pid menjebak pintu keluar, sinyal keluar diubah menjadi pesan {'EXIT', From, Reason} dan dikirim ke antrian pesan Pid.

Jadi, saat ini kode Anda menjebak sinyal 'EXIT' karena pesan ini dikirim seperti pesan lainnya ke kotak surat dan cocok dengan pola wildcard handle_info/2.

Jika Anda ingin informasi lebih lanjut tentang itu, Anda dapat membaca gen_server kode sumber dan lihat cara kerjanya. Anda juga dapat menemukan masalah Anda dijelaskan dalam kode ini.

person Mathieu Kerjouan    schedule 10.09.2016
comment
Izinkan saya mengajukan pertanyaan seperti ini: gen_server:stop juga mengirimkan sinyal keluar. Ini akan dikonversi menjadi pesan 'EXIT'. Bagaimana gen_server membedakan pesan ini dari pesan 'EXIT' saya (yang berasal dari exit) - person Majid Azimi; 11.09.2016
comment
gen_server:handle_info/2 saat ini digunakan untuk menjebak setiap sinyal (bahkan sinyal khusus). Jadi, jika Anda ingin membedakan sinyal 'EXIT' dari sinyal lain, definisi Anda tentang handle_info harus berbeda (memodifikasi pencocokan pola, menambahkan pelindung...). Untuk lebih jelasnya, sinyal 'EXIT' lebih seperti konvensi daripada standar di Erlang, karena hanya berupa pesan sederhana. - person Mathieu Kerjouan; 11.09.2016