lib9p

Go 9P library.
Log | Files | Refs

commit 51da7e87bad29b7c3983eb9cf3e32db331d1daa0
parent 58b13a91f04f113dc47ac97a78a1331a3bc7a760
Author: Matsuda Kenji <info@mtkn.jp>
Date:   Fri, 28 Jul 2023 15:26:49 +0900

add ReqPool

Diffstat:
Mfcall.go | 8++------
Areq.go | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mserver.go | 76+++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
3 files changed, 128 insertions(+), 29 deletions(-)

diff --git a/fcall.go b/fcall.go @@ -76,12 +76,6 @@ type Msg interface { String() string } -type Req struct { - srv *Server - ifcall Msg - ofcall Msg -} - // bufMsg is Msg with just an array of bytes. type bufMsg []byte @@ -346,6 +340,7 @@ type RWalk struct { qid []*Qid } +func newRWalk(buf []byte) *RWalk { panic("not implemented") } func (msg *RWalk) Size() uint32 { return uint32(4 + 1 + 2 + 2 + len(msg.Qid())*13) } @@ -414,6 +409,7 @@ type RStat struct { stat *stat } +func newRStat(buf []byte) *RStat { panic("not implemented") } func (msg *RStat) Size() uint32 { return uint32(4 + 1 + 2 + 2 + 2 + msg.stat.size()) } // TODO: collect ? func (msg *RStat) Type() MsgType { return Rstat } func (msg *RStat) Tag() uint16 { return msg.tag } diff --git a/req.go b/req.go @@ -0,0 +1,72 @@ +package lib9p + +import ( + "fmt" +) + +type Req struct { + srv *Server + ifcall Msg + ofcall Msg + pool *ReqPool +} + +func unmarshal(buf []byte) (Msg, error) { + switch t := bufMsg(buf).Type(); t { + case Tversion: + return newTVersion(buf), nil + case Rversion: + return RVersion(buf), nil + case Tauth: + return TAuth(buf), nil + case Rauth: + return RAuth(buf), nil + case Tattach: + return TAttach(buf), nil + case Rattach: + return RAttach(buf), nil + case Rerror: + return RError(buf), nil + case Twalk: + return newTWalk(buf), nil + case Rwalk: + return newRWalk(buf), nil + case Tstat: + return newTStat(buf), nil + case Rstat: + return newRStat(buf), nil + default: + return nil, fmt.Errorf("unknown message type %v", t) + } +} + +type ReqPool struct { + m map[uint16]*Req +} + +func allocReqPool() *ReqPool { + rp := new(ReqPool) + rp.m = make(map[uint16]*Req) + return rp +} + +// AllocReq allocates a Req with the specified tag in ReqPool rp. +// It returns nil, error if there is already a Req with the specified tag. +func (rp *ReqPool) addReq(tag uint16) (*Req, error) { + if _, ok := rp.m[tag]; ok { + return nil, fmt.Errorf("duplicate tag %d", tag) + } + req := new(Req) + req.pool = rp + rp.m[tag] = req + return req, nil +} + +func (rp *ReqPool) deleteReq(tag uint16) error { + _, ok := rp.m[tag] + if !ok { + return fmt.Errorf("tag %d not found", tag) + } + delete(rp.m, tag) + return nil +} +\ No newline at end of file diff --git a/server.go b/server.go @@ -10,15 +10,17 @@ import ( ) var chatty9P = false - func Chatty() { chatty9P = true } +var EDupTag = fmt.Errorf("duplicate tag") + type Server struct { FS *FS MSize uint32 fPool *FidPool + rPool *ReqPool Reader io.Reader Writer io.Writer } @@ -28,44 +30,53 @@ func NewServer(fs fs.FS, mSize uint32, r io.Reader, w io.Writer) *Server { s.FS = &FS{fs: fs} s.MSize = mSize s.fPool = allocFidPool() + s.rPool = allocReqPool() s.Reader = r s.Writer = w return s } func (s *Server) getReq() (*Req, error) { - var r Req - r.srv = s - buf, err := read9PMsg(s.Reader) if err != nil { if err == io.EOF { - return &r, err + return nil, err } - return &r, fmt.Errorf("read message: %v", err) + return nil, fmt.Errorf("read9PMsg: %v", err) } - switch t := bufMsg(buf).Type(); t { - default: + r, err := s.rPool.addReq(bufMsg(buf).Tag()) + if err != nil { + log.Printf("addReq(%d): %v", bufMsg(buf).Tag(), err) + // duplicate tag: cons up a fake Req + r := new(Req) + r.srv = s + r.ifcall, err = unmarshal(buf) + if err != nil { + log.Printf("unmarshal: %v", err) + r.ifcall = bufMsg(buf) + } + if chatty9P { + fmt.Fprintf(os.Stderr, "<-- %v\n", r.ifcall) + } + return r, EDupTag + } + + r.srv = s + r.ifcall, err = unmarshal(buf) + if err != nil { + log.Printf("unmarshal: %v", err) if chatty9P { fmt.Fprintf(os.Stderr, "<-- %v\n", bufMsg(buf)) } - return &r, fmt.Errorf("unknown message type %d", t) - case Tversion: - r.ifcall = newTVersion(buf) - case Tauth: - r.ifcall = TAuth(buf) - case Tattach: - r.ifcall = TAttach(buf) - case Twalk: - r.ifcall = newTWalk(buf) - case Tstat: - r.ifcall = newTStat(buf) + r.ifcall = bufMsg(buf) + return r, err } + if chatty9P { fmt.Fprintf(os.Stderr, "<-- %v\n", r.ifcall) } - return &r, nil + return r, nil } func sVersion(s *Server, r *Req) { @@ -164,6 +175,7 @@ func rError(r *Req, err error) { ofcall := RError(make([]byte, size)) ofcall.SetSize(size) ofcall.SetType(Rerror) + ofcall.SetTag(r.ifcall.Tag()) ofcall.SetEName(err) r.ofcall = ofcall } @@ -200,13 +212,16 @@ func rStat(r *Req, err error) {} func (s *Server) Serve() { for { r, err := s.getReq() - // TODO: check tag duplicate + if r == nil { + log.Printf("getReq returns nil request: %v", err) + break + } if err == io.EOF { log.Printf("getReq: %v\n", err) break } else if err != nil { log.Printf("getReq: %v\n", err) - respond(r, fmt.Errorf("internal error")) + respond(r, err) continue } switch r.ifcall.(type) { @@ -226,7 +241,13 @@ func (s *Server) Serve() { } } +// Respond responds to the request r with the message r.ofcall if err is nil, +// or if err is not nil, with the Rerror with the error message. +// If r is nil, or both r.ofcall and err are nil it panics. func respond(r *Req, err error) { + if r == nil { + panic("request and error are both nil") + } switch r.ofcall.(type) { default: if r.ofcall == nil { @@ -252,5 +273,14 @@ func respond(r *Req, err error) { fmt.Fprintf(os.Stderr, "--> %s\n", r.ofcall) } r.srv.Writer.Write(r.ofcall.conv2M()) - // TODO: free tag. + + // free tag. + if r.pool == nil && err != EDupTag { + panic("ReqPool is nil buf err is not EDupTag") + } + if r.pool != nil { + if err := r.pool.deleteReq(r.ifcall.Tag()); err != nil { + panic(fmt.Errorf("deleteReq: %v", err)) + } + } }