<commit_before>package weave

import (
	"bytes"
	"fmt"
	"log"
	"math/rand"
	"time"
)

const (
	InitialInterval = 5 * time.Second
	MaxInterval     = 10 * time.Minute
	MaxAttemptCount = 100
	CMInitiate      = iota
	CMStatus        = iota
	CMEstablished   = iota
	CMConnFailed    = iota
)

const (
	CSUnconnected ConnectionState = iota
	CSAttempting                  = iota
)

func StartConnectionMaker(router *Router) *ConnectionMaker {
	queryChan := make(chan *ConnectionMakerInteraction, ChannelSize)
	state := &ConnectionMaker{
		router:    router,
		queryChan: queryChan,
		cmdLineAddress: make(map[string]bool),
		targets:   make(map[string]*Target)}
	go state.queryLoop(queryChan)
	return state
}

func (cm *ConnectionMaker) InitiateConnection(address string) {
	cm.queryChan <- &ConnectionMakerInteraction{
		Interaction: Interaction{code: CMInitiate},
		address:     address}
}

func (cm *ConnectionMaker) String() string {
	resultChan := make(chan interface{}, 0)
	cm.queryChan <- &ConnectionMakerInteraction{
		Interaction: Interaction{code: CMStatus, resultChan: resultChan}}
	result := <-resultChan
	return result.(string)
}

func (cm *ConnectionMaker) queryLoop(queryChan <-chan *ConnectionMakerInteraction) {
	var tick <-chan time.Time
	maybeTick := func() {
		// would be nice to optimise this to stop ticking when there is nothing worth trying
		if tick == nil {
			tick = time.After(5 * time.Second)
		}
	}
	tickNow := func() {
		if tick == nil {
			tick = time.After(0 * time.Second)
		}
	}
	for {
		select {
		case query, ok := <-queryChan:
			if !ok {
				return
			}
			switch {
			case query.code == CMInitiate:
				cm.cmdLineAddress[query.address] = true;
				tickNow()
			case query.code == CMStatus:
				query.resultChan <- cm.status()
			case query.code == CMConnFailed:
				target := cm.targets[query.address]
				if target != nil {
					target.state = CSUnconnected
					target.tryAfter, target.tryInterval = tryAfter(target.tryInterval)
					maybeTick()
				} else {
					log.Fatal("CMConnFailed unknown address", query.address)
				}
			default:
				log.Fatal("Unexpected connection maker query:", query)
			}
		case now := <-tick:
			cm.checkStateAndAttemptConnections(now)
			tick = nil
			maybeTick()
		}
	}
}

func (cm *ConnectionMaker) checkStateAndAttemptConnections(now time.Time) {
	ourself := cm.router.Ourself

	validTarget := make(map[string]bool)

	// Any targets that are now connected, we don't need to attempt any more
	for address, _ := range cm.targets {
		if _, found := ourself.ConnectionOn(address); found {
			//log.Println("Deleting target now connected:", address)
			delete(cm.targets, address)
		}
	}

	// Add command-line targets that are not connected
	for address, _ := range cm.cmdLineAddress {
		if _, found := ourself.ConnectionOn(address); !found {
			//log.Println("Unconnected cmdline:", address)
			cm.addToTargets(address)
			validTarget[address] = true
		}
	}

	// copy the set of peers we are connected to, so we can access it without locking
	our_connected_peers := make(map[PeerName]bool)
	ourself.ForEachConnection(func(peer PeerName, _ Connection) {
		our_connected_peers[peer] = true
	})

	// Add peers that someone else is connected to, but we aren't
	cm.router.Peers.ForEach(func(name PeerName, peer *Peer) {
		peer.ForEachConnection(func(peer2 PeerName, conn Connection) {
			if peer2 != ourself.Name && !our_connected_peers[peer2] {
				address := conn.RemoteTCPAddr()
				//log.Println("Unconnected peer:", peer2, address)
				// try both portnumber of connection and standart port
				if host, port, err := ExtractHostPort(address); err == nil {
					if port != Port {
						cm.addToTargets(address)
						validTarget[address] = true
					}
					cm.addToTargets(host)
					validTarget[host] = true
				}
			}
		})
	})

	for address, target := range cm.targets {
		if validTarget[address] {
			if target.state == CSUnconnected && now.After(target.tryAfter) {
				target.attemptCount += 1
				target.state = CSAttempting
				go cm.attemptConnection(address, cm.cmdLineAddress[address])
			}
		} else {
			//log.Println("Deleting target no longer valid:", address)
			delete(cm.targets, address)<commit_msg>Keep targets around until their attempt fails<commit_after>package weave

import (
	"bytes"
	"fmt"
	"log"
	"math/rand"
	"time"
)

const (
	InitialInterval = 5 * time.Second
	MaxInterval     = 10 * time.Minute
	MaxAttemptCount = 100
	CMInitiate      = iota
	CMStatus        = iota
	CMEstablished   = iota
	CMConnFailed    = iota
)

const (
	CSUnconnected ConnectionState = iota
	CSAttempting                  = iota
)

func StartConnectionMaker(router *Router) *ConnectionMaker {
	queryChan := make(chan *ConnectionMakerInteraction, ChannelSize)
	state := &ConnectionMaker{
		router:    router,
		queryChan: queryChan,
		cmdLineAddress: make(map[string]bool),
		targets:   make(map[string]*Target)}
	go state.queryLoop(queryChan)
	return state
}

func (cm *ConnectionMaker) InitiateConnection(address string) {
	cm.queryChan <- &ConnectionMakerInteraction{
		Interaction: Interaction{code: CMInitiate},
		address:     address}
}

func (cm *ConnectionMaker) String() string {
	resultChan := make(chan interface{}, 0)
	cm.queryChan <- &ConnectionMakerInteraction{
		Interaction: Interaction{code: CMStatus, resultChan: resultChan}}
	result := <-resultChan
	return result.(string)
}

func (cm *ConnectionMaker) queryLoop(queryChan <-chan *ConnectionMakerInteraction) {
	var tick <-chan time.Time
	maybeTick := func() {
		// would be nice to optimise this to stop ticking when there is nothing worth trying
		if tick == nil {
			tick = time.After(5 * time.Second)
		}
	}
	tickNow := func() {
		if tick == nil {
			tick = time.After(0 * time.Second)
		}
	}
	for {
		select {
		case query, ok := <-queryChan:
			if !ok {
				return
			}
			switch {
			case query.code == CMInitiate:
				cm.cmdLineAddress[query.address] = true;
				tickNow()
			case query.code == CMStatus:
				query.resultChan <- cm.status()
			case query.code == CMConnFailed:
				target := cm.targets[query.address]
				if target != nil {
					target.state = CSUnconnected
					target.tryAfter, target.tryInterval = tryAfter(target.tryInterval)
					maybeTick()
				} else {
					log.Fatal("CMConnFailed unknown address", query.address)
				}
			default:
				log.Fatal("Unexpected connection maker query:", query)
			}
		case now := <-tick:
			cm.checkStateAndAttemptConnections(now)
			tick = nil
			maybeTick()
		}
	}
}

func (cm *ConnectionMaker) checkStateAndAttemptConnections(now time.Time) {
	ourself := cm.router.Ourself

	validTarget := make(map[string]bool)

	// Any targets that are now connected, we don't need to attempt any more
	for address, _ := range cm.targets {
		if _, found := ourself.ConnectionOn(address); found {
			//log.Println("Deleting target now connected:", address)
			delete(cm.targets, address)
		}
	}

	// Add command-line targets that are not connected
	for address, _ := range cm.cmdLineAddress {
		if _, found := ourself.ConnectionOn(address); !found {
			//log.Println("Unconnected cmdline:", address)
			cm.addToTargets(address)
			validTarget[address] = true
		}
	}

	// copy the set of peers we are connected to, so we can access it without locking
	our_connected_peers := make(map[PeerName]bool)
	ourself.ForEachConnection(func(peer PeerName, _ Connection) {
		our_connected_peers[peer] = true
	})

	// Add peers that someone else is connected to, but we aren't
	cm.router.Peers.ForEach(func(name PeerName, peer *Peer) {
		peer.ForEachConnection(func(peer2 PeerName, conn Connection) {
			if peer2 != ourself.Name && !our_connected_peers[peer2] {
				address := conn.RemoteTCPAddr()
				//log.Println("Unconnected peer:", peer2, address)
				// try both portnumber of connection and standart port
				if host, port, err := ExtractHostPort(address); err == nil {
					if port != Port {
						cm.addToTargets(address)
						validTarget[address] = true
					}
					cm.addToTargets(host)
					validTarget[host] = true
				}
			}
		})
	})

	for address, target := range cm.targets {
		if target.state == CSUnconnected {
			if validTarget[address] {
				if now.After(target.tryAfter) {
					target.attemptCount += 1
					target.state = CSAttempting
					go cm.attemptConnection(address, cm.cmdLineAddress[address])
				}
			} else {
				//log.Println("Deleting target no longer valid:", address)
				delete(cm.targets, address)
			}
{'fraction_non_alphanumeric': 0.11507370423204945, 'fraction_numerical': 0.0033285782215882074, 'mean_word_length': 3.574768896139206, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3905222', 'n_tokens_mistral': 3133, 'n_tokens_neox': 2675, 'n_words': 686}