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  }