code: func (s *Simulation) Close() {
  	close(s.done)
  
  	sem := make(chan struct{}, maxParallelCleanups)
  	s.mu.RLock()
  	cleanupFuncs := make([]func(), len(s.cleanupFuncs))
  	for i, f := range s.cleanupFuncs {
  		if f != nil {
  			cleanupFuncs[i] = f
  		}
  	}
  	s.mu.RUnlock()
  	var cleanupWG sync.WaitGroup
  	for _, cleanup := range cleanupFuncs {
  		cleanupWG.Add(1)
  		sem <- struct{}{}
  		go func(cleanup func()) {
  			defer cleanupWG.Done()
  			defer func() { <-sem }()
  
  			cleanup()
  		}(cleanup)
  	}
  	cleanupWG.Wait()
  
  	if s.httpSrv != nil {
  		ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
  		defer cancel()
  		err := s.httpSrv.Shutdown(ctx)
  		if err != nil {
  			log.Error("Error shutting down HTTP server!", "err", err)
  		}
  		close(s.runC)
  	}
  
  	s.shutdownWG.Wait()
  	s.Net.Shutdown()
  }
docs: // Close calls all cleanup functions that are returned by
  // ServiceFunc, waits for all of them to finish and other
  // functions that explicitly block shutdownWG
  // (like Simulation.PeerEvents) and shuts down the network
  // at the end. It is used to clean all resources from the
  // simulation.