<commit_before>package cache

import (
	"log"
	"math/rand"
	"sync"
	"time"

	"github.com/micro/go-micro/registry"
	"github.com/micro/go-micro/selector"
)

type cacheSelector struct {
	so selector.Options

	// registry cache
	sync.Mutex
	cache map[string][]*registry.Service

	// used to close or reload watcher
	reload chan bool
	exit   chan bool
}

func init() {
	rand.Seed(time.Now().UnixNano())
}

func (c *cacheSelector) quit() bool {
	select {
	case <-c.exit:
		return true
	default:
		return false
	}
}

// cp copies a service. Because we're caching handing back pointers would
// create a race condition, so we do this instead
// its fast enough
func (c *cacheSelector) cp(current []*registry.Service) []*registry.Service {
	var services []*registry.Service

	for _, service := range current {
		// copy service
		s := new(registry.Service)
		*s = *service

		// copy nodes
		var nodes []*registry.Node
		for _, node := range service.Nodes {
			n := new(registry.Node)
			*n = *node
			nodes = append(nodes, n)
		}
		s.Nodes = nodes

		// copy endpoints
		var eps []*registry.Endpoint
		for _, ep := range service.Endpoints {
			e := new(registry.Endpoint)
			*e = *ep
			eps = append(eps, e)
		}
		s.Endpoints = eps

		// append service
		services = append(services, s)
	}

	return services
}

func (c *cacheSelector) get(service string) ([]*registry.Service, error) {
	c.Lock()
	defer c.Unlock()

	// check the cache first
	services, ok := c.cache[service]

	// got results, copy and return
	if ok && len(services) > 0 {
		return c.cp(services), nil
	}

	// now ask the registry
	services, err := c.so.Registry.GetService(service)
	if err != nil {
		return nil, err

	}

	// we didn't have any results so cache
	c.cache[service] = services

	return services, nil
}

func (c *cacheSelector) update(res *registry.Result) {
	if res == nil || res.Service == nil {
		return
	}

	c.Lock()
	defer c.Unlock()

	services, ok := c.cache[res.Service.Name]
	if !ok {
		// we're not going to cache anything
		// unless there was already a lookup
		return
	}

	if len(res.Service.Nodes) == 0 {
		switch res.Action {
		case "delete":
			delete(c.cache, res.Service.Name)
		}
		return
	}<commit_msg>Eliminate a data race by copying the services<commit_after>package cache

import (
	"log"
	"math/rand"
	"sync"
	"time"

	"github.com/micro/go-micro/registry"
	"github.com/micro/go-micro/selector"
)

type cacheSelector struct {
	so selector.Options

	// registry cache
	sync.Mutex
	cache map[string][]*registry.Service

	// used to close or reload watcher
	reload chan bool
	exit   chan bool
}

func init() {
	rand.Seed(time.Now().UnixNano())
}

func (c *cacheSelector) quit() bool {
	select {
	case <-c.exit:
		return true
	default:
		return false
	}
}

// cp copies a service. Because we're caching handing back pointers would
// create a race condition, so we do this instead
// its fast enough
func (c *cacheSelector) cp(current []*registry.Service) []*registry.Service {
	var services []*registry.Service

	for _, service := range current {
		// copy service
		s := new(registry.Service)
		*s = *service

		// copy nodes
		var nodes []*registry.Node
		for _, node := range service.Nodes {
			n := new(registry.Node)
			*n = *node
			nodes = append(nodes, n)
		}
		s.Nodes = nodes

		// copy endpoints
		var eps []*registry.Endpoint
		for _, ep := range service.Endpoints {
			e := new(registry.Endpoint)
			*e = *ep
			eps = append(eps, e)
		}
		s.Endpoints = eps

		// append service
		services = append(services, s)
	}

	return services
}

func (c *cacheSelector) get(service string) ([]*registry.Service, error) {
	c.Lock()
	defer c.Unlock()

	// check the cache first
	services, ok := c.cache[service]

	// got results, copy and return
	if ok && len(services) > 0 {
		return c.cp(services), nil
	}

	// now ask the registry
	services, err := c.so.Registry.GetService(service)
	if err != nil {
		return nil, err

	}

	// we didn't have any results so cache
	c.cache[service] = c.cp(services)

	return services, nil
}

func (c *cacheSelector) update(res *registry.Result) {
	if res == nil || res.Service == nil {
		return
	}

	c.Lock()
	defer c.Unlock()

	services, ok := c.cache[res.Service.Name]
	if !ok {
		// we're not going to cache anything
		// unless there was already a lookup
		return
	}

	if len(res.Service.Nodes) == 0 {
		switch res.Action {
		case "delete":
			delete(c.cache, res.Service.Name)
		}
		return
	}
{'fraction_non_alphanumeric': 0.13364993215739485, 'fraction_numerical': 0.0009045680687471732, 'mean_word_length': 3.1144186046511626, 'pattern_counts': {'":': 2, '<': 5, '<?xml version=': 0, '>': 5, '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': '203016', 'n_tokens_mistral': 1658, 'n_tokens_neox': 1505, 'n_words': 463}