// ------------------------------------------------------------------------ // 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 ( "sync/atomic" "time" ) type ( Expiration struct { timer *time.Timer times Chrono state uint32 source uint32 timeout int64 deadline int64 expireFn func() } ) func NewExpiration(duration time.Duration, expirer func()) *Expiration { ex := &Expiration{ times: *NewChrono(false), state: uint32(TimerStateCreated), source: uint32(ExpirationSourceSelf), timeout: int64(duration), expireFn: expirer, } if duration > 0 { atomic.StoreUint32(&ex.source,uint32(ExpirationSourceTimer)) } return ex } func (e *Expiration) Reset() { e.times.Reset() atomic.StoreUint32(&e.state ,uint32(TimerStateCreated)) atomic.StoreUint32(&e.source,uint32(ExpirationSourceSelf)) atomic.StoreInt64 (&e.timeout ,0) atomic.StoreInt64 (&e.deadline,0) if e.timer != nil { if !e.timer.Stop() { select { case <- e.timer.C: default: } } } } func (e *Expiration) State() TimerState { return TimerState(atomic.LoadUint32(&e.state)) } func (e *Expiration) Chono() Chronometer { return &e.times } func (e *Expiration) Source() ExpirationSource { return ExpirationSource(atomic.LoadUint32(&e.source)) } func (e *Expiration) Timeout() time.Duration { return time.Duration(atomic.LoadInt64(&e.timeout)) } func (e *Expiration) Deadline() time.Time { return time.Unix(0,atomic.LoadInt64(&e.deadline)) } func (e *Expiration) Start(expirer Expirer) error { if !atomic.CompareAndSwapUint32(&e.state,uint32(TimerStateCreated),uint32(TimerStateStarted)) { return G.ErrAlreadyStarted } st := e.times.Start() to := time.Duration(atomic.LoadInt64(&e.timeout)) dl := st.Add(to) if expirer != nil { pd, ph := expirer.Deadline() if ph { if pd.Before(st) { atomic.StoreUint32(&e.state ,uint32(TimerStateExpired)) atomic.StoreUint32(&e.source,uint32(ExpirationSourceParent)) return G.ErrExpired } if pd.Before(dl) { atomic.StoreInt64 (&e.deadline,pd.UnixNano()) atomic.StoreUint32(&e.source ,uint32(ExpirationSourceParent)) return nil } } } if atomic.LoadUint32(&e.source) == uint32(ExpirationSourceTimer) { atomic.StoreInt64 (&e.deadline,dl.UnixNano()) if e.timer == nil { e.timer = time.NewTimer(to) } else { e.timer.Reset(to) } go func(ex *Expiration) { select { case <-ex.timer.C: atomic.StoreUint32(&ex.state,uint32(TimerStateExpired)) ex.times.Stop() if ex.expireFn != nil { ex.expireFn() } return } }(e) } return nil } func (e *Expiration) Stop() error { if !atomic.CompareAndSwapUint32(&e.state,uint32(TimerStateStarted), uint32(TimerStateStopped)) { return G.ErrNotStarted } e.times.Stop() if e.timer != nil && !e.timer.Stop() { select { case <-e.timer.C: } } return nil } /* :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: I HAVE NO DESIRE TO WALK ON WATER," SAID SIDDHARTA. "LET THE OLD SHRAMANAS SATISFY THEMSELVES WITH SUCH SKILLS. SIDDHARTA - HERMANN HESSE - ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: */