// ------------------------------------------------------------------------ // Project atila // Active Thing (activething.com) git.activething.com/go // // File name rkey.go // Created by DEV // Modified 05/02/2024 // // Copyright 2024 activething.com // ------------------------------------------------------------------------ // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ------------------------------------------------------------------------ package times import ( "activething.com/go/gogo/core/contexts" "context" "sync/atomic" "time" ) type ( Pulsar struct { state uint32 pulse int64 err atomic.Value cancel contexts.Cancel workFn PulsarWorkFnc } PulsarWorkFnc func(ctx context.Context) error ) func NewPulsar(pulse time.Duration, work PulsarWorkFnc) *Pulsar { return &Pulsar{ state: uint32(TimerStateCreated), cancel: *contexts.NewCancel(), pulse: int64(pulse), workFn: work, } } func (p *Pulsar) State() TimerState { return TimerState(atomic.LoadUint32(&p.state)) } func (p *Pulsar) Pulse() time.Duration { return time.Duration(atomic.LoadInt64(&p.pulse)) } func (p *Pulsar) Err() error { if v := p.err.Load(); v != nil { return v.(error) } return nil } func (p *Pulsar) Start(ctx context.Context) error { if !atomic.CompareAndSwapUint32(&p.state,uint32(TimerStateCreated),uint32(TimerStateStarted)) { return G.ErrAlreadyStarted } to := atomic.LoadInt64(&p.pulse) if to <= 0 { atomic.StoreUint32(&p.state,uint32(TimerStateExpired)) return G.ErrIntervalInvalid } cx := ctx if cx == nil { cx = context.Background() } else if ctx.Err() != nil { atomic.StoreUint32(&p.state,uint32(TimerStateCanceled)) return context.Canceled } go func(pl *Pulsar, tk *time.Ticker, cx context.Context) { EXIT: for { select { case <-cx.Done(): atomic.StoreUint32(&pl.state,uint32(TimerStateCanceled)) break EXIT case <-pl.cancel.Done(): break EXIT case <-tk.C: if TimerState(atomic.LoadUint32(&pl.state)) == TimerStateStarted { if er := pl.workFn(cx); er != nil { pl.err.Store(er) atomic.StoreUint32(&pl.state,uint32(TimerStateCanceled)) break EXIT } } } } tk.Stop() pl.cancel.Cancel() }(p, time.NewTicker(time.Duration(to)), cx) return nil } func (p *Pulsar) Stop() error { if atomic.CompareAndSwapUint32(&p.state,uint32(TimerStateStarted), uint32(TimerStateStopped)) || atomic.CompareAndSwapUint32(&p.state,uint32(TimerStatePaused), uint32(TimerStateStopped)) { p.cancel.Cancel() return nil } return G.ErrNotStarted } func (p *Pulsar) Pause() error { if atomic.CompareAndSwapUint32(&p.state,uint32(TimerStateStarted), uint32(TimerStatePaused)) { return nil } return G.ErrNotStarted } func (p *Pulsar) Continue() error { if atomic.CompareAndSwapUint32(&p.state,uint32(TimerStatePaused), uint32(TimerStateStarted)) { return nil } return G.ErrNotPaused } func (p *Pulsar) Done() <-chan struct{} { return p.cancel.Done() } func (p *Pulsar) Wait() { <-p.cancel.Done() } /* :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: I HAVE NO DESIRE TO WALK ON WATER," SAID SIDDHARTA. "LET THE OLD SHRAMANAS SATISFY THEMSELVES WITH SUCH SKILLS. SIDDHARTA - HERMANN HESSE - ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: */