github.com/whiteboxio/flow@v0.0.3-0.20190918184116-508d75d68a2c/pkg/corev1alpha1/actor/replicator.go (about) 1 package actor 2 3 import ( 4 "fmt" 5 "math/bits" 6 "math/rand" 7 "sync" 8 "time" 9 10 core "github.com/awesome-flow/flow/pkg/corev1alpha1" 11 ) 12 13 const ( 14 MaxPeersCnt = 64 15 ReplTimeout = 50 * time.Millisecond 16 ) 17 18 type ReplicateMode uint8 19 20 const ( 21 ReplicateUnknown ReplicateMode = iota 22 ReplicateFanout 23 ReplicateRand 24 ReplicateNcopy 25 ReplicateEach 26 ) 27 28 type Replicator struct { 29 name string 30 ctx *core.Context 31 maskfunc func(uint64, int) uint64 32 queueIn chan *core.Message 33 queuesOut []chan *core.Message 34 lock sync.Mutex 35 wg sync.WaitGroup 36 } 37 38 var _ core.Actor = (*Replicator)(nil) 39 40 func NewReplicator(name string, ctx *core.Context, params core.Params) (core.Actor, error) { 41 mode, ok := params["mode"] 42 if !ok { 43 return nil, fmt.Errorf("replicator %s is missing `mode` config", name) 44 } 45 46 r := &Replicator{ 47 name: name, 48 ctx: ctx, 49 queueIn: make(chan *core.Message), 50 queuesOut: make([]chan *core.Message, 0), 51 } 52 53 var maskfunc func(uint64, int) uint64 54 switch mode.(string) { 55 case "fanout": 56 maskfunc = maskFanout 57 case "rand": 58 maskfunc = maskRand 59 case "ncopy": 60 maskfunc = maskNcopy 61 case "each": 62 maskfunc = maskEach 63 default: 64 return nil, fmt.Errorf("replicator %s `mode` is unknown: %s", name, mode.(string)) 65 } 66 67 r.maskfunc = maskfunc 68 69 return r, nil 70 } 71 72 func maskFanout(mask uint64, lenq int) uint64 { 73 if lenq == 0 { 74 return 0 75 } 76 bshift := uint64((1 << uint64(lenq)) - 1) 77 mask &= bshift 78 mask = ((mask << 1) | (mask >> (uint64(lenq) - 1))) & bshift 79 if mask == 0 { 80 mask = 1 81 } 82 return mask 83 } 84 85 func maskRand(mask uint64, lenq int) uint64 { 86 return 1 << uint64(rand.Int63n(int64(lenq))) 87 } 88 89 func maskNcopy(uint64, int) uint64 { 90 panic("not implemented") 91 } 92 93 func maskEach(mask uint64, lenq int) uint64 { 94 return (1 << uint64(lenq)) - 1 95 } 96 97 func (r *Replicator) replicate(msg *core.Message, mask uint64) error { 98 wg := sync.WaitGroup{} 99 ix := 0 100 cnt := bits.OnesCount64(mask) 101 res := make(chan core.MsgStatus, cnt) 102 defer close(res) 103 for mask > 0 { 104 if mask&0x1 == 1 { 105 wg.Add(1) 106 go func(ix int) { 107 msgcp := msg.Copy() 108 r.queuesOut[ix] <- msgcp 109 select { 110 case s := <-msgcp.AwaitChan(): 111 res <- s 112 case <-time.After(ReplTimeout): 113 msg.Complete(core.MsgStatusTimedOut) 114 res <- core.MsgStatusTimedOut 115 } 116 wg.Done() 117 }(ix) 118 } 119 ix++ 120 mask >>= 1 121 } 122 wg.Wait() 123 var compsts uint8 124 var s core.MsgStatus 125 for i := 0; i < cnt; i++ { 126 s = <-res 127 switch s { 128 case core.MsgStatusDone: 129 compsts |= 1 << 0 130 case core.MsgStatusPartialSend: 131 compsts |= 1 << 1 132 case core.MsgStatusTimedOut: 133 compsts |= 1 << 2 134 default: 135 compsts |= 1 << 3 136 } 137 } 138 if compsts <= 1 { // 0 stands for no-send (0-mask) 139 msg.Complete(core.MsgStatusDone) 140 } else if compsts>>1 == 1 { 141 msg.Complete(core.MsgStatusPartialSend) 142 } else if compsts>>2 == 1 { 143 msg.Complete(core.MsgStatusTimedOut) 144 } else { 145 msg.Complete(core.MsgStatusFailed) 146 } 147 148 return nil 149 } 150 151 func (r *Replicator) Name() string { 152 return r.name 153 } 154 155 func (r *Replicator) Start() error { 156 go func() { 157 var mask uint64 158 for msg := range r.queueIn { 159 mask = r.maskfunc(mask, len(r.queuesOut)) 160 if err := r.replicate(msg, mask); err != nil { 161 msg.Complete(core.MsgStatusFailed) 162 } 163 } 164 }() 165 166 return nil 167 } 168 169 func (r *Replicator) Stop() error { 170 r.lock.Lock() 171 defer r.lock.Unlock() 172 173 close(r.queueIn) 174 for _, q := range r.queuesOut { 175 close(q) 176 } 177 r.wg.Wait() 178 179 return nil 180 } 181 182 func (r *Replicator) Connect(nthreads int, peer core.Receiver) error { 183 r.lock.Lock() 184 defer r.lock.Unlock() 185 186 if len(r.queuesOut) >= MaxPeersCnt { 187 return fmt.Errorf("replicator has achieved the max # of connections: %d", len(r.queuesOut)) 188 } 189 190 q := make(chan *core.Message) 191 for i := 0; i < nthreads; i++ { 192 r.wg.Add(1) 193 go func() { 194 for msg := range q { 195 if err := peer.Receive(msg); err != nil { 196 msg.Complete(core.MsgStatusFailed) 197 r.ctx.Logger().Error(err.Error()) 198 } 199 } 200 r.wg.Done() 201 }() 202 } 203 r.queuesOut = append(r.queuesOut, q) 204 205 return nil 206 } 207 208 func (r *Replicator) Receive(msg *core.Message) error { 209 r.queueIn <- msg 210 211 return nil 212 }