github.com/puellanivis/breton@v0.2.16/lib/files/wrapper/writer.go (about) 1 package wrapper 2 3 import ( 4 "bytes" 5 "context" 6 "io" 7 "net/url" 8 "os" 9 "sync" 10 "time" 11 ) 12 13 // Writer implements the files.Writer interface, that buffers all writes until a Sync or Close, before committing. 14 type Writer struct { 15 mu sync.Mutex 16 17 *Info 18 b bytes.Buffer 19 20 flush chan struct{} 21 done chan struct{} 22 errch chan error 23 } 24 25 // WriteFunc is a function that is intended to write the given byte slice to some 26 // underlying source returning any error that should be returned during the 27 // Sync or Close call which is committing the file. 28 type WriteFunc func([]byte) error 29 30 // NewWriter returns a Writer that is setup to call the given WriteFunc with 31 // the underlying buffer on every Sync, and Close. 32 func NewWriter(ctx context.Context, uri *url.URL, f WriteFunc) *Writer { 33 wr := &Writer{ 34 Info: NewInfo(uri, 0, time.Now()), 35 flush: make(chan struct{}), 36 done: make(chan struct{}), 37 errch: make(chan error), 38 } 39 40 doWrite := func() error { 41 wr.mu.Lock() 42 defer wr.mu.Unlock() 43 44 // Update ModTime to now. 45 wr.Info.SetModTime(time.Now()) 46 return f(wr.b.Bytes()) 47 } 48 49 go func() { 50 defer func() { 51 close(wr.errch) 52 close(wr.flush) 53 }() 54 55 for { 56 select { 57 case <-wr.done: 58 // For done, we only send a non-nil err, 59 // When we close the errch, it will then return nil errors. 60 if err := doWrite(); err != nil { 61 wr.errch <- err 62 } 63 return 64 65 case <-wr.flush: 66 // For flush, we send even nil errors, 67 // Otherwise, the Sync() routine would block forever waiting on an errch. 68 wr.errch <- doWrite() 69 70 case <-ctx.Done(): 71 return 72 } 73 } 74 }() 75 76 return wr 77 } 78 79 // Write performs a thread-safe Write to the underlying buffer. 80 func (w *Writer) Write(b []byte) (n int, err error) { 81 w.mu.Lock() 82 defer w.mu.Unlock() 83 84 n, err = w.b.Write(b) 85 86 w.Info.SetSize(w.b.Len()) 87 88 return n, err 89 } 90 91 func (w *Writer) signalSync() error { 92 w.mu.Lock() 93 defer w.mu.Unlock() 94 95 select { 96 case <-w.done: 97 // cannot flush a closed Writer. 98 return io.ErrClosedPipe 99 default: 100 } 101 102 w.flush <- struct{}{} 103 return nil 104 } 105 106 // Sync calls the defined WriteFunc for the Writer with the entire underlying buffer. 107 func (w *Writer) Sync() error { 108 if err := w.signalSync(); err != nil { 109 return err 110 } 111 112 // We cannot wait here under Lock, because the sync process requires the Lock. 113 return <-w.errch 114 } 115 116 func (w *Writer) markDone() error { 117 w.mu.Lock() 118 defer w.mu.Unlock() 119 120 select { 121 case <-w.done: 122 // already closed 123 return os.ErrClosed 124 default: 125 } 126 127 close(w.done) 128 return nil 129 } 130 131 // Close performs a marks the Writer as complete, which also causes a Sync. 132 func (w *Writer) Close() error { 133 if err := w.markDone(); err != nil { 134 return err 135 } 136 137 var err error 138 139 // We cannot wait here under Lock, because the sync process requires the Lock. 140 for err2 := range w.errch { 141 if err == nil { 142 err = err2 143 } 144 } 145 146 return err 147 }