mirror of
https://gitee.com/johng/gf.git
synced 2024-12-03 12:47:50 +08:00
improve panic...recover of exit feature for package ghttp/gtimer/gfsnotify (#2000)
This commit is contained in:
parent
675ae9bade
commit
98b2e8ab18
2
.github/workflows/gf.yml
vendored
2
.github/workflows/gf.yml
vendored
@ -8,6 +8,7 @@ on:
|
|||||||
- develop
|
- develop
|
||||||
- personal/**
|
- personal/**
|
||||||
- feature/**
|
- feature/**
|
||||||
|
- enhance/**
|
||||||
- fix/**
|
- fix/**
|
||||||
|
|
||||||
pull_request:
|
pull_request:
|
||||||
@ -16,6 +17,7 @@ on:
|
|||||||
- develop
|
- develop
|
||||||
- personal/**
|
- personal/**
|
||||||
- feature/**
|
- feature/**
|
||||||
|
- enhance/**
|
||||||
- fix/**
|
- fix/**
|
||||||
|
|
||||||
env:
|
env:
|
||||||
|
@ -98,6 +98,9 @@ type (
|
|||||||
// Listening file descriptor mapping.
|
// Listening file descriptor mapping.
|
||||||
// The key is either "http" or "https" and the value is its FD.
|
// The key is either "http" or "https" and the value is its FD.
|
||||||
listenerFdMap = map[string]string
|
listenerFdMap = map[string]string
|
||||||
|
|
||||||
|
// internalPanic is the custom panic for internal usage.
|
||||||
|
internalPanic string
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -119,9 +122,6 @@ const (
|
|||||||
const (
|
const (
|
||||||
supportedHttpMethods = "GET,PUT,POST,DELETE,PATCH,HEAD,CONNECT,OPTIONS,TRACE"
|
supportedHttpMethods = "GET,PUT,POST,DELETE,PATCH,HEAD,CONNECT,OPTIONS,TRACE"
|
||||||
defaultMethod = "ALL"
|
defaultMethod = "ALL"
|
||||||
exceptionExit = "exit"
|
|
||||||
exceptionExitAll = "exit_all"
|
|
||||||
exceptionExitHook = "exit_hook"
|
|
||||||
routeCacheDuration = time.Hour
|
routeCacheDuration = time.Hour
|
||||||
ctxKeyForRequest = "gHttpRequestObject"
|
ctxKeyForRequest = "gHttpRequestObject"
|
||||||
contentTypeXml = "text/xml"
|
contentTypeXml = "text/xml"
|
||||||
@ -135,6 +135,12 @@ const (
|
|||||||
gracefulShutdownTimeout = 5 * time.Second
|
gracefulShutdownTimeout = 5 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
exceptionExit internalPanic = "exit"
|
||||||
|
exceptionExitAll internalPanic = "exit_all"
|
||||||
|
exceptionExitHook internalPanic = "exit_hook"
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// methodsMap stores all supported HTTP method.
|
// methodsMap stores all supported HTTP method.
|
||||||
// It is used for quick HTTP method searching using map.
|
// It is used for quick HTTP method searching using map.
|
||||||
|
@ -71,14 +71,6 @@ func (lru *adapterMemoryLru) Pop() interface{} {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print is used for test only.
|
|
||||||
// func (lru *adapterMemoryLru) Print() {
|
|
||||||
// for _, v := range lru.list.FrontAll() {
|
|
||||||
// fmt.Printf("%v ", v)
|
|
||||||
// }
|
|
||||||
// fmt.Println()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// SyncAndClear synchronizes the keys from `rawList` to `list` and `data`
|
// SyncAndClear synchronizes the keys from `rawList` to `list` and `data`
|
||||||
// using Least Recently Used algorithm.
|
// using Least Recently Used algorithm.
|
||||||
func (lru *adapterMemoryLru) SyncAndClear(ctx context.Context) {
|
func (lru *adapterMemoryLru) SyncAndClear(ctx context.Context) {
|
||||||
@ -87,9 +79,7 @@ func (lru *adapterMemoryLru) SyncAndClear(ctx context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Data synchronization.
|
// Data synchronization.
|
||||||
var (
|
var alreadyExistItem interface{}
|
||||||
alreadyExistItem interface{}
|
|
||||||
)
|
|
||||||
for {
|
for {
|
||||||
if rawListItem := lru.rawList.PopFront(); rawListItem != nil {
|
if rawListItem := lru.rawList.PopFront(); rawListItem != nil {
|
||||||
// Deleting the key from list.
|
// Deleting the key from list.
|
||||||
@ -104,9 +94,9 @@ func (lru *adapterMemoryLru) SyncAndClear(ctx context.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Data cleaning up.
|
// Data cleaning up.
|
||||||
for i := lru.Size() - lru.cache.cap; i > 0; i-- {
|
for clearLength := lru.Size() - lru.cache.cap; clearLength > 0; clearLength-- {
|
||||||
if s := lru.Pop(); s != nil {
|
if topKey := lru.Pop(); topKey != nil {
|
||||||
lru.cache.clearByKey(s, true)
|
lru.cache.clearByKey(topKey, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,7 +137,7 @@ func (entry *Entry) Close() {
|
|||||||
// gcron.Entry relies on gtimer to implement a scheduled task check for gcron.Entry per second.
|
// gcron.Entry relies on gtimer to implement a scheduled task check for gcron.Entry per second.
|
||||||
func (entry *Entry) checkAndRun(ctx context.Context) {
|
func (entry *Entry) checkAndRun(ctx context.Context) {
|
||||||
currentTime := time.Now()
|
currentTime := time.Now()
|
||||||
if !entry.schedule.checkMeetAndUpdateLastSeconds(currentTime) {
|
if !entry.schedule.checkMeetAndUpdateLastSeconds(ctx, currentTime) {
|
||||||
intlog.Printf(
|
intlog.Printf(
|
||||||
ctx,
|
ctx,
|
||||||
`timely check, current time does not meet cron job "%s"`,
|
`timely check, current time does not meet cron job "%s"`,
|
||||||
|
@ -15,23 +15,22 @@ import (
|
|||||||
"github.com/gogf/gf/v2/container/gtype"
|
"github.com/gogf/gf/v2/container/gtype"
|
||||||
"github.com/gogf/gf/v2/errors/gcode"
|
"github.com/gogf/gf/v2/errors/gcode"
|
||||||
"github.com/gogf/gf/v2/errors/gerror"
|
"github.com/gogf/gf/v2/errors/gerror"
|
||||||
"github.com/gogf/gf/v2/internal/intlog"
|
|
||||||
"github.com/gogf/gf/v2/os/gtime"
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
"github.com/gogf/gf/v2/text/gregex"
|
"github.com/gogf/gf/v2/text/gregex"
|
||||||
)
|
)
|
||||||
|
|
||||||
// cronSchedule is the schedule for cron job.
|
// cronSchedule is the schedule for cron job.
|
||||||
type cronSchedule struct {
|
type cronSchedule struct {
|
||||||
create int64 // Created timestamp.
|
createTimestamp int64 // Created timestamp in seconds.
|
||||||
every int64 // Running interval in seconds.
|
everySeconds int64 // Running interval in seconds.
|
||||||
pattern string // The raw cron pattern string.
|
pattern string // The raw cron pattern string.
|
||||||
second map[int]struct{} // Job can run in these second numbers.
|
secondMap map[int]struct{} // Job can run in these second numbers.
|
||||||
minute map[int]struct{} // Job can run in these minute numbers.
|
minuteMap map[int]struct{} // Job can run in these minute numbers.
|
||||||
hour map[int]struct{} // Job can run in these hour numbers.
|
hourMap map[int]struct{} // Job can run in these hour numbers.
|
||||||
day map[int]struct{} // Job can run in these day numbers.
|
dayMap map[int]struct{} // Job can run in these day numbers.
|
||||||
week map[int]struct{} // Job can run in these week numbers.
|
weekMap map[int]struct{} // Job can run in these week numbers.
|
||||||
month map[int]struct{} // Job can run in these moth numbers.
|
monthMap map[int]struct{} // Job can run in these moth numbers.
|
||||||
lastTimestamp *gtype.Int64 // Last timestamp number, for seconds fix.
|
lastTimestamp *gtype.Int64 // Last timestamp number, for timestamp fix in some delay.
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -107,6 +106,7 @@ var (
|
|||||||
|
|
||||||
// newSchedule creates and returns a schedule object for given cron pattern.
|
// newSchedule creates and returns a schedule object for given cron pattern.
|
||||||
func newSchedule(pattern string) (*cronSchedule, error) {
|
func newSchedule(pattern string) (*cronSchedule, error) {
|
||||||
|
var currentTimestamp = time.Now().Unix()
|
||||||
// Check if the predefined patterns.
|
// Check if the predefined patterns.
|
||||||
if match, _ := gregex.MatchString(`(@\w+)\s*(\w*)\s*`, pattern); len(match) > 0 {
|
if match, _ := gregex.MatchString(`(@\w+)\s*(\w*)\s*`, pattern); len(match) > 0 {
|
||||||
key := strings.ToLower(match[1])
|
key := strings.ToLower(match[1])
|
||||||
@ -118,10 +118,10 @@ func newSchedule(pattern string) (*cronSchedule, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &cronSchedule{
|
return &cronSchedule{
|
||||||
create: time.Now().Unix(),
|
createTimestamp: currentTimestamp,
|
||||||
every: int64(d.Seconds()),
|
everySeconds: int64(d.Seconds()),
|
||||||
pattern: pattern,
|
pattern: pattern,
|
||||||
lastTimestamp: gtype.NewInt64(),
|
lastTimestamp: gtype.NewInt64(currentTimestamp),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
return nil, gerror.NewCodef(gcode.CodeInvalidParameter, `invalid pattern: "%s"`, pattern)
|
return nil, gerror.NewCodef(gcode.CodeInvalidParameter, `invalid pattern: "%s"`, pattern)
|
||||||
@ -130,46 +130,46 @@ func newSchedule(pattern string) (*cronSchedule, error) {
|
|||||||
// 0 0 0 1 1 2
|
// 0 0 0 1 1 2
|
||||||
if match, _ := gregex.MatchString(regexForCron, pattern); len(match) == 7 {
|
if match, _ := gregex.MatchString(regexForCron, pattern); len(match) == 7 {
|
||||||
schedule := &cronSchedule{
|
schedule := &cronSchedule{
|
||||||
create: time.Now().Unix(),
|
createTimestamp: currentTimestamp,
|
||||||
every: 0,
|
everySeconds: 0,
|
||||||
pattern: pattern,
|
pattern: pattern,
|
||||||
lastTimestamp: gtype.NewInt64(),
|
lastTimestamp: gtype.NewInt64(currentTimestamp),
|
||||||
}
|
}
|
||||||
// Second.
|
// Second.
|
||||||
if m, err := parsePatternItem(match[1], 0, 59, false); err != nil {
|
if m, err := parsePatternItem(match[1], 0, 59, false); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
schedule.second = m
|
schedule.secondMap = m
|
||||||
}
|
}
|
||||||
// Minute.
|
// Minute.
|
||||||
if m, err := parsePatternItem(match[2], 0, 59, false); err != nil {
|
if m, err := parsePatternItem(match[2], 0, 59, false); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
schedule.minute = m
|
schedule.minuteMap = m
|
||||||
}
|
}
|
||||||
// Hour.
|
// Hour.
|
||||||
if m, err := parsePatternItem(match[3], 0, 23, false); err != nil {
|
if m, err := parsePatternItem(match[3], 0, 23, false); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
schedule.hour = m
|
schedule.hourMap = m
|
||||||
}
|
}
|
||||||
// Day.
|
// Day.
|
||||||
if m, err := parsePatternItem(match[4], 1, 31, true); err != nil {
|
if m, err := parsePatternItem(match[4], 1, 31, true); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
schedule.day = m
|
schedule.dayMap = m
|
||||||
}
|
}
|
||||||
// Month.
|
// Month.
|
||||||
if m, err := parsePatternItem(match[5], 1, 12, false); err != nil {
|
if m, err := parsePatternItem(match[5], 1, 12, false); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
schedule.month = m
|
schedule.monthMap = m
|
||||||
}
|
}
|
||||||
// Week.
|
// Week.
|
||||||
if m, err := parsePatternItem(match[6], 0, 6, true); err != nil {
|
if m, err := parsePatternItem(match[6], 0, 6, true); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
schedule.week = m
|
schedule.weekMap = m
|
||||||
}
|
}
|
||||||
return schedule, nil
|
return schedule, nil
|
||||||
}
|
}
|
||||||
@ -208,6 +208,7 @@ func parsePatternItem(item string, min int, max int, allowQuestionMark bool) (ma
|
|||||||
case 6:
|
case 6:
|
||||||
// It's checking week field.
|
// It's checking week field.
|
||||||
itemType = patternItemTypeWeek
|
itemType = patternItemTypeWeek
|
||||||
|
|
||||||
case 12:
|
case 12:
|
||||||
// It's checking month field.
|
// It's checking month field.
|
||||||
itemType = patternItemTypeMonth
|
itemType = patternItemTypeMonth
|
||||||
@ -268,78 +269,48 @@ func parsePatternItemValue(value string, itemType int) (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// checkMeetAndUpdateLastSeconds checks if the given time `t` meets the runnable point for the job.
|
// checkMeetAndUpdateLastSeconds checks if the given time `t` meets the runnable point for the job.
|
||||||
func (s *cronSchedule) checkMeetAndUpdateLastSeconds(t time.Time) bool {
|
func (s *cronSchedule) checkMeetAndUpdateLastSeconds(ctx context.Context, t time.Time) bool {
|
||||||
if s.every != 0 {
|
if s.everySeconds != 0 {
|
||||||
// It checks using interval.
|
// It checks using interval.
|
||||||
if diff := t.Unix() - s.create; diff > 0 {
|
secondsAfterCreated := t.Unix() - s.createTimestamp
|
||||||
return diff%s.every == 0
|
secondsAfterCreated += int64(s.getFixedTimestampDelta(ctx, t))
|
||||||
|
if secondsAfterCreated > 0 {
|
||||||
|
return secondsAfterCreated%s.everySeconds == 0
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// It checks using normal cron pattern.
|
// It checks using normal cron pattern.
|
||||||
if _, ok := s.second[s.getFixedSecond(t)]; !ok {
|
if _, ok := s.secondMap[s.getFixedSecond(ctx, t)]; !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if _, ok := s.minute[t.Minute()]; !ok {
|
if _, ok := s.minuteMap[t.Minute()]; !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if _, ok := s.hour[t.Hour()]; !ok {
|
if _, ok := s.hourMap[t.Hour()]; !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if _, ok := s.day[t.Day()]; !ok {
|
if _, ok := s.dayMap[t.Day()]; !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if _, ok := s.month[int(t.Month())]; !ok {
|
if _, ok := s.monthMap[int(t.Month())]; !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if _, ok := s.week[int(t.Weekday())]; !ok {
|
if _, ok := s.weekMap[int(t.Weekday())]; !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// getFixedSecond checks, fixes and returns the seconds that have delay in some seconds.
|
|
||||||
// Reference: https://github.com/golang/go/issues/14410
|
|
||||||
func (s *cronSchedule) getFixedSecond(t time.Time) int {
|
|
||||||
var (
|
|
||||||
second = t.Second()
|
|
||||||
currentTimestamp = t.Unix()
|
|
||||||
lastTimestamp = s.lastTimestamp.Val()
|
|
||||||
)
|
|
||||||
switch {
|
|
||||||
case
|
|
||||||
lastTimestamp == 0,
|
|
||||||
lastTimestamp == currentTimestamp-1:
|
|
||||||
lastTimestamp = currentTimestamp
|
|
||||||
|
|
||||||
case
|
|
||||||
lastTimestamp == currentTimestamp-2,
|
|
||||||
lastTimestamp == currentTimestamp-3,
|
|
||||||
lastTimestamp == currentTimestamp:
|
|
||||||
lastTimestamp += 1
|
|
||||||
second += 1
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Too much delay, let's update the last timestamp to current one.
|
|
||||||
lastTimestamp = currentTimestamp
|
|
||||||
intlog.Printf(
|
|
||||||
context.Background(),
|
|
||||||
`too much delay, last "%d", current "%d"`,
|
|
||||||
lastTimestamp, currentTimestamp,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
second %= 60
|
|
||||||
s.lastTimestamp.Set(lastTimestamp)
|
|
||||||
return second
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next returns the next time this schedule is activated, greater than the given
|
// Next returns the next time this schedule is activated, greater than the given
|
||||||
// time. If no time can be found to satisfy the schedule, return the zero time.
|
// time. If no time can be found to satisfy the schedule, return the zero time.
|
||||||
func (s *cronSchedule) Next(t time.Time) time.Time {
|
func (s *cronSchedule) Next(t time.Time) time.Time {
|
||||||
if s.every != 0 {
|
if s.everySeconds != 0 {
|
||||||
diff := t.Unix() - s.create
|
var (
|
||||||
cnt := diff/s.every + 1
|
diff = t.Unix() - s.createTimestamp
|
||||||
return t.Add(time.Duration(cnt*s.every) * time.Second)
|
count = diff/s.everySeconds + 1
|
||||||
|
)
|
||||||
|
return t.Add(time.Duration(count*s.everySeconds) * time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start at the earliest possible time (the upcoming second).
|
// Start at the earliest possible time (the upcoming second).
|
||||||
@ -355,7 +326,7 @@ WRAP:
|
|||||||
return t // who will care the job that run in five years later
|
return t // who will care the job that run in five years later
|
||||||
}
|
}
|
||||||
|
|
||||||
for !s.match(s.month, int(t.Month())) {
|
for !s.match(s.monthMap, int(t.Month())) {
|
||||||
if !added {
|
if !added {
|
||||||
added = true
|
added = true
|
||||||
t = time.Date(t.Year(), t.Month(), 1, 0, 0, 0, 0, loc)
|
t = time.Date(t.Year(), t.Month(), 1, 0, 0, 0, 0, loc)
|
||||||
@ -387,7 +358,7 @@ WRAP:
|
|||||||
goto WRAP
|
goto WRAP
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for !s.match(s.hour, t.Hour()) {
|
for !s.match(s.hourMap, t.Hour()) {
|
||||||
if !added {
|
if !added {
|
||||||
added = true
|
added = true
|
||||||
t = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), 0, 0, 0, loc)
|
t = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), 0, 0, 0, loc)
|
||||||
@ -398,7 +369,7 @@ WRAP:
|
|||||||
goto WRAP
|
goto WRAP
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for !s.match(s.minute, t.Minute()) {
|
for !s.match(s.minuteMap, t.Minute()) {
|
||||||
if !added {
|
if !added {
|
||||||
added = true
|
added = true
|
||||||
t = t.Truncate(time.Minute)
|
t = t.Truncate(time.Minute)
|
||||||
@ -409,7 +380,7 @@ WRAP:
|
|||||||
goto WRAP
|
goto WRAP
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for !s.match(s.second, t.Second()) {
|
for !s.match(s.secondMap, t.Second()) {
|
||||||
if !added {
|
if !added {
|
||||||
added = true
|
added = true
|
||||||
t = t.Truncate(time.Second)
|
t = t.Truncate(time.Second)
|
||||||
@ -425,8 +396,8 @@ WRAP:
|
|||||||
// dayMatches returns true if the schedule's day-of-week and day-of-month
|
// dayMatches returns true if the schedule's day-of-week and day-of-month
|
||||||
// restrictions are satisfied by the given time.
|
// restrictions are satisfied by the given time.
|
||||||
func (s *cronSchedule) dayMatches(t time.Time) bool {
|
func (s *cronSchedule) dayMatches(t time.Time) bool {
|
||||||
_, ok1 := s.day[t.Day()]
|
_, ok1 := s.dayMap[t.Day()]
|
||||||
_, ok2 := s.week[int(t.Weekday())]
|
_, ok2 := s.weekMap[int(t.Weekday())]
|
||||||
return ok1 && ok2
|
return ok1 && ok2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
53
os/gcron/gcron_schedule_fix.go
Normal file
53
os/gcron/gcron_schedule_fix.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package gcron
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gogf/gf/v2/internal/intlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
// getFixedSecond checks, fixes and returns the seconds that have delay fix in some seconds.
|
||||||
|
// Reference: https://github.com/golang/go/issues/14410
|
||||||
|
func (s *cronSchedule) getFixedSecond(ctx context.Context, t time.Time) int {
|
||||||
|
return (t.Second() + s.getFixedTimestampDelta(ctx, t)) % 60
|
||||||
|
}
|
||||||
|
|
||||||
|
// getFixedTimestampDelta checks, fixes and returns the timestamp delta that have delay fix in some seconds.
|
||||||
|
// The tolerated timestamp delay is `3` seconds in default.
|
||||||
|
func (s *cronSchedule) getFixedTimestampDelta(ctx context.Context, t time.Time) int {
|
||||||
|
var (
|
||||||
|
currentTimestamp = t.Unix()
|
||||||
|
lastTimestamp = s.lastTimestamp.Val()
|
||||||
|
delta int
|
||||||
|
)
|
||||||
|
switch {
|
||||||
|
case
|
||||||
|
lastTimestamp == currentTimestamp-1:
|
||||||
|
lastTimestamp = currentTimestamp
|
||||||
|
|
||||||
|
case
|
||||||
|
lastTimestamp == currentTimestamp-2,
|
||||||
|
lastTimestamp == currentTimestamp-3,
|
||||||
|
lastTimestamp == currentTimestamp:
|
||||||
|
lastTimestamp += 1
|
||||||
|
delta = 1
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Too much delay, let's update the last timestamp to current one.
|
||||||
|
intlog.Printf(
|
||||||
|
ctx,
|
||||||
|
`too much delay, last timestamp "%d", current "%d"`,
|
||||||
|
lastTimestamp, currentTimestamp,
|
||||||
|
)
|
||||||
|
lastTimestamp = currentTimestamp
|
||||||
|
}
|
||||||
|
s.lastTimestamp.Set(lastTimestamp)
|
||||||
|
return delta
|
||||||
|
}
|
@ -29,7 +29,7 @@ func TestSlash(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
||||||
}
|
}
|
||||||
t.AssertEQ(sched.week, c.expected)
|
t.AssertEQ(sched.weekMap, c.expected)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -56,6 +56,9 @@ type Event struct {
|
|||||||
// Op is the bits union for file operations.
|
// Op is the bits union for file operations.
|
||||||
type Op uint32
|
type Op uint32
|
||||||
|
|
||||||
|
// internalPanic is the custom panic for internal usage.
|
||||||
|
type internalPanic string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
CREATE Op = 1 << iota
|
CREATE Op = 1 << iota
|
||||||
WRITE
|
WRITE
|
||||||
@ -65,8 +68,8 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
repeatEventFilterDuration = time.Millisecond // Duration for repeated event filter.
|
repeatEventFilterDuration = time.Millisecond // Duration for repeated event filter.
|
||||||
callbackExitEventPanicStr = "exit" // Custom exit event for internal usage.
|
callbackExitEventPanicStr internalPanic = "exit" // Custom exit event for internal usage.
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -44,14 +44,17 @@ type TimerOptions struct {
|
|||||||
Interval time.Duration // Interval is the interval escaped of the timer.
|
Interval time.Duration // Interval is the interval escaped of the timer.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// internalPanic is the custom panic for internal usage.
|
||||||
|
type internalPanic string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
StatusReady = 0 // Job or Timer is ready for running.
|
StatusReady = 0 // Job or Timer is ready for running.
|
||||||
StatusRunning = 1 // Job or Timer is already running.
|
StatusRunning = 1 // Job or Timer is already running.
|
||||||
StatusStopped = 2 // Job or Timer is stopped.
|
StatusStopped = 2 // Job or Timer is stopped.
|
||||||
StatusClosed = -1 // Job or Timer is closed and waiting to be deleted.
|
StatusClosed = -1 // Job or Timer is closed and waiting to be deleted.
|
||||||
panicExit = "exit" // panicExit is used for custom job exit with panic.
|
panicExit internalPanic = "exit" // panicExit is used for custom job exit with panic.
|
||||||
defaultTimerInterval = "100" // defaultTimerInterval is the default timer interval in milliseconds.
|
defaultTimerInterval = "100" // defaultTimerInterval is the default timer interval in milliseconds.
|
||||||
commandEnvKeyForInterval = "gf.gtimer.interval" // commandEnvKeyForInterval is the key for command argument or environment configuring default interval duration for timer.
|
commandEnvKeyForInterval = "gf.gtimer.interval" // commandEnvKeyForInterval is the key for command argument or environment configuring default interval duration for timer.
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -154,11 +157,3 @@ func DelayAddOnce(ctx context.Context, delay time.Duration, interval time.Durati
|
|||||||
func DelayAddTimes(ctx context.Context, delay time.Duration, interval time.Duration, times int, job JobFunc) {
|
func DelayAddTimes(ctx context.Context, delay time.Duration, interval time.Duration, times int, job JobFunc) {
|
||||||
defaultTimer.DelayAddTimes(ctx, delay, interval, times, job)
|
defaultTimer.DelayAddTimes(ctx, delay, interval, times, job)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exit is used in timing job internally, which exits and marks it closed from timer.
|
|
||||||
// The timing job will be automatically removed from timer later. It uses "panic-recover"
|
|
||||||
// mechanism internally implementing this feature, which is designed for simplification
|
|
||||||
// and convenience.
|
|
||||||
func Exit() {
|
|
||||||
panic(panicExit)
|
|
||||||
}
|
|
||||||
|
15
os/gtimer/gtimer_exit.go
Normal file
15
os/gtimer/gtimer_exit.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package gtimer
|
||||||
|
|
||||||
|
// Exit is used in timing job internally, which exits and marks it closed from timer.
|
||||||
|
// The timing job will be automatically removed from timer later. It uses "panic-recover"
|
||||||
|
// mechanism internally implementing this feature, which is designed for simplification
|
||||||
|
// and convenience.
|
||||||
|
func Exit() {
|
||||||
|
panic(panicExit)
|
||||||
|
}
|
@ -20,7 +20,7 @@ import (
|
|||||||
|
|
||||||
func TestJob_Start_Stop_Close(t *testing.T) {
|
func TestJob_Start_Stop_Close(t *testing.T) {
|
||||||
gtest.C(t, func(t *gtest.T) {
|
gtest.C(t, func(t *gtest.T) {
|
||||||
timer := New()
|
timer := gtimer.New()
|
||||||
array := garray.New(true)
|
array := garray.New(true)
|
||||||
job := timer.Add(ctx, 200*time.Millisecond, func(ctx context.Context) {
|
job := timer.Add(ctx, 200*time.Millisecond, func(ctx context.Context) {
|
||||||
array.Append(1)
|
array.Append(1)
|
||||||
@ -43,7 +43,7 @@ func TestJob_Start_Stop_Close(t *testing.T) {
|
|||||||
|
|
||||||
func TestJob_Singleton(t *testing.T) {
|
func TestJob_Singleton(t *testing.T) {
|
||||||
gtest.C(t, func(t *gtest.T) {
|
gtest.C(t, func(t *gtest.T) {
|
||||||
timer := New()
|
timer := gtimer.New()
|
||||||
array := garray.New(true)
|
array := garray.New(true)
|
||||||
job := timer.Add(ctx, 200*time.Millisecond, func(ctx context.Context) {
|
job := timer.Add(ctx, 200*time.Millisecond, func(ctx context.Context) {
|
||||||
array.Append(1)
|
array.Append(1)
|
||||||
@ -62,7 +62,7 @@ func TestJob_Singleton(t *testing.T) {
|
|||||||
|
|
||||||
func TestJob_SetTimes(t *testing.T) {
|
func TestJob_SetTimes(t *testing.T) {
|
||||||
gtest.C(t, func(t *gtest.T) {
|
gtest.C(t, func(t *gtest.T) {
|
||||||
timer := New()
|
timer := gtimer.New()
|
||||||
array := garray.New(true)
|
array := garray.New(true)
|
||||||
job := timer.Add(ctx, 200*time.Millisecond, func(ctx context.Context) {
|
job := timer.Add(ctx, 200*time.Millisecond, func(ctx context.Context) {
|
||||||
array.Append(1)
|
array.Append(1)
|
||||||
@ -76,7 +76,7 @@ func TestJob_SetTimes(t *testing.T) {
|
|||||||
|
|
||||||
func TestJob_Run(t *testing.T) {
|
func TestJob_Run(t *testing.T) {
|
||||||
gtest.C(t, func(t *gtest.T) {
|
gtest.C(t, func(t *gtest.T) {
|
||||||
timer := New()
|
timer := gtimer.New()
|
||||||
array := garray.New(true)
|
array := garray.New(true)
|
||||||
job := timer.Add(ctx, 1000*time.Millisecond, func(ctx context.Context) {
|
job := timer.Add(ctx, 1000*time.Millisecond, func(ctx context.Context) {
|
||||||
array.Append(1)
|
array.Append(1)
|
||||||
|
@ -18,13 +18,9 @@ import (
|
|||||||
"github.com/gogf/gf/v2/test/gtest"
|
"github.com/gogf/gf/v2/test/gtest"
|
||||||
)
|
)
|
||||||
|
|
||||||
func New() *gtimer.Timer {
|
|
||||||
return gtimer.New()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTimer_Add_Close(t *testing.T) {
|
func TestTimer_Add_Close(t *testing.T) {
|
||||||
gtest.C(t, func(t *gtest.T) {
|
gtest.C(t, func(t *gtest.T) {
|
||||||
timer := New()
|
timer := gtimer.New()
|
||||||
array := garray.New(true)
|
array := garray.New(true)
|
||||||
//fmt.Println("start", time.Now())
|
//fmt.Println("start", time.Now())
|
||||||
timer.Add(ctx, 200*time.Millisecond, func(ctx context.Context) {
|
timer.Add(ctx, 200*time.Millisecond, func(ctx context.Context) {
|
||||||
@ -53,7 +49,7 @@ func TestTimer_Add_Close(t *testing.T) {
|
|||||||
|
|
||||||
func TestTimer_Start_Stop_Close(t *testing.T) {
|
func TestTimer_Start_Stop_Close(t *testing.T) {
|
||||||
gtest.C(t, func(t *gtest.T) {
|
gtest.C(t, func(t *gtest.T) {
|
||||||
timer := New()
|
timer := gtimer.New()
|
||||||
array := garray.New(true)
|
array := garray.New(true)
|
||||||
timer.Add(ctx, 1000*time.Millisecond, func(ctx context.Context) {
|
timer.Add(ctx, 1000*time.Millisecond, func(ctx context.Context) {
|
||||||
array.Append(1)
|
array.Append(1)
|
||||||
@ -75,7 +71,7 @@ func TestTimer_Start_Stop_Close(t *testing.T) {
|
|||||||
|
|
||||||
func TestJob_Reset(t *testing.T) {
|
func TestJob_Reset(t *testing.T) {
|
||||||
gtest.C(t, func(t *gtest.T) {
|
gtest.C(t, func(t *gtest.T) {
|
||||||
timer := New()
|
timer := gtimer.New()
|
||||||
array := garray.New(true)
|
array := garray.New(true)
|
||||||
job := timer.AddSingleton(ctx, 500*time.Millisecond, func(ctx context.Context) {
|
job := timer.AddSingleton(ctx, 500*time.Millisecond, func(ctx context.Context) {
|
||||||
array.Append(1)
|
array.Append(1)
|
||||||
@ -93,7 +89,7 @@ func TestJob_Reset(t *testing.T) {
|
|||||||
|
|
||||||
func TestTimer_AddSingleton(t *testing.T) {
|
func TestTimer_AddSingleton(t *testing.T) {
|
||||||
gtest.C(t, func(t *gtest.T) {
|
gtest.C(t, func(t *gtest.T) {
|
||||||
timer := New()
|
timer := gtimer.New()
|
||||||
array := garray.New(true)
|
array := garray.New(true)
|
||||||
timer.AddSingleton(ctx, 200*time.Millisecond, func(ctx context.Context) {
|
timer.AddSingleton(ctx, 200*time.Millisecond, func(ctx context.Context) {
|
||||||
array.Append(1)
|
array.Append(1)
|
||||||
@ -109,7 +105,7 @@ func TestTimer_AddSingleton(t *testing.T) {
|
|||||||
|
|
||||||
func TestTimer_AddOnce(t *testing.T) {
|
func TestTimer_AddOnce(t *testing.T) {
|
||||||
gtest.C(t, func(t *gtest.T) {
|
gtest.C(t, func(t *gtest.T) {
|
||||||
timer := New()
|
timer := gtimer.New()
|
||||||
array := garray.New(true)
|
array := garray.New(true)
|
||||||
timer.AddOnce(ctx, 200*time.Millisecond, func(ctx context.Context) {
|
timer.AddOnce(ctx, 200*time.Millisecond, func(ctx context.Context) {
|
||||||
array.Append(1)
|
array.Append(1)
|
||||||
@ -131,7 +127,7 @@ func TestTimer_AddOnce(t *testing.T) {
|
|||||||
|
|
||||||
func TestTimer_AddTimes(t *testing.T) {
|
func TestTimer_AddTimes(t *testing.T) {
|
||||||
gtest.C(t, func(t *gtest.T) {
|
gtest.C(t, func(t *gtest.T) {
|
||||||
timer := New()
|
timer := gtimer.New()
|
||||||
array := garray.New(true)
|
array := garray.New(true)
|
||||||
timer.AddTimes(ctx, 200*time.Millisecond, 2, func(ctx context.Context) {
|
timer.AddTimes(ctx, 200*time.Millisecond, 2, func(ctx context.Context) {
|
||||||
array.Append(1)
|
array.Append(1)
|
||||||
@ -143,7 +139,7 @@ func TestTimer_AddTimes(t *testing.T) {
|
|||||||
|
|
||||||
func TestTimer_DelayAdd(t *testing.T) {
|
func TestTimer_DelayAdd(t *testing.T) {
|
||||||
gtest.C(t, func(t *gtest.T) {
|
gtest.C(t, func(t *gtest.T) {
|
||||||
timer := New()
|
timer := gtimer.New()
|
||||||
array := garray.New(true)
|
array := garray.New(true)
|
||||||
timer.DelayAdd(ctx, 200*time.Millisecond, 200*time.Millisecond, func(ctx context.Context) {
|
timer.DelayAdd(ctx, 200*time.Millisecond, 200*time.Millisecond, func(ctx context.Context) {
|
||||||
array.Append(1)
|
array.Append(1)
|
||||||
@ -157,7 +153,7 @@ func TestTimer_DelayAdd(t *testing.T) {
|
|||||||
|
|
||||||
func TestTimer_DelayAddJob(t *testing.T) {
|
func TestTimer_DelayAddJob(t *testing.T) {
|
||||||
gtest.C(t, func(t *gtest.T) {
|
gtest.C(t, func(t *gtest.T) {
|
||||||
timer := New()
|
timer := gtimer.New()
|
||||||
array := garray.New(true)
|
array := garray.New(true)
|
||||||
timer.DelayAddEntry(ctx, 200*time.Millisecond, 200*time.Millisecond, func(ctx context.Context) {
|
timer.DelayAddEntry(ctx, 200*time.Millisecond, 200*time.Millisecond, func(ctx context.Context) {
|
||||||
array.Append(1)
|
array.Append(1)
|
||||||
@ -171,7 +167,7 @@ func TestTimer_DelayAddJob(t *testing.T) {
|
|||||||
|
|
||||||
func TestTimer_DelayAddSingleton(t *testing.T) {
|
func TestTimer_DelayAddSingleton(t *testing.T) {
|
||||||
gtest.C(t, func(t *gtest.T) {
|
gtest.C(t, func(t *gtest.T) {
|
||||||
timer := New()
|
timer := gtimer.New()
|
||||||
array := garray.New(true)
|
array := garray.New(true)
|
||||||
timer.DelayAddSingleton(ctx, 200*time.Millisecond, 200*time.Millisecond, func(ctx context.Context) {
|
timer.DelayAddSingleton(ctx, 200*time.Millisecond, 200*time.Millisecond, func(ctx context.Context) {
|
||||||
array.Append(1)
|
array.Append(1)
|
||||||
@ -187,7 +183,7 @@ func TestTimer_DelayAddSingleton(t *testing.T) {
|
|||||||
|
|
||||||
func TestTimer_DelayAddOnce(t *testing.T) {
|
func TestTimer_DelayAddOnce(t *testing.T) {
|
||||||
gtest.C(t, func(t *gtest.T) {
|
gtest.C(t, func(t *gtest.T) {
|
||||||
timer := New()
|
timer := gtimer.New()
|
||||||
array := garray.New(true)
|
array := garray.New(true)
|
||||||
timer.DelayAddOnce(ctx, 200*time.Millisecond, 200*time.Millisecond, func(ctx context.Context) {
|
timer.DelayAddOnce(ctx, 200*time.Millisecond, 200*time.Millisecond, func(ctx context.Context) {
|
||||||
array.Append(1)
|
array.Append(1)
|
||||||
@ -205,7 +201,7 @@ func TestTimer_DelayAddOnce(t *testing.T) {
|
|||||||
|
|
||||||
func TestTimer_DelayAddTimes(t *testing.T) {
|
func TestTimer_DelayAddTimes(t *testing.T) {
|
||||||
gtest.C(t, func(t *gtest.T) {
|
gtest.C(t, func(t *gtest.T) {
|
||||||
timer := New()
|
timer := gtimer.New()
|
||||||
array := garray.New(true)
|
array := garray.New(true)
|
||||||
timer.DelayAddTimes(ctx, 200*time.Millisecond, 500*time.Millisecond, 2, func(ctx context.Context) {
|
timer.DelayAddTimes(ctx, 200*time.Millisecond, 500*time.Millisecond, 2, func(ctx context.Context) {
|
||||||
array.Append(1)
|
array.Append(1)
|
||||||
@ -246,24 +242,21 @@ func TestTimer_AddLessThanInterval(t *testing.T) {
|
|||||||
|
|
||||||
func TestTimer_AddLeveledJob1(t *testing.T) {
|
func TestTimer_AddLeveledJob1(t *testing.T) {
|
||||||
gtest.C(t, func(t *gtest.T) {
|
gtest.C(t, func(t *gtest.T) {
|
||||||
timer := New()
|
timer := gtimer.New()
|
||||||
array := garray.New(true)
|
array := garray.New(true)
|
||||||
//glog.Print("start")
|
|
||||||
timer.DelayAdd(ctx, 1000*time.Millisecond, 1000*time.Millisecond, func(ctx context.Context) {
|
timer.DelayAdd(ctx, 1000*time.Millisecond, 1000*time.Millisecond, func(ctx context.Context) {
|
||||||
//glog.Print("add")
|
|
||||||
array.Append(1)
|
array.Append(1)
|
||||||
})
|
})
|
||||||
time.Sleep(1500 * time.Millisecond)
|
time.Sleep(1500 * time.Millisecond)
|
||||||
t.Assert(array.Len(), 0)
|
t.Assert(array.Len(), 0)
|
||||||
time.Sleep(1300 * time.Millisecond)
|
time.Sleep(1300 * time.Millisecond)
|
||||||
//glog.Print("check")
|
|
||||||
t.Assert(array.Len(), 1)
|
t.Assert(array.Len(), 1)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTimer_Exit(t *testing.T) {
|
func TestTimer_Exit(t *testing.T) {
|
||||||
gtest.C(t, func(t *gtest.T) {
|
gtest.C(t, func(t *gtest.T) {
|
||||||
timer := New()
|
timer := gtimer.New()
|
||||||
array := garray.New(true)
|
array := garray.New(true)
|
||||||
timer.Add(ctx, 200*time.Millisecond, func(ctx context.Context) {
|
timer.Add(ctx, 200*time.Millisecond, func(ctx context.Context) {
|
||||||
array.Append(1)
|
array.Append(1)
|
||||||
|
Loading…
Reference in New Issue
Block a user