dominion-dev/dominion-ecs-java

When I created entities in parallel, some errors occurred

endison1986 opened this issue · 6 comments

this code is fine.

public static void main(String[] args) {
    final Dominion dominion = Dominion.create();
    final var list = List.<Runnable>of(() -> {
        for (int i = 0; i < 100; i++) {
            dominion.createEntity(new A()).add(new B());
        }
    }, () -> {
        for (int i = 0; i < 100; i++) {
            dominion.createEntity(new A()).add(new B());
        }
    }, () -> {
        for (int i = 0; i < 100; i++) {
            dominion.createEntity(new A()).add(new B());
        }
    });
    final var scheduler = dominion.createScheduler();
    scheduler.schedule(() -> list.parallelStream().forEach(Runnable::run));
    scheduler.tickAtFixedRate(10);
}

this code is also fine.

public static void main(String[] args) {
    final Dominion dominion = Dominion.create();
    final var list = List.<Runnable>of(() -> {
        for (int i = 0; i < 100; i++) {
            dominion.createEntity(new A()).add(new B());
        }
    }, () -> {
        for (int i = 0; i < 100; i++) {
            dominion.createEntity(new A()).add(new B());
        }
    }, () -> {
        for (int i = 0; i < 100; i++) {
            dominion.createEntity(new A()).add(new B()).add(new C());
        }
    });
    final var scheduler = dominion.createScheduler();
    scheduler.schedule(() -> list.stream().forEach(Runnable::run));
    scheduler.tickAtFixedRate(10);
}

but I get some error with this code

public static void main(String[] args) {
    final Dominion dominion = Dominion.create();
    final var list = List.<Runnable>of(() -> {
        for (int i = 0; i < 100; i++) {
            dominion.createEntity(new A()).add(new B());
        }
    }, () -> {
        for (int i = 0; i < 100; i++) {
            dominion.createEntity(new A()).add(new B());
        }
    }, () -> {
        for (int i = 0; i < 100; i++) {
            dominion.createEntity(new A()).add(new B()).add(new C());
        }
    });
    final var scheduler = dominion.createScheduler();
    scheduler.schedule(() -> list.parallelStream().forEach(Runnable::run));

    scheduler.tickAtFixedRate(10);
}
  严重         dominion.SystemScheduler invoke 
java.lang.IllegalArgumentException: java.lang.IllegalArgumentException: java.lang.IllegalArgumentException: Invalid chunkById [LinkedChunk={id=1, dataLength=1, capacity=4096, size=0, previous=null, next=null, of Tenant={id=1, dataLength=1, nextId=|0:1:0|, subject=root}}] retrieved by [4097]
	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:562)
	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:262)
	at dev.dominion.ecs.engine/dev.dominion.ecs.engine.SystemScheduler$Single.call(SystemScheduler.java:239)
	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.IllegalArgumentException: java.lang.IllegalArgumentException: Invalid chunkById [LinkedChunk={id=1, dataLength=1, capacity=4096, size=0, previous=null, next=null, of Tenant={id=1, dataLength=1, nextId=|0:1:0|, subject=root}}] retrieved by [4097]
	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:562)
	at java.base/java.util.concurrent.ForkJoinTask.reportException(ForkJoinTask.java:591)
	at java.base/java.util.concurrent.ForkJoinTask.invoke(ForkJoinTask.java:689)
	at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateParallel(ForEachOps.java:159)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateParallel(ForEachOps.java:173)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:233)
	at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596)
	at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:765)
	at dev.dominion.ecs.engine/dev.dominion.ecs.engine.Test.lambda$main$3(Test.java:38)
	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)
Caused by: java.lang.IllegalArgumentException: Invalid chunkById [LinkedChunk={id=1, dataLength=1, capacity=4096, size=0, previous=null, next=null, of Tenant={id=1, dataLength=1, nextId=|0:1:0|, subject=root}}] retrieved by [4097]
	at dev.dominion.ecs.engine/dev.dominion.ecs.engine.collections.ChunkedPool$Tenant.freeId(ChunkedPool.java:273)
	at dev.dominion.ecs.engine/dev.dominion.ecs.engine.collections.ChunkedPool$Tenant.freeId(ChunkedPool.java:263)
	at dev.dominion.ecs.engine/dev.dominion.ecs.engine.CompositionRepository.modifyComponents(CompositionRepository.java:167)
	at dev.dominion.ecs.engine/dev.dominion.ecs.engine.CompositionRepository.addComponent(CompositionRepository.java:185)
	at dev.dominion.ecs.engine/dev.dominion.ecs.engine.IntEntity.add(IntEntity.java:91)
	at dev.dominion.ecs.engine/dev.dominion.ecs.engine.Test.lambda$main$2(Test.java:34)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
	at java.base/java.util.AbstractList$RandomAccessSpliterator.forEachRemaining(AbstractList.java:720)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
	at java.base/java.util.stream.ForEachOps$ForEachTask.compute(ForEachOps.java:290)
	at java.base/java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:754)
	... 5 more

and this code

public static void main(String[] args) {
    final Dominion dominion = Dominion.create();
    final var list = List.<Runnable>of(() -> {
        for (int i = 0; i < 100; i++) {
            final var entity = dominion.createEntity(new A()).setState(S1);
            entity.add(new B());
            entity.setState(S2);
        }
    }, () -> {
        for (int i = 0; i < 100; i++) {
            final var entity = dominion.createEntity(new A()).setState(S1);
            entity.add(new B());
            entity.setState(S2);
        }
    }, () -> {
        for (int i = 0; i < 100; i++) {
            final var entity = dominion.createEntity(new A()).setState(S1);
            entity.add(new B());
            entity.setState(S3);
        }
    });
    final var scheduler = dominion.createScheduler();
    scheduler.schedule(() -> list.parallelStream().forEach(Runnable::run));
    scheduler.tickAtFixedRate(10);
}
  严重         dominion.SystemScheduler invoke 
java.lang.IllegalArgumentException: java.lang.IllegalArgumentException: java.lang.IllegalArgumentException: Invalid chunkById [LinkedChunk={id=2, dataLength=0, capacity=4096, size=1, previous=null, next=null, of Tenant={id=2, dataLength=0, nextId=|0:2:0|, subject=|1085:[4, 0]|}}] retrieved by [8194]
	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:562)
	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:262)
	at dev.dominion.ecs.engine/dev.dominion.ecs.engine.SystemScheduler$Single.call(SystemScheduler.java:239)
	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.IllegalArgumentException: java.lang.IllegalArgumentException: Invalid chunkById [LinkedChunk={id=2, dataLength=0, capacity=4096, size=1, previous=null, next=null, of Tenant={id=2, dataLength=0, nextId=|0:2:0|, subject=|1085:[4, 0]|}}] retrieved by [8194]
	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:562)
	at java.base/java.util.concurrent.ForkJoinTask.reportException(ForkJoinTask.java:591)
	at java.base/java.util.concurrent.ForkJoinTask.invoke(ForkJoinTask.java:689)
	at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateParallel(ForEachOps.java:159)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateParallel(ForEachOps.java:173)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:233)
	at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596)
	at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:765)
	at dev.dominion.ecs.engine/dev.dominion.ecs.engine.FixBug.lambda$main$3(FixBug.java:51)
	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)
Caused by: java.lang.IllegalArgumentException: Invalid chunkById [LinkedChunk={id=2, dataLength=0, capacity=4096, size=1, previous=null, next=null, of Tenant={id=2, dataLength=0, nextId=|0:2:0|, subject=|1085:[4, 0]|}}] retrieved by [8194]
	at dev.dominion.ecs.engine/dev.dominion.ecs.engine.collections.ChunkedPool$Tenant.freeId(ChunkedPool.java:273)
	at dev.dominion.ecs.engine/dev.dominion.ecs.engine.collections.ChunkedPool$Tenant.freeStateId(ChunkedPool.java:267)
	at dev.dominion.ecs.engine/dev.dominion.ecs.engine.IntEntity.setState(IntEntity.java:168)
	at dev.dominion.ecs.engine/dev.dominion.ecs.engine.FixBug.lambda$main$0(FixBug.java:35)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
	at java.base/java.util.AbstractList$RandomAccessSpliterator.forEachRemaining(AbstractList.java:720)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
	at java.base/java.util.stream.ForEachOps$ForEachTask.compute(ForEachOps.java:290)
	at java.base/java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:754)
	... 5 more

In my actual project, I create monster entities in a System. In order to take advantage of multi-core, I use parallelStream and create monster entities in it, and cache the Entity to Component, but I found that in the next System, the Entity in the Component obtained by the context.findEntitiesWith() method is different from the hashCode of ResultSet.entity()

example code like this.

public enum State {
    S1, S2, S3;
}

public static class C {
    private final Entity entity;

    public C(Entity entity) {
        this.entity = entity;
    }
}

public static class B {

}

public static class A {
    private C c;

    public A(Dominion dominion) {
        c = new C(dominion.createEntity(this));
        c.entity.setState(S1);
    }
}

public static void main(String[] args) {
    final Dominion dominion = Dominion.create();
    final var list = List.<Runnable>of(() -> {
        for (int i = 0; i < 200; i++) {
            final var a = new A(dominion);
            a.c.entity.add(new B());
        }
    }, () -> {
        for (int i = 0; i < 200; i++) {
            final var a = new A(dominion);
            a.c.entity.add(new B());
        }
    }, () -> {
        for (int i = 0; i < 200; i++) {
            final var a = new A(dominion);
            a.c.entity.add(new B());
        }
    });
    final var scheduler = dominion.createScheduler();
    scheduler.schedule(() -> list.parallelStream().forEach(Runnable::run));
    scheduler.schedule(() -> {
        dominion.findEntitiesWith(A.class).stream().forEach(rs->{
            if(rs.entity().hashCode() != rs.comp().c.entity.hashCode()) {
                System.out.println("1111111111111111111");
                System.exit(-1);
            }
        });
        System.exit(0);
    });
    scheduler.tickAtFixedRate(10);
}

If I remove a.c.entity.add(new B());, the program is fine.
If I change paralleStream() to stream(), this program is fine.

Hi @endison1986 ,
the last message seems to be a different problem, could you please open a different issue and move the message into the new issue?
thanks

Ok, I've moved the problem to a new issue @enricostara
I am very happy to hear from you. I analyzed it all morning and couldn't find the cause of the problem. But there is some progress, I will add information in the new issue.

Hi @endison1986,
the fix has been merged as EA and is available by using the last 0.9.0-SNAPSHOT, let me know if it works as expected

Thanks, @enricostara This problem has been fixed in the test code, and I tested in my actual project, no errors reported.
But this problem was discovered when I reproduced #156, not in my actual project.

Thanks @endison1986 , issue #165 is completely different and not even related to this one.
Then I'm closing this as it is already covered by new tests