github.com/arnodel/golua@v0.0.0-20230215163904-e0b5347eaaa1/runtime/runtimecontext.go (about) 1 package runtime 2 3 // RuntimeContextDef contains the data necessary to create an new runtime context. 4 type RuntimeContextDef struct { 5 HardLimits RuntimeResources 6 SoftLimits RuntimeResources 7 RequiredFlags ComplianceFlags 8 MessageHandler Callable 9 GCPolicy 10 } 11 12 // RuntimeContext is an interface implemented by Runtime.RuntimeContext(). It 13 // provides a public interface for the runtime context to be used by e.g. 14 // libraries (e.g. the runtime package). 15 type RuntimeContext interface { 16 HardLimits() RuntimeResources 17 SoftLimits() RuntimeResources 18 UsedResources() RuntimeResources 19 20 Status() RuntimeContextStatus 21 Parent() RuntimeContext 22 23 RequiredFlags() ComplianceFlags 24 25 SetStopLevel(StopLevel) 26 Due() bool 27 28 GCPolicy() GCPolicy 29 } 30 31 type StopLevel uint8 32 33 const ( 34 SoftStop StopLevel = 1 << iota // Forces the context to be due 35 HardStop // Forces the context to terminate 36 ) 37 38 // A ContextTerminationError is an error reserved for when the runtime context 39 // should be terminated immediately. 40 type ContextTerminationError struct { 41 message string 42 } 43 44 var _ error = ContextTerminationError{} 45 46 // Error string for a ContextTerminationError 47 func (e ContextTerminationError) Error() string { 48 return e.message 49 } 50 51 // RuntimeContextStatus describes the status of a context 52 type RuntimeContextStatus uint16 53 54 const ( 55 StatusLive RuntimeContextStatus = iota // currently executing 56 StatusDone // finished successfully (no error) 57 StatusError // finished with a Lua error 58 StatusKilled // terminated (either by user or because hard limits were reached) 59 ) 60 61 const ( 62 liveStatusString = "live" 63 doneStatusString = "done" 64 errorStatusString = "error" 65 killedStatusString = "killed" 66 ) 67 68 func (s RuntimeContextStatus) String() string { 69 switch s { 70 case StatusLive: 71 return liveStatusString 72 case StatusDone: 73 return doneStatusString 74 case StatusError: 75 return errorStatusString 76 case StatusKilled: 77 return killedStatusString 78 default: 79 return "" 80 } 81 } 82 83 // ComplianceFlags represents constraints that the code running must comply 84 // with. 85 type ComplianceFlags uint16 86 87 const ( 88 // Only execute code checks memory availability before allocating memory 89 ComplyMemSafe ComplianceFlags = 1 << iota 90 91 // Only execute code that checks cpu availability before executing a 92 // computation. 93 ComplyCpuSafe 94 95 // Only execute code that complies with IO restrictions (currently only 96 // functions that do no IO comply with this) 97 ComplyIoSafe 98 99 // Only execute code that is time safe (i.e. it will not block on long 100 // running ops, typically IO) 101 ComplyTimeSafe 102 103 complyflagsLimit 104 ) 105 106 const ( 107 memSafeString = "memsafe" 108 cpuSafeString = "cpusafe" 109 timeSafeString = "timesafe" 110 ioSafeString = "iosafe" 111 ) 112 113 var complianceFlagNames = map[ComplianceFlags]string{ 114 ComplyMemSafe: memSafeString, 115 ComplyCpuSafe: cpuSafeString, 116 ComplyTimeSafe: timeSafeString, 117 ComplyIoSafe: ioSafeString, 118 } 119 120 var complianceFlagsByName = map[string]ComplianceFlags{ 121 memSafeString: ComplyMemSafe, 122 cpuSafeString: ComplyCpuSafe, 123 timeSafeString: ComplyTimeSafe, 124 ioSafeString: ComplyIoSafe, 125 } 126 127 func (f ComplianceFlags) AddFlagWithName(name string) (ComplianceFlags, bool) { 128 fn, ok := complianceFlagsByName[name] 129 return fn | f, ok 130 } 131 132 func (f ComplianceFlags) Names() (names []string) { 133 var i ComplianceFlags 134 for i = 1; i < complyflagsLimit; i <<= 1 { 135 if i&f != 0 { 136 names = append(names, complianceFlagNames[i]) 137 } 138 } 139 return names 140 } 141 142 // RuntimeResources describe amount of resources that code can consume. 143 // Depending on the context, it could be available resources or consumed 144 // resources. For available resources, 0 means unlimited. 145 type RuntimeResources struct { 146 Cpu uint64 147 Memory uint64 148 Millis uint64 149 } 150 151 // Remove lowers the resources accounted for in the receiver by the resources 152 // accounted for in the argument. 153 func (r RuntimeResources) Remove(v RuntimeResources) RuntimeResources { 154 if r.Cpu >= v.Cpu { 155 r.Cpu -= v.Cpu 156 } else { 157 r.Cpu = 0 158 } 159 if r.Memory >= v.Memory { 160 r.Memory -= v.Memory 161 } else { 162 r.Memory = 0 163 } 164 if r.Millis >= v.Millis { 165 r.Millis -= v.Millis 166 } else { 167 r.Millis = 0 168 } 169 return r 170 } 171 172 // Merge treats the receiver and argument as describing resource limits and 173 // returns the resources describing the intersection of those limits. 174 func (r RuntimeResources) Merge(r1 RuntimeResources) RuntimeResources { 175 if smallerLimit(r1.Cpu, r.Cpu) { 176 r.Cpu = r1.Cpu 177 } 178 if smallerLimit(r1.Memory, r.Memory) { 179 r.Memory = r1.Memory 180 } 181 if smallerLimit(r1.Millis, r.Millis) { 182 r.Millis = r1.Millis 183 } 184 return r 185 } 186 187 // Dominates returns true if the resource count v doesn't reach the resource 188 // limit r. 189 func (r RuntimeResources) Dominates(v RuntimeResources) bool { 190 return !atLimit(v.Cpu, r.Cpu) && !atLimit(v.Memory, r.Memory) && !atLimit(v.Millis, r.Millis) 191 } 192 193 // n < m, but with 0 meaning +infinity for both n and m 194 func smallerLimit(n, m uint64) bool { 195 return n > 0 && (m == 0 || n < m) 196 } 197 198 // l <= v, but with 0 meaning +infinity for l 199 func atLimit(v, l uint64) bool { 200 return l > 0 && v >= l 201 } 202 203 type GCPolicy int16 204 205 const ( 206 DefaultGCPolicy GCPolicy = iota 207 ShareGCPolicy 208 IsolateGCPolicy 209 UnknownGCPolicy 210 )