Restricting Angelicism with cut and fail

We implement this semantics with some careful attention to how threads are killed:


Adding Imperative Threads to SIMPLE

Syntax for the SIMPLE language with imperative threads

Program ::= DefinitionExpression a-program (defns exp1)
Definition ::= define Identifier = ( Identifier ) Expression proc-definition (id bvars body)
Expression ::= Number const-exp (num)
::= Identifier var-exp (var)
::= Unop(Expression) unop-exp (op exp1)
::= Binop(Expression , Expression) binop-exp (op exp1 exp2)
::= if Expression then Expression else Expression if-exp (exp1 exp2 exp3)
::= (Expression Expression) call-exp (rator rands)
::= choose (Expression , Expression) choose-exp (exp1 exp2)
::= choose/cut (Expression , Expression) choose/cut-exp (exp1 exp2)
::= fail fail-exp ()
::= begin Expression Expression … end begin-exp (exp exps)
::= cas (Expression , Expression , Expression) cas-exp (exp1 exp2 exp3)
Unop ::= newref op-newref ()
::= deref op-deref ()
Binop ::= setref op-setref ()
::= + op-plus ()
::= - op-minus ()
::= * op-times ()
::= / op-div ()
::= < op-less()
::= = op-equal ()
::= > op-greater ()


Premature commitment may cause programs to fail:

       define primesearch = proc (n)
                              choose/cut (if (primetest n)
                                             then n
                                             else fail,
                                          (primesearch +(n,1)))
       define primetest = proc (n)
                            if (divides 2 n)
                               then false
                               else (primetests 3 n)
       define primetests = proc (d n)
                             if >(*(d,d),n)
                                then true
                                else if (divides d n)
                                        then false
                                        else (primetests +(d,2) n)
       define divides = proc (d n)
                          (dividesloop 1 d n)
       define dividesloop = proc (q d n)
                              if =(*(q,d),n)
                                 then true
                                 else if >(*(q,d),n)
                                         then false
                                         else (dividesloop +(q,1) d n)
       define different = proc (n except)
                            (different2 n except (primesearch n))
       define different2 = proc (n except candidate)
                             if =(except, candidate)
                                then fail
                                else candidate
       (different 8 11)

Using two threads to compute a product:

       define product = proc (m n)
                          (productFork m n newref(1) newref(false))
       define productFork = proc (m n refProduct refDone)
                              begin choose((productLoop m
                                                        (avg m n)
                                                        refProduct),
                                           (productLoop (avg m n)
                                                        n
                                                        refProduct));
                                    if deref(refDone)
                                      then deref(refProduct)
                                      else begin setref(refDone,true);
                                                 fail
                                           end
                              end
       define productLoop = proc (m n refProduct)
                              if <(m,n)
                                then begin setref(refProduct,
                                                  *(m,deref(refProduct)));
                                           (productLoop +(m,1) n refProduct)
                                     end
                                else 0
       define avg = proc (m n) /(+(m,n),2)
       (product 1 10)

The correct answer is 9! = 362880.


Let's compute 20!.

       define product = proc (m n)
                          (productFork m n newref(1) newref(false))
       define productFork = proc (m n refProduct refDone)
                              begin choose((productLoop m
                                                        (avg m n)
                                                        refProduct),
                                           (productLoop (avg m n)
                                                        n
                                                        refProduct));
                                    if deref(refDone)
                                      then deref(refProduct)
                                      else begin setref(refDone,true);
                                                 fail
                                           end
                              end
       define productLoop = proc (m n refProduct)
                              if <(m,n)
                                then begin setref(refProduct,
                                                  *(m,deref(refProduct)));
                                           (productLoop +(m,1) n refProduct)
                                     end
                                else 0
       define avg = proc (m n) /(+(m,n),2)
       (product 1 21)

The correct answer is 20! = 2432902008176640000, but we get an error when all threads fail. Why?


We can use cas (compare-and-swap) to repair the race on refDone:

       define product = proc (m n)
                          (productFork m n newref(1) newref(0))
       define productFork = proc (m n refProduct refDone)
                              begin choose((productLoop m
                                                        (avg m n)
                                                        refProduct),
                                           (productLoop (avg m n)
                                                        n
                                                        refProduct));
                                    (productJoin refProduct refDone)
                              end
       define productJoin = proc (refProduct refDone)
                              if =(0,deref(refDone))
                                then if =(0,cas(refDone,0,1))
                                       then fail
                                       else (productJoin refProduct refDone)
                                else deref(refProduct)
       define productLoop = proc (m n refProduct)
                              if <(m,n)
                                then begin setref(refProduct,
                                                  *(m,deref(refProduct)));
                                           (productLoop +(m,1) n refProduct)
                                     end
                                else 0
       define avg = proc (m n) /(+(m,n),2)
       (product 1 21)

That works pretty reliably. Let's test it 1000 times:

       define product = proc (m n)
                          (productFork m n newref(1) newref(0))
       define productFork = proc (m n refProduct refDone)
                              begin choose((productLoop m
                                                        (avg m n)
                                                        refProduct),
                                           (productLoop (avg m n)
                                                        n
                                                        refProduct));
                                    (productJoin refProduct refDone)
                              end
       define productJoin = proc (refProduct refDone)
                              if =(0,deref(refDone))
                                then if =(0,cas(refDone,0,1))
                                       then fail
                                       else (productJoin refProduct refDone)
                                else deref(refProduct)
       define productLoop = proc (m n refProduct)
                              if <(m,n)
                                then begin setref(refProduct,
                                                  *(m,deref(refProduct)));
                                           (productLoop +(m,1) n refProduct)
                                     end
                                else 0
       define avg = proc (m n) /(+(m,n),2)
       define loop = proc (m n k expected)
                       if >(k,0)
                          then if =((product m n), expected)
                                 then (loop m n -(k,1) expected)
                                 else -(0,k)
                          else expected
       (loop 1 21 1000 2432902008176640000)

Even though that code appears to work, it still contains a latent bug caused by a race condition. Here is one way to fix it:

       define product = proc (m n)
                          (productFork m n newref(1) newref(0))
       define productFork = proc (m n refProduct refDone)
                              begin choose((productLoop m
                                                        (avg m n)
                                                        refProduct),
                                           (productLoop (avg m n)
                                                        n
                                                        refProduct));
                                    (productJoin refProduct refDone)
                              end
       define productJoin = proc (refProduct refDone)
                              if =(0,deref(refDone))
                                then if =(0,cas(refDone,0,1))
                                       then fail
                                       else (productJoin refProduct refDone)
                                else deref(refProduct)
       define productLoop = proc (m n refProduct)
                              if <(m,n)
                                then begin (productLoop2 m
                                                         n
                                                         refProduct
                                                         deref(refProduct));
                                           (productLoop +(m,1) n refProduct)
                                     end
                                else 0
       define productLoop2 = proc (m n refProduct p)
                               if =(p,cas(refProduct,p,*(m,p)))
                                 then true
                                 else (productLoop2 m
                                                    n
                                                    refProduct
                                                    deref(refProduct))
       define avg = proc (m n) /(+(m,n),2)
       (product 1 21)

Interpreter for SIMPLE with Imperative Threads


Threads in Java


Last updated 7 April 2008.

Valid XHTML 1.0!