%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%%     http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%
%%

-module(erts_dirty_process_signal_handler).

-export([start/0]).

%%
%% The erts_dirty_process_signal_handler is started at
%% VM boot by the VM. It is a spawned as a system
%% process, i.e, the whole VM will terminate if
%% this process terminates.
%%
start() ->
    process_flag(trap_exit, true),
    msg_loop().

msg_loop() ->
    _ = receive
	    Request ->
                try
                    handle_request(Request)
                catch
                    _ : _ ->
                        %% Ignore all failures;
                        %% someone passed us garbage...
                        ok
                end
	end,
    msg_loop().

handle_request(Pid) when is_pid(Pid) ->
    handle_incoming_signals(Pid, 0);
handle_request({Requester, Target, Prio,
                {SysTaskOp, ReqId, Arg} = Op} = Request) ->
    case handle_sys_task(Requester, Target, SysTaskOp, ReqId, Arg, 0) of
        done ->
            ok;
        busy ->
            self() ! Request;
        normal ->
            %% Target has stopped executing dirty since the
            %% initial request was made. Dispatch the
            %% request to target and let it handle it itself...
            case erts_internal:request_system_task(Requester,
                                                   Target,
                                                   Prio,
                                                   Op) of
                ok ->
                    ok;
                dirty_execution ->
                    %% Ahh... It began executing dirty again...
                    handle_request(Request)
            end
    end;
handle_request(_Garbage) ->
    ignore.

%%
%% ----------------------------------------------------------------------------
%%

handle_incoming_signals(Pid, 5) ->
    self() ! Pid; %% Work with other requests for a while...    
handle_incoming_signals(Pid, N) ->
    case erts_internal:dirty_process_handle_signals(Pid) of
        more -> handle_incoming_signals(Pid, N+1);
        _Res -> ok
    end.

handle_sys_task(Requester, Target, check_process_code, ReqId, Module, N) ->
    case erts_internal:check_dirty_process_code(Target, Module) of
        Bool when Bool == true; Bool == false ->
            Requester ! {check_process_code, ReqId, Bool},
            done;
        busy ->
            case N > 5 of
                true ->
                    busy;
                false ->
                    handle_sys_task(Requester, Target, check_process_code,
                                    ReqId, Module, N+1)
            end;
        Res ->
            Res
    end.
