github.com/xrash/gopher-lua@v0.0.0-20160304065408-e5faab4db06a/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{reflect.SelectSend, reflect.ValueOf(nil), reflect.ValueOf(nil)}
    50  		tbl := L.CheckTable(i + 1)
    51  		dir, ok1 := tbl.RawGetInt(1).(LString)
    52  		if !ok1 {
    53  			L.ArgError(i+1, "invalid select case")
    54  		}
    55  		switch string(dir) {
    56  		case "<-|":
    57  			ch, ok := tbl.RawGetInt(2).(LChannel)
    58  			if !ok {
    59  				L.ArgError(i+1, "invalid select case")
    60  			}
    61  			cas.Chan = reflect.ValueOf((chan LValue)(ch))
    62  			v := tbl.RawGetInt(3)
    63  			if !isGoroutineSafe(v) {
    64  				L.ArgError(i+1, "can not send a function, userdata, thread or table that has a metatable")
    65  			}
    66  			cas.Send = reflect.ValueOf(v)
    67  		case "|<-":
    68  			ch, ok := tbl.RawGetInt(2).(LChannel)
    69  			if !ok {
    70  				L.ArgError(i+1, "invalid select case")
    71  			}
    72  			cas.Chan = reflect.ValueOf((chan LValue)(ch))
    73  			cas.Dir = reflect.SelectRecv
    74  		case "default":
    75  			cas.Dir = reflect.SelectDefault
    76  		default:
    77  			L.ArgError(i+1, "invalid channel direction:"+string(dir))
    78  		}
    79  		cases[i] = cas
    80  	}
    81  
    82  	pos, recv, rok := reflect.Select(cases)
    83  	lv := LNil
    84  	if recv.Kind() != 0 {
    85  		lv, _ = recv.Interface().(LValue)
    86  		if lv == nil {
    87  			lv = LNil
    88  		}
    89  	}
    90  	tbl := L.Get(pos + 1).(*LTable)
    91  	last := tbl.RawGetInt(tbl.Len())
    92  	if last.Type() == LTFunction {
    93  		L.Push(last)
    94  		switch cases[pos].Dir {
    95  		case reflect.SelectRecv:
    96  			if rok {
    97  				L.Push(LTrue)
    98  			} else {
    99  				L.Push(LFalse)
   100  			}
   101  			L.Push(lv)
   102  			L.Call(2, 0)
   103  		case reflect.SelectSend:
   104  			L.Push(tbl.RawGetInt(3))
   105  			L.Call(1, 0)
   106  		case reflect.SelectDefault:
   107  			L.Call(0, 0)
   108  		}
   109  	}
   110  	L.Push(LNumber(pos + 1))
   111  	L.Push(lv)
   112  	if rok {
   113  		L.Push(LTrue)
   114  	} else {
   115  		L.Push(LFalse)
   116  	}
   117  	return 3
   118  }
   119  
   120  var channelMethods = map[string]LGFunction{
   121  	"receive": channelReceive,
   122  	"send":    channelSend,
   123  	"close":   channelClose,
   124  }
   125  
   126  func channelReceive(L *LState) int {
   127  	rch := checkChannel(L, 1)
   128  	v, ok := rch.Recv()
   129  	if ok {
   130  		L.Push(LTrue)
   131  		L.Push(v.Interface().(LValue))
   132  	} else {
   133  		L.Push(LFalse)
   134  		L.Push(LNil)
   135  	}
   136  	return 2
   137  }
   138  
   139  func channelSend(L *LState) int {
   140  	rch := checkChannel(L, 1)
   141  	v := checkGoroutineSafe(L, 2)
   142  	rch.Send(reflect.ValueOf(v))
   143  	return 0
   144  }
   145  
   146  func channelClose(L *LState) int {
   147  	rch := checkChannel(L, 1)
   148  	rch.Close()
   149  	return 0
   150  }
   151  
   152  //