summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Martín Nieto <[email protected]>2013-05-17 11:38:37 +0200
committerCarlos Martín Nieto <[email protected]>2013-05-23 11:44:06 +0200
commitb189d7919a3d1632cb7869e71c495cede93a74dc (patch)
tree98dfa00b389a2850eacb91d221bdba8c93173fd7
parent4e0a28b064047513194a842e9c16d9beab545f41 (diff)
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.
-rw-r--r--packbuilder.go39
1 files changed, 29 insertions, 10 deletions
diff --git a/packbuilder.go b/packbuilder.go
index 8cc03bd..a5d0a83 100644
--- a/packbuilder.go
+++ b/packbuilder.go
@@ -76,10 +76,11 @@ func (pb *Packbuilder) WriteToFile(name string) error {
}
func (pb *Packbuilder) Write(w io.Writer) error {
- ch := pb.ForEach()
+ ch, stop := pb.ForEach()
for slice := range ch {
_, err := w.Write(slice)
if err != nil {
+ close(stop)
return err
}
}
@@ -90,22 +91,40 @@ func (pb *Packbuilder) Written() uint32 {
return uint32(C.git_packbuilder_written(pb.ptr))
}
+type packbuilderCbData struct {
+ ch chan<- []byte
+ stop <-chan bool
+}
+
//export packbuilderForEachCb
func packbuilderForEachCb(buf unsafe.Pointer, size C.size_t, payload unsafe.Pointer) int {
- ch := *(*chan []byte)(payload)
+ data := (*packbuilderCbData)(payload)
+ ch := data.ch
+ stop := data.stop
slice := C.GoBytes(buf, C.int(size))
- ch <- slice
+ select {
+ case <- stop:
+ return -1
+ case ch <- slice:
+ }
+
return 0
}
-func (pb *Packbuilder) forEachWrap(ch chan []byte) {
- C._go_git_packbuilder_foreach(pb.ptr, unsafe.Pointer(&ch))
- close(ch)
+func (pb *Packbuilder) forEachWrap(data *packbuilderCbData) {
+ C._go_git_packbuilder_foreach(pb.ptr, unsafe.Pointer(data))
+ close(data.ch)
}
-func (pb *Packbuilder) ForEach() chan []byte {
- ch := make(chan []byte, 0)
- go pb.forEachWrap(ch)
- return ch
+// Foreach sends the packfile as slices through the "data" channel. If
+// you want to stop the pack-building process (e.g. there's an error
+// writing to the output), close or write a value into the "stop"
+// channel.
+func (pb *Packbuilder) ForEach() (data <-chan []byte, stop chan<- bool) {
+ ch := make(chan []byte)
+ stop := make(chan bool)
+ data := packbuilderCbData{ch, stop}
+ go pb.forEachWrap(&data)
+ return ch, stop
}