...

Text file src/cmd/go/testdata/script/mod_download_concurrent_read.txt

Documentation: cmd/go/testdata/script

     1# This test simulates a process watching for changes and reading files in
     2# module cache as a module is extracted.
     3#
     4# Before Go 1.16, we extracted each module zip to a temporary directory with
     5# a random name, then renamed that into place with os.Rename. On Windows,
     6# this failed with ERROR_ACCESS_DENIED when another process (usually an
     7# anti-virus scanner) opened files in the temporary directory. This test
     8# simulates that behavior, verifying golang.org/issue/36568.
     9#
    10# Since 1.16, we extract to the final directory, but we create a .partial file
    11# so that if we crash, other processes know the directory is incomplete.
    12
    13[!GOOS:windows] skip
    14[short] skip
    15
    16go run downloader.go
    17
    18-- go.mod --
    19module example.com/m
    20
    21go 1.14
    22
    23-- downloader.go --
    24package main
    25
    26import (
    27	"fmt"
    28	"log"
    29	"os"
    30	"os/exec"
    31	"path/filepath"
    32)
    33
    34func main() {
    35	if err := run(); err != nil {
    36		log.Fatal(err)
    37	}
    38}
    39
    40// run repeatedly downloads a module while opening files in the module cache
    41// in a background goroutine.
    42//
    43// run uses a different temporary module cache in each iteration so that we
    44// don't need to clean the cache or synchronize closing files after each
    45// iteration.
    46func run() (err error) {
    47	tmpDir, err := os.MkdirTemp("", "")
    48	if err != nil {
    49		return err
    50	}
    51	defer func() {
    52		if rmErr := os.RemoveAll(tmpDir); err == nil && rmErr != nil {
    53			err = rmErr
    54		}
    55	}()
    56	for i := 0; i < 10; i++ {
    57    gopath := filepath.Join(tmpDir, fmt.Sprintf("gopath%d", i))
    58		var err error
    59		done := make(chan struct{})
    60		go func() {
    61			err = download(gopath)
    62			close(done)
    63		}()
    64		readCache(gopath, done)
    65		if err != nil {
    66			return err
    67		}
    68	}
    69	return nil
    70}
    71
    72// download downloads a module into the given cache using 'go mod download'.
    73func download(gopath string) error {
    74	cmd := exec.Command("go", "mod", "download", "-modcacherw", "rsc.io/quote@v1.5.2")
    75	cmd.Stderr = os.Stderr
    76	cmd.Env = append(os.Environ(), "GOPATH="+gopath)
    77	return cmd.Run()
    78}
    79
    80// readCache repeatedly globs for go.mod files in the given cache, then opens
    81// those files for reading. When the done chan is closed, readCache closes
    82// files and returns.
    83func readCache(gopath string, done <-chan struct{}) {
    84	files := make(map[string]*os.File)
    85	defer func() {
    86		for _, f := range files {
    87			f.Close()
    88		}
    89	}()
    90
    91	pattern := filepath.Join(gopath, "pkg/mod/rsc.io/quote@v1.5.2*/go.mod")
    92	for {
    93		select {
    94		case <-done:
    95			return
    96		default:
    97		}
    98
    99		names, _ := filepath.Glob(pattern)
   100		for _, name := range names {
   101			if files[name] != nil {
   102				continue
   103			}
   104			f, _ := os.Open(name)
   105			if f != nil {
   106				files[name] = f
   107			}
   108		}
   109	}
   110}

View as plain text