commit 51da7e87bad29b7c3983eb9cf3e32db331d1daa0
parent 58b13a91f04f113dc47ac97a78a1331a3bc7a760
Author: Matsuda Kenji <info@mtkn.jp>
Date: Fri, 28 Jul 2023 15:26:49 +0900
add ReqPool
Diffstat:
| M | fcall.go | | | 8 | ++------ |
| A | req.go | | | 73 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | server.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))
+ }
+ }
}