210 lines
4.7 KiB
Go
210 lines
4.7 KiB
Go
package gojs
|
|
|
|
import (
|
|
"container/list"
|
|
"fmt"
|
|
"github.com/ssgo/log"
|
|
"github.com/ssgo/u"
|
|
"sync"
|
|
)
|
|
|
|
type Pool struct {
|
|
plg *Program
|
|
args []any
|
|
pool *list.List
|
|
lock sync.Mutex
|
|
cond *sync.Cond
|
|
//waitChan chan bool
|
|
min uint
|
|
max uint
|
|
idle uint
|
|
total uint
|
|
maxTotal uint
|
|
free uint
|
|
waiting uint
|
|
maxWaiting uint
|
|
createTimes uint
|
|
logger *log.Logger
|
|
debug bool
|
|
}
|
|
|
|
type PoolConfig struct {
|
|
Min, Idle, Max uint
|
|
Args []any
|
|
Debug bool
|
|
}
|
|
|
|
func (p *Pool) Count() (total, maxTotal, maxWaiting, createTimes uint) {
|
|
p.lock.Lock()
|
|
defer p.lock.Unlock()
|
|
return p.total, p.maxTotal, p.maxWaiting, p.createTimes
|
|
}
|
|
|
|
func (p *Pool) Get() *Runtime {
|
|
//t1 := time.Now().UnixMicro()
|
|
p.lock.Lock()
|
|
waiting := false
|
|
//fmt.Println(u.BBlue("check wait"), p.total, p.max, p.free)
|
|
if p.max != 0 && p.free == 0 && p.total > p.max {
|
|
waiting = true
|
|
p.waiting++
|
|
if p.maxWaiting < p.waiting {
|
|
p.maxWaiting = p.waiting
|
|
}
|
|
if p.debug {
|
|
p.logger.Info("[go js pool] waiting " + fmt.Sprintln(u.BYellow(p.total), u.BYellow(p.pool.Len()), u.BMagenta(p.free), u.Dim(p.waiting)))
|
|
}
|
|
for p.free == 0 {
|
|
p.cond.Wait()
|
|
}
|
|
//p.lock.Unlock()
|
|
//<-p.waitChan
|
|
//p.lock.Lock()
|
|
if p.debug {
|
|
p.logger.Info("[go js pool] wait over " + fmt.Sprintln(u.BYellow(p.total), u.BYellow(p.pool.Len()), u.BMagenta(p.free), u.Dim(p.waiting)))
|
|
}
|
|
}
|
|
|
|
var rt *Runtime
|
|
item := p.pool.Front()
|
|
if item != nil {
|
|
p.pool.Remove(item)
|
|
if rt1, ok := item.Value.(*Runtime); ok {
|
|
rt = rt1
|
|
}
|
|
}
|
|
if rt != nil {
|
|
p.free--
|
|
if waiting {
|
|
p.waiting--
|
|
}
|
|
if p.debug {
|
|
p.logger.Info("[go js pool] get " + fmt.Sprintln(u.BYellow(p.total), u.BYellow(p.pool.Len()), u.BMagenta(p.free), u.Dim(p.waiting)))
|
|
}
|
|
} else {
|
|
//fmt.Println("new", p.total, p.free)
|
|
rt = p.new()
|
|
p.total++
|
|
p.createTimes++
|
|
if p.maxTotal < p.total {
|
|
p.maxTotal = p.total
|
|
}
|
|
if p.debug {
|
|
p.logger.Info("[go js pool] new " + fmt.Sprintln(u.BYellow(p.total), u.BYellow(p.pool.Len()), u.BMagenta(p.free), u.Dim(p.waiting)))
|
|
}
|
|
}
|
|
p.lock.Unlock()
|
|
//t2 := time.Now().UnixMicro() - t1
|
|
//fmt.Println("get", u.BRed(t2))
|
|
return rt
|
|
}
|
|
|
|
func (p *Pool) Put(rt *Runtime) {
|
|
p.lock.Lock()
|
|
//t1 := time.Now().UnixMicro()
|
|
if p.idle != 0 && p.free >= p.idle {
|
|
p.total--
|
|
if p.debug {
|
|
p.logger.Info("[go js pool] put out " + fmt.Sprintln(u.BYellow(p.total), u.BYellow(p.pool.Len()), u.BMagenta(p.free), u.Dim(p.waiting)))
|
|
}
|
|
p.lock.Unlock()
|
|
|
|
//r, err := rt.RunCode("Object.keys(globalThis)")
|
|
//if vars, ok := r.([]any); ok {
|
|
// for _, k := range vars {
|
|
// _ = rt.vm.Set(u.String(k), goja.Undefined())
|
|
// }
|
|
// r, err = rt.RunCode("Object.keys(globalThis)")
|
|
//}
|
|
//rt.RunCode("for(let k of Object.keys(globalThis)) delete globalThis[k]")
|
|
//r, err := rt.RunCode("Object.keys(globalThis)")
|
|
//fmt.Println(r, err)
|
|
//rt.Free()
|
|
return
|
|
}
|
|
p.pool.PushBack(rt)
|
|
p.free++
|
|
if p.debug {
|
|
p.logger.Info("[go js pool] put " + fmt.Sprintln(u.BYellow(p.total), u.BYellow(p.pool.Len()), u.BMagenta(p.free), u.Dim(p.waiting)))
|
|
}
|
|
//t2 := time.Now().UnixMicro() - t1
|
|
//fmt.Println("put", u.BYellow(t2))
|
|
p.lock.Unlock()
|
|
if p.waiting > 0 {
|
|
p.cond.Signal()
|
|
//p.waitChan <- true
|
|
}
|
|
}
|
|
|
|
func (p *Pool) new() *Runtime {
|
|
rt := New()
|
|
err := rt.StartFromProgram(p.plg)
|
|
if err == nil {
|
|
_, err = rt.RunMain(p.args...)
|
|
}
|
|
if err != nil {
|
|
p.logger.Error(err.Error())
|
|
return nil
|
|
}
|
|
return rt
|
|
}
|
|
|
|
func NewPool(plg *Program, opt PoolConfig, logger *log.Logger) *Pool {
|
|
if opt.Min == 0 {
|
|
opt.Min = 1
|
|
}
|
|
if opt.Max != 0 && opt.Max < opt.Min {
|
|
opt.Max = opt.Min
|
|
}
|
|
if opt.Idle != 0 && opt.Idle < opt.Min {
|
|
opt.Idle = opt.Min
|
|
}
|
|
if opt.Idle != 0 && opt.Idle > opt.Max {
|
|
opt.Idle = opt.Max
|
|
}
|
|
p := &Pool{
|
|
pool: list.New(),
|
|
plg: plg,
|
|
args: opt.Args,
|
|
min: opt.Min,
|
|
max: opt.Max,
|
|
idle: opt.Idle,
|
|
logger: logger,
|
|
debug: opt.Debug,
|
|
}
|
|
p.cond = sync.NewCond(&p.lock)
|
|
//p.waitChan = make(chan bool, opt.Max*10)
|
|
p.lock.Lock()
|
|
//t1 := time.Now().UnixMicro()
|
|
for i := 0; i < int(opt.Min); i++ {
|
|
item := p.new()
|
|
p.pool.PushBack(item)
|
|
p.total++
|
|
p.free++
|
|
p.createTimes++
|
|
}
|
|
//t2 := time.Now().UnixMicro() - t1
|
|
//fmt.Println("init", u.BBlue(t2))
|
|
p.lock.Unlock()
|
|
if p.debug {
|
|
p.logger.Info("[go js pool] init " + fmt.Sprintln(u.BYellow(p.total), u.BYellow(p.pool.Len()), u.BMagenta(p.free), u.Dim(p.waiting)))
|
|
}
|
|
return p
|
|
}
|
|
|
|
func NewPoolByFile(file string, opt PoolConfig, logger *log.Logger) *Pool {
|
|
if plg, err := CompileFile(file); err == nil {
|
|
return NewPool(plg, opt, logger)
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func NewPoolByCode(code, refFile string, opt PoolConfig, logger *log.Logger) *Pool {
|
|
if plg, err := CompileCode(code, refFile); err == nil {
|
|
return NewPool(plg, opt, logger)
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|