Skip to content

Support parallel execution of JS functions without global variables #550

Description

@davedwwang

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:

  1. The function scope is assigned to the runtime.scope field.
  2. 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?

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions