github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/lang/pipes/namedpipes.go (about) 1 package pipes 2 3 import ( 4 "errors" 5 "fmt" 6 "sync" 7 "time" 8 9 "github.com/lmorg/murex/builtins/pipes/null" 10 "github.com/lmorg/murex/lang/stdio" 11 ) 12 13 // Named is a table of created named pipes 14 type Named struct { 15 pipes map[string]pipe 16 mutex sync.Mutex 17 } 18 19 type pipe struct { 20 Pipe stdio.Io 21 Type string 22 } 23 24 // NewNamed creates a new table of named pipes 25 func NewNamed() (n Named) { 26 n.pipes = make(map[string]pipe) 27 28 n.pipes["null"] = pipe{ 29 Pipe: new(null.Null), 30 Type: "null", 31 } 32 33 return 34 } 35 36 // CreatePipe creates a named pipe using the stdin interface 37 func (n *Named) CreatePipe(name, pipeType, arguments string) error { 38 n.mutex.Lock() 39 40 if n.pipes[name].Pipe != nil { 41 n.mutex.Unlock() 42 return fmt.Errorf("named pipe `%s`already exists", name) 43 } 44 45 io, err := stdio.CreatePipe(pipeType, arguments) 46 if err != nil { 47 n.mutex.Unlock() 48 return err 49 } 50 51 n.pipes[name] = pipe{ 52 Pipe: io, 53 Type: pipeType, 54 } 55 56 io.Open() 57 n.mutex.Unlock() 58 return nil 59 } 60 61 // ExposePipe takes an existing stdio.Io interface and exposes it as a named pipe 62 func (n *Named) ExposePipe(name, pipeType string, io stdio.Io) error { 63 n.mutex.Lock() 64 65 if n.pipes[name].Pipe != nil { 66 n.mutex.Unlock() 67 return fmt.Errorf("named pipe `%s`already exists", name) 68 } 69 70 n.pipes[name] = pipe{ 71 Pipe: io, 72 Type: pipeType, 73 } 74 75 n.mutex.Unlock() 76 return nil 77 } 78 79 // Close a named pipe 80 func (n *Named) Close(name string) error { 81 n.mutex.Lock() 82 83 if n.pipes[name].Pipe == nil { 84 n.mutex.Unlock() 85 return fmt.Errorf("no pipe with the name `%s` exists", name) 86 } 87 88 if name == "null" { 89 n.mutex.Unlock() 90 return errors.New("null pipe must not be closed") 91 } 92 93 n.mutex.Unlock() 94 95 go closePipe(n, name) 96 return nil 97 } 98 99 func closePipe(n *Named, name string) { 100 time.Sleep(2 * time.Second) 101 102 n.mutex.Lock() 103 104 n.pipes[name].Pipe.Close() 105 delete(n.pipes, name) 106 107 n.mutex.Unlock() 108 } 109 110 // Deletes a named pipe without closing it (careful using this!!!) 111 func (n *Named) Delete(name string) error { 112 n.mutex.Lock() 113 114 if n.pipes[name].Pipe == nil { 115 n.mutex.Unlock() 116 return fmt.Errorf("no pipe with the name `%s` exists", name) 117 } 118 119 if name == "null" { 120 n.mutex.Unlock() 121 return errors.New("null pipe must not be closed") 122 } 123 124 n.mutex.Unlock() 125 126 delete(n.pipes, name) 127 return nil 128 } 129 130 // Get a named pipe interface from the named pipe table 131 func (n *Named) Get(name string) (stdio.Io, error) { 132 retries := 0 133 134 try: 135 n.mutex.Lock() 136 137 if n.pipes[name].Pipe == nil { 138 n.mutex.Unlock() 139 140 if retries == 5 { 141 return nil, fmt.Errorf("no pipe with the name `%s` exists, timed out waiting for pipe to be created", name) 142 } 143 time.Sleep(100 * time.Millisecond) 144 retries++ 145 goto try 146 } 147 148 p := n.pipes[name].Pipe 149 n.mutex.Unlock() 150 return p, nil 151 } 152 153 // Dump returns the named pipe table in a format that can be serialised into JSON 154 func (n *Named) Dump() map[string]string { 155 dump := make(map[string]string) 156 n.mutex.Lock() 157 for name := range n.pipes { 158 dump[name] = n.pipes[name].Type 159 } 160 n.mutex.Unlock() 161 return dump 162 }