main v0.0.10
parent baa9bc1ecc
commit 6b176cd294
  1. 37
      ottomain.go

@ -20,6 +20,7 @@ type otto_config struct {
var (
once sync.Once
semaphore chan struct{}
globalConfig otto_config
configChecked bool
configMutex sync.Mutex
)
@ -57,16 +58,18 @@ func initSemaphore() {
if !configChecked {
var cfg otto_config
if err := env.Parse(&cfg); err != nil {
// Use default if env parse fails
// Use defaults if env parse fails
cfg.MaxConcurrent = 50
cfg.Timeout = 2
}
globalConfig = cfg
semaphore = make(chan struct{}, cfg.MaxConcurrent)
configChecked = true
}
}
func ProcessRequest(script string, params map[string]interface{}) (response map[string]interface{}, err error) {
// Initialize semaphore once
// Initialize semaphore and config once
once.Do(initSemaphore)
// Acquire semaphore to limit concurrent executions
@ -78,10 +81,8 @@ func ProcessRequest(script string, params map[string]interface{}) (response map[
return nil, fmt.Errorf("too many concurrent JavaScript executions, please try again later")
}
var cfg otto_config
if err := env.Parse(&cfg); err != nil {
return nil, fmt.Errorf("env vars parse error: %w", err)
}
// Use cached config instead of parsing every time
cfg := globalConfig
vm := otto.New()
@ -99,28 +100,40 @@ func ProcessRequest(script string, params map[string]interface{}) (response map[
vm.Interrupt = make(chan func(), 1)
timeoutDone := make(chan struct{})
timeoutTimer := time.NewTimer(time.Duration(cfg.Timeout) * time.Second)
// Start timeout goroutine
go func() {
select {
case <-time.After(time.Duration(cfg.Timeout) * time.Second):
case <-timeoutTimer.C:
// Timer expired, try to interrupt
select {
case vm.Interrupt <- func() {
panic(errors.New("some code took to long! Stopping after timeout"))
}:
// Interrupt sent successfully
case <-timeoutDone:
// Script completed before timeout, exit gracefully
// Script completed before timeout, timer already stopped
return
}
case <-timeoutDone:
// Script completed before timeout, exit gracefully
// Script completed before timeout, stop timer
if !timeoutTimer.Stop() {
<-timeoutTimer.C
}
return
}
}()
defer func() {
close(timeoutDone) // Signal timeout goroutine to exit
// Clean up vm to help with garbage collection
vm = nil
// Signal timeout goroutine to exit and stop timer
close(timeoutDone)
if !timeoutTimer.Stop() {
select {
case <-timeoutTimer.C:
default:
}
}
if r := recover(); r != nil {
switch x := r.(type) {
case error:

Loading…
Cancel
Save