fix: Code review round 2 — race conditions, dead code, hardcoded paths
CRITICAL fixes: - #1 vm.go: Mutex on libModules/dynamicFuncs global slices RegisterLibModule/RegisterDynamicFunc now thread-safe RegisterLibModules copies under lock, clears, releases - #4 shutdown.go: Signal handler goroutine leak fixed Uses done channel + select for clean exit on normal shutdown HIGH fixes: - #7-8 gobridge.go: Remove dead if/else branches (both identical) - #13-14 main.go: Remove hardcoded /mnt/d/harbour-core paths Use HB_INC env var + standard /usr/local/include/harbour only - #15 main.go: Remove unused frbModSeq variable MEDIUM fixes: - #22 expr.go: Remove unused parts variable in parseInterpolatedString - #51 macro.go: Remove var _ = fmt.Sprintf import guard - macroeval.go: Remove unused lexer import and guard Total fixed this session: 12/53 issues resolved Remaining: 41 (CRITICAL: 1, HIGH: 9, MEDIUM: 16, LOW: 16) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -101,13 +101,8 @@ func GoCall(receiver Value, method string, args ...Value) []Value {
|
||||
}
|
||||
}
|
||||
|
||||
// Call
|
||||
var results []reflect.Value
|
||||
if mt.IsVariadic() {
|
||||
results = m.Call(goArgs)
|
||||
} else {
|
||||
results = m.Call(goArgs)
|
||||
}
|
||||
// Call — m.Call handles both variadic and non-variadic correctly
|
||||
results := m.Call(goArgs)
|
||||
|
||||
// Convert Go results → Harbour Values
|
||||
hbResults := make([]Value, len(results))
|
||||
@@ -145,12 +140,7 @@ func GoCallFunc(fn interface{}, args ...Value) []Value {
|
||||
}
|
||||
}
|
||||
|
||||
var results []reflect.Value
|
||||
if isVariadic {
|
||||
results = rv.Call(goArgs) // Call (not CallSlice) handles spreading
|
||||
} else {
|
||||
results = rv.Call(goArgs)
|
||||
}
|
||||
results := rv.Call(goArgs)
|
||||
hbResults := make([]Value, len(results))
|
||||
for i, r := range results {
|
||||
hbResults[i] = reflectToValue(r)
|
||||
|
||||
@@ -22,8 +22,6 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
var _ = fmt.Sprintf // ensure import
|
||||
|
||||
// MacroCompile compiles and evaluates a macro expression string.
|
||||
// Returns the result value.
|
||||
//
|
||||
|
||||
@@ -19,7 +19,6 @@ package hbrt
|
||||
|
||||
import (
|
||||
"five/compiler/ast"
|
||||
"five/compiler/lexer"
|
||||
"five/compiler/parser"
|
||||
"five/compiler/token"
|
||||
"strings"
|
||||
@@ -304,6 +303,3 @@ func (t *Thread) macroStoreIdent(name string, val Value) {
|
||||
_ = name
|
||||
_ = val
|
||||
}
|
||||
|
||||
// suppress import
|
||||
var _ = lexer.Tokenize
|
||||
|
||||
@@ -62,22 +62,44 @@ func runAtExit() {
|
||||
|
||||
// --- Signal handling ---
|
||||
|
||||
// signalDone closes to stop the signal handler goroutine on normal exit.
|
||||
var signalDone chan struct{}
|
||||
|
||||
// InstallSignalHandlers sets up OS signal handlers for clean shutdown.
|
||||
// Harbour: hb_vmSetExceptionHandler (SIGINT, SIGTERM, SIGSEGV)
|
||||
func (vm *VM) InstallSignalHandlers() {
|
||||
signalSetup.Do(func() {
|
||||
ch := make(chan os.Signal, 1)
|
||||
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
signalDone = make(chan struct{})
|
||||
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
go func() {
|
||||
sig := <-ch
|
||||
fmt.Fprintf(os.Stderr, "\nFive: received %v, shutting down...\n", sig)
|
||||
vm.Shutdown()
|
||||
os.Exit(1)
|
||||
select {
|
||||
case sig := <-sigCh:
|
||||
fmt.Fprintf(os.Stderr, "\nFive: received %v, shutting down...\n", sig)
|
||||
vm.Shutdown()
|
||||
os.Exit(1)
|
||||
case <-signalDone:
|
||||
// Normal exit — stop goroutine cleanly
|
||||
signal.Stop(sigCh)
|
||||
return
|
||||
}
|
||||
}()
|
||||
})
|
||||
}
|
||||
|
||||
// stopSignalHandler stops the signal handler goroutine (called during normal shutdown).
|
||||
func stopSignalHandler() {
|
||||
if signalDone != nil {
|
||||
select {
|
||||
case <-signalDone:
|
||||
// already closed
|
||||
default:
|
||||
close(signalDone)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Main shutdown sequence ---
|
||||
|
||||
// Shutdown executes the full VM shutdown sequence.
|
||||
@@ -127,6 +149,9 @@ func (vm *VM) doShutdown() {
|
||||
// Harbour: hb_gcCollectAll(HB_TRUE) × 3
|
||||
// Go's GC is automatic, but we can hint
|
||||
// runtime.GC() — not calling explicitly, Go handles it
|
||||
|
||||
// Phase 10: Stop signal handler goroutine (prevent leak)
|
||||
stopSignalHandler()
|
||||
}
|
||||
|
||||
// runExitProcedures executes all EXIT PROCEDURE declarations.
|
||||
|
||||
28
hbrt/vm.go
28
hbrt/vm.go
@@ -27,35 +27,47 @@ func (vm *VM) SetOnExit(f func()) {
|
||||
vm.onExit = f
|
||||
}
|
||||
|
||||
// Library modules registered via init()
|
||||
var libModules []*Module
|
||||
var dynamicFuncs []Symbol // from HB_FUNC() in #pragma BEGINDUMP
|
||||
// Library modules registered via init() — protected by mutex for FRB concurrent loading.
|
||||
var (
|
||||
libModules []*Module
|
||||
dynamicFuncs []Symbol // from HB_FUNC() in #pragma BEGINDUMP
|
||||
libRegistryMu sync.Mutex
|
||||
)
|
||||
|
||||
// RegisterLibModule registers a module from a library PRG file.
|
||||
// Called by init() in generated library code.
|
||||
func RegisterLibModule(m *Module) {
|
||||
libRegistryMu.Lock()
|
||||
libModules = append(libModules, m)
|
||||
libRegistryMu.Unlock()
|
||||
}
|
||||
|
||||
// RegisterDynamicFunc registers a Go function callable from PRG.
|
||||
// Called from init() in #pragma BEGINDUMP code via HB_FUNC().
|
||||
func RegisterDynamicFunc(name string, fn func(*Thread)) {
|
||||
libRegistryMu.Lock()
|
||||
dynamicFuncs = append(dynamicFuncs, Symbol{
|
||||
Name: name,
|
||||
Scope: FsPublic | FsLocal,
|
||||
Func: fn,
|
||||
})
|
||||
libRegistryMu.Unlock()
|
||||
}
|
||||
|
||||
// RegisterLibModules registers any pending lib modules and dynamic functions.
|
||||
func (vm *VM) RegisterLibModules() {
|
||||
for _, m := range libModules {
|
||||
libRegistryMu.Lock()
|
||||
mods := libModules
|
||||
libModules = nil
|
||||
dyns := dynamicFuncs
|
||||
dynamicFuncs = nil
|
||||
libRegistryMu.Unlock()
|
||||
|
||||
for _, m := range mods {
|
||||
vm.RegisterModule(m)
|
||||
}
|
||||
libModules = nil
|
||||
// Register HB_FUNC dynamic functions from #pragma BEGINDUMP
|
||||
for i := range dynamicFuncs {
|
||||
sym := &dynamicFuncs[i]
|
||||
for i := range dyns {
|
||||
sym := &dyns[i]
|
||||
vm.RegisterSymbol(sym)
|
||||
}
|
||||
dynamicFuncs = nil
|
||||
|
||||
Reference in New Issue
Block a user