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