github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/swarm/network/simulation/simulation.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 12:09:48</date> 10 //</624342674442293248> 11 12 // 13 // 14 // 15 // 16 // 17 // 18 // 19 // 20 // 21 // 22 // 23 // 24 // 25 // 26 // 27 28 package simulation 29 30 import ( 31 "context" 32 "errors" 33 "net/http" 34 "sync" 35 "time" 36 37 "github.com/ethereum/go-ethereum/log" 38 "github.com/ethereum/go-ethereum/node" 39 "github.com/ethereum/go-ethereum/p2p/discover" 40 "github.com/ethereum/go-ethereum/p2p/simulations" 41 "github.com/ethereum/go-ethereum/p2p/simulations/adapters" 42 ) 43 44 // 45 var ( 46 ErrNodeNotFound = errors.New("node not found") 47 ErrNoPivotNode = errors.New("no pivot node set") 48 ) 49 50 // 51 // 52 type Simulation struct { 53 // 54 // 55 Net *simulations.Network 56 57 serviceNames []string 58 cleanupFuncs []func() 59 buckets map[discover.NodeID]*sync.Map 60 pivotNodeID *discover.NodeID 61 shutdownWG sync.WaitGroup 62 done chan struct{} 63 mu sync.RWMutex 64 65 httpSrv *http.Server // 66 handler *simulations.Server // 67 runC chan struct{} // 68 } 69 70 // 71 // 72 // 73 // 74 // 75 // 76 // 77 // 78 type ServiceFunc func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) 79 80 // 81 // 82 func New(services map[string]ServiceFunc) (s *Simulation) { 83 s = &Simulation{ 84 buckets: make(map[discover.NodeID]*sync.Map), 85 done: make(chan struct{}), 86 } 87 88 adapterServices := make(map[string]adapters.ServiceFunc, len(services)) 89 for name, serviceFunc := range services { 90 s.serviceNames = append(s.serviceNames, name) 91 adapterServices[name] = func(ctx *adapters.ServiceContext) (node.Service, error) { 92 b := new(sync.Map) 93 service, cleanup, err := serviceFunc(ctx, b) 94 if err != nil { 95 return nil, err 96 } 97 s.mu.Lock() 98 defer s.mu.Unlock() 99 if cleanup != nil { 100 s.cleanupFuncs = append(s.cleanupFuncs, cleanup) 101 } 102 s.buckets[ctx.Config.ID] = b 103 return service, nil 104 } 105 } 106 107 s.Net = simulations.NewNetwork( 108 adapters.NewSimAdapter(adapterServices), 109 &simulations.NetworkConfig{ID: "0"}, 110 ) 111 112 return s 113 } 114 115 // 116 // 117 type RunFunc func(context.Context, *Simulation) error 118 119 // 120 type Result struct { 121 Duration time.Duration 122 Error error 123 } 124 125 // 126 // 127 func (s *Simulation) Run(ctx context.Context, f RunFunc) (r Result) { 128 // 129 // 130 start := time.Now() 131 if s.httpSrv != nil { 132 log.Info("Waiting for frontend to be ready...(send POST /runsim to HTTP server)") 133 // 134 select { 135 case <-s.runC: 136 case <-ctx.Done(): 137 return Result{ 138 Duration: time.Since(start), 139 Error: ctx.Err(), 140 } 141 } 142 log.Info("Received signal from frontend - starting simulation run.") 143 } 144 errc := make(chan error) 145 quit := make(chan struct{}) 146 defer close(quit) 147 go func() { 148 select { 149 case errc <- f(ctx, s): 150 case <-quit: 151 } 152 }() 153 var err error 154 select { 155 case <-ctx.Done(): 156 err = ctx.Err() 157 case err = <-errc: 158 } 159 return Result{ 160 Duration: time.Since(start), 161 Error: err, 162 } 163 } 164 165 // 166 // 167 var maxParallelCleanups = 10 168 169 // 170 // 171 // 172 // 173 // 174 // 175 func (s *Simulation) Close() { 176 close(s.done) 177 178 // 179 // 180 // 181 for _, c := range s.Net.Conns { 182 if c.Up { 183 s.Net.Disconnect(c.One, c.Other) 184 } 185 } 186 s.shutdownWG.Wait() 187 s.Net.Shutdown() 188 189 sem := make(chan struct{}, maxParallelCleanups) 190 s.mu.RLock() 191 cleanupFuncs := make([]func(), len(s.cleanupFuncs)) 192 for i, f := range s.cleanupFuncs { 193 if f != nil { 194 cleanupFuncs[i] = f 195 } 196 } 197 s.mu.RUnlock() 198 var cleanupWG sync.WaitGroup 199 for _, cleanup := range cleanupFuncs { 200 cleanupWG.Add(1) 201 sem <- struct{}{} 202 go func(cleanup func()) { 203 defer cleanupWG.Done() 204 defer func() { <-sem }() 205 206 cleanup() 207 }(cleanup) 208 } 209 cleanupWG.Wait() 210 211 if s.httpSrv != nil { 212 ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) 213 defer cancel() 214 err := s.httpSrv.Shutdown(ctx) 215 if err != nil { 216 log.Error("Error shutting down HTTP server!", "err", err) 217 } 218 close(s.runC) 219 } 220 } 221 222 // 223 // 224 // 225 func (s *Simulation) Done() <-chan struct{} { 226 return s.done 227 } 228