Skip to content

Commit adb1649

Browse files
authored
Merge pull request #60 from mfbonfigli/bugfix/59
Fix race condition in combined LAS reader
2 parents 349f31c + 42512d9 commit adb1649

File tree

5 files changed

+71
-15
lines changed

5 files changed

+71
-15
lines changed

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ You can preview a couple of tilesets generated from this tool at this [website](
5959

6060

6161
## Changelog
62+
##### Version 2.0.1
63+
* Resolved a bug that prevented the correct functioning of the join flag when processing multiple LAS files from a folder.
64+
6265
##### Version 2.0.0
6366
* Most of the code has been rewritten from the ground up, achieving much faster tiling with lower memory usage
6467
* Uses Proj v9.5.0: all projections supported by the Proj library are automatically supported by gocesiumtiler.

cmd/main.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ var tilerProvider func() (tiler.Tiler, error) = func() (tiler.Tiler, error) {
2323
return tiler.NewGoCesiumTiler()
2424
}
2525

26-
var cmdVersion = "2.0.0"
26+
var cmdVersion = "2.0.1"
2727

2828
// GitCommit is injected dynamically at build time via `go build -ldflags "-X main.GitCommit=XYZ"`
2929
var GitCommit string = "(na)"

internal/las/golas/reader.go

+1
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ func (g *Las) Next() (Point, error) {
219219
// a Seek operation right before returning the struct
220220
g.Lock()
221221
if g.current >= g.NumberOfPoints() {
222+
g.Unlock()
222223
return p, io.EOF
223224
}
224225
data := make([]byte, g.Header.PointDataRecordLength)

internal/las/reader.go

+14-14
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package las
22

33
import (
44
"fmt"
5+
"io"
56
"os"
7+
"sync/atomic"
68

79
"github.com/mfbonfigli/gocesiumtiler/v2/internal/geom"
810
"github.com/mfbonfigli/gocesiumtiler/v2/internal/las/golas"
@@ -24,8 +26,7 @@ type LasReader interface {
2426
// CombinedFileLasReader enables reading a a list of LAS files as if they were a single one
2527
// the files MUST have the same properties (SRID, etc)
2628
type CombinedFileLasReader struct {
27-
currentReader int
28-
currentCount int
29+
currentReader atomic.Int32
2930
readers []LasReader
3031
numPts int
3132
crs string
@@ -64,20 +65,19 @@ func (m *CombinedFileLasReader) GetCRS() string {
6465
}
6566

6667
func (m *CombinedFileLasReader) GetNext() (geom.Point64, error) {
67-
if m.currentReader >= len(m.readers) {
68-
return geom.Point64{}, fmt.Errorf("no points to read")
69-
}
70-
r := m.readers[m.currentReader]
71-
if m.currentCount == r.NumberOfPoints() {
72-
m.currentReader++
73-
m.currentCount = 0
74-
if m.currentReader >= len(m.readers) {
75-
return geom.Point64{}, fmt.Errorf("no points to read")
68+
for {
69+
currReader := int(m.currentReader.Load())
70+
if currReader >= len(m.readers) {
71+
return geom.Point64{}, io.EOF
72+
}
73+
pt, err := m.readers[currReader].GetNext()
74+
if err != nil {
75+
// try to move on to the next reader
76+
m.currentReader.CompareAndSwap(int32(currReader), int32(currReader)+1)
77+
continue
7678
}
77-
r = m.readers[m.currentReader]
79+
return pt, nil
7880
}
79-
m.currentCount++
80-
return r.GetNext()
8181
}
8282

8383
func (m *CombinedFileLasReader) Close() {

internal/las/reader_test.go

+52
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package las
33
import (
44
"fmt"
55
"os"
6+
"sync"
67
"testing"
78
)
89

@@ -42,3 +43,54 @@ func TestCombinedReader(t *testing.T) {
4243
t.Errorf("expected error, got none")
4344
}
4445
}
46+
47+
func TestCombinedReaderConcurrency(t *testing.T) {
48+
entries, err := os.ReadDir("./testdata")
49+
if err != nil {
50+
t.Fatal(err)
51+
}
52+
53+
files := []string{}
54+
for _, e := range entries {
55+
filename := e.Name()
56+
files = append(files, fmt.Sprintf("./testdata/%s", filename))
57+
}
58+
59+
r, err := NewCombinedFileLasReader(files, "EPSG:32633", false)
60+
if err != nil {
61+
t.Fatalf("unexpected error: %v", err)
62+
}
63+
64+
if actual := r.NumberOfPoints(); actual != 10*len(files) {
65+
t.Errorf("expected %d points got %d", 10*len(files), actual)
66+
}
67+
68+
if actual := r.GetCRS(); actual != "EPSG:32633" {
69+
t.Errorf("expected epsg %d got epsg %s", 32633, actual)
70+
}
71+
72+
e := make(chan error, 10)
73+
readFun := func(wg *sync.WaitGroup) {
74+
defer wg.Done()
75+
read := 0
76+
for i := 0; i < r.NumberOfPoints()/5; i++ {
77+
_, err := r.GetNext()
78+
if err != nil {
79+
e <- err
80+
t.Errorf("unexpected error %v", err)
81+
continue
82+
}
83+
read++
84+
}
85+
fmt.Println(read)
86+
}
87+
wg := &sync.WaitGroup{}
88+
for i := 0; i < 5; i++ {
89+
wg.Add(1)
90+
go readFun(wg)
91+
}
92+
wg.Wait()
93+
if len(e) > 0 {
94+
t.Errorf("errors detected in the error channel but none expected")
95+
}
96+
}

0 commit comments

Comments
 (0)