gf/os/gtimer/gtimer_entry.go

195 lines
5.5 KiB
Go
Raw Normal View History

// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
2019-01-01 19:43:31 +08:00
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
2019-01-01 19:43:31 +08:00
package gtimer
2019-01-01 19:43:31 +08:00
import (
2019-06-19 09:06:52 +08:00
"time"
2019-07-29 21:01:19 +08:00
"github.com/gogf/gf/container/gtype"
2019-01-01 19:43:31 +08:00
)
2020-01-21 14:46:23 +08:00
// Entry is the timing job entry to wheel.
2019-01-01 19:43:31 +08:00
type Entry struct {
2020-01-21 14:46:23 +08:00
wheel *wheel // Belonged wheel.
job JobFunc // The job function.
singleton *gtype.Bool // Singleton mode.
status *gtype.Int // Job status.
times *gtype.Int // Limit running times.
create int64 // Timer ticks when the job installed.
interval int64 // The interval ticks of the job.
createMs int64 // The timestamp in milliseconds when job installed.
intervalMs int64 // The interval milliseconds of the job.
rawIntervalMs int64 // Raw input interval in milliseconds.
2019-01-01 19:43:31 +08:00
}
2020-01-21 14:46:23 +08:00
// JobFunc is the job function.
2019-01-12 20:20:30 +08:00
type JobFunc = func()
2019-01-01 19:43:31 +08:00
2020-01-21 14:46:23 +08:00
// addEntry adds a timing job to the wheel.
func (w *wheel) addEntry(interval time.Duration, job JobFunc, singleton bool, times int, status int) *Entry {
if times <= 0 {
times = gDEFAULT_TIMES
}
2020-04-30 22:22:35 +08:00
var (
ms = interval.Nanoseconds() / 1e6
num = ms / w.intervalMs
)
2019-06-19 09:06:52 +08:00
if num == 0 {
2020-01-21 14:46:23 +08:00
// If the given interval is lesser than the one of the wheel,
// then sets it to one tick, which means it will be run in one interval.
2019-06-19 09:06:52 +08:00
num = 1
}
nowMs := time.Now().UnixNano() / 1e6
ticks := w.ticks.Val()
entry := &Entry{
wheel: w,
job: job,
times: gtype.NewInt(times),
status: gtype.NewInt(status),
create: ticks,
interval: num,
singleton: gtype.NewBool(singleton),
createMs: nowMs,
intervalMs: ms,
rawIntervalMs: ms,
}
2020-01-21 14:46:23 +08:00
// Install the job to the list of the slot.
2019-06-19 09:06:52 +08:00
w.slots[(ticks+num)%w.number].PushBack(entry)
return entry
2019-01-01 19:43:31 +08:00
}
2020-01-21 14:46:23 +08:00
// addEntryByParent adds a timing job with parent entry.
2019-01-22 13:50:10 +08:00
func (w *wheel) addEntryByParent(interval int64, parent *Entry) *Entry {
2019-06-19 09:06:52 +08:00
num := interval / w.intervalMs
if num == 0 {
num = 1
}
nowMs := time.Now().UnixNano() / 1e6
ticks := w.ticks.Val()
entry := &Entry{
wheel: w,
job: parent.job,
times: parent.times,
status: parent.status,
create: ticks,
interval: num,
singleton: parent.singleton,
createMs: nowMs,
intervalMs: interval,
rawIntervalMs: parent.rawIntervalMs,
}
w.slots[(ticks+num)%w.number].PushBack(entry)
return entry
}
2020-01-21 14:46:23 +08:00
// Status returns the status of the job.
2019-01-03 19:11:54 +08:00
func (entry *Entry) Status() int {
2019-06-19 09:06:52 +08:00
return entry.status.Val()
2019-01-01 19:43:31 +08:00
}
2020-01-21 14:46:23 +08:00
// SetStatus custom sets the status for the job.
2019-01-04 15:32:16 +08:00
func (entry *Entry) SetStatus(status int) int {
2019-06-19 09:06:52 +08:00
return entry.status.Set(status)
2019-01-04 15:32:16 +08:00
}
2020-01-21 14:46:23 +08:00
// Start starts the job.
func (entry *Entry) Start() {
2019-06-19 09:06:52 +08:00
entry.status.Set(STATUS_READY)
}
2020-01-21 14:46:23 +08:00
// Stop stops the job.
func (entry *Entry) Stop() {
2019-06-19 09:06:52 +08:00
entry.status.Set(STATUS_STOPPED)
}
2020-07-29 11:22:46 +08:00
//Reset reset the job.
func (entry *Entry) Reset() {
entry.status.Set(STATUS_RESET)
}
2020-01-21 14:46:23 +08:00
// Close closes the job, and then it will be removed from the timer.
2019-01-03 19:11:54 +08:00
func (entry *Entry) Close() {
2019-06-19 09:06:52 +08:00
entry.status.Set(STATUS_CLOSED)
2019-01-02 10:18:00 +08:00
}
2020-01-21 14:46:23 +08:00
// IsSingleton checks and returns whether the job in singleton mode.
2019-01-03 19:11:54 +08:00
func (entry *Entry) IsSingleton() bool {
2019-06-19 09:06:52 +08:00
return entry.singleton.Val()
2019-01-01 19:43:31 +08:00
}
2020-01-21 14:46:23 +08:00
// SetSingleton sets the job singleton mode.
2019-01-03 19:11:54 +08:00
func (entry *Entry) SetSingleton(enabled bool) {
2019-06-19 09:06:52 +08:00
entry.singleton.Set(enabled)
2019-01-01 19:43:31 +08:00
}
2020-01-21 14:46:23 +08:00
// SetTimes sets the limit running times for the job.
2019-01-03 19:11:54 +08:00
func (entry *Entry) SetTimes(times int) {
2019-06-19 09:06:52 +08:00
entry.times.Set(times)
2019-01-09 12:54:37 +08:00
}
2020-01-21 14:46:23 +08:00
// Run runs the job.
2019-01-09 12:54:37 +08:00
func (entry *Entry) Run() {
2019-06-19 09:06:52 +08:00
entry.job()
2019-01-01 19:43:31 +08:00
}
2020-01-21 14:46:23 +08:00
// check checks if the job should be run in given ticks and timestamp milliseconds.
2019-01-22 22:07:46 +08:00
func (entry *Entry) check(nowTicks int64, nowMs int64) (runnable, addable bool) {
2019-06-19 09:06:52 +08:00
switch entry.status.Val() {
case STATUS_STOPPED:
return false, true
case STATUS_CLOSED:
return false, false
2020-07-29 11:22:46 +08:00
case STATUS_RESET:
return false, true
2019-06-19 09:06:52 +08:00
}
2020-01-21 14:46:23 +08:00
// Firstly checks using the ticks, this may be low precision as one tick is a little bit long.
2019-06-19 09:06:52 +08:00
if diff := nowTicks - entry.create; diff > 0 && diff%entry.interval == 0 {
2020-01-21 14:46:23 +08:00
// If not the lowest level wheel.
2019-06-19 09:06:52 +08:00
if entry.wheel.level > 0 {
diffMs := nowMs - entry.createMs
switch {
2020-01-21 14:46:23 +08:00
// Add it to the next slot, which means it will run on next interval.
2019-06-19 09:06:52 +08:00
case diffMs < entry.wheel.timer.intervalMs:
entry.wheel.slots[(nowTicks+entry.interval)%entry.wheel.number].PushBack(entry)
return false, false
2020-01-21 14:46:23 +08:00
// Normal rolls on the job.
2019-06-19 09:06:52 +08:00
case diffMs >= entry.wheel.timer.intervalMs:
2020-01-21 14:46:23 +08:00
// Calculate the leftover milliseconds,
// if it is greater than the minimum interval, then re-install it.
2019-06-19 09:06:52 +08:00
if leftMs := entry.intervalMs - diffMs; leftMs > entry.wheel.timer.intervalMs {
2020-01-21 14:46:23 +08:00
// Re-calculate and re-installs the job proper slot.
2019-06-19 09:06:52 +08:00
entry.wheel.timer.doAddEntryByParent(leftMs, entry)
return false, false
}
}
}
2020-01-21 14:46:23 +08:00
// Singleton mode check.
2019-06-19 09:06:52 +08:00
if entry.IsSingleton() {
2020-01-21 14:46:23 +08:00
// Note that it is atomic operation to ensure concurrent safety.
2019-06-19 09:06:52 +08:00
if entry.status.Set(STATUS_RUNNING) == STATUS_RUNNING {
return false, true
}
}
2020-01-21 14:46:23 +08:00
// Limit running times.
2019-06-19 09:06:52 +08:00
times := entry.times.Add(-1)
if times <= 0 {
2020-01-21 14:46:23 +08:00
// Note that it is atomic operation to ensure concurrent safety.
2019-06-19 09:06:52 +08:00
if entry.status.Set(STATUS_CLOSED) == STATUS_CLOSED || times < 0 {
return false, false
}
}
2020-01-21 14:46:23 +08:00
// This means it does not limit the running times.
// I know it's ugly, but it is surely high performance for running times limit.
2019-06-19 09:06:52 +08:00
if times < 2000000000 && times > 1000000000 {
entry.times.Set(gDEFAULT_TIMES)
}
return true, true
}
return false, true
2019-01-01 19:43:31 +08:00
}