Skip to content

Commit ce2bc07

Browse files
authored
improve thread safety (#131)
1 parent d4f8606 commit ce2bc07

File tree

8 files changed

+152
-156
lines changed

8 files changed

+152
-156
lines changed

entry.go

+37-52
Original file line numberDiff line numberDiff line change
@@ -20,45 +20,40 @@ type Entry struct {
2020
}
2121

2222
// Get returns the component from the entry
23-
func Get[T any](e *Entry, ctype component.IComponentType) *T {
24-
return (*T)(e.Component(ctype))
23+
func Get[T any](e *Entry, c component.IComponentType) *T {
24+
return (*T)(e.Component(c))
2525
}
2626

2727
// GetComponents uses reflection to convert the unsafe.Pointers of an entry into its component data instances.
2828
// Note that this is likely to be slow and should not be used in hot paths or if not necessary.
2929
func GetComponents(e *Entry) []any {
30-
archetypeIdx := e.loc.Archetype
31-
s := e.World.StorageAccessor().Archetypes[archetypeIdx]
32-
cs := s.ComponentTypes()
3330
var instances []any
34-
for _, ctyp := range cs {
35-
instancePtr := e.Component(ctyp)
36-
componentType := ctyp.Typ()
37-
val := reflect.NewAt(componentType, instancePtr)
38-
valInstance := reflect.Indirect(val).Interface()
39-
instances = append(instances, valInstance)
31+
for _, c := range e.World.StorageAccessor().Archetypes[e.loc.Archetype].ComponentTypes() {
32+
instances = append(instances, reflect.Indirect(
33+
reflect.NewAt(c.Typ(), e.Component(c))).Interface(),
34+
)
4035
}
4136
return instances
4237
}
4338

4439
// Add adds the component to the entry.
45-
func Add[T any](e *Entry, ctype component.IComponentType, component *T) {
46-
e.AddComponent(ctype, unsafe.Pointer(component))
40+
func Add[T any](e *Entry, c component.IComponentType, component *T) {
41+
e.AddComponent(c, unsafe.Pointer(component))
4742
}
4843

4944
// Set sets the comopnent of the entry.
50-
func Set[T any](e *Entry, ctype component.IComponentType, component *T) {
51-
e.SetComponent(ctype, unsafe.Pointer(component))
45+
func Set[T any](e *Entry, c component.IComponentType, component *T) {
46+
e.SetComponent(c, unsafe.Pointer(component))
5247
}
5348

5449
// SetValue sets the value of the component.
55-
func SetValue[T any](e *Entry, ctype component.IComponentType, value T) {
56-
*Get[T](e, ctype) = value
50+
func SetValue[T any](e *Entry, c component.IComponentType, value T) {
51+
*Get[T](e, c) = value
5752
}
5853

5954
// GetValue gets the value of the component.
60-
func GetValue[T any](e *Entry, ctype component.IComponentType) T {
61-
return *Get[T](e, ctype)
55+
func GetValue[T any](e *Entry, c component.IComponentType) T {
56+
return *Get[T](e, c)
6257
}
6358

6459
// Remove removes the component from the entry.
@@ -85,60 +80,50 @@ func (e *Entry) Entity() Entity {
8580
}
8681

8782
// Component returns the component.
88-
func (e *Entry) Component(ctype component.IComponentType) unsafe.Pointer {
89-
c := e.loc.Component
90-
a := e.loc.Archetype
91-
return e.World.components.Storage(ctype).Component(a, c)
83+
func (e *Entry) Component(c component.IComponentType) unsafe.Pointer {
84+
return e.World.components.Storage(c).Component(e.loc.Archetype, e.loc.Component)
9285
}
9386

9487
// SetComponent sets the component.
95-
func (e *Entry) SetComponent(ctype component.IComponentType, component unsafe.Pointer) {
96-
c := e.loc.Component
97-
a := e.loc.Archetype
98-
e.World.components.Storage(ctype).SetComponent(a, c, component)
88+
func (e *Entry) SetComponent(c component.IComponentType, component unsafe.Pointer) {
89+
e.World.components.Storage(c).SetComponent(e.loc.Archetype, e.loc.Component, component)
9990
}
10091

10192
// AddComponent adds the component to the entity.
102-
func (e *Entry) AddComponent(ctype component.IComponentType, components ...unsafe.Pointer) {
93+
func (e *Entry) AddComponent(c component.IComponentType, components ...unsafe.Pointer) {
10394
if len(components) > 1 {
10495
panic("AddComponent: component argument must be a single value")
10596
}
106-
if !e.HasComponent(ctype) {
107-
c := e.loc.Component
108-
a := e.loc.Archetype
109-
110-
base_layout := e.World.archetypes[a].Layout().Components()
111-
target_arc := e.World.getArchetypeForComponents(append(base_layout, ctype))
112-
e.World.TransferArchetype(a, target_arc, c)
113-
97+
if !e.HasComponent(c) {
98+
archetypeIndex := e.loc.Archetype
99+
targetArchetype := e.World.getArchetypeForComponents(
100+
append(e.World.archetypes[archetypeIndex].Layout().Components(), c),
101+
)
102+
e.World.TransferArchetype(archetypeIndex, targetArchetype, e.loc.Component)
114103
e.loc = e.World.Entry(e.entity).loc
115104
}
116105
if len(components) == 1 {
117-
e.SetComponent(ctype, components[0])
106+
e.SetComponent(c, components[0])
118107
}
119108
}
120109

121110
// RemoveComponent removes the component from the entity.
122-
func (e *Entry) RemoveComponent(ctype component.IComponentType) {
123-
if !e.Archetype().Layout().HasComponent(ctype) {
111+
func (e *Entry) RemoveComponent(c component.IComponentType) {
112+
if !e.Archetype().Layout().HasComponent(c) {
124113
return
125114
}
126115

127-
c := e.loc.Component
128-
a := e.loc.Archetype
129-
130-
base_layout := e.World.archetypes[a].Layout().Components()
131-
target_layout := make([]component.IComponentType, 0, len(base_layout)-1)
132-
for _, c2 := range base_layout {
133-
if c2 == ctype {
116+
baseLayout := e.World.archetypes[e.loc.Archetype].Layout().Components()
117+
targetLayout := make([]component.IComponentType, 0, len(baseLayout)-1)
118+
for _, c2 := range baseLayout {
119+
if c2 == c {
134120
continue
135121
}
136-
target_layout = append(target_layout, c2)
122+
targetLayout = append(targetLayout, c2)
137123
}
138124

139-
target_arc := e.World.getArchetypeForComponents(target_layout)
140-
e.World.TransferArchetype(e.loc.Archetype, target_arc, c)
141-
125+
targetArchetype := e.World.getArchetypeForComponents(targetLayout)
126+
e.World.TransferArchetype(e.loc.Archetype, targetArchetype, e.loc.Component)
142127
e.loc = e.World.Entry(e.entity).loc
143128
}
144129

@@ -159,8 +144,8 @@ func (e *Entry) Archetype() *storage.Archetype {
159144
}
160145

161146
// HasComponent returns true if the entity has the given component type.
162-
func (e *Entry) HasComponent(componentType component.IComponentType) bool {
163-
return e.Archetype().Layout().HasComponent(componentType)
147+
func (e *Entry) HasComponent(c component.IComponentType) bool {
148+
return e.Archetype().Layout().HasComponent(c)
164149
}
165150

166151
func (e *Entry) String() string {

internal/storage/archetype.go

+13-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package storage
22

33
import (
44
"github.com/yohamta/donburi/component"
5+
"sync"
56
)
67

78
type ArchetypeIndex int
@@ -12,6 +13,15 @@ type Archetype struct {
1213
index ArchetypeIndex
1314
entities []Entity
1415
layout *Layout
16+
lock sync.Mutex
17+
}
18+
19+
func (archetype *Archetype) Lock() {
20+
archetype.lock.Lock()
21+
}
22+
23+
func (archetype *Archetype) Unlock() {
24+
archetype.lock.Unlock()
1525
}
1626

1727
// NewArchetype creates a new archetype.
@@ -39,9 +49,9 @@ func (archetype *Archetype) ComponentTypes() []component.IComponentType {
3949
}
4050

4151
// SwapRemove removes an entity from the archetype and returns it.
42-
func (archetype *Archetype) SwapRemove(entity_index int) Entity {
43-
removed := archetype.entities[entity_index]
44-
archetype.entities[entity_index] = archetype.entities[len(archetype.entities)-1]
52+
func (archetype *Archetype) SwapRemove(entityIndex int) Entity {
53+
removed := archetype.entities[entityIndex]
54+
archetype.entities[entityIndex] = archetype.entities[len(archetype.entities)-1]
4555
archetype.entities = archetype.entities[:len(archetype.entities)-1]
4656
return removed
4757
}

internal/storage/entity.go

+11-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ type Entity uint64
1212
type EntityId uint32
1313

1414
const idMask Entity = 0xFFFFFFFF00000000
15-
const versionMask Entity = 0xFFFFFFF
15+
const versionMask Entity = 0x0FFFFFF
16+
const readyMask Entity = 0x1000000
1617

1718
// NewEntity creates a new entity.
1819
// The id is a unique identifier for the entity.
@@ -34,9 +35,17 @@ func (e Entity) Version() uint32 {
3435
return uint32(e & Entity(versionMask))
3536
}
3637

38+
func (e Entity) IsReady() bool {
39+
return e&readyMask != 0
40+
}
41+
42+
func (e Entity) Ready() Entity {
43+
return e | readyMask
44+
}
45+
3746
// IncVersion increments the entity version.
3847
func (e Entity) IncVersion() Entity {
39-
return e&idMask | ((e + 1) & versionMask)
48+
return e&idMask | ((e+1)&versionMask)&^readyMask
4049
}
4150

4251
func (e Entity) String() string {

internal/storage/index.go

+3-5
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,8 @@ func (idx *Index) Push(layout *Layout) {
3838
}
3939

4040
// SearchFrom searches for archetypes that match the given filter from the given index.
41-
func (idx *Index) SearchFrom(f filter.LayoutFilter, start int) *ArchetypeIterator {
42-
iterator := &ArchetypeIterator{
43-
current: 0,
44-
}
41+
func (idx *Index) SearchFrom(f filter.LayoutFilter, start int) ArchetypeIterator {
42+
iterator := ArchetypeIterator{current: 0}
4543
iterator.values = []ArchetypeIndex{}
4644
for i := start; i < len(idx.layouts); i++ {
4745
if f.MatchesLayout(idx.layouts[i]) {
@@ -52,6 +50,6 @@ func (idx *Index) SearchFrom(f filter.LayoutFilter, start int) *ArchetypeIterato
5250
}
5351

5452
// Search searches for archetypes that match the given filter.
55-
func (idx *Index) Search(filter filter.LayoutFilter) *ArchetypeIterator {
53+
func (idx *Index) Search(filter filter.LayoutFilter) ArchetypeIterator {
5654
return idx.SearchFrom(filter, 0)
5755
}

internal/storage/iterator.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ func (it *EntityIterator) HasNext() bool {
2222
}
2323

2424
// Next returns the next entity list.
25-
func (it *EntityIterator) Next() []Entity {
25+
func (it *EntityIterator) Next() *Archetype {
2626
archetypeIndex := it.indices[it.current]
2727
it.current++
28-
return it.archetypes[archetypeIndex].Entities()
28+
return it.archetypes[archetypeIndex]
2929
}

internal/storage/storage.go

+13-13
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ func NewStorage() *Storage {
2525
// PushComponent stores the new data of the component in the archetype.
2626
func (cs *Storage) PushComponent(component component.IComponentType, archetypeIndex ArchetypeIndex) {
2727
if len(cs.storages) <= int(archetypeIndex) {
28-
cs.ensureCapacity(archetypeIndex)
28+
cs.ensureCapacity()
2929
}
3030
if v := cs.storages[archetypeIndex]; v == nil {
3131
cs.storages[archetypeIndex] = []unsafe.Pointer{}
@@ -46,21 +46,21 @@ func (cs *Storage) SetComponent(archetypeIndex ArchetypeIndex, componentIndex Co
4646
}
4747

4848
// MoveComponent moves the pointer to data of the component in the archetype.
49-
func (cs *Storage) MoveComponent(source ArchetypeIndex, index ComponentIndex, dst ArchetypeIndex) {
50-
if len(cs.storages) <= int(dst) {
51-
cs.ensureCapacity(dst)
49+
func (cs *Storage) MoveComponent(srcIndex ArchetypeIndex, index ComponentIndex, dstIndex ArchetypeIndex) {
50+
if len(cs.storages) <= int(dstIndex) {
51+
cs.ensureCapacity()
5252
}
5353

54-
src_slice := cs.storages[source]
55-
dst_slice := cs.storages[dst]
54+
src := cs.storages[srcIndex]
55+
dst := cs.storages[dstIndex]
5656

57-
value := src_slice[index]
58-
src_slice[index] = src_slice[len(src_slice)-1]
59-
src_slice = src_slice[:len(src_slice)-1]
60-
cs.storages[source] = src_slice
57+
value := src[index]
58+
src[index] = src[len(src)-1]
59+
src = src[:len(src)-1]
60+
cs.storages[srcIndex] = src
6161

62-
dst_slice = append(dst_slice, value)
63-
cs.storages[dst] = dst_slice
62+
dst = append(dst, value)
63+
cs.storages[dstIndex] = dst
6464
}
6565

6666
// SwapRemove removes the pointer to data of the component in the archetype.
@@ -82,7 +82,7 @@ func (cs *Storage) Contains(archetypeIndex ArchetypeIndex, componentIndex Compon
8282
return cs.storages[archetypeIndex][componentIndex] != nil
8383
}
8484

85-
func (cs *Storage) ensureCapacity(archetypeIndex ArchetypeIndex) {
85+
func (cs *Storage) ensureCapacity() {
8686
newStorages := make([][]unsafe.Pointer, len(cs.storages)*2)
8787
copy(newStorages, cs.storages)
8888
cs.storages = newStorages

query.go

+22-23
Original file line numberDiff line numberDiff line change
@@ -17,33 +17,33 @@ type cache struct {
1717
// So it is not recommended to create a new query every time you want
1818
// to filter entities with the same query.
1919
type Query struct {
20-
layout_matches map[WorldId]*cache
21-
filter filter.LayoutFilter
20+
layoutMatches map[WorldId]*cache
21+
filter filter.LayoutFilter
2222
}
2323

2424
// NewQuery creates a new query.
2525
// It receives arbitrary filters that are used to filter entities.
2626
func NewQuery(filter filter.LayoutFilter) *Query {
2727
return &Query{
28-
layout_matches: make(map[WorldId]*cache),
29-
filter: filter,
28+
layoutMatches: make(map[WorldId]*cache),
29+
filter: filter,
3030
}
3131
}
3232

3333
// Each iterates over all entities that match the query.
3434
func (q *Query) Each(w World, callback func(*Entry)) {
3535
accessor := w.StorageAccessor()
36-
result := q.evaluateQuery(w, &accessor)
37-
iter := storage.NewEntityIterator(0, accessor.Archetypes, result)
38-
f := func(entity storage.Entity) {
39-
entry := w.Entry(entity)
40-
callback(entry)
41-
}
36+
iter := storage.NewEntityIterator(0, accessor.Archetypes, q.evaluateQuery(w, &accessor))
4237
for iter.HasNext() {
43-
entities := iter.Next()
44-
for _, entity := range entities {
45-
f(entity)
38+
archetype := iter.Next()
39+
archetype.Lock()
40+
for _, entity := range archetype.Entities() {
41+
entry := w.Entry(entity)
42+
if entry.entity.IsReady() {
43+
callback(entry)
44+
}
4645
}
46+
archetype.Unlock()
4747
}
4848
}
4949

@@ -55,26 +55,25 @@ func (q *Query) EachEntity(w World, callback func(*Entry)) {
5555
// Count returns the number of entities that match the query.
5656
func (q *Query) Count(w World) int {
5757
accessor := w.StorageAccessor()
58-
result := q.evaluateQuery(w, &accessor)
59-
iter := storage.NewEntityIterator(0, accessor.Archetypes, result)
58+
iter := storage.NewEntityIterator(0, accessor.Archetypes, q.evaluateQuery(w, &accessor))
6059
ret := 0
6160
for iter.HasNext() {
62-
entities := iter.Next()
63-
ret += len(entities)
61+
archetype := iter.Next()
62+
ret += len(archetype.Entities())
6463
}
6564
return ret
6665
}
6766

6867
// First returns the first entity that matches the query.
6968
func (q *Query) First(w World) (entry *Entry, ok bool) {
7069
accessor := w.StorageAccessor()
71-
result := q.evaluateQuery(w, &accessor)
72-
iter := storage.NewEntityIterator(0, accessor.Archetypes, result)
70+
iter := storage.NewEntityIterator(0, accessor.Archetypes, q.evaluateQuery(w, &accessor))
7371
if !iter.HasNext() {
7472
return nil, false
7573
}
7674
for iter.HasNext() {
77-
entities := iter.Next()
75+
archetype := iter.Next()
76+
entities := archetype.Entities()
7877
if len(entities) > 0 {
7978
return w.Entry(entities[0]), true
8079
}
@@ -89,13 +88,13 @@ func (q *Query) FirstEntity(w World) (entry *Entry, ok bool) {
8988

9089
func (q *Query) evaluateQuery(world World, accessor *StorageAccessor) []storage.ArchetypeIndex {
9190
w := world.Id()
92-
if _, ok := q.layout_matches[w]; !ok {
93-
q.layout_matches[w] = &cache{
91+
if _, ok := q.layoutMatches[w]; !ok {
92+
q.layoutMatches[w] = &cache{
9493
archetypes: make([]storage.ArchetypeIndex, 0),
9594
seen: 0,
9695
}
9796
}
98-
cache := q.layout_matches[w]
97+
cache := q.layoutMatches[w]
9998
for it := accessor.Index.SearchFrom(q.filter, cache.seen); it.HasNext(); {
10099
cache.archetypes = append(cache.archetypes, it.Next())
101100
}

0 commit comments

Comments
 (0)