commit 71591b179b8665ee7e40531f92b5051c47a78297
parent 9dcc186047d9863578b9ec63ef4751eb15882663
Author: Matsuda Kenji <info@mtkn.jp>
Date: Fri, 3 Nov 2023 16:25:02 +0900
implement semfs
Diffstat:
| A | cmd/semfs/fs.go | | | 160 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | cmd/semfs/main.go | | | 43 | +++++++++++++++++++++++++++++++++++++++++++ |
2 files changed, 203 insertions(+), 0 deletions(-)
diff --git a/cmd/semfs/fs.go b/cmd/semfs/fs.go
@@ -0,0 +1,160 @@
+package main
+
+import (
+ "context"
+ "errors"
+ "io"
+ "io/fs"
+ "strings"
+ "strconv"
+ "time"
+
+ "git.mtkn.jp/lib9p"
+ "log"
+)
+
+type semFS struct {
+ semfiles []*semFile
+}
+
+func (fsys *semFS) OpenFile(name string, omode lib9p.OpenMode, perm fs.FileMode) (lib9p.File, error) {
+ wnames := strings.Split(name, "/")
+ if len(wnames) != 1 {
+ return nil, errors.New("not found")
+ }
+ switch wnames[0] {
+ case "", ".", "/":
+ return fsys, nil
+ default:
+ for _, f := range fsys.semfiles {
+ if f.name == wnames[0] {
+ return f, nil
+ }
+ }
+ return nil, errors.New("not found")
+ }
+}
+
+func (root *semFS) Stat() (*lib9p.FileInfo, error) {
+ t := uint32(time.Now().Unix())
+ return &lib9p.FileInfo{Stat: lib9p.Stat{
+ Qid: lib9p.Qid{Type: lib9p.QTDIR, Vers: 0, Path: 0},
+ Mode: fs.ModeDir | 0777,
+ Atime: t,
+ Mtime: t,
+ Length: 0,
+ Name: ".",
+ }}, nil
+}
+
+func (root *semFS) Close() error { return nil }
+func (root *semFS) Create(name string, uid string, mode lib9p.OpenMode, perm lib9p.FileMode) (lib9p.File, error) {
+ for _, f := range root.semfiles {
+ if f.name == name {
+ return nil, errors.New("file already exists")
+ }
+ }
+ ctx, cancel := context.WithCancel(context.Background())
+ newfile := &semFile{
+ name: name,
+ sem: 0,
+ path: uint64(len(root.semfiles)+1),
+ cancel: cancel,
+ }
+ newfile.start(ctx)
+ root.semfiles = append(root.semfiles, newfile)
+ return newfile, nil
+}
+func (root *semFS) Read(p []byte) (n int, err error) {
+ return 0, errors.New("is a directory")
+}
+
+func (root *semFS) ReadDir(n int) ([]*lib9p.DirEntry, error) {
+ de := make([]*lib9p.DirEntry, len(root.semfiles))
+ for i, f := range root.semfiles {
+ fi, err := f.Stat()
+ if err != nil {
+ return nil, err
+ }
+ de[i] = fi
+ }
+ return de, nil
+}
+
+type semFile struct {
+ name string
+ sem int
+ path uint64
+ offset int
+ rchan chan<- chan struct{}
+ wchan chan<- int
+ queue []chan struct{}
+ cancel context.CancelFunc
+}
+
+func (f *semFile) start(ctx context.Context) {
+ rchan := make(chan chan struct{})
+ wchan := make(chan int)
+ go func(rchan <-chan chan struct{}, wchan <-chan int) {
+ for {
+ select {
+ case <-ctx.Done():
+ return
+ case c := <-rchan:
+ if f.sem > 0 {
+ f.sem--
+ close(c)
+ } else {
+ f.queue = append(f.queue, c)
+ }
+ case n := <- wchan:
+ f.sem += n
+ for len(f.queue) > 0 && f.sem > 0 {
+ close(f.queue[0])
+ f.queue = f.queue[1:]
+ f.sem--
+ }
+ }
+ }
+ }(rchan, wchan)
+ f.rchan = rchan
+ f.wchan = wchan
+}
+
+func (f *semFile) Stat() (*lib9p.FileInfo, error) {
+ t := uint32(time.Now().Unix())
+ return &lib9p.FileInfo{Stat: lib9p.Stat{
+ Qid: lib9p.Qid{Type: lib9p.QTFILE, Vers: 0, Path: f.path},
+ Mode: 0666,
+ Atime: t,
+ Mtime: t,
+ Length: int64(f.sem),
+ Name: f.name,
+ }}, nil
+}
+
+func (f *semFile) WStat(stat *lib9p.Stat) error { return nil }
+
+func (f *semFile) Close() error {
+ return nil
+}
+
+func (f *semFile) Read(p []byte) (n int, err error) {
+ if len(p) > 0 {
+ log.Println("chan")
+ c := make(chan struct{})
+ f.rchan <- c
+ <-c
+ return 0, io.EOF
+ }
+ return 0, nil
+}
+
+func (f *semFile) Write(p []byte) (int, error) {
+ n, err := strconv.Atoi(strings.Split(string(p), "\n")[0])
+ if err != nil {
+ return 0, errors.New("invalid sem")
+ }
+ f.wchan <- n
+ return len(p), nil
+}
diff --git a/cmd/semfs/main.go b/cmd/semfs/main.go
@@ -0,0 +1,43 @@
+package main
+
+import (
+ "context"
+ "flag"
+ "fmt"
+ "log"
+ "net"
+ "os"
+
+ "git.mtkn.jp/lib9p"
+)
+
+var dFlag = flag.Bool("D", false, "Prints chatty message to the stderr.")
+
+func main() {
+ flag.Parse()
+ if flag.NArg() != 0 {
+ fmt.Fprintf(os.Stderr, "usage: %s [-D]\n", os.Args[0])
+ os.Exit(1)
+ }
+ listener, err := net.Listen("tcp", "127.0.0.1:5640")
+ if err != nil {
+ log.Fatalf("listen tcp: %v", err)
+ }
+ fsys := new(semFS)
+ for {
+ conn, err := listener.Accept()
+ if err != nil {
+ log.Printf("accept connection: %v", err)
+ continue
+ }
+ go handle(conn, fsys)
+ }
+}
+
+func handle(conn net.Conn, fsys *semFS) {
+ srv := lib9p.NewServer(fsys, 8*1024, conn, conn)
+ if *dFlag {
+ srv.Chatty()
+ }
+ srv.Serve(context.Background())
+}