package org.multiverse.stms.gamma.integration.blocking;

import org.junit.Before;
import org.junit.Test;
import org.multiverse.TestThread;
import org.multiverse.TestUtils;
import org.multiverse.api.Txn;
import org.multiverse.api.callables.TxnVoidCallable;
import org.multiverse.api.references.TxnLong;

import static org.multiverse.TestUtils.joinAll;
import static org.multiverse.TestUtils.startAll;
import static org.multiverse.api.StmUtils.*;
import static org.multiverse.api.TxnThreadLocal.clearThreadLocalTxn;

public class ManyListenersStressTest {

    private int refCount = 10;
    private int threadCount = 5;
    private volatile boolean stop;
    private TxnLong[] refs;
    private StressThread[] threads;

    @Before
    public void setUp() {
        clearThreadLocalTxn();
        stop = false;

        threads = new StressThread[threadCount];
        for (int k = 0; k < threadCount; k++) {
            threads[k] = new StressThread(k + 1, k == threadCount - 1 ? 1 : k + 2);
        }

        refs = new TxnLong[refCount];
        for (int k = 0; k < refCount; k++) {
            refs[k] = newTxnLong(0);
        }
    }

    @Test
    public void test() {
        refs[0].atomicSet(1);

        startAll(threads);
        TestUtils.sleepMs(30000);
        stop = true;
        refs[0].atomicSet(-1);
        joinAll(threads);
    }

    class StressThread extends TestThread {
        private int wakeup;
        long count;
        private int signal;

        public StressThread(int wakeup, int signal) {
            super("StressThread-" + wakeup);
            this.wakeup = wakeup;
            this.signal = signal;
        }

        @Override
        public void doRun() throws Exception {
            while (!stop) {
                atomic(new TxnVoidCallable() {
                    @Override
                    public void call(Txn tx) throws Exception {
                        for (TxnLong ref : refs) {
                            final long value = ref.get();

                            if (value == -1) {
                                return;
                            }

                            if (value == wakeup) {
                                ref.set(0);
                                refs[TestUtils.randomInt(refs.length)].set(signal);
                                return;
                            }
                        }

                        retry();
                    }
                });
                count++;
                if (count % 10000 == 0) {
                    System.out.printf("%s is at %s\n", getName(), count);
                }
            }
        }
    }
}
