github.com/tul/gopher-lua@v0.0.0-20181008131706-f6fcaab0c612/channellib.go (about)

     1  package lua
     2  
     3  import (
     4  	"reflect"
     5  )
     6  
     7  func checkChannel(L *LState, idx int) reflect.Value {
     8  	ch := L.CheckChannel(idx)
     9  	return reflect.ValueOf(ch)
    10  }
    11  
    12  func checkGoroutineSafe(L *LState, idx int) LValue {
    13  	v := L.CheckAny(2)
    14  	if !isGoroutineSafe(v) {
    15  		L.ArgError(2, "can not send a function, userdata, thread or table that has a metatable")
    16  	}
    17  	return v
    18  }
    19  
    20  func OpenChannel(L *LState) int {
    21  	var mod LValue
    22  	//_, ok := L.G.builtinMts[int(LTChannel)]
    23  	//	if !ok {
    24  	mod = L.RegisterModule(ChannelLibName, channelFuncs)
    25  	mt := L.SetFuncs(L.NewTable(), channelMethods)
    26  	mt.RawSetString("__index", mt)
    27  	L.G.builtinMts[int(LTChannel)] = mt
    28  	//	}
    29  	L.Push(mod)
    30  	return 1
    31  }
    32  
    33  var channelFuncs = map[string]LGFunction{
    34  	"make":   channelMake,
    35  	"select": channelSelect,
    36  }
    37  
    38  func channelMake(L *LState) int {
    39  	buffer := L.OptInt(1, 0)
    40  	L.Push(LChannel(make(chan LValue, buffer)))
    41  	return 1
    42  }
    43  
    44  func channelSelect(L *LState) int {
    45  	//TODO check case table size
    46  	cases := make([]reflect.SelectCase, L.GetTop())
    47  	top := L.GetTop()
    48  	for i := 0; i < top; i++ {
    49  		cas := reflect.SelectCase{
    50  			Dir:  reflect.SelectSend,
    51  			Chan: reflect.ValueOf(nil),
    52  			Send: reflect.ValueOf(nil),
    53  		}
    54  		tbl := L.CheckTable(i + 1)
    55  		dir, ok1 := tbl.RawGetInt(1).(LString)
    56  		if !ok1 {
    57  			L.ArgError(i+1, "invalid select case")
    58  		}
    59  		switch string(dir) {
    60  		case "<-|":
    61  			ch, ok := tbl.RawGetInt(2).(LChannel)
    62  			if !ok {
    63  				L.ArgError(i+1, "invalid select case")
    64  			}
    65  			cas.Chan = reflect.ValueOf((chan LValue)(ch))
    66  			v := tbl.RawGetInt(3)
    67  			if !isGoroutineSafe(v) {
    68  				L.ArgError(i+1, "can not send a function, userdata, thread or table that has a metatable")
    69  			}
    70  			cas.Send = reflect.ValueOf(v)
    71  		case "|<-":
    72  			ch, ok := tbl.RawGetInt(2).(LChannel)
    73  			if !ok {
    74  				L.ArgError(i+1, "invalid select case")
    75  			}
    76  			cas.Chan = reflect.ValueOf((chan LValue)(ch))
    77  			cas.Dir = reflect.SelectRecv
    78  		case "default":
    79  			cas.Dir = reflect.SelectDefault
    80  		default:
    81  			L.ArgError(i+1, "invalid channel direction:"+string(dir))
    82  		}
    83  		cases[i] = cas
    84  	}
    85  
    86  	pos, recv, rok := reflect.Select(cases)
    87  	lv := LNil
    88  	if recv.Kind() != 0 {
    89  		lv, _ = recv.Interface().(LValue)
    90  		if lv == nil {
    91  			lv = LNil
    92  		}
    93  	}
    94  	tbl := L.Get(pos + 1).(*LTable)
    95  	last := tbl.RawGetInt(tbl.Len())
    96  	if last.Type() == LTFunction {
    97  		L.Push(last)
    98  		switch cases[pos].Dir {
    99  		case reflect.SelectRecv:
   100  			if rok {
   101  				L.Push(LTrue)
   102  			} else {
   103  				L.Push(LFalse)
   104  			}
   105  			L.Push(lv)
   106  			L.Call(2, 0)
   107  		case reflect.SelectSend:
   108  			L.Push(tbl.RawGetInt(3))
   109  			L.Call(1, 0)
   110  		case reflect.SelectDefault:
   111  			L.Call(0, 0)
   112  		}
   113  	}
   114  	L.Push(LNumber(pos + 1))
   115  	L.Push(lv)
   116  	if rok {
   117  		L.Push(LTrue)
   118  	} else {
   119  		L.Push(LFalse)
   120  	}
   121  	return 3
   122  }
   123  
   124  var channelMethods = map[string]LGFunction{
   125  	"receive": channelReceive,
   126  	"send":    channelSend,
   127  	"close":   channelClose,
   128  }
   129  
   130  func channelReceive(L *LState) int {
   131  	rch := checkChannel(L, 1)
   132  	v, ok := rch.Recv()
   133  	if ok {
   134  		L.Push(LTrue)
   135  		L.Push(v.Interface().(LValue))
   136  	} else {
   137  		L.Push(LFalse)
   138  		L.Push(LNil)
   139  	}
   140  	return 2
   141  }
   142  
   143  func channelSend(L *LState) int {
   144  	rch := checkChannel(L, 1)
   145  	v := checkGoroutineSafe(L, 2)
   146  	rch.Send(reflect.ValueOf(v))
   147  	return 0
   148  }
   149  
   150  func channelClose(L *LState) int {
   151  	rch := checkChannel(L, 1)
   152  	rch.Close()
   153  	return 0
   154  }
   155  
   156  //