Repository: syncthing/syncthing
File: lib/model/service_map.go (Go)

Code before:
// Copyright (C) 2023 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.

package model

import (
	"context"
	"fmt"
	"time"

	"github.com/syncthing/syncthing/lib/svcutil"
	"github.com/thejerf/suture/v4"
)

// A serviceMap is a utility map of arbitrary keys to a suture.Service of
// some kind, where adding and removing services ensures they are properly
// started and stopped on the given Supervisor. The serviceMap is itself a
// suture.Service and should be added to a Supervisor.
type serviceMap[K comparable, S suture.Service] struct {
	services   map[K]S
	tokens     map[K]suture.ServiceToken
	supervisor *suture.Supervisor
}

func newServiceMap[K comparable, S suture.Service]() *serviceMap[K, S] {
	m := &serviceMap[K, S]{
		services: make(map[K]S),
		tokens:   make(map[K]suture.ServiceToken),
	}
	m.supervisor = suture.New(m.String(), svcutil.SpecWithDebugLogger(l))
	return m
}

// Add adds a service to the map, starting it on the supervisor. If there is
// already a service at the given key, it is removed first.
func (s *serviceMap[K, S]) Add(k K, v S) {
	if tok, ok := s.tokens[k]; ok {
		// There is already a service at this key, remove it first.
		s.supervisor.Remove(tok)
	}
	s.services[k] = v
	s.tokens[k] = s.supervisor.Add(v)
}


Review comment (suggestion):
```suggestion
// suture.Service and should be added to a Supervisor.
// Not safe for concurrent use.
```

Code after:
// Copyright (C) 2023 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.

package model

import (
	"context"
	"fmt"
	"time"

	"github.com/syncthing/syncthing/lib/events"
	"github.com/syncthing/syncthing/lib/svcutil"
	"github.com/thejerf/suture/v4"
)

// A serviceMap is a utility map of arbitrary keys to a suture.Service of
// some kind, where adding and removing services ensures they are properly
// started and stopped on the given Supervisor. The serviceMap is itself a
// suture.Service and should be added to a Supervisor.
// Not safe for concurrent use.
type serviceMap[K comparable, S suture.Service] struct {
	services    map[K]S
	tokens      map[K]suture.ServiceToken
	supervisor  *suture.Supervisor
	eventLogger events.Logger
}

func newServiceMap[K comparable, S suture.Service](eventLogger events.Logger) *serviceMap[K, S] {
	m := &serviceMap[K, S]{
		services:    make(map[K]S),
		tokens:      make(map[K]suture.ServiceToken),
		eventLogger: eventLogger,
	}
	m.supervisor = suture.New(m.String(), svcutil.SpecWithDebugLogger(l))
	return m
}

// Add adds a service to the map, starting it on the supervisor. If there is
// already a service at the given key, it is removed first.
func (s *serviceMap[K, S]) Add(k K, v S) {
	if tok, ok := s.tokens[k]; ok {
		// There is already a service at this key, remove it first.
		s.supervisor.Remove(tok)
		s.eventLogger.Log(events.Failure, fmt.Sprintf("%s replaced service at key %v", s, k))