// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package unique

import (
	
	
	
	
	
	_ 
)

// Handle is a globally unique identity for some value of type T.
//
// Two handles compare equal exactly if the two values used to create the handles
// would have also compared equal. The comparison of two handles is trivial and
// typically much more efficient than comparing the values used to create them.
type Handle[ comparable] struct {
	value *
}

// Value returns a shallow copy of the T value that produced the Handle.
func ( Handle[]) ()  {
	return *.value
}

// Make returns a globally unique handle for a value of type T. Handles
// are equal if and only if the values used to produce them are equal.
func [ comparable]( ) Handle[] {
	// Find the map for type T.
	 := abi.TypeOf()
	,  := uniqueMaps.Load()
	if ! {
		// This is a good time to initialize cleanup, since we must go through
		// this path on the first use of Make, and it's not on the hot path.
		setupMake.Do(registerCleanup)
		 = addUniqueMap[]()
	}
	 := .(*uniqueMap[])

	// Keep around any values we allocate for insertion. There
	// are a few different ways we can race with other threads
	// and create values that we might discard. By keeping
	// the first one we make around, we can avoid generating
	// more than one per racing thread.
	var (
		     * // Keep this around to keep it alive.
		 weak.Pointer[]
	)
	 := func() weak.Pointer[] {
		if  == nil {
			 = new()
			* = clone(, &.cloneSeq)
			 = weak.Make()
		}
		return 
	}
	var  *
	for {
		// Check the map.
		,  := .Load()
		if ! {
			// Try to insert a new value into the map.
			, _ = .LoadOrStore(, ())
		}
		// Now that we're sure there's a value in the map, let's
		// try to get the pointer we need out of it.
		 = .Strong()
		if  != nil {
			break
		}
		// The weak pointer is nil, so the old value is truly dead.
		// Try to remove it and start over.
		.CompareAndDelete(, )
	}
	runtime.KeepAlive()
	return Handle[]{}
}

var (
	// uniqueMaps is an index of type-specific concurrent maps used for unique.Make.
	//
	// The two-level map might seem odd at first since the HashTrieMap could have "any"
	// as its key type, but the issue is escape analysis. We do not want to force lookups
	// to escape the argument, and using a type-specific map allows us to avoid that where
	// possible (for example, for strings and plain-ol'-data structs). We also get the
	// benefit of not cramming every different type into a single map, but that's certainly
	// not enough to outweigh the cost of two map lookups. What is worth it though, is saving
	// on those allocations.
	uniqueMaps = concurrent.NewHashTrieMap[*abi.Type, any]() // any is always a *uniqueMap[T].

	// cleanupFuncs are functions that clean up dead weak pointers in type-specific
	// maps in uniqueMaps. We express cleanup this way because there's no way to iterate
	// over the sync.Map and call functions on the type-specific data structures otherwise.
	// These cleanup funcs each close over one of these type-specific maps.
	//
	// cleanupMu protects cleanupNotify and is held across the entire cleanup. Used for testing.
	// cleanupNotify is a test-only mechanism that allow tests to wait for the cleanup to run.
	cleanupMu      sync.Mutex
	cleanupFuncsMu sync.Mutex
	cleanupFuncs   []func()
	cleanupNotify  []func() // One-time notifications when cleanups finish.
)

type uniqueMap[ comparable] struct {
	*concurrent.HashTrieMap[, weak.Pointer[]]
	cloneSeq
}

func addUniqueMap[ comparable]( *abi.Type) *uniqueMap[] {
	// Create a map for T and try to register it. We could
	// race with someone else, but that's fine; it's one
	// small, stray allocation. The number of allocations
	// this can create is bounded by a small constant.
	 := &uniqueMap[]{
		HashTrieMap: concurrent.NewHashTrieMap[, weak.Pointer[]](),
		cloneSeq:    makeCloneSeq(),
	}
	,  := uniqueMaps.LoadOrStore(, )
	if ! {
		// Add a cleanup function for the new map.
		cleanupFuncsMu.Lock()
		cleanupFuncs = append(cleanupFuncs, func() {
			// Delete all the entries whose weak references are nil and clean up
			// deleted entries.
			.All()(func( ,  weak.Pointer[]) bool {
				if .Strong() == nil {
					.CompareAndDelete(, )
				}
				return true
			})
		})
		cleanupFuncsMu.Unlock()
	}
	return .(*uniqueMap[])
}

// setupMake is used to perform initial setup for unique.Make.
var setupMake sync.Once

// startBackgroundCleanup sets up a background goroutine to occasionally call cleanupFuncs.
func registerCleanup() {
	runtime_registerUniqueMapCleanup(func() {
		// Lock for cleanup.
		cleanupMu.Lock()

		// Grab funcs to run.
		cleanupFuncsMu.Lock()
		 := cleanupFuncs
		cleanupFuncsMu.Unlock()

		// Run cleanup.
		for ,  := range  {
			()
		}

		// Run cleanup notifications.
		for ,  := range cleanupNotify {
			()
		}
		cleanupNotify = nil

		// Finished.
		cleanupMu.Unlock()
	})
}

// Implemented in runtime.

//go:linkname runtime_registerUniqueMapCleanup
func runtime_registerUniqueMapCleanup( func())