1616import static java .lang .foreign .ValueLayout .JAVA_LONG ;
1717
1818public class Flecs implements AutoCloseable {
19-
2019 private final MemorySegment nativeWorld ;
2120 private final Arena arena ;
2221 private final ComponentRegistry componentRegistry ;
23- private boolean closed = false ;
24- private final ThreadLocal <NameBuffer > threadLocalNameBuffer ;
25- private final ThreadLocal <ComponentBuffer > threadLocalComponentBuffer ;
26- private final ThreadLocal <EntityDescBuffer > threadLocalEntityDesc ;
2722 private final Map <Long , SystemCallbacks > systemCallbacks ;
2823 private final Map <Long , ObserverCallbacks > observerCallbacks ;
24+ private static final ScopedValue <FlecsBuffers > CONTEXT = ScopedValue .newInstance ();
25+ private final FlecsBuffers defaultBuffers ;
26+ private final boolean owned ;
27+ private boolean closed ;
2928
3029 static {
3130 FlecsLoader .load ();
@@ -37,6 +36,19 @@ private record SystemCallbacks(Query.IterCallback iterCallback, Query.RunCallbac
3736 private record ObserverCallbacks (Query .IterCallback iterCallback , Query .RunCallback runCallback , Query .EntityCallback entityCallback ) {
3837 }
3938
39+ public record FlecsBuffers (NameBuffer nameBuffer , ComponentBuffer componentBuffer , EntityDescBuffer entityDescBuffer ) implements AutoCloseable {
40+ public FlecsBuffers () {
41+ this (new NameBuffer (64 ), new ComponentBuffer (256 ), new EntityDescBuffer ());
42+ }
43+
44+ @ Override
45+ public void close () {
46+ this .nameBuffer .close ();
47+ this .componentBuffer .close ();
48+ this .entityDescBuffer .close ();
49+ }
50+ }
51+
4052 private static final class NameBuffer implements AutoCloseable {
4153 private Arena arena ;
4254 private MemorySegment segment ;
@@ -118,30 +130,30 @@ public void close() {
118130 }
119131
120132 public Flecs () {
121- this .arena = Arena .ofConfined ();
133+ this .arena = Arena .ofShared ();
122134 this .nativeWorld = flecs_h .ecs_init ();
123135
124136 if (this .nativeWorld == null || this .nativeWorld .address () == 0 ) {
125137 throw new IllegalStateException ("Flecs world initialization failed" );
126138 }
127139
128140 this .componentRegistry = new ComponentRegistry (this );
129- this .threadLocalNameBuffer = ThreadLocal .withInitial (() -> new NameBuffer (64 ));
130- this .threadLocalComponentBuffer = ThreadLocal .withInitial (() -> new ComponentBuffer (256 ));
131- this .threadLocalEntityDesc = ThreadLocal .withInitial (EntityDescBuffer ::new );
132141 this .systemCallbacks = new ConcurrentHashMap <>();
133142 this .observerCallbacks = new ConcurrentHashMap <>();
143+ this .defaultBuffers = new FlecsBuffers ();
144+ this .closed = false ;
145+ this .owned = true ;
134146 }
135147
136148 private Flecs (MemorySegment stagePtr , ComponentRegistry sharedRegistry ) {
137- this .arena = null ;
149+ this .arena = Arena . ofConfined () ;
138150 this .nativeWorld = stagePtr ;
139151 this .componentRegistry = sharedRegistry ;
140- this .threadLocalNameBuffer = ThreadLocal .withInitial (() -> new NameBuffer (64 ));
141- this .threadLocalComponentBuffer = ThreadLocal .withInitial (() -> new ComponentBuffer (256 ));
142- this .threadLocalEntityDesc = ThreadLocal .withInitial (EntityDescBuffer ::new );
143152 this .systemCallbacks = new ConcurrentHashMap <>();
144153 this .observerCallbacks = new ConcurrentHashMap <>();
154+ this .defaultBuffers = null ;
155+ this .closed = false ;
156+ this .owned = false ;
145157 }
146158
147159 public long entity () {
@@ -156,14 +168,15 @@ public long entity(String name) {
156168 byte [] utf8 = name .getBytes (StandardCharsets .UTF_8 );
157169 int len = utf8 .length ;
158170
159- NameBuffer nameBuffer = this .threadLocalNameBuffer . get ();
160- MemorySegment nameSegment = nameBuffer .ensure (len + 1 );
171+ FlecsBuffers buffers = this .getBuffers ();
172+ MemorySegment nameSegment = buffers . nameBuffer () .ensure (len + 1 );
161173 nameSegment .asSlice (0 , len ).copyFrom (MemorySegment .ofArray (utf8 ));
162174 nameSegment .set (ValueLayout .JAVA_BYTE , len , (byte )0 );
163175
164- MemorySegment desc = this . threadLocalEntityDesc . get ().get ();
176+ MemorySegment desc = buffers . entityDescBuffer ().get ();
165177 desc .fill ((byte ) 0 );
166178 ecs_entity_desc_t .name (desc , nameSegment );
179+
167180 return flecs_h .ecs_entity_init (this .nativeWorld , desc );
168181 }
169182
@@ -175,7 +188,8 @@ public Entity obtainEntity(long entityId) {
175188 }
176189
177190 MemorySegment getComponentBuffer (long size ) {
178- return this .threadLocalComponentBuffer .get ().ensure (size );
191+ FlecsBuffers buffers = this .getBuffers ();
192+ return buffers .componentBuffer ().ensure (size );
179193 }
180194
181195 public EcsLongList entityBulk (int count ) {
@@ -293,8 +307,8 @@ public long lookup(String name) {
293307 byte [] utf8 = name .getBytes (StandardCharsets .UTF_8 );
294308 int len = utf8 .length ;
295309
296- NameBuffer buffer = this .threadLocalNameBuffer . get ();
297- MemorySegment segment = buffer .ensure (len + 1 );
310+ FlecsBuffers buffers = this .getBuffers ();
311+ MemorySegment segment = buffers . nameBuffer () .ensure (len + 1 );
298312
299313 segment .asSlice (0 , len ).copyFrom (MemorySegment .ofArray (utf8 ));
300314 segment .set (ValueLayout .JAVA_BYTE , len , (byte )0 );
@@ -571,6 +585,22 @@ void registerObserverCallbacks(long observerId, Query.IterCallback iterCallback,
571585 }
572586 }
573587
588+ private FlecsBuffers getBuffers () {
589+ if (CONTEXT .isBound ()) {
590+ return CONTEXT .get ();
591+ } else if (this .defaultBuffers != null ) {
592+ return this .defaultBuffers ;
593+ } else {
594+ throw new IllegalStateException ("No FlecsBuffers available in this context" );
595+ }
596+ }
597+
598+ public static void runScoped (Runnable runnable ) {
599+ try (var buffers = new FlecsBuffers ()) {
600+ ScopedValue .where (CONTEXT , buffers ).run (runnable );
601+ }
602+ }
603+
574604 public void setStageCount (int stages ) {
575605 this .checkClosed ();
576606 flecs_h .ecs_set_stage_count (this .nativeWorld , stages );
@@ -722,20 +752,15 @@ public void fromJson(String json) {
722752 @ Override
723753 public void close () {
724754 if (!this .closed ) {
725- this .closed = true ;
726- if (this .nativeWorld != null && this .nativeWorld .address () != 0 && this .arena != null ) {
755+ if (this .owned && this .nativeWorld != null && this .nativeWorld .address () != 0 ) {
727756 flecs_h .ecs_fini (this .nativeWorld );
728757 }
758+
729759 if (this .arena != null ) {
730760 this .arena .close ();
731761 }
732762 }
733- this .threadLocalNameBuffer .get ().close ();
734- this .threadLocalComponentBuffer .get ().close ();
735- this .threadLocalEntityDesc .get ().close ();
736- this .threadLocalNameBuffer .remove ();
737- this .threadLocalComponentBuffer .remove ();
738- this .threadLocalEntityDesc .remove ();
763+ this .closed = true ;
739764 }
740765
741766 @ Override
0 commit comments