github.com/braveheart12/just@v0.8.7/ledger/artifactmanager/hotdatawaiter.go (about) 1 /* 2 * Copyright 2019 Insolar Technologies 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package artifactmanager 18 19 import ( 20 "context" 21 "sync" 22 23 "github.com/insolar/insolar/core" 24 "github.com/insolar/insolar/instrumentation/inslogger" 25 ) 26 27 // HotDataWaiter provides waiting system for a specific jet 28 // We tend to think, that it will be used for waiting hot-data in handler 29 // Also, because of the some jet pitfalls, we need to have an instrument 30 // to handler edge-cases from pulse manager. 31 // The main case is when a light material executes a jet for more then 1 pulse 32 // If it happens, we need to stop waiters from raising and waiting 33 //go:generate minimock -i github.com/insolar/insolar/ledger/storage.HotDataWaiter -o ./ -s _mock.go 34 type HotDataWaiter interface { 35 Wait(ctx context.Context, jetID core.RecordID) error 36 Unlock(ctx context.Context, jetID core.RecordID) error 37 ThrowTimeout(ctx context.Context) 38 } 39 40 // HotDataWaiterConcrete is an implementation of HotDataWaiter 41 type HotDataWaiterConcrete struct { 42 lock sync.Mutex 43 waiters map[core.RecordID]waiter 44 timeout chan struct{} 45 } 46 47 type waiter chan struct{} 48 49 func (w waiter) isClosed() bool { 50 select { 51 case <-w: 52 return true 53 default: 54 } 55 return false 56 } 57 58 // NewHotDataWaiterConcrete is a constructor 59 func NewHotDataWaiterConcrete() *HotDataWaiterConcrete { 60 return &HotDataWaiterConcrete{ 61 waiters: map[core.RecordID]waiter{}, 62 timeout: make(chan struct{}), 63 } 64 } 65 66 func (w *HotDataWaiterConcrete) waiterForJet(jetID core.RecordID) waiter { 67 if _, ok := w.waiters[jetID]; !ok { 68 w.waiters[jetID] = make(waiter) 69 } 70 return w.waiters[jetID] 71 } 72 73 // Wait waits for the raising one of two channels. 74 // If hotDataChannel or timeoutChannel was raised, the method returns error 75 // Either nil or ErrHotDataTimeout 76 func (w *HotDataWaiterConcrete) Wait(ctx context.Context, jetID core.RecordID) error { 77 w.lock.Lock() 78 waiter := w.waiterForJet(jetID) 79 timeout := w.timeout 80 w.lock.Unlock() 81 82 select { 83 case <-waiter: 84 return nil 85 case <-timeout: 86 return core.ErrHotDataTimeout 87 } 88 } 89 90 // Unlock raises hotDataChannel 91 func (w *HotDataWaiterConcrete) Unlock(ctx context.Context, jetID core.RecordID) error { 92 w.lock.Lock() 93 defer w.lock.Unlock() 94 95 waiter := w.waiterForJet(jetID) 96 if waiter.isClosed() { 97 return ErrWaiterNotLocked 98 } 99 close(waiter) 100 return nil 101 } 102 103 // ThrowTimeout raises all timeoutChannel 104 func (w *HotDataWaiterConcrete) ThrowTimeout(ctx context.Context) { 105 w.lock.Lock() 106 defer w.lock.Unlock() 107 108 inslogger.FromContext(ctx).Debug("raising timeout for requests") 109 close(w.timeout) 110 w.timeout = make(chan struct{}) 111 w.waiters = map[core.RecordID]waiter{} 112 }