Package org.neo4j.collections.list

Source Code of org.neo4j.collections.list.TestUnrolledLinkedListConcurrency$StateSynchronizer

/**
* Copyright (c) 2002-2013 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.collections.list;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.junit.Ignore;
import org.junit.Test;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Transaction;

public class TestUnrolledLinkedListConcurrency extends UnrolledLinkedListTestCase
{
    private enum States
    {
        WRITE, READ
    }

    private ExecutorService executorService = Executors.newSingleThreadExecutor();

    @Test
    public void testAddInSamePageHavingReadPastWithReadTransaction() throws Exception
    {
        final ArrayList<Node> nodes = createNodes( 4 );
        final UnrolledLinkedList list = new UnrolledLinkedList( graphDb(), new IdComparator(), 4 );
        final StateSynchronizer sync = new StateSynchronizer( States.class );

        FutureTask<Boolean> reader = new ThrowingFutureTask<Boolean>( new Callable<Boolean>()
        {
            @Override
            public Boolean call() throws Exception
            {
                Transaction tx = graphDb().beginTx();
                try
                {
                    ArrayList<Node> innerNodes = new ArrayList<Node>( nodes.subList( 0, 3 ) );
                    Collections.reverse( innerNodes );
                    assertTrue( sync.wait( States.READ ) );
                    UnrolledLinkedList innerList = new UnrolledLinkedList( list.getBaseNode() );

                    int count = 0;
                    for ( Node node : innerList )
                    {
                        assertEquals( innerNodes.get( count ), node );
                        if ( ++count == 2 )
                        {
                            // wont receive read signal as writer will be waiting to gain a write lock on base node
                            // will timeout then finish its reading process, whereby the writer will gain write lock
                            // and complete its write process
                            assertFalse( sync.signalAndWait( States.WRITE, States.READ, 1, TimeUnit.SECONDS ) );
                        }
                    }

                    tx.success();
                    return true;
                }
                finally
                {
                    tx.finish();
                }
            }
        } );
        executorService.execute( reader );

        int count = 0;
        for ( Node node : nodes )
        {
            list.addNode( node );
            if ( ++count == 3 )
            {
                // commits the nodes added through by the writer so the reader can then see them
                // also releases the write lock held on base node allowing reader thread access
                restartTx();
                assertTrue( sync.signalAndWait( States.READ, States.WRITE ) );
            }
        }
        restartTx();
        sync.signal( States.READ );

        assertTrue( reader.get( 1000, TimeUnit.MILLISECONDS ) );
    }

    @Test
    @Ignore("Not relevant anymore as there are no non-transactional reads")
    public void testAddInSamePageHavingReadPastWithoutReadTransaction() throws Exception
    {
        final ArrayList<Node> nodes = createNodes( 4 );
        final UnrolledLinkedList list = new UnrolledLinkedList( graphDb(), new IdComparator(), 4 );
        final StateSynchronizer sync = new StateSynchronizer( States.class );

        FutureTask<Boolean> reader = new ThrowingFutureTask<Boolean>( new Callable<Boolean>()
        {
            @Override
            public Boolean call() throws Exception
            {
                ArrayList<Node> innerNodes = new ArrayList<Node>( nodes.subList( 0, 3 ) );
                Collections.reverse( innerNodes );
                assertTrue( sync.wait( States.READ ) );
                UnrolledLinkedList innerList = new UnrolledLinkedList( list.getBaseNode() );

                int count = 0;
                for ( Node node : innerList )
                {
                    assertEquals( innerNodes.get( count ), node );
                    if ( ++count == 2 )
                    {
                        // will receive read signal as there will be no read lock against the base node therefore
                        // the writer add node will not block
                        assertTrue( sync.signalAndWait( States.WRITE, States.READ, 1, TimeUnit.SECONDS ) );
                    }
                }

                return true;
            }
        } );
        executorService.execute( reader );

        int count = 0;
        for ( Node node : nodes )
        {
            list.addNode( node );
            if ( ++count == 3 )
            {
                // commits the nodes added through by the writer so the reader can then see them
                // also releases the write lock held on base node allowing reader thread access
                restartTx();
                assertTrue( sync.signalAndWait( States.READ, States.WRITE ) );
            }
        }
        restartTx();
        sync.signal( States.READ );

        assertTrue( reader.get( 1000, TimeUnit.MILLISECONDS ) );
    }

    private static class StateSynchronizer
    {
        private static final long MAX_WAIT_SECONDS = 10;
        private final Lock lock;
        private final Map<Enum, Condition> conditions;
        private Enum state;

        public StateSynchronizer( Class<? extends Enum> states )
        {
            lock = new ReentrantLock();
            conditions = new HashMap<Enum, Condition>();

            for ( Enum state : states.getEnumConstants() )
            {
                conditions.put( state, lock.newCondition() );
            }
        }

        public boolean wait( Enum waitState, long timeout, TimeUnit unit ) throws InterruptedException
        {
            if ( !conditions.containsKey( waitState ) )
            {
                throw new IllegalArgumentException( "Not a valid state: " + waitState );
            }

            lock.lock();
            try
            {
                return waitState.equals( state ) ||
                    conditions.get( waitState ).await( timeout, unit );
            }
            finally
            {
                lock.unlock();
            }
        }

        public boolean wait( Enum waitState ) throws InterruptedException
        {
            if ( !conditions.containsKey( waitState ) )
            {
                throw new IllegalArgumentException( "Not a valid state: " + waitState );
            }

            lock.lock();

            try
            {
                return waitState.equals( state ) ||
                    conditions.get( waitState ).await( MAX_WAIT_SECONDS, TimeUnit.SECONDS );
            }
            finally
            {
                lock.unlock();
            }
        }

        public boolean signalAndWait( Enum state, Enum waitState, long timeout, TimeUnit unit )
            throws InterruptedException
        {
            if ( !conditions.containsKey( state ) )
            {
                throw new IllegalArgumentException( "Not a valid state: " + state );
            }
            if ( !conditions.containsKey( waitState ) )
            {
                throw new IllegalArgumentException( "Not a valid state: " + waitState );
            }

            lock.lock();
            try
            {
                this.state = state;
                conditions.get( state ).signal();
                return conditions.get( waitState ).await( timeout, unit );
            }
            finally
            {
                lock.unlock();
            }
        }

        public boolean signalAndWait( Enum state, Enum waitState ) throws InterruptedException
        {
            if ( !conditions.containsKey( state ) )
            {
                throw new IllegalArgumentException( "Not a valid state: " + state );
            }
            if ( !conditions.containsKey( waitState ) )
            {
                throw new IllegalArgumentException( "Not a valid state: " + waitState );
            }

            lock.lock();
            try
            {
                this.state = state;
                conditions.get( state ).signal();
                return conditions.get( waitState ).await( MAX_WAIT_SECONDS, TimeUnit.SECONDS );
            }
            finally
            {
                lock.unlock();
            }
        }

        public void signal( Enum state )
        {
            if ( !conditions.containsKey( state ) )
            {
                throw new IllegalArgumentException( "Not a valid state: " + state );
            }

            lock.lock();
            try
            {
                this.state = state;
                conditions.get( state ).signal();
            }
            finally
            {
                lock.unlock();
            }
        }
    }

    private static class ThrowingFutureTask<V> extends FutureTask<V>
    {
        public ThrowingFutureTask( Callable<V> callable )
        {
            super( callable );
        }

        @Override
        protected void done()
        {
            super.done();
            try
            {
                if ( !isCancelled() )
                {
                    get();
                }
            }
            catch ( ExecutionException e )
            {
                Throwable cause = e.getCause();
                if ( cause instanceof RuntimeException )
                {
                    throw (RuntimeException) cause;
                }
                if ( cause instanceof Error )
                {
                    throw (Error) cause;
                }
            }
            catch ( InterruptedException e )
            {
                throw new AssertionError( e );
            }
        }
    }
}
TOP

Related Classes of org.neo4j.collections.list.TestUnrolledLinkedListConcurrency$StateSynchronizer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.