package rcmgr

import (
	
	
	
	
	

	
)

// resources tracks the current state of resource consumption
type resources struct {
	limit Limit

	nconnsIn, nconnsOut     int
	nstreamsIn, nstreamsOut int
	nfd                     int

	memory int64
}

// A resourceScope can be a DAG, where a downstream node is not allowed to outlive an upstream node
// (ie cannot call Done in the upstream node before the downstream node) and account for resources
// using a linearized parent set.
// A resourceScope can be a span scope, where it has a specific owner; span scopes create a tree rooted
// at the owner (which can be a DAG scope) and can outlive their parents -- this is important because
// span scopes are the main *user* interface for memory management, and the user may call
// Done in a span scope after the system has closed the root of the span tree in some background
// goroutine.
// If we didn't make this distinction we would have a double release problem in that case.
type resourceScope struct {
	sync.Mutex
	done   bool
	refCnt int

	spanID int

	rc    resources
	owner *resourceScope   // set in span scopes, which define trees
	edges []*resourceScope // set in DAG scopes, it's the linearized parent set

	name    string   // for debugging purposes
	trace   *trace   // debug tracing
	metrics *metrics // metrics collection
}

var _ network.ResourceScope = (*resourceScope)(nil)
var _ network.ResourceScopeSpan = (*resourceScope)(nil)

func newResourceScope( Limit,  []*resourceScope,  string,  *trace,  *metrics) *resourceScope {
	for ,  := range  {
		.IncRef()
	}
	 := &resourceScope{
		rc:      resources{limit: },
		edges:   ,
		name:    ,
		trace:   ,
		metrics: ,
	}
	.trace.CreateScope(, )
	return 
}

func newResourceScopeSpan( *resourceScope,  int) *resourceScope {
	 := &resourceScope{
		rc:      resources{limit: .rc.limit},
		owner:   ,
		name:    fmt.Sprintf("%s.span-%d", .name, ),
		trace:   .trace,
		metrics: .metrics,
	}
	.trace.CreateScope(.name, .rc.limit)
	return 
}

// IsSpan will return true if this name was created by newResourceScopeSpan
func ( string) bool {
	return strings.Contains(, ".span-")
}

func addInt64WithOverflow( int64,  int64) ( int64,  bool) {
	 =  + 
	return , ( > ) == ( > 0)
}

// mulInt64WithOverflow checks for overflow in multiplying two int64s. See
// https://groups.google.com/g/golang-nuts/c/h5oSN5t3Au4/m/KaNQREhZh0QJ
func mulInt64WithOverflow(,  int64) ( int64,  bool) {
	const  = 1<<63 - 1
	const  = -( + 1)
	 =  * 
	if  == 0 ||  == 0 ||  == 1 ||  == 1 {
		return , true
	}
	if  ==  ||  ==  {
		return , false
	}
	return , / == 
}

// Resources implementation
func ( *resources) ( int64,  uint8) error {
	if  < 0 {
		return fmt.Errorf("can't reserve negative memory. rsvp=%v", )
	}

	 := .limit.GetMemoryLimit()
	if  == math.MaxInt64 {
		// Special case where we've set max limits.
		return nil
	}

	,  := addInt64WithOverflow(.memory, )

	,  := mulInt64WithOverflow(1+int64(), )
	if ! {
		 := big.NewInt()
		.Mul(, big.NewInt(1+int64()))
		.Rsh(, 8) // Divide 256
		// necessarily a Int64 since we multiplied a int64 != MaxInt64 with
		// a uint8+1 (max 255+1 = 256) and divided by 256
		 = .Int64()
	} else {
		 =  / 256
	}

	if ! ||  >  {
		return &ErrMemoryLimitExceeded{
			current:   .memory,
			attempted: ,
			limit:     ,
			priority:  ,
			err:       network.ErrResourceLimitExceeded,
		}
	}
	return nil
}

func ( *resources) ( int64,  uint8) error {
	if  := .checkMemory(, );  != nil {
		return 
	}

	.memory += 
	return nil
}

func ( *resources) ( int64) {
	.memory -= 

	// sanity check for bugs upstream
	if .memory < 0 {
		log.Warn("BUG: too much memory released")
		.memory = 0
	}
}

func ( *resources) ( network.Direction) error {
	if  == network.DirInbound {
		return .addStreams(1, 0)
	}
	return .addStreams(0, 1)
}

func ( *resources) (,  int) error {
	if  > 0 {
		 := .limit.GetStreamLimit(network.DirInbound)
		if .nstreamsIn+ >  {
			return &ErrStreamOrConnLimitExceeded{
				current:   .nstreamsIn,
				attempted: ,
				limit:     ,
				err:       fmt.Errorf("cannot reserve inbound stream: %w", network.ErrResourceLimitExceeded),
			}
		}
	}
	if  > 0 {
		 := .limit.GetStreamLimit(network.DirOutbound)
		if .nstreamsOut+ >  {
			return &ErrStreamOrConnLimitExceeded{
				current:   .nstreamsOut,
				attempted: ,
				limit:     ,
				err:       fmt.Errorf("cannot reserve outbound stream: %w", network.ErrResourceLimitExceeded),
			}
		}
	}

	if  := .limit.GetStreamTotalLimit(); .nstreamsIn++.nstreamsOut+ >  {
		return &ErrStreamOrConnLimitExceeded{
			current:   .nstreamsIn + .nstreamsOut,
			attempted:  + ,
			limit:     ,
			err:       fmt.Errorf("cannot reserve stream: %w", network.ErrResourceLimitExceeded),
		}
	}

	.nstreamsIn += 
	.nstreamsOut += 
	return nil
}

func ( *resources) ( network.Direction) {
	if  == network.DirInbound {
		.removeStreams(1, 0)
	} else {
		.removeStreams(0, 1)
	}
}

func ( *resources) (,  int) {
	.nstreamsIn -= 
	.nstreamsOut -= 

	if .nstreamsIn < 0 {
		log.Warn("BUG: too many inbound streams released")
		.nstreamsIn = 0
	}
	if .nstreamsOut < 0 {
		log.Warn("BUG: too many outbound streams released")
		.nstreamsOut = 0
	}
}

func ( *resources) ( network.Direction,  bool) error {
	var  int
	if  {
		 = 1
	}

	if  == network.DirInbound {
		return .addConns(1, 0, )
	}

	return .addConns(0, 1, )
}

func ( *resources) (, ,  int) error {
	if  > 0 {
		 := .limit.GetConnLimit(network.DirInbound)
		if .nconnsIn+ >  {
			return &ErrStreamOrConnLimitExceeded{
				current:   .nconnsIn,
				attempted: ,
				limit:     ,
				err:       fmt.Errorf("cannot reserve inbound connection: %w", network.ErrResourceLimitExceeded),
			}
		}
	}
	if  > 0 {
		 := .limit.GetConnLimit(network.DirOutbound)
		if .nconnsOut+ >  {
			return &ErrStreamOrConnLimitExceeded{
				current:   .nconnsOut,
				attempted: ,
				limit:     ,
				err:       fmt.Errorf("cannot reserve outbound connection: %w", network.ErrResourceLimitExceeded),
			}
		}
	}

	if  := .limit.GetConnTotalLimit(); .nconnsIn++.nconnsOut+ >  {
		return &ErrStreamOrConnLimitExceeded{
			current:   .nconnsIn + .nconnsOut,
			attempted:  + ,
			limit:     ,
			err:       fmt.Errorf("cannot reserve connection: %w", network.ErrResourceLimitExceeded),
		}
	}
	if  > 0 {
		 := .limit.GetFDLimit()
		if .nfd+ >  {
			return &ErrStreamOrConnLimitExceeded{
				current:   .nfd,
				attempted: ,
				limit:     ,
				err:       fmt.Errorf("cannot reserve file descriptor: %w", network.ErrResourceLimitExceeded),
			}
		}
	}

	.nconnsIn += 
	.nconnsOut += 
	.nfd += 
	return nil
}

func ( *resources) ( network.Direction,  bool) {
	var  int
	if  {
		 = 1
	}

	if  == network.DirInbound {
		.removeConns(1, 0, )
	} else {
		.removeConns(0, 1, )
	}
}

func ( *resources) (, ,  int) {
	.nconnsIn -= 
	.nconnsOut -= 
	.nfd -= 

	if .nconnsIn < 0 {
		log.Warn("BUG: too many inbound connections released")
		.nconnsIn = 0
	}
	if .nconnsOut < 0 {
		log.Warn("BUG: too many outbound connections released")
		.nconnsOut = 0
	}
	if .nfd < 0 {
		log.Warn("BUG: too many file descriptors released")
		.nfd = 0
	}
}

func ( *resources) () network.ScopeStat {
	return network.ScopeStat{
		Memory:             .memory,
		NumStreamsInbound:  .nstreamsIn,
		NumStreamsOutbound: .nstreamsOut,
		NumConnsInbound:    .nconnsIn,
		NumConnsOutbound:   .nconnsOut,
		NumFD:              .nfd,
	}
}

// resourceScope implementation
func ( *resourceScope) ( error) error {
	return fmt.Errorf("%s: %w", .name, )
}

func ( *resourceScope) ( int,  uint8) error {
	.Lock()
	defer .Unlock()

	if .done {
		return .wrapError(network.ErrResourceScopeClosed)
	}

	if  := .rc.reserveMemory(int64(), );  != nil {
		log.Debugw("blocked memory reservation", logValuesMemoryLimit(.name, "", .rc.stat(), )...)
		.trace.BlockReserveMemory(.name, , int64(), .rc.memory)
		.metrics.BlockMemory()
		return .wrapError()
	}

	if  := .reserveMemoryForEdges(, );  != nil {
		.rc.releaseMemory(int64())
		.metrics.BlockMemory()
		return .wrapError()
	}

	.trace.ReserveMemory(.name, , int64(), .rc.memory)
	.metrics.AllowMemory()
	return nil
}

func ( *resourceScope) ( int,  uint8) error {
	if .owner != nil {
		return .owner.ReserveMemory(, )
	}

	var  int
	var  error
	for ,  := range .edges {
		var  network.ScopeStat
		,  = .ReserveMemoryForChild(int64(), )
		if  != nil {
			log.Debugw("blocked memory reservation from constraining edge", logValuesMemoryLimit(.name, .name, , )...)
			break
		}

		++
	}

	if  != nil {
		// we failed because of a constraint; undo memory reservations
		for ,  := range .edges[:] {
			.ReleaseMemoryForChild(int64())
		}
	}

	return 
}

func ( *resourceScope) ( int) {
	if .owner != nil {
		.owner.ReleaseMemory()
		return
	}

	for ,  := range .edges {
		.ReleaseMemoryForChild(int64())
	}
}

func ( *resourceScope) ( int64,  uint8) (network.ScopeStat, error) {
	.Lock()
	defer .Unlock()

	if .done {
		return .rc.stat(), .wrapError(network.ErrResourceScopeClosed)
	}

	if  := .rc.reserveMemory(, );  != nil {
		.trace.BlockReserveMemory(.name, , , .rc.memory)
		return .rc.stat(), .wrapError()
	}

	.trace.ReserveMemory(.name, , , .rc.memory)
	return network.ScopeStat{}, nil
}

func ( *resourceScope) ( int) {
	.Lock()
	defer .Unlock()

	if .done {
		return
	}

	.rc.releaseMemory(int64())
	.releaseMemoryForEdges()
	.trace.ReleaseMemory(.name, int64(), .rc.memory)
}

func ( *resourceScope) ( int64) {
	.Lock()
	defer .Unlock()

	if .done {
		return
	}

	.rc.releaseMemory()
	.trace.ReleaseMemory(.name, , .rc.memory)
}

func ( *resourceScope) ( network.Direction) error {
	.Lock()
	defer .Unlock()

	if .done {
		return .wrapError(network.ErrResourceScopeClosed)
	}

	if  := .rc.addStream();  != nil {
		log.Debugw("blocked stream", logValuesStreamLimit(.name, "", , .rc.stat(), )...)
		.trace.BlockAddStream(.name, , .rc.nstreamsIn, .rc.nstreamsOut)
		return .wrapError()
	}

	if  := .addStreamForEdges();  != nil {
		.rc.removeStream()
		return .wrapError()
	}

	.trace.AddStream(.name, , .rc.nstreamsIn, .rc.nstreamsOut)
	return nil
}

func ( *resourceScope) ( network.Direction) error {
	if .owner != nil {
		return .owner.AddStream()
	}

	var  error
	var  int
	for ,  := range .edges {
		var  network.ScopeStat
		,  = .AddStreamForChild()
		if  != nil {
			log.Debugw("blocked stream from constraining edge", logValuesStreamLimit(.name, .name, , , )...)
			break
		}
		++
	}

	if  != nil {
		for ,  := range .edges[:] {
			.RemoveStreamForChild()
		}
	}

	return 
}

func ( *resourceScope) ( network.Direction) (network.ScopeStat, error) {
	.Lock()
	defer .Unlock()

	if .done {
		return .rc.stat(), .wrapError(network.ErrResourceScopeClosed)
	}

	if  := .rc.addStream();  != nil {
		.trace.BlockAddStream(.name, , .rc.nstreamsIn, .rc.nstreamsOut)
		return .rc.stat(), .wrapError()
	}

	.trace.AddStream(.name, , .rc.nstreamsIn, .rc.nstreamsOut)
	return network.ScopeStat{}, nil
}

func ( *resourceScope) ( network.Direction) {
	.Lock()
	defer .Unlock()

	if .done {
		return
	}

	.rc.removeStream()
	.removeStreamForEdges()
	.trace.RemoveStream(.name, , .rc.nstreamsIn, .rc.nstreamsOut)
}

func ( *resourceScope) ( network.Direction) {
	if .owner != nil {
		.owner.RemoveStream()
		return
	}

	for ,  := range .edges {
		.RemoveStreamForChild()
	}
}

func ( *resourceScope) ( network.Direction) {
	.Lock()
	defer .Unlock()

	if .done {
		return
	}

	.rc.removeStream()
	.trace.RemoveStream(.name, , .rc.nstreamsIn, .rc.nstreamsOut)
}

func ( *resourceScope) ( network.Direction,  bool) error {
	.Lock()
	defer .Unlock()

	if .done {
		return .wrapError(network.ErrResourceScopeClosed)
	}

	if  := .rc.addConn(, );  != nil {
		log.Debugw("blocked connection", logValuesConnLimit(.name, "", , , .rc.stat(), )...)
		.trace.BlockAddConn(.name, , , .rc.nconnsIn, .rc.nconnsOut, .rc.nfd)
		return .wrapError()
	}

	if  := .addConnForEdges(, );  != nil {
		.rc.removeConn(, )
		return .wrapError()
	}

	.trace.AddConn(.name, , , .rc.nconnsIn, .rc.nconnsOut, .rc.nfd)
	return nil
}

func ( *resourceScope) ( network.Direction,  bool) error {
	if .owner != nil {
		return .owner.AddConn(, )
	}

	var  error
	var  int
	for ,  := range .edges {
		var  network.ScopeStat
		,  = .AddConnForChild(, )
		if  != nil {
			log.Debugw("blocked connection from constraining edge", logValuesConnLimit(.name, .name, , , , )...)
			break
		}
		++
	}

	if  != nil {
		for ,  := range .edges[:] {
			.RemoveConnForChild(, )
		}
	}

	return 
}

func ( *resourceScope) ( network.Direction,  bool) (network.ScopeStat, error) {
	.Lock()
	defer .Unlock()

	if .done {
		return .rc.stat(), .wrapError(network.ErrResourceScopeClosed)
	}

	if  := .rc.addConn(, );  != nil {
		.trace.BlockAddConn(.name, , , .rc.nconnsIn, .rc.nconnsOut, .rc.nfd)
		return .rc.stat(), .wrapError()
	}

	.trace.AddConn(.name, , , .rc.nconnsIn, .rc.nconnsOut, .rc.nfd)
	return network.ScopeStat{}, nil
}

func ( *resourceScope) ( network.Direction,  bool) {
	.Lock()
	defer .Unlock()

	if .done {
		return
	}

	.rc.removeConn(, )
	.removeConnForEdges(, )
	.trace.RemoveConn(.name, , , .rc.nconnsIn, .rc.nconnsOut, .rc.nfd)
}

func ( *resourceScope) ( network.Direction,  bool) {
	if .owner != nil {
		.owner.RemoveConn(, )
	}

	for ,  := range .edges {
		.RemoveConnForChild(, )
	}
}

func ( *resourceScope) ( network.Direction,  bool) {
	.Lock()
	defer .Unlock()

	if .done {
		return
	}

	.rc.removeConn(, )
	.trace.RemoveConn(.name, , , .rc.nconnsIn, .rc.nconnsOut, .rc.nfd)
}

func ( *resourceScope) ( network.ScopeStat) error {
	.Lock()
	defer .Unlock()

	if .done {
		return .wrapError(network.ErrResourceScopeClosed)
	}

	if  := .rc.reserveMemory(.Memory, network.ReservationPriorityAlways);  != nil {
		.trace.BlockReserveMemory(.name, 255, .Memory, .rc.memory)
		return .wrapError()
	}

	if  := .rc.addStreams(.NumStreamsInbound, .NumStreamsOutbound);  != nil {
		.trace.BlockAddStreams(.name, .NumStreamsInbound, .NumStreamsOutbound, .rc.nstreamsIn, .rc.nstreamsOut)
		.rc.releaseMemory(.Memory)
		return .wrapError()
	}

	if  := .rc.addConns(.NumConnsInbound, .NumConnsOutbound, .NumFD);  != nil {
		.trace.BlockAddConns(.name, .NumConnsInbound, .NumConnsOutbound, .NumFD, .rc.nconnsIn, .rc.nconnsOut, .rc.nfd)

		.rc.releaseMemory(.Memory)
		.rc.removeStreams(.NumStreamsInbound, .NumStreamsOutbound)
		return .wrapError()
	}

	.trace.ReserveMemory(.name, 255, .Memory, .rc.memory)
	.trace.AddStreams(.name, .NumStreamsInbound, .NumStreamsOutbound, .rc.nstreamsIn, .rc.nstreamsOut)
	.trace.AddConns(.name, .NumConnsInbound, .NumConnsOutbound, .NumFD, .rc.nconnsIn, .rc.nconnsOut, .rc.nfd)

	return nil
}

func ( *resourceScope) ( network.ScopeStat) {
	.Lock()
	defer .Unlock()

	if .done {
		return
	}

	.rc.releaseMemory(.Memory)
	.rc.removeStreams(.NumStreamsInbound, .NumStreamsOutbound)
	.rc.removeConns(.NumConnsInbound, .NumConnsOutbound, .NumFD)

	.trace.ReleaseMemory(.name, .Memory, .rc.memory)
	.trace.RemoveStreams(.name, .NumStreamsInbound, .NumStreamsOutbound, .rc.nstreamsIn, .rc.nstreamsOut)
	.trace.RemoveConns(.name, .NumConnsInbound, .NumConnsOutbound, .NumFD, .rc.nconnsIn, .rc.nconnsOut, .rc.nfd)
}

func ( *resourceScope) ( network.ScopeStat) {
	.Lock()
	defer .Unlock()

	if .done {
		return
	}

	.rc.releaseMemory(.Memory)
	.rc.removeStreams(.NumStreamsInbound, .NumStreamsOutbound)
	.rc.removeConns(.NumConnsInbound, .NumConnsOutbound, .NumFD)

	if .owner != nil {
		.owner.()
	} else {
		for ,  := range .edges {
			.ReleaseForChild()
		}
	}

	.trace.ReleaseMemory(.name, .Memory, .rc.memory)
	.trace.RemoveStreams(.name, .NumStreamsInbound, .NumStreamsOutbound, .rc.nstreamsIn, .rc.nstreamsOut)
	.trace.RemoveConns(.name, .NumConnsInbound, .NumConnsOutbound, .NumFD, .rc.nconnsIn, .rc.nconnsOut, .rc.nfd)
}

func ( *resourceScope) () int {
	.spanID++
	return .spanID
}

func ( *resourceScope) () (network.ResourceScopeSpan, error) {
	.Lock()
	defer .Unlock()

	if .done {
		return nil, .wrapError(network.ErrResourceScopeClosed)
	}

	.refCnt++
	return newResourceScopeSpan(, .nextSpanID()), nil
}

func ( *resourceScope) () {
	.Lock()
	defer .Unlock()

	.doneUnlocked()
}

func ( *resourceScope) () {
	if .done {
		return
	}
	 := .rc.stat()
	if .owner != nil {
		.owner.ReleaseResources()
		.owner.DecRef()
	} else {
		for ,  := range .edges {
			.ReleaseForChild()
			.DecRef()
		}
	}

	.rc.nstreamsIn = 0
	.rc.nstreamsOut = 0
	.rc.nconnsIn = 0
	.rc.nconnsOut = 0
	.rc.nfd = 0
	.rc.memory = 0

	.done = true

	.trace.DestroyScope(.name)
}

func ( *resourceScope) () network.ScopeStat {
	.Lock()
	defer .Unlock()

	return .rc.stat()
}

func ( *resourceScope) () {
	.Lock()
	defer .Unlock()

	.refCnt++
}

func ( *resourceScope) () {
	.Lock()
	defer .Unlock()

	.refCnt--
}

func ( *resourceScope) () bool {
	.Lock()
	defer .Unlock()

	if .done {
		return true
	}

	if .refCnt > 0 {
		return false
	}

	 := .rc.stat()
	return .NumStreamsInbound == 0 &&
		.NumStreamsOutbound == 0 &&
		.NumConnsInbound == 0 &&
		.NumConnsOutbound == 0 &&
		.NumFD == 0
}