Francisco J Ballesteroszxfs put fix1449f3310 years agoHistory
333 lines · 5.8 KB · Go
RawDownload
package cmd import (	"clive/ch"	"clive/dbg"	"io"	"os"	"runtime"	"sort"	"strconv"	"strings"	"sync"	"sync/atomic") struct ioChan {	sync.Mutex	isIn  bool	inc   <-chan face{}	outc  chan<- face{}	donec chan bool	fd    io.Closer // will go in the future	ref   int32     // <0 means it's never closed.	name  string	ux    bool	uxfd  int} struct ioSet {	sync.Mutex	ref int32	set map[string]*ioChan} func (cr *ioChan) refer() {	if cr != nil && atomic.LoadInt32(&cr.ref) >= 0 {		atomic.AddInt32(&cr.ref, 1)	}} func (cr *ioChan) close() {	if cr == nil {		return	} 	// If the ref counter is >= 0, we must close (and wait for outstanding I/O)	// only if a decref makes it 0.	// However, OS in/out/err have their ref set to -1 so they are never closed	// which means that we must wait from I/O if it's the main context the one	// exiting, because in that case the entire UNIX process will die.	mainexits := ctx() == mainctx 	if old := atomic.LoadInt32(&cr.ref); mainexits || old >= 0 && atomic.AddInt32(&cr.ref, -1) <= 0 {		if old == 0 {			dbg.Warn("app: too many closes on chan refs")		}		close(cr.inc)		close(cr.outc)		if cr.donec != nil {			<-cr.donec		}		if cr.fd != nil {			cr.fd.Close()		}	}} // TODO: We shouldn't be using "err" anymore.// Errors should be sent along the output stream.// The printer at the end of the pipe could print them in-line// to os.Stderr, after making a flush of os.Stdout so the error shows up// where it should.// The risk is that we miss error diags if the consumer eats them and// does not print them.// But It's worth considering. func (cr *ioChan) start() {	c := make(chan face{})	if cr.uxfd < 0 {		close(c)		if cr.isIn {			cr.inc = c		} else {			cr.outc = c		}		return	}	var fd *os.File	switch cr.uxfd {	case 0:		fd = os.Stdin	case 1:		fd = os.Stdout	case 2:		fd = os.Stderr	default:		fd = os.NewFile(uintptr(cr.uxfd), cr.name)		cr.fd = fd	}	if cr.isIn {		cr.inc = c		rfn := ch.ReadMsgs		if cr.ux {			rfn = ch.ReadBytes		}		go func() {			_, _, err := rfn(fd, c)			close(c, err)		}()	} else {		cr.outc = c		donec := make(chan bool)		cr.donec = donec		if cr.ux {			go func() {				_, _, err := ch.WriteBytes(fd, c)				close(c, err)				close(donec)			}()		} else {			go func() {				_, _, err := ch.WriteMsgs(fd, 1, c)				close(c, err)				close(donec)			}()		}		runtime.AtExit(func() {			close(c)			<-donec		})	}} func (io *ioSet) addIn(name string, c <-chan face{}) *ioChan {	io.Lock()	defer io.Unlock()	oc, ok := io.set[name]	if ok {		oc.close()	}	nc := &ioChan{name: name, ref: 1, inc: c, isIn: true, uxfd: -1}	nc.outc = make(chan face{})	close(nc.outc, "not for output")	io.set[name] = nc	return nc} func (io *ioSet) addOut(name string, c chan<- face{}) *ioChan {	io.Lock()	defer io.Unlock()	oc, ok := io.set[name]	if ok {		oc.close()	}	nc := &ioChan{name: name, ref: 1, outc: c, isIn: false, uxfd: -1}	nc.inc = make(chan face{})	close(nc.inc, "not for input")	io.set[name] = nc	return nc} func (io *ioSet) addUXIn(name string, fd int) *ioChan {	io.Lock()	defer io.Unlock()	oc, ok := io.set[name]	if ok {		oc.close()	}	nc := &ioChan{name: name, ref: 1, isIn: true, uxfd: fd}	nc.outc = make(chan face{})	close(nc.outc, "not for output")	io.set[name] = nc	return nc } func (io *ioSet) addUXOut(name string, fd int) *ioChan {	io.Lock()	defer io.Unlock()	oc, ok := io.set[name]	if ok {		oc.close()	}	nc := &ioChan{name: name, ref: 1, isIn: false, uxfd: fd}	nc.inc = make(chan face{})	close(nc.inc, "not for input")	io.set[name] = nc	return nc} func (io *ioSet) del(name string) {	io.Lock()	defer io.Unlock()	if c, ok := io.set[name]; ok {		delete(io.set, name)		c.close()	}} func (io *ioSet) get(name string) *ioChan {	io.Lock()	defer io.Unlock()	c, ok := io.set[name]	if !ok {		return nil	}	c.Lock()	defer c.Unlock()	if (c.isIn && c.inc == nil) || (!c.isIn && c.outc == nil) {		c.start()	}	return c} func (io *ioSet) refer() {	io.Lock()	io.ref++	io.Unlock()} func (io *ioSet) dup() *ioSet {	io.Lock()	defer io.Unlock()	nio := &ioSet{		ref: 1,		set: map[string]*ioChan{},	}	for k, cr := range io.set {		cr.refer()		nio.set[k] = cr	}	return nio} func (io *ioSet) chans() (in []string, out []string) {	io.Lock()	defer io.Unlock()	for k, cr := range io.set {		if cr.isIn {			in = append(in, k)		} else {			out = append(out, k)		}	}	sort.Sort(sort.StringSlice(in))	sort.Sort(sort.StringSlice(out))	return in, out} func (io *ioSet) close() {	io.Lock()	defer io.Unlock()	io.ref--	if io.ref < 0 {		panic("ioset ref < 0")	}	if io.ref == 0 {		for _, cr := range io.set {			cr.close()		}	}} func (io *ioSet) unixIO(name ...string) {	io.Lock()	defer io.Unlock()	if len(name) > 0 {		for _, n := range name {			if cf, ok := io.set[n]; ok {				cf.ux = true			}		}		return	}	for _, cr := range io.set {		cr.Lock()		cr.ux = true		cr.Unlock()	} } func (io *ioSet) addUXio() {	env := os.Environ()	for _, v := range env {		if !strings.HasPrefix(v, "cliveio#") {			continue		}		toks := strings.Split(v, "=")		if len(toks) != 2 {			continue		}		cname := strings.TrimPrefix(toks[0], "cliveio#")		fdval := toks[1]		if len(fdval) < 2 {			continue		}		dir := fdval[0]		if dir != '<' && dir != '>' {			continue		}		fdname := fdval[1:]		n, err := strconv.Atoi(fdname)		if err != nil {			continue		}		if dir == '<' {			io.addUXIn(cname, n)		} else {			io.addUXOut(cname, n)		}	}} // Initialize a new io from the osfunc mkIO() *ioSet {	io := &ioSet{		ref: 1,		set: map[string]*ioChan{},	}	nc := io.addUXIn("in", 0)	nc.ref = -1	nc = io.addUXOut("out", 1)	nc.ref = -1	nc = io.addUXOut("err", 2)	nc.ref = -1	c := make(chan face{})	close(c)	nc = io.addIn("null", c)	nc.outc = c	io.addUXio()	return io}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t