From eed5441ad10624ee5b241d6545e8f3073ed37aca Mon Sep 17 00:00:00 2001 From: MrNavaStar Date: Mon, 11 Nov 2024 03:05:06 -0700 Subject: [PATCH 1/3] work on new thread TODO items --- lua/lua.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lua/lua.go b/lua/lua.go index 15d365bf..2df2f26f 100644 --- a/lua/lua.go +++ b/lua/lua.go @@ -353,8 +353,13 @@ func (L *State) NewThread() *State { //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.freeIndices = make([]uint, len(L.freeIndices)) + copy(L2.freeIndices, L.freeIndices) + L2.allocfn = L.allocfn + L2.hookFn = L.hookFn + L2.ctx = L.ctx + return L2 } // lua_next From 5719708d03ca70944a37e33c0cff17411d69ef77 Mon Sep 17 00:00:00 2001 From: MrNavaStar Date: Fri, 15 Nov 2024 13:48:08 -0700 Subject: [PATCH 2/3] threading works - mostly --- _example/threads.go | 41 ++++++++++++++++++++++++ lua/golua.go | 30 ++++++++++++------ lua/lua.go | 76 ++++++++++++++++++++++++++++++--------------- 3 files changed, 113 insertions(+), 34 deletions(-) create mode 100644 _example/threads.go diff --git a/_example/threads.go b/_example/threads.go new file mode 100644 index 00000000..acff181d --- /dev/null +++ b/_example/threads.go @@ -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() +} diff --git a/lua/golua.go b/lua/golua.go index 09ad9025..113438bf 100644 --- a/lua/golua.go +++ b/lua/golua.go @@ -37,14 +37,16 @@ type State struct { // Wrapped lua_State object s *C.lua_State + mutex *sync.Mutex + // index of this object inside the goStates array Index uintptr // Registry of go object that have been pushed to Lua VM - registry []interface{} + registry *[]interface{} // Freelist for funcs indices, to allow for freeing - freeIndices []uint + freeIndices *[]uint // User self defined memory alloc func for the lua State allocfn *Alloc @@ -84,10 +86,13 @@ func getGoState(gostateindex uintptr) *State { //export golua_callgofunction func golua_callgofunction(gostateindex uintptr, fid uint) int { L1 := getGoState(gostateindex) + L1.mutex.Lock() + defer L1.mutex.Unlock() + if fid < 0 { panic(&LuaError{0, "Requested execution of an unknown function", L1.StackTrace()}) } - f := L1.registry[fid].(LuaGoFunction) + f := (*L1.registry)[fid].(LuaGoFunction) return f(L1) } @@ -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.mutex.Lock() + iface := (*L.registry)[iid] + L.mutex.Unlock() + ifacevalue := reflect.ValueOf(iface).Elem() + field_name := C.GoString(field_name_cstr) fval := ifacevalue.FieldByName(field_name) if fval.Kind() == reflect.Ptr { @@ -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.mutex.Lock() + iface := (*L.registry)[iid] + L.mutex.Unlock() + ifacevalue := reflect.ValueOf(iface).Elem() fval := ifacevalue.FieldByName(C.GoString(field_name)) if fval.Kind() == reflect.Ptr { @@ -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.mutex.Lock() + f := (*L1.registry)[id].(LuaGoFunction) + L1.mutex.Unlock() return f(L1) } diff --git a/lua/lua.go b/lua/lua.go index 2df2f26f..883c03b8 100644 --- a/lua/lua.go +++ b/lua/lua.go @@ -48,6 +48,7 @@ package lua import "C" import ( "fmt" + "sync" "unsafe" ) @@ -59,7 +60,9 @@ 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 := make([]interface{}, 0, 8) + freeIndices := make([]uint, 0, 8) + newstate := &State{L, &sync.Mutex{}, 0, ®istry, &freeIndices, nil, nil, nil} registerGoState(newstate) C.clua_setgostate(L, C.size_t(newstate.Index)) C.clua_initstate(L) @@ -67,25 +70,27 @@ func newState(L *C.lua_State) *State { } func (L *State) addFreeIndex(i uint) { - freelen := len(L.freeIndices) + freelen := len(*L.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.freeIndices) { + newSlice := make([]uint, freelen, cap(*L.freeIndices)*2) + copy(newSlice, *L.freeIndices) + L.freeIndices = &newSlice } //reslice - L.freeIndices = L.freeIndices[0 : freelen+1] - L.freeIndices[freelen] = i + newSlice := (*L.freeIndices)[0 : freelen+1] + L.freeIndices = &newSlice + (*L.freeIndices)[freelen] = i } func (L *State) getFreeIndex() (index uint, ok bool) { - freelen := len(L.freeIndices) + freelen := len(*L.freeIndices) //if there exist entries in the freelist if freelen > 0 { - i := L.freeIndices[freelen-1] //get index + i := (*L.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 + newSlice := (*L.freeIndices)[0 : freelen-1] //'pop' index from list + L.freeIndices = &newSlice //fmt.Printf("Free indices after: %v\n", L.freeIndices) return i, true } @@ -94,34 +99,41 @@ func (L *State) getFreeIndex() (index uint, ok bool) { //returns the registered function id func (L *State) register(f interface{}) uint { + L.mutex.Lock() + defer L.mutex.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)) //reallocate backing array if necessary - if index+1 > uint(cap(L.registry)) { - newcap := cap(L.registry) * 2 + if index+1 > uint(cap(*L.registry)) { + newcap := cap(*L.registry) * 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) + L.registry = &newSlice } //reslice - L.registry = L.registry[0 : index+1] + newSlice := (*L.registry)[0 : index+1] + L.registry = &newSlice } //fmt.Printf("\tregistering %d %v\n", index, f) - L.registry[index] = f + (*L.registry)[index] = f return index } func (L *State) unregister(fid uint) { + L.mutex.Lock() + defer L.mutex.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))) && ((*L.registry)[fid] != nil) { + (*L.registry)[fid] = nil L.addFreeIndex(fid) } } @@ -192,7 +204,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.mutex.Lock() + f := (*L.registry)[uint(i)].(LuaGoFunction) + L.mutex.Unlock() //free registry entry L.unregister(uint(i)) return f @@ -350,12 +364,16 @@ func (L *State) NewTable() { // lua_newthread func (L *State) NewThread() *State { + L.mutex.Lock() + defer L.mutex.Unlock() + //TODO: call newState with result from C.lua_newthread and return it //TODO: should have same lists as parent // but may complicate gc L2 := newState(C.lua_newthread(L.s)) - L2.freeIndices = make([]uint, len(L.freeIndices)) - copy(L2.freeIndices, L.freeIndices) + L2.mutex = L.mutex + L2.registry = L.registry + L2.freeIndices = L.freeIndices L2.allocfn = L.allocfn L2.hookFn = L.hookFn L2.ctx = L.ctx @@ -513,7 +531,11 @@ func (L *State) ToGoFunction(index int) (f LuaGoFunction) { if fid < 0 { return nil } - return L.registry[fid].(LuaGoFunction) + + L.mutex.Lock() + function := (*L.registry)[fid].(LuaGoFunction) + L.mutex.Unlock() + return function } // Returns the value at index as a Go Struct (it must be something pushed with PushGoStruct) @@ -525,7 +547,11 @@ func (L *State) ToGoStruct(index int) (f interface{}) { if fid < 0 { return nil } - return L.registry[fid] + + L.mutex.Lock() + idx := (*L.registry)[fid] + L.mutex.Unlock() + return idx } // lua_tostring From 46aafa60d5f2bbcf272e1491b454774e0deda0fa Mon Sep 17 00:00:00 2001 From: MrNavaStar Date: Fri, 22 Nov 2024 15:30:27 -0700 Subject: [PATCH 3/3] move registry items into own struct --- go.sum | 0 lua/golua.go | 38 ++++++++++++------------- lua/lua.go | 78 ++++++++++++++++++++++++---------------------------- 3 files changed, 55 insertions(+), 61 deletions(-) create mode 100644 go.sum diff --git a/go.sum b/go.sum new file mode 100644 index 00000000..e69de29b diff --git a/lua/golua.go b/lua/golua.go index 113438bf..adda1472 100644 --- a/lua/golua.go +++ b/lua/golua.go @@ -31,22 +31,22 @@ 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 { // Wrapped lua_State object s *C.lua_State - mutex *sync.Mutex - // 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 @@ -86,13 +86,13 @@ func getGoState(gostateindex uintptr) *State { //export golua_callgofunction func golua_callgofunction(gostateindex uintptr, fid uint) int { L1 := getGoState(gostateindex) - L1.mutex.Lock() - defer L1.mutex.Unlock() + 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) } @@ -110,9 +110,9 @@ var typeOfBytes = reflect.TypeOf([]byte(nil)) func golua_interface_newindex_callback(gostateindex uintptr, iid uint, field_name_cstr *C.char) int { L := getGoState(gostateindex) - L.mutex.Lock() - iface := (*L.registry)[iid] - L.mutex.Unlock() + 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) @@ -207,9 +207,9 @@ func golua_interface_newindex_callback(gostateindex uintptr, iid uint, field_nam func golua_interface_index_callback(gostateindex uintptr, iid uint, field_name *C.char) int { L := getGoState(gostateindex) - L.mutex.Lock() - iface := (*L.registry)[iid] - L.mutex.Unlock() + 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)) @@ -277,9 +277,9 @@ func golua_gchook(gostateindex uintptr, id uint) int { //export golua_callpanicfunction func golua_callpanicfunction(gostateindex uintptr, id uint) int { L1 := getGoState(gostateindex) - L1.mutex.Lock() - f := (*L1.registry)[id].(LuaGoFunction) - L1.mutex.Unlock() + L1.registry.mu.Lock() + f := L1.registry.items[id].(LuaGoFunction) + L1.registry.mu.Unlock() return f(L1) } diff --git a/lua/lua.go b/lua/lua.go index 883c03b8..4b8dc858 100644 --- a/lua/lua.go +++ b/lua/lua.go @@ -60,9 +60,8 @@ type LuaStackEntry struct { } func newState(L *C.lua_State) *State { - registry := make([]interface{}, 0, 8) - freeIndices := make([]uint, 0, 8) - newstate := &State{L, &sync.Mutex{}, 0, ®istry, &freeIndices, 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) @@ -70,27 +69,25 @@ func newState(L *C.lua_State) *State { } 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 - newSlice := (*L.freeIndices)[0 : freelen+1] - L.freeIndices = &newSlice - (*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) - newSlice := (*L.freeIndices)[0 : freelen-1] //'pop' index from list - L.freeIndices = &newSlice + 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 } @@ -99,41 +96,40 @@ func (L *State) getFreeIndex() (index uint, ok bool) { //returns the registered function id func (L *State) register(f interface{}) uint { - L.mutex.Lock() - defer L.mutex.Unlock() + 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 - newSlice := (*L.registry)[0 : index+1] - L.registry = &newSlice + 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.mutex.Lock() - defer L.mutex.Unlock() + 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) } } @@ -204,9 +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: - L.mutex.Lock() - f := (*L.registry)[uint(i)].(LuaGoFunction) - L.mutex.Unlock() + L.registry.mu.Lock() + f := L.registry.items[uint(i)].(LuaGoFunction) + L.registry.mu.Unlock() //free registry entry L.unregister(uint(i)) return f @@ -364,16 +360,14 @@ func (L *State) NewTable() { // lua_newthread func (L *State) NewThread() *State { - L.mutex.Lock() - defer L.mutex.Unlock() + 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 L2 := newState(C.lua_newthread(L.s)) - L2.mutex = L.mutex L2.registry = L.registry - L2.freeIndices = L.freeIndices L2.allocfn = L.allocfn L2.hookFn = L.hookFn L2.ctx = L.ctx @@ -532,9 +526,9 @@ func (L *State) ToGoFunction(index int) (f LuaGoFunction) { return nil } - L.mutex.Lock() - function := (*L.registry)[fid].(LuaGoFunction) - L.mutex.Unlock() + L.registry.mu.Lock() + function := L.registry.items[fid].(LuaGoFunction) + L.registry.mu.Unlock() return function } @@ -548,9 +542,9 @@ func (L *State) ToGoStruct(index int) (f interface{}) { return nil } - L.mutex.Lock() - idx := (*L.registry)[fid] - L.mutex.Unlock() + L.registry.mu.Lock() + idx := L.registry.items[fid] + L.registry.mu.Unlock() return idx }