about thread safe
endison1986 opened this issue · 14 comments
I use dominion on the server side,so there are some question about thread safe.
I need createEntity, addComponent or removeComponent, above action must run in System?
If I want to do above action in network thread,will there be any thread safety issues?
Are there any examples of usage on the server side, such as how to handle packet from network.
I use dominion on the server side,so there are some question about thread safe.
I need createEntity, addComponent or removeComponent, above action must run in System?
If I want to do above action in network thread,will there be any thread safety issues?
You don't need to follow any special rules to create your systems in Dominion, it can be any Runnable, Callable or even simple methods you want. Then you can safely run in network threads.
Are there any examples of usage on the server side, such as how to handle packet from network.
I have no special examples for this. As a general rule, you might have network threads about creating an entity for each new request with a "Request" component tag + other components that can better define packets, and several parallel/specialized systems to process all these different entity types and to provide an appropriate response. Might not be applicable in all scenarios, but just an idea of how to apply ECS models in such a context.
thanks for your reply,if my understanding is correct,I can create entity in network thread and do my game logic in systems.
public class A {}
public static void main(String[] args) {
var d = Dominion.create();
var s = d.createScheduler();
s.schedule(() -> {
d.findEntitiesWith(A.class).stream().forEach(rs -> {
rs.entity().removeType(A.class);
});
});
for(var i = 0; i < 50; i++) {
new Thread(()-> {
for(var j = 0; j< 100; j++) {
d.createEntity(new A());
}
}).start();
}
s.tickAtFixedRate(3);
}
this is my test code, I run and get some error message, please help me,thanks
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.SystemScheduler.forkAndJoin(SystemScheduler.java:113)
at dev.dominion.ecs.engine.SystemScheduler$Single.call(SystemScheduler.java:262)
at 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.NullPointerException: Cannot invoke "dev.dominion.ecs.api.Entity.removeType(java.lang.Class)" because the return value of "dev.dominion.ecs.api.Results$With1.entity()" is null
at com.test.Test.lambda$main$0(Test.java:11)
at java.base/java.util.Iterator.forEachRemaining(Iterator.java:133)
at java.base/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1845)
at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:762)
at com.test.Test.lambda$main$1(Test.java:10)
at 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)
can you help me, please @enricostara 🙂
I don't know whether my method is wrong
Hi @endison1986 , your method it's fine, let me perform some checks
@enricostara I'm so happy to received your reply and will wait for you to fix this issue. Dominion-ECS is powerful, fast and easy to use ECS framework. I like it and hope it be better and better sincerely, thank you and good luck friend.
@endison1986 Thanks so much for your support! We lacked concurrency unit tests in entity creation. I'm working on adding the missing tests and fixing the code. It will take a few days
Hi @enricostara I get a new error when I add a second component B
public static void main(String[] args) {
var d = Dominion.create();
var s = d.createScheduler();
s.schedule(() -> {
d.findEntitiesWith(A.class, B.class).iterator();
});
for(var i = 0; i < 50; i++) {
new Thread(()-> {
for(var j = 0; j< 100; j++) {
d.createEntity(new A(), new B());
}
}).start();
}
s.tickAtFixedRate(3);
}
Exception in thread "Thread-0" Exception in thread "Thread-9" Exception in thread "Thread-13" Exception in thread "Thread-2" Exception in thread "Thread-4" Exception in thread "Thread-10" Exception in thread "Thread-15" Exception in thread "Thread-1" java.lang.ArrayIndexOutOfBoundsException: Index 6 out of bounds for length 4
at dev.dominion.ecs.engine.system.ClassIndex.getIndexKey(ClassIndex.java:153)
at dev.dominion.ecs.engine.CompositionRepository.getOrCreate(CompositionRepository.java:92)
at dev.dominion.ecs.engine.EntityRepository.createEntity(EntityRepository.java:44)
at dev.dominion.ecs.engine.TestClass.lambda$main$1(VirtualWorld.java:53)
at java.base/java.lang.Thread.run(Thread.java:833)
Exception in thread "Thread-12" Exception in thread "Thread-14" Exception in thread "Thread-7" java.lang.ArrayIndexOutOfBoundsException: Index 6 out of bounds for length 4
at dev.dominion.ecs.engine.system.ClassIndex.getIndexKey(ClassIndex.java:153)
at dev.dominion.ecs.engine.CompositionRepository.getOrCreate(CompositionRepository.java:92)
at dev.dominion.ecs.engine.EntityRepository.createEntity(EntityRepository.java:44)
at dev.dominion.ecs.engine.TestClass.lambda$main$1(VirtualWorld.java:53)
at java.base/java.lang.Thread.run(Thread.java:833)
Exception in thread "Thread-8" java.lang.ArrayIndexOutOfBoundsException: Index 6 out of bounds for length 4
at dev.dominion.ecs.engine.system.ClassIndex.getIndexKey(ClassIndex.java:153)
at dev.dominion.ecs.engine.CompositionRepository.getOrCreate(CompositionRepository.java:92)
at dev.dominion.ecs.engine.EntityRepository.createEntity(EntityRepository.java:44)
at dev.dominion.ecs.engine.TestClass.lambda$main$1(VirtualWorld.java:53)
at java.base/java.lang.Thread.run(Thread.java:833)
Exception in thread "Thread-5" Exception in thread "Thread-11" Exception in thread "Thread-6" java.lang.ArrayIndexOutOfBoundsException: Index 6 out of bounds for length 4
at dev.dominion.ecs.engine.system.ClassIndex.getIndexKey(ClassIndex.java:153)
at dev.dominion.ecs.engine.CompositionRepository.getOrCreate(CompositionRepository.java:92)
at dev.dominion.ecs.engine.EntityRepository.createEntity(EntityRepository.java:44)
at dev.dominion.ecs.engine.TestClass.lambda$main$1(VirtualWorld.java:53)
at java.base/java.lang.Thread.run(Thread.java:833)
Exception in thread "Thread-3" java.lang.ArrayIndexOutOfBoundsException: Index 6 out of bounds for length 4
at dev.dominion.ecs.engine.system.ClassIndex.getIndexKey(ClassIndex.java:153)
at dev.dominion.ecs.engine.CompositionRepository.getOrCreate(CompositionRepository.java:92)
at dev.dominion.ecs.engine.EntityRepository.createEntity(EntityRepository.java:44)
at dev.dominion.ecs.engine.TestClass.lambda$main$1(VirtualWorld.java:53)
at java.base/java.lang.Thread.run(Thread.java:833)
java.lang.ArrayIndexOutOfBoundsException: Index 6 out of bounds for length 4
at dev.dominion.ecs.engine.system.ClassIndex.getIndexKey(ClassIndex.java:153)
at dev.dominion.ecs.engine.CompositionRepository.getOrCreate(CompositionRepository.java:92)
at dev.dominion.ecs.engine.EntityRepository.createEntity(EntityRepository.java:44)
at dev.dominion.ecs.engine.TestClass.lambda$main$1(VirtualWorld.java:53)
at java.base/java.lang.Thread.run(Thread.java:833)
java.lang.ArrayIndexOutOfBoundsException: Index 6 out of bounds for length 4
at dev.dominion.ecs.engine.system.ClassIndex.getIndexKey(ClassIndex.java:153)
at dev.dominion.ecs.engine.CompositionRepository.getOrCreate(CompositionRepository.java:92)
at dev.dominion.ecs.engine.EntityRepository.createEntity(EntityRepository.java:44)
at dev.dominion.ecs.engine.TestClass.lambda$main$1(VirtualWorld.java:53)
at java.base/java.lang.Thread.run(Thread.java:833)
java.lang.ArrayIndexOutOfBoundsException: Index 6 out of bounds for length 4
at dev.dominion.ecs.engine.system.ClassIndex.getIndexKey(ClassIndex.java:153)
at dev.dominion.ecs.engine.CompositionRepository.getOrCreate(CompositionRepository.java:92)
at dev.dominion.ecs.engine.EntityRepository.createEntity(EntityRepository.java:44)
at dev.dominion.ecs.engine.TestClass.lambda$main$1(VirtualWorld.java:53)
at java.base/java.lang.Thread.run(Thread.java:833)
java.lang.ArrayIndexOutOfBoundsException: Index 6 out of bounds for length 4
at dev.dominion.ecs.engine.system.ClassIndex.getIndexKey(ClassIndex.java:153)
at dev.dominion.ecs.engine.CompositionRepository.getOrCreate(CompositionRepository.java:92)
at dev.dominion.ecs.engine.EntityRepository.createEntity(EntityRepository.java:44)
at dev.dominion.ecs.engine.TestClass.lambda$main$1(VirtualWorld.java:53)
at java.base/java.lang.Thread.run(Thread.java:833)
java.lang.ArrayIndexOutOfBoundsException: Index 6 out of bounds for length 4
at dev.dominion.ecs.engine.system.ClassIndex.getIndexKey(ClassIndex.java:153)
at dev.dominion.ecs.engine.CompositionRepository.getOrCreate(CompositionRepository.java:92)
at dev.dominion.ecs.engine.EntityRepository.createEntity(EntityRepository.java:44)
at dev.dominion.ecs.engine.TestClass.lambda$main$1(VirtualWorld.java:53)
at java.base/java.lang.Thread.run(Thread.java:833)
java.lang.ArrayIndexOutOfBoundsException: Index 6 out of bounds for length 4
at dev.dominion.ecs.engine.system.ClassIndex.getIndexKey(ClassIndex.java:153)
at dev.dominion.ecs.engine.CompositionRepository.getOrCreate(CompositionRepository.java:92)
at dev.dominion.ecs.engine.EntityRepository.createEntity(EntityRepository.java:44)
at dev.dominion.ecs.engine.TestClass.lambda$main$1(VirtualWorld.java:53)
at java.base/java.lang.Thread.run(Thread.java:833)
java.lang.ArrayIndexOutOfBoundsException: Index 6 out of bounds for length 4
at dev.dominion.ecs.engine.system.ClassIndex.getIndexKey(ClassIndex.java:153)
at dev.dominion.ecs.engine.CompositionRepository.getOrCreate(CompositionRepository.java:92)
at dev.dominion.ecs.engine.EntityRepository.createEntity(EntityRepository.java:44)
at dev.dominion.ecs.engine.TestClass.lambda$main$1(VirtualWorld.java:53)
at java.base/java.lang.Thread.run(Thread.java:833)
java.lang.ArrayIndexOutOfBoundsException: Index 6 out of bounds for length 4
at dev.dominion.ecs.engine.system.ClassIndex.getIndexKey(ClassIndex.java:153)
at dev.dominion.ecs.engine.CompositionRepository.getOrCreate(CompositionRepository.java:92)
at dev.dominion.ecs.engine.EntityRepository.createEntity(EntityRepository.java:44)
at dev.dominion.ecs.engine.TestClass.lambda$main$1(VirtualWorld.java:53)
at java.base/java.lang.Thread.run(Thread.java:833)
java.lang.ArrayIndexOutOfBoundsException: Index 6 out of bounds for length 4
at dev.dominion.ecs.engine.system.ClassIndex.getIndexKey(ClassIndex.java:153)
at dev.dominion.ecs.engine.CompositionRepository.getOrCreate(CompositionRepository.java:92)
at dev.dominion.ecs.engine.EntityRepository.createEntity(EntityRepository.java:44)
at dev.dominion.ecs.engine.TestClass.lambda$main$1(VirtualWorld.java:53)
at java.base/java.lang.Thread.run(Thread.java:833)
java.lang.ArrayIndexOutOfBoundsException: Index 6 out of bounds for length 4
at dev.dominion.ecs.engine.system.ClassIndex.getIndexKey(ClassIndex.java:153)
at dev.dominion.ecs.engine.CompositionRepository.getOrCreate(CompositionRepository.java:92)
at dev.dominion.ecs.engine.EntityRepository.createEntity(EntityRepository.java:44)
at dev.dominion.ecs.engine.TestClass.lambda$main$1(VirtualWorld.java:53)
at java.base/java.lang.Thread.run(Thread.java:833)
java.lang.ArrayIndexOutOfBoundsException: Index 6 out of bounds for length 4
at dev.dominion.ecs.engine.system.ClassIndex.getIndexKey(ClassIndex.java:153)
at dev.dominion.ecs.engine.CompositionRepository.getOrCreate(CompositionRepository.java:92)
at dev.dominion.ecs.engine.EntityRepository.createEntity(EntityRepository.java:44)
at dev.dominion.ecs.engine.TestClass.lambda$main$1(VirtualWorld.java:53)
at java.base/java.lang.Thread.run(Thread.java:833)
Hi @enricostara I analysis source code and found that it is not the reason for multithreading
I run under code and get the error also.
public static void main(String[] args) throws InterruptedException {
var d = Dominion.create();
var s = d.createScheduler();
for (var j = 0; j < 4096; j++) {
d.createEntity(new A(), new B());
}
s.schedule(() -> d.findEntitiesWith(A.class, B.class).stream().forEach(rs -> rs.entity().removeType(A.class)));
s.tickAtFixedRate(3);
}
Get you also found the problem in 4096
, if modify to 4095
, the program will run normally!
So I suspect it's LinkedChunk's problem
Hi @endison1986, thanks for the analysis. As already mentioned, this issue is going to require some fundamental changes. It's not only related to thread safety, but I've discovered at least two other issues related to this.
Unfortunately, as I'm traveling again for the holidays, I won't be able to work on it 100% these days, but I should be able to fix it by next week (I hope).
Ok @enricostara , I read the source code again, not only increased my understanding of the source code, but also learned more programming ideas and skills, thank you for your efforts, and look forward to your results
Hi @endison1986,
please run with the latest 0.8.0-SNAPSHOT to check if this issue is fixed