From ef590872ff236293fe498d0968fa0990a56b0414 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Mon, 16 Mar 2020 13:32:54 +0000 Subject: [PATCH] terraform/schema: Swap mutex for semaphore Fixes #26 --- go.mod | 1 + internal/terraform/schema/schema_storage.go | 31 +++++++++++++++------ 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 159f57783..c55b27e40 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/mitchellh/cli v1.0.0 github.com/sourcegraph/go-lsp v0.0.0-20200117082640-b19bb38222e2 github.com/zclconf/go-cty v1.2.1 + golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 // indirect golang.org/x/text v0.3.2 ) diff --git a/internal/terraform/schema/schema_storage.go b/internal/terraform/schema/schema_storage.go index 0ed9c9541..3f2cb135e 100644 --- a/internal/terraform/schema/schema_storage.go +++ b/internal/terraform/schema/schema_storage.go @@ -1,15 +1,16 @@ package schema import ( + "context" "fmt" "io/ioutil" "log" - "sync" "time" "github.com/hashicorp/go-version" tfjson "github.com/hashicorp/terraform-json" "github.com/hashicorp/terraform-ls/internal/terraform/exec" + "golang.org/x/sync/semaphore" ) type Reader interface { @@ -29,9 +30,9 @@ type Storage struct { logger *log.Logger - // mu ensures atomic reading and obtaining of schemas + // sem ensures atomic reading and obtaining of schemas // as the process of obtaining it may not be thread-safe - mu sync.RWMutex + sem *semaphore.Weighted // sync makes operations synchronous which makes testing easier sync bool @@ -42,6 +43,7 @@ var defaultLogger = log.New(ioutil.Discard, "", 0) func NewStorage() *Storage { return &Storage{ logger: defaultLogger, + sem: semaphore.NewWeighted(1), } } @@ -70,9 +72,12 @@ func (s *Storage) ObtainSchemasForWorkspace(tf *exec.Executor, dir string) error } func (s *Storage) obtainSchemasForWorkspace(tf *exec.Executor, dir string) error { - s.logger.Printf("Obtaining lock before retrieving schema for %q ...", dir) - s.mu.Lock() - defer s.mu.Unlock() + s.logger.Printf("Acquiring semaphore before retrieving schema for %q ...", dir) + err := s.sem.Acquire(context.Background(), 1) + if err != nil { + return fmt.Errorf("failed to acquire semaphore: %w", err) + } + defer s.sem.Release(1) // Checking the version here may be excessive // TODO: Find a way to centralize this @@ -99,11 +104,19 @@ func (s *Storage) obtainSchemasForWorkspace(tf *exec.Executor, dir string) error } func (s *Storage) ProviderConfigSchema(name string) (*tfjson.Schema, error) { - s.logger.Printf("Obtaining lock before reading %q provider schema", name) - s.mu.RLock() - defer s.mu.RUnlock() + s.logger.Printf("Acquiring semaphore before reading %q provider schema", name) + acquired := s.sem.TryAcquire(1) + if !acquired { + return nil, fmt.Errorf("schema unavailable temporarily") + } + defer s.sem.Release(1) s.logger.Printf("Reading %q provider schema", name) + + if s.ps == nil { + return nil, &SchemaUnavailableErr{"provider", name} + } + schema, ok := s.ps.Schemas[name] if !ok { return nil, &SchemaUnavailableErr{"provider", name}