github.com/daeglee/go-ethereum@v0.0.0-20190504220456-cad3e8d18e9b/swarm/network/simulation/simulation.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package simulation 18 19 import ( 20 "context" 21 "errors" 22 "net/http" 23 "sync" 24 "time" 25 26 "github.com/ethereum/go-ethereum/log" 27 "github.com/ethereum/go-ethereum/node" 28 "github.com/ethereum/go-ethereum/p2p/enode" 29 "github.com/ethereum/go-ethereum/p2p/simulations" 30 "github.com/ethereum/go-ethereum/p2p/simulations/adapters" 31 "github.com/ethereum/go-ethereum/swarm/network" 32 ) 33 34 // Common errors that are returned by functions in this package. 35 var ( 36 ErrNodeNotFound = errors.New("node not found") 37 ) 38 39 // Simulation provides methods on network, nodes and services 40 // to manage them. 41 type Simulation struct { 42 // Net is exposed as a way to access lower level functionalities 43 // of p2p/simulations.Network. 44 Net *simulations.Network 45 46 serviceNames []string 47 cleanupFuncs []func() 48 buckets map[enode.ID]*sync.Map 49 shutdownWG sync.WaitGroup 50 done chan struct{} 51 mu sync.RWMutex 52 neighbourhoodSize int 53 54 httpSrv *http.Server //attach a HTTP server via SimulationOptions 55 handler *simulations.Server //HTTP handler for the server 56 runC chan struct{} //channel where frontend signals it is ready 57 } 58 59 // ServiceFunc is used in New to declare new service constructor. 60 // The first argument provides ServiceContext from the adapters package 61 // giving for example the access to NodeID. Second argument is the sync.Map 62 // where all "global" state related to the service should be kept. 63 // All cleanups needed for constructed service and any other constructed 64 // objects should ne provided in a single returned cleanup function. 65 // Returned cleanup function will be called by Close function 66 // after network shutdown. 67 type ServiceFunc func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) 68 69 // New creates a new simulation instance 70 // Services map must have unique keys as service names and 71 // every ServiceFunc must return a node.Service of the unique type. 72 // This restriction is required by node.Node.Start() function 73 // which is used to start node.Service returned by ServiceFunc. 74 func New(services map[string]ServiceFunc) (s *Simulation) { 75 s = &Simulation{ 76 buckets: make(map[enode.ID]*sync.Map), 77 done: make(chan struct{}), 78 neighbourhoodSize: network.NewKadParams().NeighbourhoodSize, 79 } 80 81 adapterServices := make(map[string]adapters.ServiceFunc, len(services)) 82 for name, serviceFunc := range services { 83 // Scope this variables correctly 84 // as they will be in the adapterServices[name] function accessed later. 85 name, serviceFunc := name, serviceFunc 86 s.serviceNames = append(s.serviceNames, name) 87 adapterServices[name] = func(ctx *adapters.ServiceContext) (node.Service, error) { 88 s.mu.Lock() 89 defer s.mu.Unlock() 90 b, ok := s.buckets[ctx.Config.ID] 91 if !ok { 92 b = new(sync.Map) 93 } 94 service, cleanup, err := serviceFunc(ctx, b) 95 if err != nil { 96 return nil, err 97 } 98 if cleanup != nil { 99 s.cleanupFuncs = append(s.cleanupFuncs, cleanup) 100 } 101 s.buckets[ctx.Config.ID] = b 102 return service, nil 103 } 104 } 105 106 s.Net = simulations.NewNetwork( 107 adapters.NewTCPAdapter(adapterServices), 108 &simulations.NetworkConfig{ID: "0"}, 109 ) 110 111 return s 112 } 113 114 // RunFunc is the function that will be called 115 // on Simulation.Run method call. 116 type RunFunc func(context.Context, *Simulation) error 117 118 // Result is the returned value of Simulation.Run method. 119 type Result struct { 120 Duration time.Duration 121 Error error 122 } 123 124 // Run calls the RunFunc function while taking care of 125 // cancellation provided through the Context. 126 func (s *Simulation) Run(ctx context.Context, f RunFunc) (r Result) { 127 //if the option is set to run a HTTP server with the simulation, 128 //init the server and start it 129 start := time.Now() 130 if s.httpSrv != nil { 131 log.Info("Waiting for frontend to be ready...(send POST /runsim to HTTP server)") 132 //wait for the frontend to connect 133 select { 134 case <-s.runC: 135 case <-ctx.Done(): 136 return Result{ 137 Duration: time.Since(start), 138 Error: ctx.Err(), 139 } 140 } 141 log.Info("Received signal from frontend - starting simulation run.") 142 } 143 errc := make(chan error) 144 quit := make(chan struct{}) 145 defer close(quit) 146 go func() { 147 select { 148 case errc <- f(ctx, s): 149 case <-quit: 150 } 151 }() 152 var err error 153 select { 154 case <-ctx.Done(): 155 err = ctx.Err() 156 case err = <-errc: 157 } 158 return Result{ 159 Duration: time.Since(start), 160 Error: err, 161 } 162 } 163 164 // Maximal number of parallel calls to cleanup functions on 165 // Simulation.Close. 166 var maxParallelCleanups = 10 167 168 // Close calls all cleanup functions that are returned by 169 // ServiceFunc, waits for all of them to finish and other 170 // functions that explicitly block shutdownWG 171 // (like Simulation.PeerEvents) and shuts down the network 172 // at the end. It is used to clean all resources from the 173 // simulation. 174 func (s *Simulation) Close() { 175 close(s.done) 176 177 sem := make(chan struct{}, maxParallelCleanups) 178 s.mu.RLock() 179 cleanupFuncs := make([]func(), len(s.cleanupFuncs)) 180 for i, f := range s.cleanupFuncs { 181 if f != nil { 182 cleanupFuncs[i] = f 183 } 184 } 185 s.mu.RUnlock() 186 var cleanupWG sync.WaitGroup 187 for _, cleanup := range cleanupFuncs { 188 cleanupWG.Add(1) 189 sem <- struct{}{} 190 go func(cleanup func()) { 191 defer cleanupWG.Done() 192 defer func() { <-sem }() 193 194 cleanup() 195 }(cleanup) 196 } 197 cleanupWG.Wait() 198 199 if s.httpSrv != nil { 200 ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) 201 defer cancel() 202 err := s.httpSrv.Shutdown(ctx) 203 if err != nil { 204 log.Error("Error shutting down HTTP server!", "err", err) 205 } 206 close(s.runC) 207 } 208 209 s.shutdownWG.Wait() 210 s.Net.Shutdown() 211 } 212 213 // Done returns a channel that is closed when the simulation 214 // is closed by Close method. It is useful for signaling termination 215 // of all possible goroutines that are created within the test. 216 func (s *Simulation) Done() <-chan struct{} { 217 return s.done 218 }