Repository: opentofu/opentofu
File: internal/providercache/installer.go (Go)

Code before:
			// we'll abort early, because subsequent operations against
			// that context will fail immediately anyway.
			return nil, err
		}

		lock := locks.Provider(provider)
		var preferredHashes []getproviders.Hash
		if lock != nil && lock.Version() == version { // hash changes are expected if the version is also changing
			preferredHashes = lock.PreferredHashes()
		}

		// If our target directory already has the provider version that fulfills the lock file, carry on
		if installed := i.targetDir.ProviderVersion(provider, version); installed != nil {
			if len(preferredHashes) > 0 {
				if matches, _ := installed.MatchesAnyHash(preferredHashes); matches {
					if cb := evts.ProviderAlreadyInstalled; cb != nil {
						cb(provider, version)
					}
					continue
				}
			}
		}

		if i.globalCacheDir != nil {
			// Try to lock the provider's directory.
			unlockProvider, err := i.globalCacheDir.Lock(ctx, provider, version)
			if err != nil {
				errs[provider] = err
				if cb := evts.LinkFromCacheFailure; cb != nil {
					cb(provider, version, err)
				}
				continue
			}
			unlock = func() {
				err = unlockProvider()
				if err != nil {
					// Don't overwrite existing errors
					if errs[provider] != nil {
						errs[provider] = err
					}
					// Still report it via the callback
					if cb := evts.LinkFromCacheFailure; cb != nil {
						cb(provider, version, err)
					}
				}
			}

			// Step 3a: If our global cache already has this version available then
			// we'll just link it in.
			installed, err := tryInstallPackageFromCacheDir(
				ctx,

Review comment (performance):
This seems reasonable for solving exactly the problem as stated, but it seems like if we were to move this down to just before [line 568](https://github.com/opentofu/opentofu/blob/f653350af64ce92ed5c516690f6ae089e0802492/internal/providercache/installer.go#L568) then we could rely on some of the conditionals we already have in place for the global vs. local situation, and also get locking of the _local_ directory at the same time, which is not quite as popular a request but still something I've seen folks ask about before.

Specifically I'm thinking about adding something like the following just before the line I indicated:

```go
unlockInstallTo, err := installTo.Lock(ctx, provider, version)
// (...error handling...)
var unlockLinkTo func()
if linkTo != nil {
    unlockLinkTo, err = linkTo.Lock(ctx, provider, version)
    // (...error handling...)
}
```

...and then unconditionally call `unlockInstallTo` and conditionally call `unlockLinkTo` afterwards.


Code after:
	reqs getproviders.Requirements,
	mode InstallMode,
	provider addrs.Provider,
	version getproviders.Version,
	targetPlatform getproviders.Platform,
) (*getproviders.PackageAuthenticationResult, error) {
	evts := installerEventsForContext(ctx)

	// The unlock function may be used if the globalCacheDir is set.  It is unlocked in the *next* iteration of the loop or after the loop exits
	// We create a file lock per-provider to prevent modification from different processes using the shared global cache
	// We lock per-provider to prevent deadlocks and release as soon as possible (instead of defer at the function scope)
	// This is not ideal, but the best option for not generating a large code diff
	var unlock func()
	defer func() {
		if unlock != nil {
			// Free remaining lock on return
			unlock()
		}
	}()

	lock := locks.Provider(provider)
	var preferredHashes []getproviders.Hash
	if lock != nil && lock.Version() == version { // hash changes are expected if the version is also changing
		preferredHashes = lock.PreferredHashes()
	}

	// If our target directory already has the provider version that fulfills the lock file, carry on
	if installed := i.targetDir.ProviderVersion(provider, version); installed != nil {
		if len(preferredHashes) > 0 {
			if matches, _ := installed.MatchesAnyHash(preferredHashes); matches {
				if cb := evts.ProviderAlreadyInstalled; cb != nil {
					cb(provider, version)
				}
				return nil, nil
			}
		}
	}

	if i.globalCacheDir != nil {
		// Try to lock the provider's directory.
		unlockProvider, err := i.globalCacheDir.Lock(ctx, provider, version)
		if err != nil {
			if cb := evts.LinkFromCacheFailure; cb != nil {
				cb(provider, version, err)
			}
			return nil, err
		}
		unlock = func() {
			err = unlockProvider()
			if err != nil {
				log.Printf("[ERROR] Unable to clear provider lock: %s", err.Error())