cue/cuecontext: allow concurrent use

This was currently only not allowed for imports.

Really import indices should be unique per context, but
even in that case, concurrency should be allowed.

Fixes #1414

Signed-off-by: Marcel van Lohuizen <mpvl@golang.org>

Change-Id: I944357c7b68cd242d19b323a1380c751a7fe49a5
Signed-off-by: Marcel van Lohuizen <mpvl@golang.org>
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/529629
Reviewed-by: Paul Jolly <paul@myitcv.io>
diff --git a/cue/cuecontext/cuecontext_test.go b/cue/cuecontext/cuecontext_test.go
index 2a780aa..aa5444a 100644
--- a/cue/cuecontext/cuecontext_test.go
+++ b/cue/cuecontext/cuecontext_test.go
@@ -54,3 +54,21 @@
 		})
 	}
 }
+
+// TestConcurrency tests whether concurrent use of an index is allowed.
+// This test only functions well with the --race flag.
+func TestConcurrency(t *testing.T) {
+	c := New()
+	go func() {
+		c.CompileString(`
+		package foo
+		a: 1
+		`)
+	}()
+	go func() {
+		c.CompileString(`
+		package bar
+		a: 2
+		`)
+	}()
+}
diff --git a/internal/core/runtime/build.go b/internal/core/runtime/build.go
index e3fb7ab..da5634b 100644
--- a/internal/core/runtime/build.go
+++ b/internal/core/runtime/build.go
@@ -135,7 +135,7 @@
 		return nil // TODO: check the builtin package exists here.
 	}
 
-	if v := x.index.importsByBuild[pkg]; v != nil {
+	if v := x.getNodeFromInstance(pkg); v != nil {
 		return pkg.Err
 	}
 
diff --git a/internal/core/runtime/imports.go b/internal/core/runtime/imports.go
index 243db4f..403a8a0 100644
--- a/internal/core/runtime/imports.go
+++ b/internal/core/runtime/imports.go
@@ -58,13 +58,17 @@
 //
 // All instances belonging to the same package should share this index.
 type index struct {
-	// Change this to Instance at some point.
-	// From *structLit/*Vertex -> Instance
+	// lock is used to guard imports-related maps.
+	// TODO: makes these per cuecontext.
+	lock           sync.RWMutex
 	imports        map[*adt.Vertex]*build.Instance
 	importsByPath  map[string]*adt.Vertex
 	importsByBuild map[*build.Instance]*adt.Vertex
-	builtinPaths   map[string]PackageFunc // Full path
-	builtinShort   map[string]string      // Commandline shorthand
+
+	// These are initialized during Go package initialization time and do not
+	// need to be guarded.
+	builtinPaths map[string]PackageFunc // Full path
+	builtinShort map[string]string      // Commandline shorthand
 
 	typeCache sync.Map // map[reflect.Type]evaluated
 }
@@ -86,6 +90,9 @@
 }
 
 func (r *Runtime) AddInst(path string, key *adt.Vertex, p *build.Instance) {
+	r.index.lock.Lock()
+	defer r.index.lock.Unlock()
+
 	x := r.index
 	if key == nil {
 		panic("key must not be nil")
@@ -98,14 +105,23 @@
 }
 
 func (r *Runtime) GetInstanceFromNode(key *adt.Vertex) *build.Instance {
+	r.index.lock.RLock()
+	defer r.index.lock.RUnlock()
+
 	return r.index.imports[key]
 }
 
 func (r *Runtime) getNodeFromInstance(key *build.Instance) *adt.Vertex {
+	r.index.lock.RLock()
+	defer r.index.lock.RUnlock()
+
 	return r.index.importsByBuild[key]
 }
 
 func (r *Runtime) LoadImport(importPath string) (*adt.Vertex, errors.Error) {
+	r.index.lock.Lock()
+	defer r.index.lock.Unlock()
+
 	x := r.index
 
 	key := x.importsByPath[importPath]