%%% $Id: prodcons.oz,v 1.0 2002/11/05 12:23:00 dada Exp $
%%% http://dada.perl.it/shootout/
%%%
%%% contributed by Isaac Gouy

%%  Section 11.5 of the Oz Tutorial provides these
%%  implementations of Event and UnitBufferM and states:
%%  in Oz, it is very rare to write programs in the 
%%  (traditional) monitor style shown above. In general
%%  it is very awkward.
%%
%%  There's an extensive treatment of Oz concurrency in 
%%  the book 'Concepts, Techniques, and Models of Computer 
%%  Programming' - find it online with google.
%%
%%  Usage: start from command line with
%%     ozc -x prodcons.oz -o prodcons.oz.exe
%%     prodcons.oz.exe 100000

functor
import System Application

define

Produced
Consumed

class Event from BaseObject 
   prop locking
   attr f r
   meth init 
      X in f <- X r <- X
   end 
   meth put(I)
      X in lock @r=I|X r<-X end 
   end 
   meth get(?I)
      X in lock @f=I|X f<-X end {Wait I}
   end
   meth wait 
      {self get(_)}
   end 
   meth notify 
      {self put(unit)}
   end    
end


class UnitBufferM 
   attr item empty psignal csignal
   prop locking
   meth init 
      empty <- true 
      psignal <- {New Event init}
      csignal <- {New Event init}
   end 
   meth put(I)
      X in 
      lock 
         if @empty then 
            item <- I
            empty <- false 
            X = yes
            {@csignal notify}
         else X = no end 
      end 
      if X == no then 
         {@psignal wait}
         {self put(I)}
      end 
   end 
   meth get(I)
      X in 
      lock 
         if {Not @empty} then 
            I = @item
            empty <- true 
            {@psignal notify}
            X = yes
         else X = no end 
      end 
      if X == no then 
         {@csignal wait}
         {self get(I)}
      end 
   end 
end


proc {Producer N I B}
   if N > 0 then
      {B put(I)}
      %% {System.showInfo 'Produced '#I} %% just to check synchronization
      {Producer N-1 I+1 B}
   else Produced = {NewCell I} end
end 


proc {Consumer N I B}
   if N > 0 then
      {B get(I)}
      %% {System.showInfo 'Consumed '#I} %% just to check synchronization
      {Consumer N-1 I+1 B}
   else Consumed = {NewCell I} end
end 


in
   local Args N UB in
      [Args] = {Application.getArgs plain}
      N = {String.toInt Args}

      UB = {New UnitBufferM init}
      thread {Producer N 0 UB} end
      thread {Consumer N 0 UB} end

         %% Oz is a dataflow language.
         %% The main thread will wait until logic variables  
         %% Produced and Consumed have been given values
      {System.showInfo {Access Produced}#' '#{Access Consumed}}
   end
   {Application.exit 0}
end