github.com/arnodel/golua@v0.0.0-20230215163904-e0b5347eaaa1/lib/coroutine/coroutine.go (about) 1 package coroutine 2 3 import ( 4 "fmt" 5 6 "github.com/arnodel/golua/lib/packagelib" 7 rt "github.com/arnodel/golua/runtime" 8 ) 9 10 // LibLoader allows loading the coroutine lib 11 var LibLoader = packagelib.Loader{ 12 Load: load, 13 Name: "coroutine", 14 } 15 16 func load(r *rt.Runtime) (rt.Value, func()) { 17 pkg := rt.NewTable() 18 19 rt.SolemnlyDeclareCompliance( 20 rt.ComplyCpuSafe|rt.ComplyMemSafe|rt.ComplyTimeSafe|rt.ComplyIoSafe, 21 22 r.SetEnvGoFunc(pkg, "close", close, 1, false), // Lua 5.4 23 r.SetEnvGoFunc(pkg, "create", create, 1, false), 24 r.SetEnvGoFunc(pkg, "isyieldable", isyieldable, 1, false), 25 r.SetEnvGoFunc(pkg, "resume", resume, 1, true), 26 r.SetEnvGoFunc(pkg, "running", running, 0, false), 27 r.SetEnvGoFunc(pkg, "status", status, 1, false), 28 r.SetEnvGoFunc(pkg, "wrap", wrap, 1, false), 29 r.SetEnvGoFunc(pkg, "yield", yield, 0, true), 30 ) 31 32 return rt.TableValue(pkg), nil 33 } 34 35 func close(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 36 if err := c.Check1Arg(); err != nil { 37 return nil, err 38 } 39 co, err := c.ThreadArg(0) 40 if err != nil { 41 return nil, err 42 } 43 ok, err := co.Close(t) 44 if !ok { 45 return nil, fmt.Errorf("cannot close %s thread", statusString(t, co)) 46 } 47 next := c.Next() 48 t.Push1(next, rt.BoolValue(err == nil)) 49 if err != nil { 50 t.Push1(next, rt.ErrorValue(err)) 51 } 52 return next, nil 53 } 54 55 func create(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 56 var f rt.Callable 57 err := c.Check1Arg() 58 if err == nil { 59 f, err = c.CallableArg(0) 60 } 61 if err != nil { 62 return nil, err 63 } 64 co := rt.NewThread(t.Runtime) 65 co.Start(f) 66 return c.PushingNext1(t.Runtime, rt.ThreadValue(co)), nil 67 } 68 69 func resume(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 70 var co *rt.Thread 71 err := c.Check1Arg() 72 if err == nil { 73 co, err = c.ThreadArg(0) 74 } 75 if err != nil { 76 return nil, err 77 } 78 res, err := co.Resume(t, c.Etc()) 79 next := c.Next() 80 if err == nil { 81 t.Push1(next, rt.BoolValue(true)) 82 t.Push(next, res...) 83 } else { 84 t.Push1(next, rt.BoolValue(false)) 85 t.Push1(next, rt.ErrorValue(err)) 86 } 87 return next, nil 88 } 89 90 func yield(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 91 res, err := t.Yield(c.Etc()) 92 if err != nil { 93 return nil, err 94 } 95 return c.PushingNext(t.Runtime, res...), nil 96 } 97 98 func isyieldable(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 99 var ( 100 co = t 101 err error 102 ) 103 if c.NArgs() >= 1 { 104 co, err = c.ThreadArg(0) 105 if err != nil { 106 return nil, err 107 } 108 } 109 return c.PushingNext1(t.Runtime, rt.BoolValue(!co.IsMain())), nil 110 } 111 112 func status(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 113 var co *rt.Thread 114 err := c.Check1Arg() 115 if err == nil { 116 co, err = c.ThreadArg(0) 117 } 118 if err != nil { 119 return nil, err 120 } 121 return c.PushingNext1(t.Runtime, rt.StringValue(statusString(t, co))), nil 122 } 123 124 func running(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 125 next := c.Next() 126 t.Push1(next, rt.ThreadValue(t)) 127 t.Push1(next, rt.BoolValue(t.IsMain())) 128 return next, nil 129 } 130 131 func wrap(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 132 var f rt.Callable 133 err := c.Check1Arg() 134 if err == nil { 135 f, err = c.CallableArg(0) 136 } 137 if err != nil { 138 return nil, err 139 } 140 co := rt.NewThread(t.Runtime) 141 co.Start(f) 142 w := rt.NewGoFunction(func(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 143 res, err := co.Resume(t, c.Etc()) 144 if err != nil { 145 return nil, err 146 } 147 return c.PushingNext(t.Runtime, res...), nil 148 }, "wrap", 0, true) 149 w.SolemnlyDeclareCompliance(rt.ComplyCpuSafe | rt.ComplyMemSafe | rt.ComplyTimeSafe | rt.ComplyIoSafe) 150 next := c.Next() 151 t.Push1(next, rt.FunctionValue(w)) 152 return next, nil 153 } 154 155 func statusString(running, co *rt.Thread) (status string) { 156 if co == running { 157 status = "running" 158 } else { 159 switch co.Status() { 160 case rt.ThreadDead: 161 status = "dead" 162 case rt.ThreadSuspended: 163 status = "suspended" 164 case rt.ThreadOK: 165 status = "normal" 166 default: 167 status = "unknown" 168 } 169 } 170 return 171 }