revert name from Job to Entry for package gtimer/gcron

This commit is contained in:
John Guo
2021-05-26 09:55:33 +08:00
parent fab6c4048d
commit fc88001a8c
11 changed files with 130 additions and 130 deletions

View File

@ -50,7 +50,7 @@ func GetLogLevel() int {
// Add adds a timed task to default cron object.
// A unique <name> can be bound with the timed task.
// It returns and error if the <name> is already used.
func Add(pattern string, job func(), name ...string) (*Job, error) {
func Add(pattern string, job func(), name ...string) (*Entry, error) {
return defaultCron.Add(pattern, job, name...)
}
@ -58,21 +58,21 @@ func Add(pattern string, job func(), name ...string) (*Job, error) {
// A singleton timed task is that can only be running one single instance at the same time.
// A unique <name> can be bound with the timed task.
// It returns and error if the <name> is already used.
func AddSingleton(pattern string, job func(), name ...string) (*Job, error) {
func AddSingleton(pattern string, job func(), name ...string) (*Entry, error) {
return defaultCron.AddSingleton(pattern, job, name...)
}
// AddOnce adds a timed task which can be run only once, to default cron object.
// A unique <name> can be bound with the timed task.
// It returns and error if the <name> is already used.
func AddOnce(pattern string, job func(), name ...string) (*Job, error) {
func AddOnce(pattern string, job func(), name ...string) (*Entry, error) {
return defaultCron.AddOnce(pattern, job, name...)
}
// AddTimes adds a timed task which can be run specified times, to default cron object.
// A unique <name> can be bound with the timed task.
// It returns and error if the <name> is already used.
func AddTimes(pattern string, times int, job func(), name ...string) (*Job, error) {
func AddTimes(pattern string, times int, job func(), name ...string) (*Entry, error) {
return defaultCron.AddTimes(pattern, times, job, name...)
}
@ -100,7 +100,7 @@ func DelayAddTimes(delay time.Duration, pattern string, times int, job func(), n
// Search returns a scheduled task with the specified <name>.
// It returns nil if no found.
func Search(name string) *Job {
func Search(name string) *Entry {
return defaultCron.Search(name)
}
@ -115,7 +115,7 @@ func Size() int {
}
// Entries return all timed tasks as slice.
func Entries() []*Job {
func Entries() []*Entry {
return defaultCron.Entries()
}

View File

@ -60,20 +60,20 @@ func (c *Cron) GetLogLevel() int {
// Add adds a timed task.
// A unique <name> can be bound with the timed task.
// It returns and error if the <name> is already used.
func (c *Cron) Add(pattern string, job func(), name ...string) (*Job, error) {
func (c *Cron) Add(pattern string, job func(), name ...string) (*Entry, error) {
if len(name) > 0 {
if c.Search(name[0]) != nil {
return nil, errors.New(fmt.Sprintf(`cron job "%s" already exists`, name[0]))
}
}
return c.addJob(pattern, job, false, name...)
return c.addEntry(pattern, job, false, name...)
}
// AddSingleton adds a singleton timed task.
// A singleton timed task is that can only be running one single instance at the same time.
// A unique <name> can be bound with the timed task.
// It returns and error if the <name> is already used.
func (c *Cron) AddSingleton(pattern string, job func(), name ...string) (*Job, error) {
func (c *Cron) AddSingleton(pattern string, job func(), name ...string) (*Entry, error) {
if entry, err := c.Add(pattern, job, name...); err != nil {
return nil, err
} else {
@ -85,7 +85,7 @@ func (c *Cron) AddSingleton(pattern string, job func(), name ...string) (*Job, e
// AddOnce adds a timed task which can be run only once.
// A unique <name> can be bound with the timed task.
// It returns and error if the <name> is already used.
func (c *Cron) AddOnce(pattern string, job func(), name ...string) (*Job, error) {
func (c *Cron) AddOnce(pattern string, job func(), name ...string) (*Entry, error) {
if entry, err := c.Add(pattern, job, name...); err != nil {
return nil, err
} else {
@ -97,7 +97,7 @@ func (c *Cron) AddOnce(pattern string, job func(), name ...string) (*Job, error)
// AddTimes adds a timed task which can be run specified times.
// A unique <name> can be bound with the timed task.
// It returns and error if the <name> is already used.
func (c *Cron) AddTimes(pattern string, times int, job func(), name ...string) (*Job, error) {
func (c *Cron) AddTimes(pattern string, times int, job func(), name ...string) (*Entry, error) {
if entry, err := c.Add(pattern, job, name...); err != nil {
return nil, err
} else {
@ -146,9 +146,9 @@ func (c *Cron) DelayAddTimes(delay time.Duration, pattern string, times int, job
// Search returns a scheduled task with the specified <name>.
// It returns nil if no found.
func (c *Cron) Search(name string) *Job {
func (c *Cron) Search(name string) *Entry {
if v := c.entries.Get(name); v != nil {
return v.(*Job)
return v.(*Entry)
}
return nil
}
@ -182,7 +182,7 @@ func (c *Cron) Stop(name ...string) {
// Remove deletes scheduled task which named <name>.
func (c *Cron) Remove(name string) {
if v := c.entries.Get(name); v != nil {
v.(*Job).Close()
v.(*Entry).Close()
}
}
@ -197,10 +197,10 @@ func (c *Cron) Size() int {
}
// Entries return all timed tasks as slice(order by registered time asc).
func (c *Cron) Entries() []*Job {
func (c *Cron) Entries() []*Entry {
array := garray.NewSortedArraySize(c.entries.Size(), func(v1, v2 interface{}) int {
entry1 := v1.(*Job)
entry2 := v2.(*Job)
entry1 := v1.(*Entry)
entry2 := v2.(*Entry)
if entry1.Time.Nanosecond() > entry2.Time.Nanosecond() {
return 1
}
@ -208,13 +208,13 @@ func (c *Cron) Entries() []*Job {
}, true)
c.entries.RLockFunc(func(m map[string]interface{}) {
for _, v := range m {
array.Add(v.(*Job))
array.Add(v.(*Entry))
}
})
entries := make([]*Job, array.Len())
entries := make([]*Entry, array.Len())
array.RLockFunc(func(array []interface{}) {
for k, v := range array {
entries[k] = v.(*Job)
entries[k] = v.(*Entry)
}
})
return entries

View File

@ -17,29 +17,29 @@ import (
"github.com/gogf/gf/util/gconv"
)
// Timed task entry.
type Job struct {
// Entry is timing task entry.
type Entry struct {
cron *Cron // Cron object belonged to.
job *gtimer.Job // Associated gtimer.Job.
entry *gtimer.Entry // Associated gtimer.Entry.
schedule *cronSchedule // Timed schedule object.
jobName string // Callback function name(address info).
times *gtype.Int // Running times limit.
Name string // Job name.
Name string // Entry name.
Job func() `json:"-"` // Callback function.
Time time.Time // Registered time.
}
// addJob creates and returns a new Job object.
// addEntry creates and returns a new Entry object.
// Param <job> is the callback function for timed task execution.
// Param <singleton> specifies whether timed task executing in singleton mode.
// Param <name> names this entry for manual control.
func (c *Cron) addJob(pattern string, job func(), singleton bool, name ...string) (*Job, error) {
func (c *Cron) addEntry(pattern string, job func(), singleton bool, name ...string) (*Entry, error) {
schedule, err := newSchedule(pattern)
if err != nil {
return nil, err
}
// No limit for <times>, for gtimer checking scheduling every second.
entry := &Job{
entry := &Entry{
cron: c,
schedule: schedule,
jobName: runtime.FuncForPC(reflect.ValueOf(job).Pointer()).Name(),
@ -57,57 +57,57 @@ func (c *Cron) addJob(pattern string, job func(), singleton bool, name ...string
// It should start running after the entry is added to the entries map,
// to avoid the task from running during adding where the entries
// does not have the entry information, which might cause panic.
entry.job = gtimer.AddJob(time.Second, entry.check, singleton, -1, gtimer.StatusStopped)
entry.entry = gtimer.AddEntry(time.Second, entry.check, singleton, -1, gtimer.StatusStopped)
c.entries.Set(entry.Name, entry)
entry.job.Start()
entry.entry.Start()
return entry, nil
}
// IsSingleton return whether this entry is a singleton timed task.
func (entry *Job) IsSingleton() bool {
return entry.job.IsSingleton()
func (entry *Entry) IsSingleton() bool {
return entry.entry.IsSingleton()
}
// SetSingleton sets the entry running in singleton mode.
func (entry *Job) SetSingleton(enabled bool) {
entry.job.SetSingleton(true)
func (entry *Entry) SetSingleton(enabled bool) {
entry.entry.SetSingleton(true)
}
// SetTimes sets the times which the entry can run.
func (entry *Job) SetTimes(times int) {
func (entry *Entry) SetTimes(times int) {
entry.times.Set(times)
}
// Status returns the status of entry.
func (entry *Job) Status() int {
return entry.job.Status()
func (entry *Entry) Status() int {
return entry.entry.Status()
}
// SetStatus sets the status of the entry.
func (entry *Job) SetStatus(status int) int {
return entry.job.SetStatus(status)
func (entry *Entry) SetStatus(status int) int {
return entry.entry.SetStatus(status)
}
// Start starts running the entry.
func (entry *Job) Start() {
entry.job.Start()
func (entry *Entry) Start() {
entry.entry.Start()
}
// Stop stops running the entry.
func (entry *Job) Stop() {
entry.job.Stop()
func (entry *Entry) Stop() {
entry.entry.Stop()
}
// Close stops and removes the entry from cron.
func (entry *Job) Close() {
func (entry *Entry) Close() {
entry.cron.entries.Remove(entry.Name)
entry.job.Close()
entry.entry.Close()
}
// Timed task check execution.
// The running times limits feature is implemented by gcron.Job and cannot be implemented by gtimer.Job.
// gcron.Job relies on gtimer to implement a scheduled task check for gcron.Job per second.
func (entry *Job) check() {
// Timing task check execution.
// The running times limits feature is implemented by gcron.Entry and cannot be implemented by gtimer.Entry.
// gcron.Entry relies on gtimer to implement a scheduled task check for gcron.Entry per second.
func (entry *Entry) check() {
if entry.schedule.meet(time.Now()) {
path := entry.cron.GetLogPath()
level := entry.cron.GetLogLevel()
@ -125,7 +125,7 @@ func (entry *Job) check() {
// Running times check.
times := entry.times.Add(-1)
if times <= 0 {
if entry.job.SetStatus(StatusClosed) == StatusClosed || times < 0 {
if entry.entry.SetStatus(StatusClosed) == StatusClosed || times < 0 {
return
}
}
@ -139,7 +139,7 @@ func (entry *Job) check() {
} else {
glog.Path(path).Level(level).Debugf("[gcron] %s(%s) %s end", entry.Name, entry.schedule.pattern, entry.jobName)
}
if entry.job.Status() == StatusClosed {
if entry.entry.Status() == StatusClosed {
entry.Close()
}
}()

View File

@ -16,7 +16,7 @@ import (
"github.com/gogf/gf/test/gtest"
)
func TestCron_Job_Operations(t *testing.T) {
func TestCron_Entry_Operations(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
cron = gcron.New()

View File

@ -80,11 +80,11 @@ func SetInterval(interval time.Duration, job JobFunc) {
}
// Add adds a timing job to the default timer, which runs in interval of <interval>.
func Add(interval time.Duration, job JobFunc) *Job {
func Add(interval time.Duration, job JobFunc) *Entry {
return defaultTimer.Add(interval, job)
}
// AddJob adds a timing job to the default timer with detailed parameters.
// AddEntry adds a timing job to the default timer with detailed parameters.
//
// The parameter <interval> specifies the running interval of the job.
//
@ -95,22 +95,22 @@ func Add(interval time.Duration, job JobFunc) *Job {
// exits if its run times exceeds the <times>.
//
// The parameter <status> specifies the job status when it's firstly added to the timer.
func AddJob(interval time.Duration, job JobFunc, singleton bool, times int, status int) *Job {
return defaultTimer.AddJob(interval, job, singleton, times, status)
func AddEntry(interval time.Duration, job JobFunc, singleton bool, times int, status int) *Entry {
return defaultTimer.AddEntry(interval, job, singleton, times, status)
}
// AddSingleton is a convenience function for add singleton mode job.
func AddSingleton(interval time.Duration, job JobFunc) *Job {
func AddSingleton(interval time.Duration, job JobFunc) *Entry {
return defaultTimer.AddSingleton(interval, job)
}
// AddOnce is a convenience function for adding a job which only runs once and then exits.
func AddOnce(interval time.Duration, job JobFunc) *Job {
func AddOnce(interval time.Duration, job JobFunc) *Entry {
return defaultTimer.AddOnce(interval, job)
}
// AddTimes is a convenience function for adding a job which is limited running times.
func AddTimes(interval time.Duration, times int, job JobFunc) *Job {
func AddTimes(interval time.Duration, times int, job JobFunc) *Entry {
return defaultTimer.AddTimes(interval, times, job)
}
@ -120,10 +120,10 @@ func DelayAdd(delay time.Duration, interval time.Duration, job JobFunc) {
defaultTimer.DelayAdd(delay, interval, job)
}
// DelayAddJob adds a timing job after delay of <interval> duration.
// Also see AddJob.
func DelayAddJob(delay time.Duration, interval time.Duration, job JobFunc, singleton bool, times int, status int) {
defaultTimer.DelayAddJob(delay, interval, job, singleton, times, status)
// DelayAddEntry adds a timing job after delay of <interval> duration.
// Also see AddEntry.
func DelayAddEntry(delay time.Duration, interval time.Duration, job JobFunc, singleton bool, times int, status int) {
defaultTimer.DelayAddEntry(delay, interval, job, singleton, times, status)
}
// DelayAddSingleton adds a timing job after delay of <interval> duration.

View File

@ -11,8 +11,8 @@ import (
"math"
)
// Job is the timing job.
type Job struct {
// Entry is the timing job.
type Entry struct {
job JobFunc // The job function.
timer *Timer // Belonged timer.
ticks int64 // The job runs every ticks.
@ -26,21 +26,21 @@ type Job struct {
type JobFunc = func()
// Status returns the status of the job.
func (j *Job) Status() int {
return j.status.Val()
func (entry *Entry) Status() int {
return entry.status.Val()
}
// Run runs the timer job asynchronously.
func (j *Job) Run() {
leftRunningTimes := j.times.Add(-1)
func (entry *Entry) Run() {
leftRunningTimes := entry.times.Add(-1)
if leftRunningTimes < 0 {
j.status.Set(StatusClosed)
entry.status.Set(StatusClosed)
return
}
// This means it does not limit the running times.
// I know it's ugly, but it is surely high performance for running times limit.
if leftRunningTimes < 2000000000 && leftRunningTimes > 1000000000 {
j.times.Set(math.MaxInt32)
entry.times.Set(math.MaxInt32)
}
go func() {
defer func() {
@ -48,35 +48,35 @@ func (j *Job) Run() {
if err != panicExit {
panic(err)
} else {
j.Close()
entry.Close()
return
}
}
if j.Status() == StatusRunning {
j.SetStatus(StatusReady)
if entry.Status() == StatusRunning {
entry.SetStatus(StatusReady)
}
}()
j.job()
entry.job()
}()
}
// doCheckAndRunByTicks checks the if job can run in given timer ticks,
// it runs asynchronously if the given `currentTimerTicks` meets or else
// it increments its ticks and waits for next running check.
func (j *Job) doCheckAndRunByTicks(currentTimerTicks int64) {
func (entry *Entry) doCheckAndRunByTicks(currentTimerTicks int64) {
// Ticks check.
if currentTimerTicks < j.nextTicks.Val() {
if currentTimerTicks < entry.nextTicks.Val() {
return
}
j.nextTicks.Set(currentTimerTicks + j.ticks)
entry.nextTicks.Set(currentTimerTicks + entry.ticks)
// Perform job checking.
switch j.status.Val() {
switch entry.status.Val() {
case StatusRunning:
if j.IsSingleton() {
if entry.IsSingleton() {
return
}
case StatusReady:
if !j.status.Cas(StatusReady, StatusRunning) {
if !entry.status.Cas(StatusReady, StatusRunning) {
return
}
case StatusStopped:
@ -85,50 +85,50 @@ func (j *Job) doCheckAndRunByTicks(currentTimerTicks int64) {
return
}
// Perform job running.
j.Run()
entry.Run()
}
// SetStatus custom sets the status for the job.
func (j *Job) SetStatus(status int) int {
return j.status.Set(status)
func (entry *Entry) SetStatus(status int) int {
return entry.status.Set(status)
}
// Start starts the job.
func (j *Job) Start() {
j.status.Set(StatusReady)
func (entry *Entry) Start() {
entry.status.Set(StatusReady)
}
// Stop stops the job.
func (j *Job) Stop() {
j.status.Set(StatusStopped)
func (entry *Entry) Stop() {
entry.status.Set(StatusStopped)
}
// Close closes the job, and then it will be removed from the timer.
func (j *Job) Close() {
j.status.Set(StatusClosed)
func (entry *Entry) Close() {
entry.status.Set(StatusClosed)
}
// Reset reset the job, which resets its ticks for next running.
func (j *Job) Reset() {
j.nextTicks.Set(j.timer.ticks.Val() + j.ticks)
func (entry *Entry) Reset() {
entry.nextTicks.Set(entry.timer.ticks.Val() + entry.ticks)
}
// IsSingleton checks and returns whether the job in singleton mode.
func (j *Job) IsSingleton() bool {
return j.singleton.Val()
func (entry *Entry) IsSingleton() bool {
return entry.singleton.Val()
}
// SetSingleton sets the job singleton mode.
func (j *Job) SetSingleton(enabled bool) {
j.singleton.Set(enabled)
func (entry *Entry) SetSingleton(enabled bool) {
entry.singleton.Set(enabled)
}
// Job returns the job function of this job.
func (j *Job) Job() JobFunc {
return j.job
func (entry *Entry) Job() JobFunc {
return entry.job
}
// SetTimes sets the limit running times for the job.
func (j *Job) SetTimes(times int) {
j.times.Set(times)
func (entry *Entry) SetTimes(times int) {
entry.times.Set(times)
}

View File

@ -27,11 +27,11 @@ func New(options ...TimerOptions) *Timer {
}
// Add adds a timing job to the timer, which runs in interval of <interval>.
func (t *Timer) Add(interval time.Duration, job JobFunc) *Job {
return t.createJob(interval, job, false, defaultTimes, StatusReady)
func (t *Timer) Add(interval time.Duration, job JobFunc) *Entry {
return t.createEntry(interval, job, false, defaultTimes, StatusReady)
}
// AddJob adds a timing job to the timer with detailed parameters.
// AddEntry adds a timing job to the timer with detailed parameters.
//
// The parameter <interval> specifies the running interval of the job.
//
@ -42,23 +42,23 @@ func (t *Timer) Add(interval time.Duration, job JobFunc) *Job {
// exits if its run times exceeds the <times>.
//
// The parameter <status> specifies the job status when it's firstly added to the timer.
func (t *Timer) AddJob(interval time.Duration, job JobFunc, singleton bool, times int, status int) *Job {
return t.createJob(interval, job, singleton, times, status)
func (t *Timer) AddEntry(interval time.Duration, job JobFunc, singleton bool, times int, status int) *Entry {
return t.createEntry(interval, job, singleton, times, status)
}
// AddSingleton is a convenience function for add singleton mode job.
func (t *Timer) AddSingleton(interval time.Duration, job JobFunc) *Job {
return t.createJob(interval, job, true, defaultTimes, StatusReady)
func (t *Timer) AddSingleton(interval time.Duration, job JobFunc) *Entry {
return t.createEntry(interval, job, true, defaultTimes, StatusReady)
}
// AddOnce is a convenience function for adding a job which only runs once and then exits.
func (t *Timer) AddOnce(interval time.Duration, job JobFunc) *Job {
return t.createJob(interval, job, true, 1, StatusReady)
func (t *Timer) AddOnce(interval time.Duration, job JobFunc) *Entry {
return t.createEntry(interval, job, true, 1, StatusReady)
}
// AddTimes is a convenience function for adding a job which is limited running times.
func (t *Timer) AddTimes(interval time.Duration, times int, job JobFunc) *Job {
return t.createJob(interval, job, true, times, StatusReady)
func (t *Timer) AddTimes(interval time.Duration, times int, job JobFunc) *Entry {
return t.createEntry(interval, job, true, times, StatusReady)
}
// DelayAdd adds a timing job after delay of <interval> duration.
@ -69,11 +69,11 @@ func (t *Timer) DelayAdd(delay time.Duration, interval time.Duration, job JobFun
})
}
// DelayAddJob adds a timing job after delay of <interval> duration.
// Also see AddJob.
func (t *Timer) DelayAddJob(delay time.Duration, interval time.Duration, job JobFunc, singleton bool, times int, status int) {
// DelayAddEntry adds a timing job after delay of <interval> duration.
// Also see AddEntry.
func (t *Timer) DelayAddEntry(delay time.Duration, interval time.Duration, job JobFunc, singleton bool, times int, status int) {
t.AddOnce(delay, func() {
t.AddJob(interval, job, singleton, times, status)
t.AddEntry(interval, job, singleton, times, status)
})
}
@ -116,8 +116,8 @@ func (t *Timer) Close() {
t.status.Set(StatusClosed)
}
// createJob creates and adds a timing job to the timer.
func (t *Timer) createJob(interval time.Duration, job JobFunc, singleton bool, times int, status int) *Job {
// createEntry creates and adds a timing job to the timer.
func (t *Timer) createEntry(interval time.Duration, job JobFunc, singleton bool, times int, status int) *Entry {
if times <= 0 {
times = defaultTimes
}
@ -130,7 +130,7 @@ func (t *Timer) createJob(interval time.Duration, job JobFunc, singleton bool, t
intervalTicksOfJob = 1
}
nextTicks := t.ticks.Val() + intervalTicksOfJob
j := &Job{
entry := &Entry{
job: job,
timer: t,
ticks: intervalTicksOfJob,
@ -139,6 +139,6 @@ func (t *Timer) createJob(interval time.Duration, job JobFunc, singleton bool, t
singleton: gtype.NewBool(singleton),
nextTicks: gtype.NewInt64(nextTicks),
}
t.queue.Push(j, nextTicks)
return j
t.queue.Push(entry, nextTicks)
return entry
}

View File

@ -50,19 +50,19 @@ func (t *Timer) proceed(currentTimerTicks int64) {
if value == nil {
break
}
job := value.(*Job)
entry := value.(*Entry)
// It checks if it meets the ticks requirement.
if jobNextTicks := job.nextTicks.Val(); currentTimerTicks < jobNextTicks {
if jobNextTicks := entry.nextTicks.Val(); currentTimerTicks < jobNextTicks {
// It push the job back if current ticks does not meet its running ticks requirement.
t.queue.Push(job, job.nextTicks.Val())
t.queue.Push(entry, entry.nextTicks.Val())
break
}
// It checks the job running requirements and then does asynchronous running.
job.doCheckAndRunByTicks(currentTimerTicks)
entry.doCheckAndRunByTicks(currentTimerTicks)
// Status check: push back or ignore it.
if job.Status() != StatusClosed {
if entry.Status() != StatusClosed {
// It pushes the job back to queue for next running.
t.queue.Push(job, job.nextTicks.Val())
t.queue.Push(entry, entry.nextTicks.Val())
}
}
}

View File

@ -39,10 +39,10 @@ func TestSetInterval(t *testing.T) {
})
}
func TestAddJob(t *testing.T) {
func TestAddEntry(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.New(true)
gtimer.AddJob(200*time.Millisecond, func() {
gtimer.AddEntry(200*time.Millisecond, func() {
array.Append(1)
}, false, 2, gtimer.StatusReady)
time.Sleep(1100 * time.Millisecond)
@ -86,10 +86,10 @@ func TestDelayAdd(t *testing.T) {
})
}
func TestDelayAddJob(t *testing.T) {
func TestDelayAddEntry(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.New(true)
gtimer.DelayAddJob(200*time.Millisecond, 200*time.Millisecond, func() {
gtimer.DelayAddEntry(200*time.Millisecond, 200*time.Millisecond, func() {
array.Append(1)
}, false, 2, gtimer.StatusReady)
time.Sleep(300 * time.Millisecond)

View File

@ -158,7 +158,7 @@ func TestTimer_DelayAddJob(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
timer := New()
array := garray.New(true)
timer.DelayAddJob(200*time.Millisecond, 200*time.Millisecond, func() {
timer.DelayAddEntry(200*time.Millisecond, 200*time.Millisecond, func() {
array.Append(1)
}, false, 100, gtimer.StatusReady)
time.Sleep(250 * time.Millisecond)