Skip to content

A concurrency issue #190

@endison1986

Description

@endison1986

During development, I got an exception log that looked like it was looping to a null value. So I wrote a piece of test code to try to restore the error. Judging from the test code, this should be a concurrency issue.

Is this result expected?

dominion.SystemScheduler invoke 
java.lang.NullPointerException
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480)
	at java.base/java.util.concurrent.ForkJoinTask.getThrowableException(ForkJoinTask.java:564)
	at java.base/java.util.concurrent.ForkJoinTask.reportException(ForkJoinTask.java:591)
	at java.base/java.util.concurrent.ForkJoinTask.joinForPoolInvoke(ForkJoinTask.java:1042)
	at java.base/java.util.concurrent.ForkJoinPool.invoke(ForkJoinPool.java:2639)
	at dev.dominion.ecs.engine/dev.dominion.ecs.engine.SystemScheduler.forkAndJoin(SystemScheduler.java:113)
	at dev.dominion.ecs.engine/dev.dominion.ecs.engine.SystemScheduler$Single.call(SystemScheduler.java:280)
	at dev.dominion.ecs.engine/dev.dominion.ecs.engine.SystemScheduler$Single.call(SystemScheduler.java:257)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
	at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.lang.NullPointerException: Cannot invoke "dev.dominion.ecs.engine.collections.ChunkedPool$Item.getChunk()" because "item" is null
	at dev.dominion.ecs.engine/dev.dominion.ecs.engine.collections.ChunkedPool$PoolDataIteratorWithState.next(ChunkedPool.java:754)
	at dev.dominion.ecs.engine/dev.dominion.ecs.engine.DataComposition$IteratorWith1Next.next(DataComposition.java:306)
	at dev.dominion.ecs.engine/dev.dominion.ecs.engine.DataComposition$IteratorWith1Next.next(DataComposition.java:294)
	at java.base/java.lang.Iterable.forEach(Iterable.java:74)
	at dev.dominion.ecs.engine/dev.dominion.ecs.engine.Test.lambda$main$0(Test.java:34)
	at dev.dominion.ecs.engine/dev.dominion.ecs.engine.SystemScheduler$2.compute(SystemScheduler.java:116)
	at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:194)
	at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373)
	at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182)
	at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655)
	at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622)
	at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165)

Test code:

public class Test {
    public enum S {
        A, B
    }
    public record A(int id) {
    }
    public static void consume(Object c) {
        try {
            TimeUnit.MILLISECONDS.sleep(10);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
    public static void main(String[] args) {
        EntityRepository entityRepository = (EntityRepository) new EntityRepository.Factory().create("test");
        final var list = new ArrayList<Entity>(1000);
        for (int i = 0; i < 1000; i++) {
            list.add(entityRepository.createEntity(new A(i)).setState(S.A));
        }
        Scheduler scheduler = entityRepository.createScheduler();

        scheduler.schedule(() -> entityRepository.findEntitiesWith(A.class).withState(S.A).forEach(Test::consume));


        new Thread(() -> {
            while (true) {
                try {
                    TimeUnit.MILLISECONDS.sleep(10);
                    for (int i = 0; i < 1000; i++) {
                        list.get(i).setState(S.B);
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }).start();

        scheduler.tickAtFixedRate(20);
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions