Skip to content

Commit c2fc2d4

Browse files
committed
Merge branch 'dev' into 'master'
v1.1.2 See merge request objectbox/objectbox-go!85
2 parents de02d9a + efa5e4f commit c2fc2d4

File tree

4 files changed

+103
-6
lines changed

4 files changed

+103
-6
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ id, err := box.Put(&Person{ FirstName: "Joe", LastName: "Green" })
1818
Want details? **[Read the docs](https://golang.objectbox.io/)** or
1919
**[check out the API reference](https://godoc.org/github.com/objectbox/objectbox-go/objectbox)**.
2020

21-
Latest release: [v1.1.1 (2020-02-14)](https://golang.objectbox.io/)
21+
Latest release: [v1.1.2 (2020-03-18)](https://golang.objectbox.io/)
2222

2323
Some features
2424
-------------

objectbox/query.go

+24
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ func queryFinalizer(query *Query) {
7070

7171
// The native query object in the ObjectBox core is not tied with other resources.
7272
// Thus timing of the Close call is independent from other resources.
73+
// Warning: it's important the object is kept around until a native call returns, e.g. using `runtime.KeepAlive(query)`.
7374
func (query *Query) installFinalizer() {
7475
runtime.SetFinalizer(query, queryFinalizer)
7576
}
@@ -80,6 +81,8 @@ func (query *Query) errorClosed() error {
8081

8182
// Find returns all objects matching the query
8283
func (query *Query) Find() (objects interface{}, err error) {
84+
defer runtime.KeepAlive(query)
85+
8386
if query.cQuery == nil {
8487
return 0, query.errorClosed()
8588
}
@@ -113,6 +116,8 @@ func (query *Query) Limit(limit uint64) *Query {
113116

114117
// FindIds returns IDs of all objects matching the query
115118
func (query *Query) FindIds() ([]uint64, error) {
119+
defer runtime.KeepAlive(query)
120+
116121
if query.cQuery == nil {
117122
return nil, query.errorClosed()
118123
}
@@ -137,6 +142,7 @@ func (query *Query) Count() (uint64, error) {
137142
if err := cCall(func() C.obx_err { return C.obx_query_count(query.cQuery, &cResult) }); err != nil {
138143
return 0, err
139144
}
145+
runtime.KeepAlive(query)
140146
return uint64(cResult), nil
141147
}
142148

@@ -155,6 +161,8 @@ func (query *Query) Remove() (count uint64, err error) {
155161
if err := cCall(func() C.obx_err { return C.obx_query_remove(query.cQuery, &cResult) }); err != nil {
156162
return 0, err
157163
}
164+
165+
runtime.KeepAlive(query)
158166
return uint64(cResult), nil
159167
}
160168

@@ -163,9 +171,11 @@ func (query *Query) DescribeParams() (string, error) {
163171
if query.cQuery == nil {
164172
return "", query.errorClosed()
165173
}
174+
166175
// no need to free, it's handled by the cQuery internally
167176
cResult := C.obx_query_describe_params(query.cQuery)
168177

178+
runtime.KeepAlive(query)
169179
return C.GoString(cResult), nil
170180
}
171181

@@ -205,6 +215,8 @@ type propertyOrAlias interface {
205215

206216
// SetStringParams changes query parameter values on the given property
207217
func (query *Query) SetStringParams(identifier propertyOrAlias, values ...string) error {
218+
defer runtime.KeepAlive(query)
219+
208220
if err := query.checkIdentifier(identifier); err != nil {
209221
return err
210222
}
@@ -236,6 +248,8 @@ func (query *Query) SetStringParams(identifier propertyOrAlias, values ...string
236248

237249
// SetStringParamsIn changes query parameter values on the given property
238250
func (query *Query) SetStringParamsIn(identifier propertyOrAlias, values ...string) error {
251+
defer runtime.KeepAlive(query)
252+
239253
if err := query.checkIdentifier(identifier); err != nil {
240254
return err
241255
}
@@ -263,6 +277,8 @@ func (query *Query) SetStringParamsIn(identifier propertyOrAlias, values ...stri
263277

264278
// SetInt64Params changes query parameter values on the given property
265279
func (query *Query) SetInt64Params(identifier propertyOrAlias, values ...int64) error {
280+
defer runtime.KeepAlive(query)
281+
266282
if err := query.checkIdentifier(identifier); err != nil {
267283
return err
268284
}
@@ -299,6 +315,8 @@ func (query *Query) SetInt64Params(identifier propertyOrAlias, values ...int64)
299315

300316
// SetInt64ParamsIn changes query parameter values on the given property
301317
func (query *Query) SetInt64ParamsIn(identifier propertyOrAlias, values ...int64) error {
318+
defer runtime.KeepAlive(query)
319+
302320
if err := query.checkIdentifier(identifier); err != nil {
303321
return err
304322
}
@@ -323,6 +341,8 @@ func (query *Query) SetInt64ParamsIn(identifier propertyOrAlias, values ...int64
323341

324342
// SetInt32ParamsIn changes query parameter values on the given property
325343
func (query *Query) SetInt32ParamsIn(identifier propertyOrAlias, values ...int32) error {
344+
defer runtime.KeepAlive(query)
345+
326346
if err := query.checkIdentifier(identifier); err != nil {
327347
return err
328348
}
@@ -347,6 +367,8 @@ func (query *Query) SetInt32ParamsIn(identifier propertyOrAlias, values ...int32
347367

348368
// SetFloat64Params changes query parameter values on the given property
349369
func (query *Query) SetFloat64Params(identifier propertyOrAlias, values ...float64) error {
370+
defer runtime.KeepAlive(query)
371+
350372
if err := query.checkIdentifier(identifier); err != nil {
351373
return err
352374
}
@@ -384,6 +406,8 @@ func (query *Query) SetFloat64Params(identifier propertyOrAlias, values ...float
384406

385407
// SetBytesParams changes query parameter values on the given property
386408
func (query *Query) SetBytesParams(identifier propertyOrAlias, values ...[]byte) error {
409+
defer runtime.KeepAlive(query)
410+
387411
if err := query.checkIdentifier(identifier); err != nil {
388412
return err
389413
}

objectbox/version.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func (v Version) String() string {
4343
// VersionGo returns the Version of the ObjectBox-Go binding
4444
func VersionGo() Version {
4545
// for label, use `beta.0` format, increasing the counter for each subsequent release
46-
return Version{1, 1, 1, ""}
46+
return Version{1, 1, 2, ""}
4747
}
4848

4949
// VersionLib returns the Version of the dynamic linked ObjectBox library

test/concurrency_test.go

+77-4
Original file line numberDiff line numberDiff line change
@@ -95,13 +95,14 @@ func concurrentInsert(t *testing.T, count, concurrency int, putAsync bool) {
9595

9696
assert.NoErr(t, env.ObjectBox.AwaitAsyncCompletion())
9797

98-
t.Log("validating counts")
9998
if len(errors) != 0 {
100-
t.Errorf("encountered %d errors", len(errors))
101-
for err := range errors {
102-
t.Log(err)
99+
t.Errorf("encountered %d errors:", len(errors))
100+
for i := 0; i < len(errors); i++ {
101+
t.Log(" ", <-errors)
103102
}
104103
}
104+
105+
t.Log("validating counts")
105106
assert.Eq(t, 0, len(errors))
106107
assert.Eq(t, count, len(ids))
107108

@@ -121,3 +122,75 @@ func concurrentInsert(t *testing.T, count, concurrency int, putAsync bool) {
121122
}
122123
}
123124
}
125+
126+
// TestConcurrentQuery checks concurrently running queries.
127+
// Previously there was an issue with finalizers, with query being closed during the native call.
128+
func TestConcurrentQuery(t *testing.T) {
129+
env := iot.NewTestEnv()
130+
defer env.Close()
131+
132+
box := iot.BoxForEvent(env.ObjectBox)
133+
134+
err := box.RemoveAll()
135+
assert.NoErr(t, err)
136+
137+
var objects = 10000
138+
var queries = 500
139+
var concurrency = 4
140+
141+
if testing.Short() || strings.Contains(strings.ToLower(runtime.GOARCH), "arm") {
142+
objects = 5000
143+
queries = 200
144+
concurrency = 2
145+
}
146+
147+
assert.NoErr(t, env.ObjectBox.RunInWriteTx(func() error {
148+
for i := objects; i > 0; i-- {
149+
if _, e := box.Put(&iot.Event{
150+
Device: "my device",
151+
}); e != nil {
152+
return e
153+
}
154+
}
155+
return nil
156+
}))
157+
158+
// prepare channels and launch the goroutines
159+
errors := make(chan error, queries)
160+
161+
t.Logf("launching %d routines to execute %d queries each, over %d objects", concurrency, queries, objects)
162+
163+
var wg sync.WaitGroup
164+
wg.Add(concurrency)
165+
for i := concurrency; i > 0; i-- {
166+
go func() {
167+
defer wg.Done()
168+
for j := queries; j > 0; j-- {
169+
var e error
170+
if j%2 == 0 {
171+
_, e = box.Query(iot.Event_.Id.GreaterThan(0)).Find()
172+
} else {
173+
_, e = box.Query(iot.Event_.Id.GreaterThan(0)).FindIds()
174+
}
175+
if e != nil {
176+
errors <- e
177+
break
178+
}
179+
}
180+
}()
181+
}
182+
183+
// collect and check results after everything is done
184+
t.Log("waiting for all goroutines to finish")
185+
wg.Wait()
186+
187+
assert.NoErr(t, env.ObjectBox.AwaitAsyncCompletion())
188+
189+
if len(errors) != 0 {
190+
t.Errorf("encountered %d errors:", len(errors))
191+
for i := 0; i < len(errors); i++ {
192+
t.Log(" ", <-errors)
193+
}
194+
}
195+
assert.Eq(t, 0, len(errors))
196+
}

0 commit comments

Comments
 (0)