github.com/arnodel/golua@v0.0.0-20230215163904-e0b5347eaaa1/lib/runtimelib/ctx.go (about) 1 package runtimelib 2 3 import ( 4 "fmt" 5 "strings" 6 7 rt "github.com/arnodel/golua/runtime" 8 ) 9 10 var contextRegistryKey = rt.AsValue(contextRegistry{}) 11 12 func createContextMetatable(r *rt.Runtime) { 13 contextMeta := rt.NewTable() 14 15 rt.SolemnlyDeclareCompliance( 16 rt.ComplyCpuSafe|rt.ComplyMemSafe|rt.ComplyTimeSafe|rt.ComplyIoSafe, 17 18 r.SetEnvGoFunc(contextMeta, "__index", context__index, 2, false), 19 r.SetEnvGoFunc(contextMeta, "__tostring", context__tostring, 1, false), 20 ) 21 22 resourcesMeta := rt.NewTable() 23 rt.SolemnlyDeclareCompliance( 24 rt.ComplyCpuSafe|rt.ComplyMemSafe|rt.ComplyTimeSafe|rt.ComplyIoSafe, 25 26 r.SetEnvGoFunc(resourcesMeta, "__index", resources__index, 2, false), 27 r.SetEnvGoFunc(resourcesMeta, "__tostring", resources__tostring, 1, false), 28 ) 29 r.SetRegistry(contextRegistryKey, rt.AsValue(&contextRegistry{ 30 contextMeta: contextMeta, 31 resourcesMeta: resourcesMeta, 32 })) 33 } 34 35 type contextRegistry struct { 36 contextMeta *rt.Table 37 resourcesMeta *rt.Table 38 } 39 40 func getRegistry(r *rt.Runtime) *contextRegistry { 41 return r.Registry(contextRegistryKey).Interface().(*contextRegistry) 42 } 43 44 func newContextValue(r *rt.Runtime, ctx rt.RuntimeContext) rt.Value { 45 return r.NewUserDataValue(ctx, getRegistry(r).contextMeta) 46 } 47 48 func valueToContext(v rt.Value) (rt.RuntimeContext, bool) { 49 u, ok := v.TryUserData() 50 if !ok { 51 return nil, false 52 } 53 ctx, ok := u.Value().(rt.RuntimeContext) 54 if !ok { 55 return nil, false 56 } 57 return ctx, true 58 } 59 60 func newResourcesValue(r *rt.Runtime, res rt.RuntimeResources) rt.Value { 61 return r.NewUserDataValue(res, getRegistry(r).resourcesMeta) 62 } 63 64 func valueToResources(v rt.Value) (res rt.RuntimeResources, ok bool) { 65 var u *rt.UserData 66 u, ok = v.TryUserData() 67 if !ok { 68 return 69 } 70 res, ok = u.Value().(rt.RuntimeResources) 71 return 72 } 73 74 func contextArg(c *rt.GoCont, n int) (rt.RuntimeContext, error) { 75 ctx, ok := valueToContext(c.Arg(n)) 76 if ok { 77 return ctx, nil 78 } 79 return nil, fmt.Errorf("#%d must be a runtime context", n+1) 80 } 81 82 func optContextArg(t *rt.Thread, c *rt.GoCont, n int) (rt.RuntimeContext, error) { 83 if n >= c.NArgs() { 84 return t.RuntimeContext(), nil 85 } 86 return contextArg(c, n) 87 } 88 89 func resourcesArg(c *rt.GoCont, n int) (rt.RuntimeResources, error) { 90 res, ok := valueToResources(c.Arg(n)) 91 if ok { 92 return res, nil 93 } 94 return res, fmt.Errorf("#%d must be a runtime resources", n+1) 95 } 96 97 func context__index(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 98 ctx, err := contextArg(c, 0) 99 if err != nil { 100 return nil, err 101 } 102 key, err := c.StringArg(1) 103 if err != nil { 104 return nil, err 105 } 106 val := rt.NilValue 107 switch key { 108 case "kill": 109 val = newResourcesValue(t.Runtime, ctx.HardLimits()) 110 case "stop": 111 val = newResourcesValue(t.Runtime, ctx.SoftLimits()) 112 case "used": 113 val = newResourcesValue(t.Runtime, ctx.UsedResources()) 114 case "status": 115 val = statusValue(ctx.Status()) 116 case "parent": 117 val = rt.NilValue 118 case "flags": 119 val = rt.StringValue(strings.Join(ctx.RequiredFlags().Names(), " ")) 120 case "due": 121 val = rt.BoolValue(ctx.Due()) 122 case "killnow": 123 val = rt.FunctionValue(killnowGoF) 124 case "stopnow": 125 val = rt.FunctionValue(stopnowGoF) 126 } 127 return c.PushingNext1(t.Runtime, val), nil 128 } 129 130 func context__tostring(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 131 ctx, err := contextArg(c, 0) 132 if err != nil { 133 return nil, err 134 } 135 return c.PushingNext1(t.Runtime, statusValue(ctx.Status())), nil 136 } 137 138 func resources__index(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 139 res, err := resourcesArg(c, 0) 140 if err != nil { 141 return nil, err 142 } 143 key, err := c.StringArg(1) 144 if err != nil { 145 return nil, err 146 } 147 val := rt.NilValue 148 switch key { 149 case cpuName: 150 n := res.Cpu 151 if n > 0 { 152 val = resToVal(n) 153 } 154 case memoryName: 155 n := res.Memory 156 if n > 0 { 157 val = resToVal(n) 158 } 159 case secondsName: 160 n := res.Millis 161 if n > 0 { 162 val = rt.FloatValue(float64(n) / 1000) 163 } 164 case millisName: 165 n := res.Millis 166 if n > 0 { 167 val = rt.FloatValue(float64(n)) 168 } 169 default: 170 // We'll return nil 171 } 172 return c.PushingNext1(t.Runtime, val), nil 173 } 174 175 func resources__tostring(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 176 res, err := resourcesArg(c, 0) 177 if err != nil { 178 return nil, err 179 } 180 vals := make([]string, 0, 3) 181 if res.Cpu > 0 { 182 vals = append(vals, fmt.Sprintf("%s=%d", cpuName, res.Cpu)) 183 } 184 if res.Memory > 0 { 185 vals = append(vals, fmt.Sprintf("%s=%d", memoryName, res.Memory)) 186 } 187 if res.Millis > 0 { 188 vals = append(vals, fmt.Sprintf("%s=%g", secondsName, float64(res.Millis)/1000)) 189 } 190 s := "[" + strings.Join(vals, ",") + "]" 191 t.RequireBytes(len(s)) 192 return c.PushingNext1(t.Runtime, rt.StringValue(s)), nil 193 } 194 195 func resToVal(v uint64) rt.Value { 196 return rt.IntValue(int64(v)) 197 } 198 199 func statusValue(st rt.RuntimeContextStatus) rt.Value { 200 s := st.String() 201 if s == "" { 202 return rt.NilValue 203 } 204 return rt.StringValue(s) 205 } 206 207 func killnow(t *rt.Thread, c *rt.GoCont) (next rt.Cont, err error) { 208 ctx, err := optContextArg(t, c, 0) 209 if err != nil { 210 return nil, err 211 } 212 ctx.SetStopLevel(rt.HardStop) 213 return nil, nil 214 } 215 216 func stopnow(t *rt.Thread, c *rt.GoCont) (next rt.Cont, err error) { 217 ctx, err := optContextArg(t, c, 0) 218 if err != nil { 219 return nil, err 220 } 221 ctx.SetStopLevel(rt.SoftStop) 222 return c.Next(), nil 223 } 224 225 func due(t *rt.Thread, c *rt.GoCont) (next rt.Cont, retErr error) { 226 ctx, err := optContextArg(t, c, 0) 227 if err != nil { 228 return nil, err 229 } 230 return c.PushingNext1(t.Runtime, rt.BoolValue(ctx.Due())), nil 231 } 232 233 var ( 234 killnowGoF = rt.NewGoFunction(killnow, "killnow", 1, false) 235 stopnowGoF = rt.NewGoFunction(stopnow, "stopnow", 1, false) 236 dueGoF = rt.NewGoFunction(due, "due", 1, false) 237 ) 238 239 func init() { 240 rt.SolemnlyDeclareCompliance( 241 rt.ComplyCpuSafe|rt.ComplyMemSafe|rt.ComplyTimeSafe|rt.ComplyIoSafe, 242 killnowGoF, 243 stopnowGoF, 244 dueGoF, 245 ) 246 }