Error occurs when invoking the same JS function concurrently via multiple goroutines.
Here the code:
func IfJs() string {
return `function callIf(i) {
var g = i == 1;
if (g) {
i = i + 1;
}
return i;
}
`
}
func TestIfFuncJsParal(t *testing.T) {
javascriptVM := New()
_, err := javascriptVM.Run(IfJs())
require.NoError(t, err)
paral := 100
wg := sync.WaitGroup{}
wg.Add(paral)
for i := 0; i < paral; i++ {
go func() {
defer wg.Done()
f, err := javascriptVM.Get("callIf")
require.NoError(t, err)
_, err = f.Call(f, 1)
require.NoError(t, err)
}()
}
wg.Wait()
}
Here the ERROR:
fatal error: concurrent map writes
Description:
The root issue stems from how compiled callIf functions become nodeFunctionObject instances, each holding a pointer to a same runtime object (constructed via New()). When the Call method executes:
- The function scope is assigned to the runtime.scope field.
- Concurrent goroutines sharing the same runtime end up accessing:
The same scope
The same stash
The same underlying map
This creates unsafe concurrent map access when storing variables, triggering the observed race conditions.
Proposed Solutions
Option 1: ThreadLocal Isolation
Pros: Explicit scope isolation
Cons:
- Introduces external dependencies
- Anti-pattern in Go
Option 2: Context-Based Scope Passing
- Avoid shared scope by storing scope in context
- Built-in cancellation support via context
Request for Feedback
Which approach would better align with otto’s design philosophy? Are there alternative patterns worth considering?
Error occurs when invoking the same JS function concurrently via multiple goroutines.
Here the code:
Here the ERROR:
Description:
The root issue stems from how compiled callIf functions become nodeFunctionObject instances, each holding a pointer to a same runtime object (constructed via New()). When the Call method executes:
This creates unsafe concurrent map access when storing variables, triggering the observed race conditions.
Proposed Solutions
Option 1: ThreadLocal Isolation
Pros: Explicit scope isolation
Cons:
Option 2: Context-Based Scope Passing
Request for Feedback
Which approach would better align with otto’s design philosophy? Are there alternative patterns worth considering?