Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(134)

Side by Side Diff: go/src/infra/libs/git/get_object.go

Issue 662113003: Drover's back, baby! (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git/+/master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 package git
2
3 import "bufio"
4 import "fmt"
5 import "os/exec"
6 import "strconv"
7 import "strings"
8 import "sync"
9 import "sync/atomic"
10 import "unsafe"
11
12 import "infra/libs/infra_util"
13
14 // GetObject will asynchronously fetch |objectish| from the Repo, and
15 // return a channel for the result. If the object is missing, GetObject will
16 // push nil to the channel.
17 func (r *Repo) GetObject(objectish string) InternableObject {
18 r.ensureCatFileServer(&r.catFile, false)
19
20 rchan := make(chan *catFileReply, 1)
21 (*r.catFile) <- catFileRequest{
22 objectish: objectish,
23 reply: rchan,
24 }
25
26 if rsp := <-rchan; rsp == nil {
27 return nil
28 } else {
29 switch rsp.typ {
30 case "blob":
31 return BlobFromRawWithID(rsp.id, rsp.data)
32 case "tree":
33 if t, err := TreeFromRawWithID(rsp.id, rsp.data); err != nil {
34 panic(err)
35 } else {
36 return t
37 }
38 case "commit":
39 if c, err := CommitFromRawWithID(rsp.id, rsp.data); err != nil {
40 panic(err)
41 } else {
42 return c
43 }
44 default:
45 panic(fmt.Errorf("unsupported object type: %s", rsp.typ) )
46 }
47 }
48 }
49
50 func (r *Repo) GetObjectID(id ObjectID) InternableObject {
51 return r.GetObject(id.String())
52 }
53
54 // HasObject will return true iff the Repo contains the objectish
55 func (r *Repo) HasObject(objectish string) bool {
56 r.ensureCatFileServer(&r.catFileCheck, true)
57 rchan := make(chan *catFileReply, 1)
58 (*r.catFileCheck) <- catFileRequest{
59 objectish: objectish,
60 reply: rchan,
61 }
62 return (<-rchan) != nil
63 }
64
65 func (r *Repo) HasObjectID(id ObjectID) bool {
66 return r.HasObject(id.String())
67 }
68
69 type blobOpt bool
70
71 const (
72 WithBlobs blobOpt = true
73 NoBlobs = false
74 )
75
76 type fullTree bool
77
78 const (
79 FullTree fullTree = true
M-A Ruel 2014/10/18 00:47:05 move constants and simple types at the top
iannucci 2014/10/20 21:11:57 Done.
80 MissingOK = false
81 )
82
83 // GetFullTree gets a recursively-enumerated Tree.
84 //
85 // blobOpt:
86 // WithBlobs - Load blobs from Repo
87 // NoBlobs - Blobs will be EmptyObject
88 //
89 // fullTree:
90 // FullTree - All entries in tree must load
91 // MissingOK - Missing entries will remain EmptyObject (or an !Complete() Tree )
92 func (r *Repo) GetFullTree(treeish string, b blobOpt, f fullTree) *Tree {
93 base, ok := r.GetObject(treeish).(*Tree)
94 if !ok {
95 if f == FullTree {
96 panic(fmt.Errorf("could not load object %s", treeish))
97 } else {
98 return nil
99 }
100 }
101 grp := sync.WaitGroup{}
102 for p, c := range base.children {
103 p := p
M-A Ruel 2014/10/18 00:47:05 Oops?
iannucci 2014/10/20 21:11:57 Nope, to avoid aliasing p in the func()
M-A Ruel 2014/10/21 00:55:53 It's because you need to call with p as a paramete
104 c := c
105 grp.Add(1)
106 go func() {
107 defer grp.Done()
108 switch c.Mode.Type() {
109 case "tree":
110 subtree := r.GetFullTreeID(c.Object.ID(), b, f)
111 if subtree == nil {
112 if f == FullTree {
113 panic(fmt.Errorf("could not load tree %s", c.Object.ID()))
114 }
115 } else {
116 base.children[p] = &Child{subtree, c.Mod e}
117 }
118 case "blob":
119 if b == WithBlobs {
120 rslt := r.GetObjectID(c.Object.ID())
121 if rslt == nil && f == FullTree {
122 panic(fmt.Errorf("could not load object %s", c.Object.ID()))
123 }
124 base.children[p] = &Child{rslt, c.Mode}
125 }
126 }
127 }()
128 }
129 grp.Wait()
130 return base
131 }
132
133 func (r *Repo) GetFullTreeID(tree ObjectID, b blobOpt, f fullTree) *Tree {
134 return r.GetFullTree(tree.String(), b, f)
135 }
136
137 /// Private
138
139 type catFileReply struct {
140 id ObjectID
141 typ string
142 size int
143 data []byte
144 }
145
146 type catFileRequest struct {
147 objectish string
148 reply chan<- *catFileReply
149 }
150
151 func (r *Repo) ensureCatFileServer(ch **chan<- catFileRequest, checkOnly bool) {
M-A Ruel 2014/10/18 00:47:05 Double pointer is unnecessary and you shouldn't no
iannucci 2014/10/20 21:11:57 Come again? How do I safely start the service lazi
152 if *ch == nil {
153 c := make(chan catFileRequest, 16)
154 swapped := atomic.CompareAndSwapPointer(
155 (*unsafe.Pointer)(unsafe.Pointer(ch)),
156 nil,
157 unsafe.Pointer(&c),
158 )
159 if swapped {
160 go r.catFileServer(c, checkOnly)
161 }
162 }
163 }
164
165 func (r *Repo) catFileServer(rchan chan catFileRequest, checkOnly bool) {
166 defer func() {
167 atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&r.catFile) ), nil)
168 close(rchan)
169
170 if err := recover(); err != nil {
171 fmt.Println("recovering panick'd catFileServer", err)
172 }
173 }()
174
175 arg := "--batch"
176 if checkOnly {
177 arg = "--batch-check"
178 }
179 catFile := exec.Command("git", "cat-file", arg)
180 catFile.Dir = r.Path
181 in, err := catFile.StdinPipe()
182 if err != nil {
183 panic(err)
M-A Ruel 2014/10/18 00:47:05 Send error back in channel
iannucci 2014/10/20 21:11:57 Nowhere to send it to, this condition should never
184 }
185 defer in.Close()
186 outRaw, err := catFile.StdoutPipe()
187 if err != nil {
188 panic(err)
189 }
190 defer outRaw.Close()
191 out := bufio.NewReader(outRaw)
192
193 if err = catFile.Start(); err != nil {
194 panic(err)
195 }
196
197 nom := infra_util.Nom(out)
198 yoink := infra_util.Yoink(out)
199
200 for req := range rchan {
201 if strings.ContainsAny(req.objectish, "\n") {
202 panic("catFile request may not contain a newline")
203 }
204
205 in.Write([]byte(req.objectish + "\n"))
206 rsp := nom('\n')
207 if strings.HasSuffix(rsp, " missing") {
208 req.reply <- nil
209 continue
210 }
211
212 parts := strings.Split(rsp, " ")
213 objID, typ, sizeStr := parts[0], parts[1], parts[2]
214 size, err := strconv.ParseUint(sizeStr, 10, 64)
215 if err != nil {
216 panic(err)
217 }
218
219 data := []byte{}
220 if !checkOnly {
221 data = yoink(int(size))
222 out.ReadByte() // drop extra newline
223 }
224 req.reply <- &catFileReply{
225 id: MakeObjectID(objID),
226 typ: typ,
227 data: data,
228 size: int(size),
229 }
230 }
231 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698