Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP - Working on new thread TODO items #119

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions _example/threads.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package main

import (
"strconv"
"sync"

"github.com/aarzilli/golua/lua"
)

func test(L *lua.State) int {
println("hello from thread: " + strconv.Itoa(L.ToInteger(-1)))
return 0
}

var wg sync.WaitGroup

func main() {
L := lua.NewState()
defer L.Close()
L.OpenLibs()

L.PushGoFunction(test)
L.SetGlobal("test")

for i := 0; i < 20; i++ {
wg.Add(1)
go func() {
defer wg.Done()
thread := L.NewThread()

thread.GetGlobal("test")
thread.PushInteger(int64(i))
if err := thread.Call(1, 0); err != nil {
println("pain")
println(err)
}
}()
}

wg.Wait()
}
Empty file added go.sum
Empty file.
36 changes: 24 additions & 12 deletions lua/golua.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ type HookFunction func(L *State)
// The errorstring used by State.SetExecutionLimit
const ExecutionQuantumExceeded = "Lua execution quantum exceeded"

type goRegistry struct {
mu sync.Mutex
items []interface{}
freeIndices []uint
}

// Wrapper to keep cgo from complaining about incomplete ptr type
//export State
type State struct {
Expand All @@ -40,11 +46,7 @@ type State struct {
// index of this object inside the goStates array
Index uintptr

// Registry of go object that have been pushed to Lua VM
registry []interface{}

// Freelist for funcs indices, to allow for freeing
freeIndices []uint
registry *goRegistry

// User self defined memory alloc func for the lua State
allocfn *Alloc
Expand Down Expand Up @@ -84,10 +86,13 @@ func getGoState(gostateindex uintptr) *State {
//export golua_callgofunction
func golua_callgofunction(gostateindex uintptr, fid uint) int {
L1 := getGoState(gostateindex)
L1.registry.mu.Lock()
defer L1.registry.mu.Unlock()

if fid < 0 {
panic(&LuaError{0, "Requested execution of an unknown function", L1.StackTrace()})
}
f := L1.registry[fid].(LuaGoFunction)
f := L1.registry.items[fid].(LuaGoFunction)
return f(L1)
}

Expand All @@ -104,11 +109,13 @@ var typeOfBytes = reflect.TypeOf([]byte(nil))
//export golua_interface_newindex_callback
func golua_interface_newindex_callback(gostateindex uintptr, iid uint, field_name_cstr *C.char) int {
L := getGoState(gostateindex)
iface := L.registry[iid]
ifacevalue := reflect.ValueOf(iface).Elem()

field_name := C.GoString(field_name_cstr)
L.registry.mu.Lock()
iface := L.registry.items[iid]
L.registry.mu.Unlock()

ifacevalue := reflect.ValueOf(iface).Elem()
field_name := C.GoString(field_name_cstr)
fval := ifacevalue.FieldByName(field_name)

if fval.Kind() == reflect.Ptr {
Expand Down Expand Up @@ -199,9 +206,12 @@ func golua_interface_newindex_callback(gostateindex uintptr, iid uint, field_nam
//export golua_interface_index_callback
func golua_interface_index_callback(gostateindex uintptr, iid uint, field_name *C.char) int {
L := getGoState(gostateindex)
iface := L.registry[iid]
ifacevalue := reflect.ValueOf(iface).Elem()

L.registry.mu.Lock()
iface := L.registry.items[iid]
L.registry.mu.Unlock()

ifacevalue := reflect.ValueOf(iface).Elem()
fval := ifacevalue.FieldByName(C.GoString(field_name))

if fval.Kind() == reflect.Ptr {
Expand Down Expand Up @@ -267,7 +277,9 @@ func golua_gchook(gostateindex uintptr, id uint) int {
//export golua_callpanicfunction
func golua_callpanicfunction(gostateindex uintptr, id uint) int {
L1 := getGoState(gostateindex)
f := L1.registry[id].(LuaGoFunction)
L1.registry.mu.Lock()
f := L1.registry.items[id].(LuaGoFunction)
L1.registry.mu.Unlock()
return f(L1)
}

Expand Down
75 changes: 50 additions & 25 deletions lua/lua.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ package lua
import "C"
import (
"fmt"
"sync"
"unsafe"
)

Expand All @@ -59,33 +60,34 @@ type LuaStackEntry struct {
}

func newState(L *C.lua_State) *State {
newstate := &State{L, 0, make([]interface{}, 0, 8), make([]uint, 0, 8), nil, nil, nil}
registry := &goRegistry{sync.Mutex{}, make([]interface{}, 0, 8), make([]uint, 0, 8)}
newstate := &State{L, 0, registry, nil, nil, nil}
registerGoState(newstate)
C.clua_setgostate(L, C.size_t(newstate.Index))
C.clua_initstate(L)
return newstate
}

func (L *State) addFreeIndex(i uint) {
freelen := len(L.freeIndices)
freelen := len(L.registry.freeIndices)
//reallocate if necessary
if freelen+1 > cap(L.freeIndices) {
newSlice := make([]uint, freelen, cap(L.freeIndices)*2)
copy(newSlice, L.freeIndices)
L.freeIndices = newSlice
if freelen+1 > cap(L.registry.freeIndices) {
newSlice := make([]uint, freelen, cap(L.registry.freeIndices)*2)
copy(newSlice, L.registry.freeIndices)
L.registry.freeIndices = newSlice
}
//reslice
L.freeIndices = L.freeIndices[0 : freelen+1]
L.freeIndices[freelen] = i
L.registry.freeIndices = L.registry.freeIndices[0 : freelen+1]
L.registry.freeIndices[freelen] = i
}

func (L *State) getFreeIndex() (index uint, ok bool) {
freelen := len(L.freeIndices)
freelen := len(L.registry.freeIndices)
//if there exist entries in the freelist
if freelen > 0 {
i := L.freeIndices[freelen-1] //get index
i := L.registry.freeIndices[freelen-1] //get index
//fmt.Printf("Free indices before: %v\n", L.freeIndices)
L.freeIndices = L.freeIndices[0 : freelen-1] //'pop' index from list
L.registry.freeIndices = L.registry.freeIndices[0 : freelen-1] //'pop' index from list
//fmt.Printf("Free indices after: %v\n", L.freeIndices)
return i, true
}
Expand All @@ -94,34 +96,40 @@ func (L *State) getFreeIndex() (index uint, ok bool) {

//returns the registered function id
func (L *State) register(f interface{}) uint {
L.registry.mu.Lock()
defer L.registry.mu.Unlock()

//fmt.Printf("Registering %v\n")
index, ok := L.getFreeIndex()
//fmt.Printf("\tfreeindex: index = %v, ok = %v\n", index, ok)
//if not ok, then we need to add new index by extending the slice
if !ok {
index = uint(len(L.registry))
index = uint(len(L.registry.items))
//reallocate backing array if necessary
if index+1 > uint(cap(L.registry)) {
newcap := cap(L.registry) * 2
if index+1 > uint(cap(L.registry.items)) {
newcap := cap(L.registry.items) * 2
if index+1 > uint(newcap) {
newcap = int(index + 1)
}
newSlice := make([]interface{}, index, newcap)
copy(newSlice, L.registry)
L.registry = newSlice
copy(newSlice, L.registry.items)
L.registry.items = newSlice
}
//reslice
L.registry = L.registry[0 : index+1]
L.registry.items = L.registry.items[0 : index+1]
}
//fmt.Printf("\tregistering %d %v\n", index, f)
L.registry[index] = f
L.registry.items[index] = f
return index
}

func (L *State) unregister(fid uint) {
L.registry.mu.Lock()
defer L.registry.mu.Unlock()

//fmt.Printf("Unregistering %d (len: %d, value: %v)\n", fid, len(L.registry), L.registry[fid])
if (fid < uint(len(L.registry))) && (L.registry[fid] != nil) {
L.registry[fid] = nil
if fid < uint(len(L.registry.items)) && L.registry.items[fid] != nil {
L.registry.items[fid] = nil
L.addFreeIndex(fid)
}
}
Expand Down Expand Up @@ -192,7 +200,9 @@ func (L *State) AtPanic(panicf LuaGoFunction) (oldpanicf LuaGoFunction) {
oldres := interface{}(C.clua_atpanic(L.s, C.uint(fid)))
switch i := oldres.(type) {
case C.uint:
f := L.registry[uint(i)].(LuaGoFunction)
L.registry.mu.Lock()
f := L.registry.items[uint(i)].(LuaGoFunction)
L.registry.mu.Unlock()
//free registry entry
L.unregister(uint(i))
return f
Expand Down Expand Up @@ -350,11 +360,18 @@ func (L *State) NewTable() {

// lua_newthread
func (L *State) NewThread() *State {
L.registry.mu.Lock()
defer L.registry.mu.Unlock()

//TODO: call newState with result from C.lua_newthread and return it
//TODO: should have same lists as parent
// but may complicate gc
s := C.lua_newthread(L.s)
return &State{s, 0, nil, nil, nil, nil, nil}
L2 := newState(C.lua_newthread(L.s))
L2.registry = L.registry
L2.allocfn = L.allocfn
L2.hookFn = L.hookFn
L2.ctx = L.ctx
return L2
}

// lua_next
Expand Down Expand Up @@ -508,7 +525,11 @@ func (L *State) ToGoFunction(index int) (f LuaGoFunction) {
if fid < 0 {
return nil
}
return L.registry[fid].(LuaGoFunction)

L.registry.mu.Lock()
function := L.registry.items[fid].(LuaGoFunction)
L.registry.mu.Unlock()
return function
}

// Returns the value at index as a Go Struct (it must be something pushed with PushGoStruct)
Expand All @@ -520,7 +541,11 @@ func (L *State) ToGoStruct(index int) (f interface{}) {
if fid < 0 {
return nil
}
return L.registry[fid]

L.registry.mu.Lock()
idx := L.registry.items[fid]
L.registry.mu.Unlock()
return idx
}

// lua_tostring
Expand Down