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  }