Prologue: critical section, rollback, error handling

I am trying to write a critical section protected by a mutex in SWI-Prolog and have been looking at using setup_call_cleanup/3

and setup_call_catcher_cleanup/4

.

The problem is that my goal is a sequence of operations that any one can fail, which means the system goes back to the beginning setup_call_cleanup

and causes a cleanup. Unfortunately, I am unable to report the bug properly on backtracking. To illustrate my problem, consider this simple example:

setup_call_cleanup(
      mutex_lock(mtx),
      ( Step1 = true, Step2 = true, Step3 = true ),
      ( mutex_unlock(mtx), writeln([Step1, Step2, Step3]) ).

      

and compare it with the following:

setup_call_cleanup(
      mutex_lock(mtx),
      ( Step1 = true, Step2 = true, fail, Step3 = true ),
      ( mutex_unlock(mtx), writeln([Step1, Step2, Step3]) ).

      

In the first case, everything is fine - I can see all the steps taken. But in the second case, I don't see that Step1

and Step2

. I would like to see this because they may have external side effects that the rollback cannot undo. Also, I don't want to include error handling in the target to make the critical section as fast and fast as possible.

I have two ideas:

  • Decorate each step nb_setval

    to keep a value indicating completed steps,
  • The steps are re-code, so they throw exceptions that carry the details of the problem.

The former will make the code rather bloated, while the latter seems too heavy for my needs. Is there something like setup_nb_call_cleanup

?

+3


source to share


2 answers


Thanks Jan for the inspiration; very helpful. I ended up coding a similar rule step_by_step

:

step_by_step(Goal, Steps, Error) :-
    step_by_step_(Goal, 0, Steps, Error).

step_by_step_((A, B), InStep, OutStep, Error) :-
    !,
    step_by_step_(A, InStep, OutStep1, Error),
    ( var(Error) ->
        step_by_step_(B, OutStep1, OutStep, Error)
    ;
        OutStep = InStep
    ).

step_by_step_(Goal, InStep, OutStep, Error) :-
    ( catch(Goal, Ex, (Error = exception(Ex), OutStep = InStep)) *->
        (OutStep is InStep + 1 ; true), !
    ;
        Error = false(Goal),
        OutStep = InStep
    ).

      

I'm not happy (OutStep is InStep + 1 ; true), !

but couldn't find a better way.

Anyway, the rule gives me what I want:

- if everything goes well, it just goes through all the steps in sequence:



?- step_by_step((Step1 = true, Step2 = true, Step3 = true), Steps, Error).
Step1 = Step2, Step2 = Step3, Step3 = true,
Steps = 3.

      

- if one of the steps fails or throws an exception, it returns the number of steps successfully completed and the failed target:

?- step_by_step((Step1 = true, Step2 = true, fail, Step3 = true), Steps, Error).
Step1 = Step2, Step2 = true,
Steps = 2,
Error = false(fail).

      

or an exception:

?- step_by_step((Step1 = true, Step2 = true, throw(bomb), Step3 = true), Steps, Error).
Step1 = Step2, Step2 = true,
Steps = 2,
Error = exception(bomb).

      

0


source


The trick, I think, is to run the targets one by one, to guard against mistakes and failed attempts and failed steps. A good start -

until_failure((A,B), Result) :-
    !,
    until_failure(A, Result),
    (   var(Result)
    ->  until_failure(B, Result)
    ;   true
    ).
until_failure(G, Result) :-
    (   catch(G, Result, true)
    *-> true
    ;   Result = false(G)
    ).

      

Now you can run for example



?- until_failure((Step1 = true,
               Step2 = true, 
               fail,
               Step3 = true), Result),
   writeln([Step1, Step2, Step3]).

[true, true, _5742]
Result = false(fail)

      

See http://swish.swi-prolog.org/p/ReQWsvCg.swinb . SWISH does not allow handling of mutexes, but you can easily wrap this internally with_mutex/2

. The details depend on how you want to deal with determinism.

+2


source







All Articles