/**
 * Object monitor signal/wait
 *
 * Demonstrates object monitor signal/wait thread synchronization.
 */

class Store
{
    /**
     * Put
     *
     * params:  value
     * return:  
     */
    public synchronized method put (value)
    {
        while (@available == true) {
            try {
                System.Concurrent.Monitor.wait(self)
            } else {
                print(last_excp())
            }
        }

        @seq = value
        true^@available

        System.Concurrent.Monitor.signal(self)
    }

    
    /**
     * Get
     *
     * params:  
     * return:  value
     */
    public synchronized method get ()
    {
        while (@available == false) {
            try {
                System.Concurrent.Monitor.wait(self)
            } else {
                print(last_excp())
            }
        }

        false^@available

        System.Concurrent.Monitor.signal(self)

        @seq
    }

    
    /**
     * Initialize
     *
     * params:  
     * return:  
     */
    method initialize ()
    {
        field @seq = 0
        field @available =false
    }
}

class Producer
{
    /**
     * Run
     *
     * params:  
     * return:  
     */
    method run ()
    {
        for (i; 0..9) {
            @store.put(i)
            print("Producer #" + @id + " put: " + i)
            System.sleep(System.random(0,9) * 10)
        }
    }

    
    /**
     * Start
     *
     * params:  
     * return:  thread (Thread)
     */
    public method start ()
    {
        thread = System.Concurrent.Thread.new(get_method("run"))

        thread.start(self)

        // return
        thread
    }

  
    /**
     * Initialize
     *
     * params:  id
     *          store
     * return:  
     */
    method initialize (id, store)
    {
        field @id = id
        field @store = store
    }
}

class Consumer
{
    /**
     * Run
     *
     * params:  
     * return:  
     */
    method run ()
    {
        for (i; 0..9) {
            value = @store.get()
            print("Consumer #" + @id + " got: " + value)
        }
    }

    
    /**
     * Start
     *
     * params:  
     * return:  thread (Thread)
     */
    public method start ()
    {
        thread = System.Concurrent.Thread.new(get_method("run"))

        thread.start(self)

        // return
        thread
    }

    
    /**
     * Initialize
     *
     * params:  id
     *          store
     * return:  
     */
    method initialize (id, store)
    {
        field @id = id
        field @store = store
    }
}


/**
 * Main
 *
 * params:  
 * return:  
 */
method main ()
{
    store = Store.new()
    producer = Producer.new(1, store)
    consumer = Consumer.new(1, store)
    threads = Array.new()

    threads.add(producer.start())
    threads.add(consumer.start())

    // join threads
    for (i,t; threads) {
        t.join()
    }
}

main()