Skip to content

Commit b189d79

Browse files
committed
Allow aborting the pack writing operation
In case of an error in the writer, the packbuilder will stay around waiting for someone to read from its channel. The state associated with a packbuilder is non-trivial and it will keep a reference to the object, so the GC won't be able to free it. Change the ForEach interface to also return a "stop" channel. Closing the channel or writing into it will cause the first receive clause to act, making the callback to return -1, aborting the operation and ending the goroutine, freeing its hold on the packbuilder.
1 parent 4e0a28b commit b189d79

File tree

1 file changed

+29
-10
lines changed

1 file changed

+29
-10
lines changed

packbuilder.go

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,11 @@ func (pb *Packbuilder) WriteToFile(name string) error {
7676
}
7777

7878
func (pb *Packbuilder) Write(w io.Writer) error {
79-
ch := pb.ForEach()
79+
ch, stop := pb.ForEach()
8080
for slice := range ch {
8181
_, err := w.Write(slice)
8282
if err != nil {
83+
close(stop)
8384
return err
8485
}
8586
}
@@ -90,22 +91,40 @@ func (pb *Packbuilder) Written() uint32 {
9091
return uint32(C.git_packbuilder_written(pb.ptr))
9192
}
9293

94+
type packbuilderCbData struct {
95+
ch chan<- []byte
96+
stop <-chan bool
97+
}
98+
9399
//export packbuilderForEachCb
94100
func packbuilderForEachCb(buf unsafe.Pointer, size C.size_t, payload unsafe.Pointer) int {
95-
ch := *(*chan []byte)(payload)
101+
data := (*packbuilderCbData)(payload)
102+
ch := data.ch
103+
stop := data.stop
96104

97105
slice := C.GoBytes(buf, C.int(size))
98-
ch <- slice
106+
select {
107+
case <- stop:
108+
return -1
109+
case ch <- slice:
110+
}
111+
99112
return 0
100113
}
101114

102-
func (pb *Packbuilder) forEachWrap(ch chan []byte) {
103-
C._go_git_packbuilder_foreach(pb.ptr, unsafe.Pointer(&ch))
104-
close(ch)
115+
func (pb *Packbuilder) forEachWrap(data *packbuilderCbData) {
116+
C._go_git_packbuilder_foreach(pb.ptr, unsafe.Pointer(data))
117+
close(data.ch)
105118
}
106119

107-
func (pb *Packbuilder) ForEach() chan []byte {
108-
ch := make(chan []byte, 0)
109-
go pb.forEachWrap(ch)
110-
return ch
120+
// Foreach sends the packfile as slices through the "data" channel. If
121+
// you want to stop the pack-building process (e.g. there's an error
122+
// writing to the output), close or write a value into the "stop"
123+
// channel.
124+
func (pb *Packbuilder) ForEach() (data <-chan []byte, stop chan<- bool) {
125+
ch := make(chan []byte)
126+
stop := make(chan bool)
127+
data := packbuilderCbData{ch, stop}
128+
go pb.forEachWrap(&data)
129+
return ch, stop
111130
}

0 commit comments

Comments
 (0)