mirror of
https://github.com/Hilbis/Hilbish
synced 2025-07-01 08:42:04 +00:00
148 lines
2.9 KiB
Go
148 lines
2.9 KiB
Go
// multi threading library
|
|
// Yarn is a simple multithreading library. Threads are individual Lua states,
|
|
// so they do NOT share the same environment as the code that runs the thread.
|
|
// Bait and Commanders are shared though, so you *can* throw hooks from 1 thread to another.
|
|
/*
|
|
Example:
|
|
|
|
```lua
|
|
local yarn = require 'yarn'
|
|
|
|
-- calling t will run the yarn thread.
|
|
local t = yarn.thread(print)
|
|
t 'printing from another lua state!'
|
|
```
|
|
*/
|
|
package yarn
|
|
|
|
import (
|
|
"fmt"
|
|
"hilbish/util"
|
|
"os"
|
|
|
|
"github.com/arnodel/golua/lib/packagelib"
|
|
rt "github.com/arnodel/golua/runtime"
|
|
)
|
|
|
|
var yarnMetaKey = rt.StringValue("hshyarn")
|
|
var globalSpool *Yarn
|
|
|
|
type Yarn struct {
|
|
initializer func(*rt.Runtime)
|
|
Loader packagelib.Loader
|
|
}
|
|
|
|
// #type
|
|
type Thread struct {
|
|
rtm *rt.Runtime
|
|
f rt.Callable
|
|
}
|
|
|
|
func New(init func(*rt.Runtime)) *Yarn {
|
|
yrn := &Yarn{
|
|
initializer: init,
|
|
}
|
|
yrn.Loader = packagelib.Loader{
|
|
Load: yrn.loaderFunc,
|
|
Name: "yarn",
|
|
}
|
|
|
|
globalSpool = yrn
|
|
|
|
return yrn
|
|
}
|
|
|
|
func (y *Yarn) loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
|
|
yarnMeta := rt.NewTable()
|
|
yarnMeta.Set(rt.StringValue("__call"), rt.FunctionValue(rt.NewGoFunction(yarnrun, "__call", 1, true)))
|
|
rtm.SetRegistry(yarnMetaKey, rt.TableValue(yarnMeta))
|
|
|
|
exports := map[string]util.LuaExport{
|
|
"thread": {
|
|
Function: yarnthread,
|
|
ArgNum: 1,
|
|
Variadic: false,
|
|
},
|
|
}
|
|
|
|
mod := rt.NewTable()
|
|
util.SetExports(rtm, mod, exports)
|
|
|
|
return rt.TableValue(mod), nil
|
|
}
|
|
|
|
func (y *Yarn) init(th *Thread) {
|
|
y.initializer(th.rtm)
|
|
}
|
|
|
|
// thread(fun) -> @Thread
|
|
// Creates a new, fresh Yarn thread.
|
|
// `fun` is the function that will run in the thread.
|
|
func yarnthread(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|
if err := c.Check1Arg(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
fun, err := c.CallableArg(0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
yrn := &Thread{
|
|
rtm: rt.New(os.Stdout),
|
|
f: fun,
|
|
}
|
|
globalSpool.init(yrn)
|
|
|
|
return c.PushingNext(t.Runtime, rt.UserDataValue(yarnUserData(t.Runtime, yrn))), nil
|
|
}
|
|
|
|
func yarnrun(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|
if err := c.Check1Arg(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
yrn, err := yarnArg(c, 0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
yrn.Run(c.Etc())
|
|
|
|
return c.Next(), nil
|
|
}
|
|
|
|
func (y *Thread) Run(args []rt.Value) {
|
|
go func() {
|
|
term := rt.NewTerminationWith(y.rtm.MainThread().CurrentCont(), 0, true)
|
|
err := rt.Call(y.rtm.MainThread(), rt.FunctionValue(y.f), args, term)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}()
|
|
}
|
|
|
|
func yarnArg(c *rt.GoCont, arg int) (*Thread, error) {
|
|
j, ok := valueToYarn(c.Arg(arg))
|
|
if !ok {
|
|
return nil, fmt.Errorf("#%d must be a yarn thread", arg+1)
|
|
}
|
|
|
|
return j, nil
|
|
}
|
|
|
|
func valueToYarn(val rt.Value) (*Thread, bool) {
|
|
u, ok := val.TryUserData()
|
|
if !ok {
|
|
return nil, false
|
|
}
|
|
|
|
j, ok := u.Value().(*Thread)
|
|
return j, ok
|
|
}
|
|
|
|
func yarnUserData(rtm *rt.Runtime, t *Thread) *rt.UserData {
|
|
yarnMeta := rtm.Registry(yarnMetaKey)
|
|
return rt.NewUserData(t, yarnMeta.AsTable())
|
|
}
|