// Package qver keeps track of 9P qid versions. package qver // Version is a 9P qid.version. // // A Version may have several child Versions, representing a directory // hierarchy. The root version is incremented whenever any of the // children's versions change. // // Version is safe to share between goroutines. type Version struct { req <-chan struct{} res <-chan message bump chan<- struct{} regChild chan<- childReg kill chan<- struct{} } type Bumper interface { // Bump increments the version. Bump() } type version struct { req chan<- struct{} res chan<- message bump <-chan struct{} regChild <-chan childReg kill <-chan struct{} v uint32 UpdateFunc state interface{} children []*child } type childReg struct { child *Version done chan<- struct{} } type child struct { *Version last uint32 } type message struct { ver uint32 err error } func New(opts ...Option) *Version { o := parse(opts...) reqc := make(chan struct{}) resc := make(chan message) bumpc := make(chan struct{}) childc := make(chan childReg) killc := make(chan struct{}) go (&version{ reqc, resc, bumpc, childc, killc, 0, o.UpdateFunc, o.state, nil, }).run() v := &Version{reqc, resc, bumpc, childc, killc} if o.parent != nil { o.parent.register(v) } return v } func (v *version) run() { defer close(v.req) defer close(v.res) for { select { case v.req <- struct{}{}: err := v.update() v.res <- message{v.v, err} case <-v.bump: v.v++ case creg := <-v.regChild: cver, _ := creg.child.Get() v.children = append(v.children, &child{creg.child, cver}) close(creg.done) case <-v.kill: return } } } func (v *version) update() error { if v.UpdateFunc != nil { var err error v.v, v.state, err = v.UpdateFunc(v.v, v.state) if err != nil { return err } } var modified bool for _, c := range v.children { mod, err := c.update() if err != nil { return err } modified = modified || mod } if modified { v.v++ } return nil } // Returns true if modified. func (c *child) update() (bool, error) { _, ok := <-c.req if !ok { // closed return false, nil } msg := <-c.res if msg.err != nil { return false, msg.err } if msg.ver != c.last { // modified c.last = msg.ver return true, nil } // not modified return false, nil } func (v *Version) register(child *Version) { done := make(chan struct{}) v.regChild <- childReg{child, done} <-done } func (v *Version) Close() { v.kill <- struct{}{} close(v.kill) close(v.bump) close(v.regChild) } // Get returns the current version. // // If the Version was created with the Update option, Get calls the // UpdateFunc and returns the new version. func (v *Version) Get() (uint32, error) { <-v.req msg := <-v.res return msg.ver, msg.err } // Bump increments the version. func (v *Version) Bump() { v.bump <- struct{}{} }