github.com/shogo82148/std@v1.22.1-0.20240327122250-4e474527810c/cmd/go/internal/cache/prog.go (about)

     1  // Copyright 2023 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package cache
     6  
     7  import (
     8  	"github.com/shogo82148/std/bufio"
     9  	"github.com/shogo82148/std/context"
    10  	"github.com/shogo82148/std/encoding/json"
    11  	"github.com/shogo82148/std/io"
    12  	"github.com/shogo82148/std/os/exec"
    13  	"github.com/shogo82148/std/sync"
    14  	"github.com/shogo82148/std/sync/atomic"
    15  	"github.com/shogo82148/std/time"
    16  )
    17  
    18  // ProgCache implements Cache via JSON messages over stdin/stdout to a child
    19  // helper process which can then implement whatever caching policy/mechanism it
    20  // wants.
    21  //
    22  // See https://github.com/golang/go/issues/59719
    23  type ProgCache struct {
    24  	cmd    *exec.Cmd
    25  	stdout io.ReadCloser
    26  	stdin  io.WriteCloser
    27  	bw     *bufio.Writer
    28  	jenc   *json.Encoder
    29  
    30  	// can are the commands that the child process declared that it supports.
    31  	// This is effectively the versioning mechanism.
    32  	can map[ProgCmd]bool
    33  
    34  	// fuzzDirCache is another Cache implementation to use for the FuzzDir
    35  	// method. In practice this is the default GOCACHE disk-based
    36  	// implementation.
    37  	//
    38  	// TODO(bradfitz): maybe this isn't ideal. But we'd need to extend the Cache
    39  	// interface and the fuzzing callers to be less disk-y to do more here.
    40  	fuzzDirCache Cache
    41  
    42  	closing      atomic.Bool
    43  	ctx          context.Context
    44  	ctxCancel    context.CancelFunc
    45  	readLoopDone chan struct{}
    46  
    47  	mu         sync.Mutex
    48  	nextID     int64
    49  	inFlight   map[int64]chan<- *ProgResponse
    50  	outputFile map[OutputID]string
    51  
    52  	// writeMu serializes writing to the child process.
    53  	// It must never be held at the same time as mu.
    54  	writeMu sync.Mutex
    55  }
    56  
    57  // ProgCmd is a command that can be issued to a child process.
    58  //
    59  // If the interface needs to grow, we can add new commands or new versioned
    60  // commands like "get2".
    61  type ProgCmd string
    62  
    63  // ProgRequest is the JSON-encoded message that's sent from cmd/go to
    64  // the GOCACHEPROG child process over stdin. Each JSON object is on its
    65  // own line. A ProgRequest of Type "put" with BodySize > 0 will be followed
    66  // by a line containing a base64-encoded JSON string literal of the body.
    67  type ProgRequest struct {
    68  	// ID is a unique number per process across all requests.
    69  	// It must be echoed in the ProgResponse from the child.
    70  	ID int64
    71  
    72  	// Command is the type of request.
    73  	// The cmd/go tool will only send commands that were declared
    74  	// as supported by the child.
    75  	Command ProgCmd
    76  
    77  	// ActionID is non-nil for get and puts.
    78  	ActionID []byte `json:",omitempty"`
    79  
    80  	// ObjectID is set for Type "put" and "output-file".
    81  	ObjectID []byte `json:",omitempty"`
    82  
    83  	// Body is the body for "put" requests. It's sent after the JSON object
    84  	// as a base64-encoded JSON string when BodySize is non-zero.
    85  	// It's sent as a separate JSON value instead of being a struct field
    86  	// send in this JSON object so large values can be streamed in both directions.
    87  	// The base64 string body of a ProgRequest will always be written
    88  	// immediately after the JSON object and a newline.
    89  	Body io.Reader `json:"-"`
    90  
    91  	// BodySize is the number of bytes of Body. If zero, the body isn't written.
    92  	BodySize int64 `json:",omitempty"`
    93  }
    94  
    95  // ProgResponse is the JSON response from the child process to cmd/go.
    96  //
    97  // With the exception of the first protocol message that the child writes to its
    98  // stdout with ID==0 and KnownCommands populated, these are only sent in
    99  // response to a ProgRequest from cmd/go.
   100  //
   101  // ProgResponses can be sent in any order. The ID must match the request they're
   102  // replying to.
   103  type ProgResponse struct {
   104  	ID  int64
   105  	Err string `json:",omitempty"`
   106  
   107  	// KnownCommands is included in the first message that cache helper program
   108  	// writes to stdout on startup (with ID==0). It includes the
   109  	// ProgRequest.Command types that are supported by the program.
   110  	//
   111  	// This lets us extend the protocol gracefully over time (adding "get2",
   112  	// etc), or fail gracefully when needed. It also lets us verify the program
   113  	// wants to be a cache helper.
   114  	KnownCommands []ProgCmd `json:",omitempty"`
   115  
   116  	Miss     bool       `json:",omitempty"`
   117  	OutputID []byte     `json:",omitempty"`
   118  	Size     int64      `json:",omitempty"`
   119  	Time     *time.Time `json:",omitempty"`
   120  
   121  	// DiskPath is the absolute path on disk of the ObjectID corresponding
   122  	// a "get" request's ActionID (on cache hit) or a "put" request's
   123  	// provided ObjectID.
   124  	DiskPath string `json:",omitempty"`
   125  }
   126  
   127  func (c *ProgCache) Get(a ActionID) (Entry, error)
   128  
   129  func (c *ProgCache) OutputFile(o OutputID) string
   130  
   131  func (c *ProgCache) Put(a ActionID, file io.ReadSeeker) (_ OutputID, size int64, _ error)
   132  
   133  func (c *ProgCache) Close() error
   134  
   135  func (c *ProgCache) FuzzDir() string