gojs/pool.go
Star f37b64c2df many update
support pool
2024-10-07 23:02:11 +08:00

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
}
}