aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/link/internal/ld/outbuf.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/link/internal/ld/outbuf.go')
-rw-r--r--src/cmd/link/internal/ld/outbuf.go110
1 files changed, 98 insertions, 12 deletions
diff --git a/src/cmd/link/internal/ld/outbuf.go b/src/cmd/link/internal/ld/outbuf.go
index f8e65ef8ae..8e083477ec 100644
--- a/src/cmd/link/internal/ld/outbuf.go
+++ b/src/cmd/link/internal/ld/outbuf.go
@@ -9,6 +9,7 @@ import (
"cmd/internal/sys"
"cmd/link/internal/sym"
"encoding/binary"
+ "errors"
"log"
"os"
)
@@ -23,19 +24,100 @@ import (
// Second, it provides a very cheap offset counter that doesn't require
// any system calls to read the value.
//
-// It also mmaps the output file (if available). The intended usage is:
+// Third, it also mmaps the output file (if available). The intended usage is:
// - Mmap the output file
// - Write the content
// - possibly apply any edits in the output buffer
// - Munmap the output file
// - possibly write more content to the file, which will not be edited later.
+//
+// And finally, it provides a mechanism by which you can multithread the
+// writing of output files. This mechanism is accomplished by copying a OutBuf,
+// and using it in the thread/goroutine.
+//
+// Parallel OutBuf is intended to be used like:
+//
+// func write(out *OutBuf) {
+// var wg sync.WaitGroup
+// for i := 0; i < 10; i++ {
+// wg.Add(1)
+// view, err := out.View(start[i])
+// if err != nil {
+// // handle output
+// continue
+// }
+// go func(out *OutBuf, i int) {
+// // do output
+// wg.Done()
+// }(view, i)
+// }
+// wg.Wait()
+// }
type OutBuf struct {
- arch *sys.Arch
- off int64
- w *bufio.Writer
- buf []byte // backing store of mmap'd output file
- f *os.File
- encbuf [8]byte // temp buffer used by WriteN methods
+ arch *sys.Arch
+ off int64
+ w *bufio.Writer
+ buf []byte // backing store of mmap'd output file
+ name string
+ f *os.File
+ encbuf [8]byte // temp buffer used by WriteN methods
+ isView bool // true if created from View()
+ start, length uint64 // start and length mmaped data.
+}
+
+func (out *OutBuf) Open(name string) error {
+ if out.f != nil {
+ return errors.New("cannont open more than one file")
+ }
+ f, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0775)
+ if err != nil {
+ return err
+ }
+ out.off = 0
+ out.name = name
+ out.w = bufio.NewWriter(f)
+ out.f = f
+ return nil
+}
+
+func NewOutBuf(arch *sys.Arch) *OutBuf {
+ return &OutBuf{
+ arch: arch,
+ }
+}
+
+var viewError = errors.New("output not mmapped")
+
+func (out *OutBuf) View(start uint64) (*OutBuf, error) {
+ if out.buf == nil {
+ return nil, viewError
+ }
+ return &OutBuf{
+ arch: out.arch,
+ name: out.name,
+ buf: out.buf,
+ off: int64(start),
+ start: start,
+ length: out.length,
+ isView: true,
+ }, nil
+}
+
+var viewCloseError = errors.New("cannot Close OutBuf from View")
+
+func (out *OutBuf) Close() error {
+ if out.isView {
+ return viewCloseError
+ }
+ out.Flush()
+ if out.f == nil {
+ return nil
+ }
+ if err := out.f.Close(); err != nil {
+ return err
+ }
+ out.f = nil
+ return nil
}
func (out *OutBuf) SeekSet(p int64) {
@@ -45,7 +127,7 @@ func (out *OutBuf) SeekSet(p int64) {
if out.buf == nil {
out.Flush()
if _, err := out.f.Seek(p, 0); err != nil {
- Exitf("seeking to %d in %s: %v", p, out.f.Name(), err)
+ Exitf("seeking to %d in %s: %v", p, out.name, err)
}
}
out.off = p
@@ -154,13 +236,16 @@ func (out *OutBuf) WriteStringPad(s string, n int, pad []byte) {
// edit to the symbol content.
// If the output file is not Mmap'd, just writes the content.
func (out *OutBuf) WriteSym(s *sym.Symbol) {
+ // NB: We inline the Write call for speediness.
if out.buf != nil {
start := out.off
- out.Write(s.P)
+ n := copy(out.buf[out.off:], s.P)
+ out.off += int64(n)
s.P = out.buf[start:out.off]
s.Attr.Set(sym.AttrReadOnly, false)
} else {
- out.Write(s.P)
+ n, _ := out.w.Write(s.P)
+ out.off += int64(n)
}
}
@@ -168,10 +253,11 @@ func (out *OutBuf) Flush() {
var err error
if out.buf != nil {
err = out.Msync()
- } else {
+ }
+ if out.w != nil {
err = out.w.Flush()
}
if err != nil {
- Exitf("flushing %s: %v", out.f.Name(), err)
+ Exitf("flushing %s: %v", out.name, err)
}
}