github.com/15mga/kiwi@v0.0.2-0.20240324021231-b95d5c3ac751/worker/worker.go (about)

     1  package worker
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/15mga/kiwi"
     6  	"sync"
     7  
     8  	"github.com/15mga/kiwi/util"
     9  )
    10  
    11  func NewWorker[T any](fn func(T)) *Worker[T] {
    12  	b := &Worker[T]{
    13  		ch: make(chan struct{}, 1),
    14  		fn: fn,
    15  		pool: sync.Pool{
    16  			New: func() any {
    17  				return &job[T]{}
    18  			},
    19  		},
    20  	}
    21  	return b
    22  }
    23  
    24  type Worker[T any] struct {
    25  	mtx  sync.Mutex
    26  	ch   chan struct{}
    27  	head *job[T]
    28  	tail *job[T]
    29  	curr *job[T]
    30  	fn   func(T)
    31  	pool sync.Pool
    32  }
    33  
    34  func (w *Worker[T]) Start() {
    35  	go func() {
    36  		defer func() {
    37  			if err := recover(); err != nil {
    38  				//fmt.Printf("\033[31m!!!recover!!!\u001B[0m\n%s%s\n", err, util.GetStack(5))
    39  				kiwi.Error2(util.EcServiceErr, util.M{
    40  					"error": fmt.Sprint(err),
    41  				})
    42  				w.Start()
    43  			}
    44  		}()
    45  
    46  		w.do()
    47  
    48  		for range w.ch {
    49  			for {
    50  				w.mtx.Lock()
    51  				w.curr = w.head
    52  				w.head = nil
    53  				w.tail = nil
    54  				w.mtx.Unlock()
    55  
    56  				if w.curr == nil {
    57  					break
    58  				}
    59  				w.do()
    60  			}
    61  		}
    62  	}()
    63  }
    64  
    65  func (w *Worker[T]) Dispose() {
    66  	close(w.ch)
    67  }
    68  
    69  func (w *Worker[T]) Push(item T) {
    70  	e := w.pool.Get().(*job[T])
    71  	e.value = item
    72  	w.mtx.Lock()
    73  	if w.head != nil {
    74  		w.tail.next = e
    75  	} else {
    76  		w.head = e
    77  	}
    78  	w.tail = e
    79  	w.mtx.Unlock()
    80  
    81  	select {
    82  	case w.ch <- struct{}{}:
    83  	default:
    84  	}
    85  }
    86  
    87  func (w *Worker[T]) do() {
    88  	var (
    89  		j   *job[T]
    90  		val T
    91  	)
    92  	for {
    93  		if w.curr == nil {
    94  			break
    95  		}
    96  		j = w.curr
    97  		val = j.value
    98  		w.curr = j.next
    99  		j.next = nil
   100  		w.pool.Put(j)
   101  		w.fn(val)
   102  	}
   103  }
   104  
   105  type job[T any] struct {
   106  	next  *job[T]
   107  	value T
   108  }
   109  
   110  type FnJob func(*Job)
   111  
   112  func NewJobWorker(fn FnJob) *JobWorker {
   113  	w := &JobWorker{
   114  		pcr: fn,
   115  	}
   116  	w.Worker = NewWorker[*Job](w.process)
   117  	return w
   118  }
   119  
   120  type JobWorker struct {
   121  	*Worker[*Job]
   122  	pcr FnJob
   123  }
   124  
   125  func (w *JobWorker) process(j *Job) {
   126  	w.pcr(j)
   127  	j.Data = nil
   128  	_JobPool.Put(j)
   129  }
   130  
   131  func (w *JobWorker) Push(name JobName, data ...any) {
   132  	j := _JobPool.Get().(*Job)
   133  	j.Name = name
   134  	j.Data = data
   135  	w.Worker.Push(j)
   136  }
   137  
   138  type JobName = string
   139  
   140  type Job struct {
   141  	Name JobName
   142  	Data []any
   143  }
   144  
   145  var (
   146  	_JobPool = sync.Pool{
   147  		New: func() any {
   148  			return &Job{}
   149  		},
   150  	}
   151  )
   152  
   153  func SpawnJob() *Job {
   154  	return _JobPool.Get().(*Job)
   155  }
   156  
   157  func RecycleJob(job *Job) {
   158  	_JobPool.Put(job)
   159  }
   160  
   161  type FnJobData struct {
   162  	Fn     util.FnAnySlc
   163  	Params []any
   164  }
   165  
   166  type FnWorker struct {
   167  	*Worker[FnJobData]
   168  }
   169  
   170  func NewFnWorker() *FnWorker {
   171  	return &FnWorker{
   172  		Worker: NewWorker[FnJobData](func(data FnJobData) {
   173  			data.Fn(data.Params)
   174  		}),
   175  	}
   176  }
   177  
   178  func (w *FnWorker) Push(fn util.FnAnySlc, params ...any) {
   179  	w.Worker.Push(FnJobData{
   180  		Fn:     fn,
   181  		Params: params,
   182  	})
   183  }