Producer/Consumer Threads Back to the Win32 Shootout
Back to dada's perl lab

[The Original Shootout]   [NEWS]   [FAQ]   [Methodology]   [Platform Details]   [Acknowledgements]   [Scorecard]  
All Source For Producer/Consumer Threads
prodcons.bigforth
\ $Id: prodcons.bigforth,v 1.1 2001/06/20 20:55:43 doug Exp $
\ http://www.bagley.org/~doug/shootout/
\ from Bernd Paysan

\ read NUM from last command line argument
0. argc @ 1- arg >number 2drop drop constant NUM

Variable count
Variable data
Variable produced
Variable consumed

\ note: no mutex is needed here. bigFORTH's tasker is cooperative
\ and switches tasks only with PAUSE.

: producer 
  up@ swap 2 $1000 dup NewTask pass
  0 ?DO
     BEGIN  count @ 1 =  WHILE  pause  REPEAT
     1 count ! I data !
     1 produced +!
  LOOP wake ;

: consumer 
  up@ swap 2 $1000 dup NewTask pass
  0 swap 0 ?DO
     BEGIN  count @ 0=  WHILE  pause  REPEAT
     0 count ! drop data @
     1 consumed +!
  LOOP drop wake ;

NUM producer
NUM consumer

\ There is no "main" task - to synchronize, each of the two new
\ threads get the task address of the starting task, and wake it
\ when they are done. The main task therefore has to stop twice
\ (and wait to be woken up)

stop stop

produced @ .
consumed @ 1 u.r cr

bye \ th-th-that's all folks!
prodcons.csharp
// $Id: prodcons.csharp,v 1.0 2002/04/30 17:25:00 dada Exp $
// http://dada.perl.it/shootout/

// based on a sample from the Microsoft .NET SDK Documentation

using System;
using System.Threading;
using System.Collections;

class prodcons {

    private int   m_produced = 0;
    private int   m_consumed = 0;
    private int   m_count = 0;
    private Queue m_smplQueue;
    
    public prodcons(int count) {
        m_count = count;
        m_smplQueue = new Queue(); 
    }

    public void Producer() {
        lock(m_smplQueue) {
            while(m_produced < m_count) {
               //Wait, if the queue is busy.
               Monitor.Wait(m_smplQueue);
               //Push one element.
               m_smplQueue.Enqueue(m_produced);
               //Release the waiting thread.
               Monitor.Pulse(m_smplQueue);   
               m_produced++;
            }
        }
    }

    public void Consumer() {

        while(m_consumed < m_count) {
            lock(m_smplQueue) {
                Monitor.Pulse(m_smplQueue);
                while(Monitor.Wait(m_smplQueue,1000)) {
                    //Pop the first element.
                    m_smplQueue.Dequeue();
                    //Release the waiting thread.
                    Monitor.Pulse(m_smplQueue);
                    m_consumed++;
                }
            }
        }
    }
   
    public void run() {         
         //Create the first thread.
         Thread tProducer = new Thread(new ThreadStart(this.Producer));
         //Create the second thread.
         Thread tConsumer = new Thread(new ThreadStart(this.Consumer));
         //Start threads.
         tProducer.Start();
         tConsumer.Start();
         //wait to the end of the two threads
         tProducer.Join();
         tConsumer.Join();         
         //Print the number of the queue elements.
         Console.WriteLine(this.m_produced.ToString() + " " + this.m_consumed.ToString());     
    }
}

class App {
    public static int Main(String[] args) {
        int n;
        n = System.Convert.ToInt32(args[0]);
        if(n < 1) n = 1;
        new prodcons(n).run();
        return 0;
    }
}
prodcons.cygperl
#!/usr/local/test/bin/perl
# $Id: prodcons.perl,v 1.2 2001/01/19 04:29:45 doug Exp $
# http://www.bagley.org/~doug/shootout/

use strict;
use Thread qw(cond_wait cond_signal);

my $count = 0;
my $data = 0;
my $produced = 0;
my $consumed = 0;

sub consumer {
    my $n = shift;
    while (1) {
    lock($count);
    cond_wait($count) while ($count == 0);
    my $i = $data;
    $count = 0;
    $consumed++;
    last if ($i == $n);
    cond_signal($count);
    }
}

sub producer {
    my $n = shift;
    for (my $i=1; $i<=$n; $i++) {
    lock($count);
    cond_wait($count) while ($count == 1);
    $data = $i;
    $count = 1;
    $produced++;
    cond_signal($count);
    }
}

sub main {
    my $n = ($ARGV[0] < 1) ? 1 : $ARGV[0];
    my $p = Thread->new(\&producer, $n);
    my $c = Thread->new(\&consumer, $n);
    $p->join;
    $c->join;
    print "$produced $consumed\n";
}

&main();
prodcons.erlang
%%% $Id: prodcons.erlang,v 1.0 2002/10/21 16:58:00 dada Exp $
%%% http://dada.perl.it/shootout
%%%
%%% contributed by James Hague

-module(prodcons).
-export([main/0, main/1, producer/2, consumer/1]).

main() -> main(['1']).
main([Arg]) ->
    N = list_to_integer(atom_to_list(Arg)),
    spawn(prodcons, producer, [self(), N]),
    receive
        {Produced, Consumed} ->
            io:fwrite("~w ~w~n", [Produced, Consumed]),
            halt(0)
    end.

producer(From, N) ->
    Result = producer_loop(spawn(prodcons, consumer, [0]), 0, N),
    From ! Result.

producer_loop(Consumer, Produced, 0) ->
    Consumer ! {self(), stop},
    receive
        Consumed -> {Produced, Consumed}
    end;
producer_loop(Consumer, Produced, N) ->
    Consumer ! {self(), N},
    receive
        ok -> producer_loop(Consumer, Produced + 1, N - 1)
    end.

consumer(Consumed) ->
    receive
        {From, stop} ->
            From ! Consumed;
        {From, I} ->
            From ! ok,
            consumer(Consumed + 1)
    end.

prodcons.gcc
/* -*- mode: c -*-
 * $Id: prodcons.gcc,v 1.3 2001/05/27 18:25:11 doug Exp $
 * http://www.bagley.org/~doug/shootout/
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <sys/types.h>
#include <pthread.h>

pthread_mutex_t mutex;
pthread_cond_t control;
void producer(unsigned int *arg);
void consumer(unsigned int *arg);
unsigned int count, data, consumed, produced;


int
main(int argc, char *argv[]) {
    unsigned int n = ((argc == 2) ? atoi(argv[1]) : 1);
    pthread_t t1, t2;
    
    count = data = consumed = produced = 0;

    if (pthread_mutex_init(&mutex, NULL)) {
    perror("pthread_mutex_init");
    exit(1);
    }
    if (pthread_cond_init(&control, NULL)) {
    perror("pthread_cond_init");
    exit(1);
    }
    if (pthread_create(&t1, (pthread_attr_t *)NULL,
               (void * (*)(void *))producer, (void *)&n)) {
    perror("pthread_create");
    exit(1);
    }
    if (pthread_create(&t2, (pthread_attr_t *)NULL,
               (void * (*)(void *))consumer, (void *)&n)) {
    perror("pthread_create");
    exit(1);
    }
  
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    fprintf(stdout, "%d %d\n", produced, consumed);
    return(0);
}


void producer(unsigned int *arg) {
    unsigned int i, n = *arg;
    for (i=1; i<=n; i++) {
    pthread_mutex_lock(&mutex);
    while (count == 1) {
        pthread_cond_wait(&control, &mutex);
    }
    data = i;
    count = 1;
    pthread_cond_signal(&control);
    pthread_mutex_unlock(&mutex);
    produced++;
    }
}
 

void consumer(unsigned int *arg) {
    unsigned int i = 0, n = *arg;
    while (1) {
    pthread_mutex_lock(&mutex);
    while (count == 0) {
        pthread_cond_wait(&control, &mutex);
    }
    i = data;
    count = 0;
    pthread_cond_signal(&control);
    pthread_mutex_unlock(&mutex);
    consumed++;
    if (i == n) return;
    }
}

prodcons.gforth
\ $Id: prodcons.gforth,v 1.1 2001/06/20 20:55:44 doug Exp $
\ http://www.bagley.org/~doug/shootout/
\ from Bernd Paysan

require tasker.fs

\ read NUM from last command line argument
0. argc @ 1- arg >number 2drop drop constant NUM

Variable pcount
Variable data
Variable produced
Variable consumed

\ note: no mutex is needed here. bigFORTH's tasker is cooperative
\ and switches tasks only with PAUSE.

: producer 
  next-task swap 2 $1000 NewTask pass
  0 ?DO
     BEGIN  pcount @ 1 =  WHILE  pause  REPEAT
     1 pcount ! I data !
     1 produced +!
  LOOP wake ;

: consumer 
  next-task swap 2 $1000 NewTask pass
  0 swap 0 ?DO
     BEGIN  pcount @ 0=  WHILE  pause  REPEAT
     0 pcount ! drop data @
     1 consumed +!
  LOOP drop wake ;

NUM producer
NUM consumer

\ There is no "main" task - to synchronize, each of the two new
\ threads get the task address of the starting task, and wake it
\ when they are done. The main task therefore has to stop twice
\ (and wait to be woken up)

stop stop

produced @ .
consumed @ 1 u.r cr

bye \ th-th-that's all folks!
prodcons.ghc
-- $Id: prodcons.ghc,v 1.1 2001/02/28 01:08:27 doug Exp $
-- http://www.bagley.org/~doug/shootout/
-- from Josef Svenningsson
 
module Main where

import CVar
import MVar
import Concurrent
import Exception
import IOExts
import System

producer :: Int -> IORef Int -> CVar Int -> IO ()
producer n p ch = sequence_ (map send [1..n])
  where send i = do writeCVar ch i
                    prod <- readIORef p
                    writeIORef p (prod+1)

consumer :: Int -> IORef Int -> CVar Int -> IO ()
consumer n c ch = cons 1
  where cons i | n <= i = return ()
        cons i
            = do i <- readCVar ch
                 con <- readIORef c
                 writeIORef c (con+1)
                 cons i

myForkIO :: IO () -> IO (MVar ())
myForkIO io = do
   mvar <- newEmptyMVar
   forkIO (io `finally` putMVar mvar ())
   return mvar

join :: MVar () -> IO ()
join mvar = readMVar mvar

main = do (a:_) <- getArgs
          let n = read a
          produced <- newIORef 0
          consumed <- newIORef 0
          channel <- newCVar
          p <- myForkIO (producer n produced channel)
          c <- myForkIO (consumer n consumed channel)
          join p; join c
          prod <- readIORef produced
          cons <- readIORef consumed
          putStrLn (show prod ++ " " ++ show cons)
prodcons.gnat
-- $Id: prodcons.gnat,v 1.0 2003/06/11 12:10:00 dada Exp $
-- http://dada.perl.it/shootout/
-- Ada 95 code by C.C.

with Ada.Strings.Fixed, Ada.Command_Line, Text_IO;

procedure ProdCons is

   type Data_Type is new Integer;
   End_Of_Data    : constant Data_Type := Data_Type'First;

   protected Queue is
      entry Put (Data : Data_Type);
      entry Get (Data_Out : out Data_Type);
   private
      Count          : Natural := 0;
      Buffer         : Data_Type;
   end Queue;

   protected body Queue is
      entry Put (Data : Data_Type)
         when Count = 0 is
      begin
         Buffer := Data;
         Count := Count + 1;
      end Put;

      entry Get (Data_Out : out Data_Type)
         when Count /= 0 is
      begin
         Data_Out := Buffer;
         Count := Count - 1;
      end Get;
   end Queue;

   Produced, Consumed : Natural := 0;

   task type Producer_Task (N : Natural);
   task type Consumer_Task (N : Natural);

   task body Producer_Task is
   begin
      for Data_K in 1 .. Data_Type (N) loop
         Queue.Put (Data => Data_K);
         Produced := Produced + 1;
      end loop;
      Queue.Put (Data => End_Of_Data);
   end Producer_Task;

   task body Consumer_Task is
      Data        : Data_Type;
   begin
      loop
         Queue.Get (Data_Out => Data);
         exit when Data = End_Of_Data;
         Consumed := Consumed + 1;
      end loop;
   end Consumer_Task;

   function L_Trim (Source : String; Side : Ada.Strings.Trim_End :=
               Ada.Strings.Left) return String renames Ada.Strings.Fixed.Trim;
   N        : Natural := 0;
begin
   begin
      N := Natural'Value (Ada.Command_Line.Argument (1));
   exception
      when Constraint_Error => null;
   end;
   declare
      Producer    : Producer_Task (N => N);
      Consumer    : Consumer_Task (N => N);
   begin
      null;
   end;
   Text_IO.Put_Line (L_Trim (Natural'Image (Produced)) &
            Natural'Image (Consumed));
end ProdCons;

prodcons.guile
#!/usr/local/bin/guile \
-e main -s
!#

;;; $Id: prodcons.guile,v 1.3 2001/06/29 23:12:37 doug Exp $
;;; http://www.bagley.org/~doug/shootout/

(use-modules (ice-9 threads))

(define mutex (make-mutex))
(define access (make-condition-variable))
(define count 0)
(define data 0)
(define produced 0)
(define consumed 0)

;; the consumer thread definition seems wrong
;; how does it ever stop/get joined?
(define (consumer n)
  (let ((i 0))
    (while #t
     (lock-mutex mutex)
     (while (= count 0)
        (wait-condition-variable access mutex))
     (set! i data)
     (set! count 0)
     (signal-condition-variable access)
     (unlock-mutex mutex)
     (set! consumed (+ consumed 1)))))

(define (producer n)
  (do ((i 1 (+ i 1)))
      ((>; i n))
    (lock-mutex mutex)
    (while (= count 1)
      (wait-condition-variable access mutex))
    (set! data i)
    (set! count 1)
    (signal-condition-variable access)
    (unlock-mutex mutex)
    (set! produced (+ produced 1))))

(define (main args)
  (let ((n (or (and (= (length args) 2) (string->;number (cadr args))) 1)))
    (let ((c (make-thread (lambda () (consumer n)))))
      (producer n)
      (join-thread c)
      (display produced) (display " ") (display consumed) (newline))))
prodcons.ici
// $Id: prodcons.ici,v 1.0 2003/01/03 12:06:00 dada Exp $
// http://dada.perl.it/shootout
//
// contributed by Tim Long

static n = argv[1] ? int(argv[1]) : 1;
static count = 0;
static consumed = 0;
static produced = 0;
static data = 0;

static
producer()
{
    for (i := 1; i <= n; ++i)
    {
        waitfor (count == 0; "access")
        {
            data = i;
            count = 1;
            wakeup("access");
        }
        ++produced;
    }
    return 1;
}

static
consumer()
{
    do
    {
        waitfor (count != 0; "access")
        {
            i = data;
            count = 0;
            wakeup("access");
        }
        ++consumed;

    } while (i != n);
    return 1;
}

p := thread(producer);
c := thread(consumer);
waitfor (p.result; p)
    ;
waitfor (c.result; c)
    ;
printf("%d %d\n", produced, consumed);
prodcons.java
// $Id: prodcons.java,v 1.1 2000/12/20 13:41:33 doug Exp $
// http://www.bagley.org/~doug/shootout/
// Producer-Consumer Example by Bill Lear
// Adapted from http://java.sun.com/docs/books/tutorial/essential/threads

public class prodcons {
    private class CubbyHole {
        private int m_contents;
        private boolean m_available = false;

        public synchronized int get() {
            while (m_available == false) {
                try {
                    wait();
                } catch (InterruptedException e) { }
            }
            m_available = false;
            notifyAll();
            return m_contents;
        }

        public synchronized void put(int value) {
            while (m_available == true) {
                try {
                    wait();
                } catch (InterruptedException e) { }
            }
            m_contents = value;
            m_available = true;
            notifyAll();
        }
    }

    private class Producer extends Thread {
        private CubbyHole m_cubbyhole;
        private int m_count;

        public Producer(CubbyHole c, int count) {
            m_cubbyhole = c;
            m_count = count;
        }

        public void run() {
            for (int i = 0; i < m_count; i++) {
                m_cubbyhole.put(i);
                ++m_produced;
            }
        }
    }

    private class Consumer extends Thread {
        private CubbyHole m_cubbyhole;
        private int m_count;

        public Consumer(CubbyHole c, int count) {
            m_cubbyhole = c;
            m_count = count;
        }

        public void run() {
            int value = 0;
            for (int i = 0; i < m_count; i++) {
                value = m_cubbyhole.get();
                ++m_consumed;
            }
        }
    }

    public void run() {
        m_producer.start();
        m_consumer.start();
        try { m_producer.join(); } catch (InterruptedException e) { }
        try { m_consumer.join(); } catch (InterruptedException e) { }
        System.out.println(m_produced + " " + m_consumed);
    }

    public prodcons(int count) {
        CubbyHole m_cubbyhole = new CubbyHole();
        m_producer = new Producer(m_cubbyhole, count);
        m_consumer = new Consumer(m_cubbyhole, count);
    }

    public static void main(String[] args) {
        int count = 1;
        try { count = Integer.parseInt(args[0]); } catch (Exception e) { }
        new prodcons(count).run();
    }

    private Producer m_producer;
    private Consumer m_consumer;
    private int m_produced = 0;
    private int m_consumed = 0;
}
prodcons.mingw32
/* -*- mode: c -*-
 * $Id: prodcons.gcc,v 1.3 2001/05/27 18:25:11 doug Exp $
 * http://www.bagley.org/~doug/shootout/
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <sys/types.h>
#include <pthread.h>

pthread_mutex_t mutex;
pthread_cond_t control;
void producer(unsigned int *arg);
void consumer(unsigned int *arg);
unsigned int count, data, consumed, produced;


int
main(int argc, char *argv[]) {
    unsigned int n = ((argc == 2) ? atoi(argv[1]) : 1);
    pthread_t t1, t2;
    
    count = data = consumed = produced = 0;

    if (pthread_mutex_init(&mutex, NULL)) {
    perror("pthread_mutex_init");
    exit(1);
    }
    if (pthread_cond_init(&control, NULL)) {
    perror("pthread_cond_init");
    exit(1);
    }
    if (pthread_create(&t1, (pthread_attr_t *)NULL,
               (void * (*)(void *))producer, (void *)&n)) {
    perror("pthread_create");
    exit(1);
    }
    if (pthread_create(&t2, (pthread_attr_t *)NULL,
               (void * (*)(void *))consumer, (void *)&n)) {
    perror("pthread_create");
    exit(1);
    }
  
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    fprintf(stdout, "%d %d\n", produced, consumed);
    return(0);
}


void producer(unsigned int *arg) {
    unsigned int i, n = *arg;
    for (i=1; i<=n; i++) {
    pthread_mutex_lock(&mutex);
    while (count == 1) {
        pthread_cond_wait(&control, &mutex);
    }
    data = i;
    count = 1;
    pthread_cond_signal(&control);
    pthread_mutex_unlock(&mutex);
    produced++;
    }
}
 

void consumer(unsigned int *arg) {
    unsigned int i = 0, n = *arg;
    while (1) {
    pthread_mutex_lock(&mutex);
    while (count == 0) {
        pthread_cond_wait(&control, &mutex);
    }
    i = data;
    count = 0;
    pthread_cond_signal(&control);
    pthread_mutex_unlock(&mutex);
    consumed++;
    if (i == n) return;
    }
}

prodcons.ocaml
(*
 * $Id: prodcons.ocaml,v 1.6 2001/07/28 21:52:59 doug Exp $
 * http://www.bagley.org/~doug/shootout/
 *
 * ocamlopt -thread unix.cmxa threads.cmxa prodcons.ml -o prodcons
 *  or
 * ocamlc -thread unix.cma threads.cma prodcons.ml -o prodcons
 *)

let count = ref 0
let data = ref 0
let produced = ref 0
let consumed = ref 0
let m = Mutex.create ()
let c = Condition.create ()

let producer n =
  for i = 1 to n do
    Mutex.lock m;
    while !count = 1 do Condition.wait c m done;
    data := i;
    incr count;
    Condition.signal c;
    Mutex.unlock m;
    incr produced
  done

let consumer n =
  let i = ref 0 in
  while !i <> n do
    Mutex.lock m;
    while !count = 0 do Condition.wait c m done;
    i := !data;
    decr count;
    Condition.signal c;
    Mutex.unlock m;
    incr consumed
  done

let n = if Array.length Sys.argv > 1 then int_of_string Sys.argv.(1) else 1
let p = Thread.create producer n and c = Thread.create consumer n;;
Thread.join p; Thread.join c;
Printf.printf "%d %d\n" !produced !consumed
prodcons.ocamlb
(*
 * $Id: prodcons.ocaml,v 1.6 2001/07/28 21:52:59 doug Exp $
 * http://www.bagley.org/~doug/shootout/
 *
 * ocamlopt -thread unix.cmxa threads.cmxa prodcons.ml -o prodcons
 *  or
 * ocamlc -thread unix.cma threads.cma prodcons.ml -o prodcons
 *)

let count = ref 0
let data = ref 0
let produced = ref 0
let consumed = ref 0
let m = Mutex.create ()
let c = Condition.create ()

let producer n =
  for i = 1 to n do
    Mutex.lock m;
    while !count = 1 do Condition.wait c m done;
    data := i;
    incr count;
    Condition.signal c;
    Mutex.unlock m;
    incr produced
  done

let consumer n =
  let i = ref 0 in
  while !i <> n do
    Mutex.lock m;
    while !count = 0 do Condition.wait c m done;
    i := !data;
    decr count;
    Condition.signal c;
    Mutex.unlock m;
    incr consumed
  done

let n = if Array.length Sys.argv > 1 then int_of_string Sys.argv.(1) else 1
let p = Thread.create producer n and c = Thread.create consumer n;;
Thread.join p; Thread.join c;
Printf.printf "%d %d\n" !produced !consumed
prodcons.oz
%%% $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

prodcons.perl
#!/usr/local/test/bin/perl
# $Id: prodcons.perl,v 1.2 2001/01/19 04:29:45 doug Exp $
# http://www.bagley.org/~doug/shootout/

use strict;
use Thread qw(cond_wait cond_signal);

my $count = 0;
my $data = 0;
my $produced = 0;
my $consumed = 0;

sub consumer {
    my $n = shift;
    while (1) {
    lock($count);
    cond_wait($count) while ($count == 0);
    my $i = $data;
    $count = 0;
    $consumed++;
    last if ($i == $n);
    cond_signal($count);
    }
}

sub producer {
    my $n = shift;
    for (my $i=1; $i<=$n; $i++) {
    lock($count);
    cond_wait($count) while ($count == 1);
    $data = $i;
    $count = 1;
    $produced++;
    cond_signal($count);
    }
}

sub main {
    my $n = ($ARGV[0] < 1) ? 1 : $ARGV[0];
    my $p = Thread->new(\&producer, $n);
    my $c = Thread->new(\&consumer, $n);
    $p->join;
    $c->join;
    print "$produced $consumed\n";
}

&main();
prodcons.pike
#!/usr/local/bin/pike// -*- mode: pike -*-
// $Id: prodcons.pike,v 1.3 2000/12/20 05:03:58 doug Exp $
// http://www.bagley.org/~doug/shootout/

inherit Thread.Condition: access;
inherit Thread.Mutex: mutex;
int data, consumed, produced, count;

void producer(int n) {
    for (int i=1; i<=n; i++) {
    object mtx = mutex::lock();
    while (count != 0) access::wait(mtx);
    data = i;
    count += 1;
    destruct(mtx);
    access::signal();
    produced += 1;
    }
}

void consumer(int n) {
    while (1) {
    object mtx = mutex::lock();
    while (count == 0) access::wait(mtx);
    int i = data;
    count -= 1;
    access::signal();
    destruct(mtx);
    consumed += 1;
    if (i == n) break;
    }
}

void main(int argc, array(string) argv) {
    int n = (int)argv[-1];
    if (n < 1) n = 1;
    data = consumed = produced = count = 0;
    thread_create(producer, n);
    consumer(n);
    write("%d %d\n", produced, consumed);
}
prodcons.pliant
# $Id: prodcons.pliant,v 1.0 2002/02/25 11:58:00 dada Exp $
# http://dada.perl.it/shootout/

module "/pliant/language/context.pli"

gvar Sem s
gvar Int count := 0
gvar Int data := 0
gvar Int produced := 0
gvar Int consumed := 0
gvar Int done := 0

function consumer n
  arg Int n
  var Int i
  part forever
    part consumer_wait
      if(count = 0)
        restart consumer_wait
      leave consumer_wait
    i := data
    count := 0
    # console "consuming " i eol
    consumed := consumed + 1
    if (i = n)
      leave forever
    restart forever

function producer n
  arg Int n
  for (var Int i) 1 n
    part producer_wait
      if(count = 1)
        restart producer_wait
      leave producer_wait
    data := i
    count := 1
    # console "producing " i eol
    produced := produced + 1
  done := 1

gvar Str s_n := cast ((pliant_script_args translate Address 1) map CStr) Str
if (s_n parse (gvar Int n))
  thread
    # console "starting producer thread..." eol
    share produced
    share data
    share count
    share s
    share done
    producer n
  thread
    # console "starting consumer thread..." eol  
    share consumed
    share data
    share count
    share s 
    consumer n
  part wait_done
    if (done = 1)
      leave wait_done      
    restart wait_done  
  console produced " " consumed eol
  s release
else
  console "usage: prodcons.pliant <number>" eol
prodcons.poplisp
;;; -*- mode: lisp -*-
;;; $Id: prodcons.cmucl,v 1.1 2001/04/26 03:53:05 doug Exp $
;;; http://www.bagley.org/~doug/shootout/
;;; From Jochen Schmidt

(defparameter *counter* 0)
(defparameter *produced* 0)
(defparameter *consumed* 0)
(defparameter *data* 0)
(defparameter *mutex* (mp:make-lock "Big Lock"))

(defun producer (n)
  (declare (optimize (speed 3) (safety 0))
           (fixnum n))
  (loop :for i :of-type fixnum :from 1 :to n
        :do 
        (mp:process-wait "Producer is waiting on Consumer" #'(lambda () (= *counter* 0)))
        (mp:with-lock-held (*mutex*)
          (setf *data* i
                    *counter* 1))
        (incf *produced*)))

(defun consumer (n)
  (declare (optimize (speed 3) (safety 0))
           (fixnum n))
  (let ((i 0))
    (declare (fixnum i))
    (loop
     (mp:process-wait "Consumer is waiting on Producer" #'(lambda () (= *counter* 1)))
     (mp:with-lock-held (*mutex*)
       (setf i *data*
             *counter* 0))
     (incf *consumed*)
     (when (= i n)
       (return)))))

  (let ((n (parse-integer (or (car pop11::poparglist) "1"))))
    (declare (optimize (speed 3) (safety 0))
         (fixnum n))
    (setf *counter* 0
      *produced* 0
      *consumed* 0
      *data* 0)
    (let ((producer (mp:make-process #'(lambda () (funcall #'producer n)) :name "Producer"))
      (consumer (mp:make-process #'(lambda () (funcall #'consumer n)) :name "Consumer")))
      (mp:process-wait "Wait on Producer" #'(lambda () (eq (mp:process-state producer) :killed)))
      (mp:process-wait "Wait on Consumer" #'(lambda () (eq (mp:process-state consumer) :killed)))
      (format t "~A ~A~%" *produced* *consumed*))
prodcons.python
#!/usr/local/bin/python
# $Id: prodcons.python,v 1.1 2000/12/20 04:33:20 doug Exp $
# http://www.bagley.org/~doug/shootout/

import sys
from threading import * 

access = Condition()
count = 0
consumed = 0
produced = 0
data = 0

def consumer(n):
    global count, data, consumed
    while 1:
        access.acquire()
        while count == 0:
            access.wait()
        i = data
        count = 0
        access.notify()
        access.release()
        consumed += 1
        if i == n:
            break

def producer(n):
    global count, data, produced
    for i in xrange(1,n+1):
        access.acquire()
        while count == 1:
            access.wait()
        data = i
        count = 1
        access.notify()
        access.release()
        produced += 1

def main(n):
    t1 = Thread(target=producer, args=(n,))
    t2 = Thread(target=consumer, args=(n,))
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print produced, consumed
    
main(int(sys.argv[1]))


prodcons.ruby
#!/usr/local/bin/ruby
# -*- mode: ruby -*-
# $Id: prodcons.ruby,v 1.2 2000/12/20 04:33:20 doug Exp $
# http://www.bagley.org/~doug/shootout/

require 'thread'

def main(n)
    mutex = Mutex.new
    access = ConditionVariable.new
    count = data = consumed = produced = 0
    consumer = Thread.new do
    i = 0
    loop do
        mutex.synchronize {
        while count == 0 do access.wait(mutex) end
        i = data
        count = 0
        access.signal
        }
        consumed += 1
        if i == n then break end
    end
    end
    producer = Thread.new do
    for i in 1 .. n do
        mutex.synchronize {
        while count == 1 do access.wait(mutex) end
        data = i
        count = 1
        access.signal
        }
        produced += 1
    end
    end
    producer.join
    consumer.join
    puts "#{produced} #{consumed}"
end

main(Integer(ARGV.shift || 1))
prodcons.smlnj
(* -*- mode: sml -*-
 * $Id: prodcons.smlnj,v 1.1 2001/09/01 01:40:21 doug Exp $
 * http://www.bagley.org/~doug/shootout/
 * from Matthias Blume
 *)

(* producer-consumer threads in SML/NJ
 * (concurrency primitives re-implemented "by hand" on top of call/cc
 * using the code in John Reppy's book "Concurrent Programming in ML")
 *
 * (C) 2001 Lucent Technologies, Bell Labs
 * written by Matthias Blume
 *)

structure Queue :> sig
    exception Empty
    type tt = unit SMLofNJ.Cont.cont
    type q
    val new : unit -> q
    val enqueue : q * tt -> unit
    val dequeue : q -> tt
    val empty : q -> bool
end = struct

    exception Empty

    type tt = unit SMLofNJ.Cont.cont
    type q = tt list ref * tt list ref

    fun new () : q = (ref [], ref [])

    fun enqueue ((f as ref [], ref []) : q, x) = f := [x]
      | enqueue ((_, b as ref xs), x) = b := x :: xs

    fun dequeue ((f, b) : q) =
    case !f of
        [] => (case rev (!b) of
               x :: xs => (f := xs; b := []; x)
             | [] => raise Empty)
      | x :: xs => (f := xs; x)

    fun empty ((ref [], ref []) : q) = true
      | empty _ = false

end

structure Mutex :> sig

    val yield : unit -> unit
    val fork : (unit -> unit) -> unit
    val exit : unit -> 'a

    type mutex
    type condition

    val mutex : unit -> mutex
    val lock : mutex -> unit
    val unlock : mutex -> unit

    val condition : mutex -> condition
    val wait : condition -> unit
    val signal : condition -> unit

    val run : (unit -> unit) * Time.time -> unit

end = struct

    local
    structure C = SMLofNJ.Cont
    structure Q = Queue
    type tt = unit C.cont

    (* We take the easy way out: Simply drop signals that
     * arrive during an atomic section on the floor.  This is
     * enough for our purpose and simplifies the coding... *)
    val atomicState = ref false
    fun atomicBegin () = atomicState := true
    fun atomicEnd () = atomicState := false
    val readyQ : Q.q = Q.new ()

    fun dispatch () = C.throw (Q.dequeue readyQ) ()

    fun sigH (_: Signals.signal, _: int, k: tt) =
        if !atomicState then k
        else (Q.enqueue (readyQ, k); Q.dequeue readyQ)
    in
        
        fun yield () =
        (atomicBegin ();
         C.callcc (fn k => (Q.enqueue (readyQ, k); dispatch ()));
         atomicEnd ())

    fun exit () = (atomicBegin (); dispatch ())

    fun fork f = let
        val k = C.isolate (fn () => (atomicEnd ();
                     f () handle _ => ();
                     exit ()))
    in
        atomicBegin ();
        Q.enqueue (readyQ, k);
        atomicEnd ()
    end

    
        datatype mutex = Mutex of { locked : bool ref, blocked : Q.q }

    fun mutex () = Mutex { locked = ref false, blocked = Q.new () }

    fun lock (Mutex { locked, blocked }) =
        (atomicBegin ();
         if !locked then
         C.callcc (fn k => (Q.enqueue (blocked, k);
                    dispatch ()))
         else locked := true;
         atomicEnd ())

    fun unlock (Mutex { locked, blocked }) =
        (atomicBegin ();
         if Q.empty blocked then locked := false
         else C.callcc (fn k => (Q.enqueue (readyQ, k);
                     C.throw (Q.dequeue blocked) ()));
         atomicEnd ())


        
    datatype condition = Cond of { mutex : mutex, waiting : Q.q }

    fun condition m = Cond { mutex = m, waiting = Q.new () }

    fun wait (Cond { mutex = m as Mutex { locked, blocked }, waiting }) =
        (atomicBegin ();
         C.callcc (fn k =>
              (Q.enqueue (waiting, k);
               if Q.empty blocked then (locked := false;
                            dispatch ())
               else C.throw (Q.dequeue blocked) ()));
         if !locked then
         C.callcc (fn k => (Q.enqueue (blocked, k);
                    dispatch ()))
         else locked := true;
         atomicEnd ())

    fun signal (Cond { waiting, ... }) =
        (atomicBegin ();
         if Q.empty waiting then ()
         else Q.enqueue (readyQ, Q.dequeue waiting);
         atomicEnd ())

    fun run (f, t) = let
        val oh = Signals.setHandler (Signals.sigALRM,
                     Signals.HANDLER sigH)
        val _ = SMLofNJ.IntervalTimer.setIntTimer (SOME t)
        fun reset () =
        (ignore (Signals.setHandler (Signals.sigALRM, oh));
         SMLofNJ.IntervalTimer.setIntTimer NONE)
             
    in
        (f () handle e => (reset (); raise e))
        before reset ()
    end
    end
end

structure ProdCons : sig
    val main : string * string list -> OS.Process.status
end = struct

    fun doit n = let

    val c_running = Mutex.mutex ()
    val p_running = Mutex.mutex ()

    val consumer's_turn = ref false
    val data = ref 0
    val produced = ref 0
    val consumed = ref 0
    val m = Mutex.mutex ()
    val c = Mutex.condition m

    fun producer () = let
        fun wait () = if !consumer's_turn then wait (Mutex.wait c) else ()
        fun loop i =
        if i <= n then
            let val _ = Mutex.lock m
            val _ = wait ()
            in
            data := i;
            consumer's_turn := true;
            produced := !produced + 1;
            Mutex.signal c;
            Mutex.unlock m;
            loop (i + 1)
            end
        else ()
    in
        loop 1 before Mutex.unlock p_running
    end
         
    fun consumer () = let
        fun wait () = if !consumer's_turn then () else wait (Mutex.wait c)
        fun loop () = let
        val _ = Mutex.lock m
        val _ = wait ()
        val i = !data
        in
        consumer's_turn := false;
        consumed := !consumed + 1;
        Mutex.signal c;
        Mutex.unlock m;
        if i <> n then loop () else ()
        end
    in
        loop () before Mutex.unlock c_running
    end

    
    val _ = Mutex.lock p_running
    val _ = Mutex.lock c_running

    val p = Mutex.fork producer
    val c = Mutex.fork consumer
    in
    
    Mutex.lock p_running;
    Mutex.lock c_running;

    TextIO.output (TextIO.stdOut,
               concat [Int.toString (!produced), " ",
                   Int.toString (!consumed), "\n"])
    end

    fun main (_, args) = let
    val n = case args of [] => 1
               | (x :: _) => getOpt (Int.fromString x, 1)
    in
    Mutex.run (fn () => doit n, Time.fromMilliseconds 1);
    OS.Process.success
    end
end

val _ = SMLofNJ.exportFn("prodcons", ProdCons.main);