Ctrl K
ComunidadGuías…

internal/mtprotoedge/transport_ownership.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package mtprotoedge

import (
	"context"
	"sync"
	"sync/atomic"
	"time"

	"github.com/iamxvbaba/td/bin"
	"github.com/iamxvbaba/td/transport"
)

const (
	physicalTransportClosedBit     = uint64(1) << 63
	physicalTransportGenerationMax = physicalTransportClosedBit - 1
)

// physicalTransportOwner separates the lifetime of one physical socket from
// the logical Conn generations that successively use it. state is either an
// open generation in [1, physicalTransportGenerationMax], or that generation
// with physicalTransportClosedBit set.
//
// Transfer and owner-close race through one atomic state transition. This is
// the critical property: when transfer wins, a stale generation can no longer
// close the socket; when close wins, no later generation can be published.
type physicalTransportOwner struct {
	raw transport.Conn

	// writeMu orders a completed send before generation transfer. CloseAny and
	// owner-close deliberately do not wait for it: raw.Close must be able to
	// interrupt a transport implementation blocked inside Send.
	writeMu sync.Mutex
	state   atomic.Uint64

	// binding identifies the logical Conn currently attached to the open
	// generation. Physical close fences that exact Conn before touching raw;
	// generation matching prevents a stale close from retiring a replacement.
	bindingMu        sync.Mutex
	boundGeneration  uint64
	boundLogicalConn *Conn

	closeDone chan struct{}
	closeErr  error
}

// physicalTransportLease is the capability owned by one logical Conn
// generation. It implements transport.Conn so exchange/protocol-error paths
// can use the initial lease from the moment serveConn starts.
type physicalTransportLease struct {
	owner      *physicalTransportOwner
	generation uint64
}

var _ transport.Conn = (*physicalTransportLease)(nil)

func newPhysicalTransportOwner(raw transport.Conn) (*physicalTransportOwner, *physicalTransportLease) {
	owner := &physicalTransportOwner{
		raw:       raw,
		closeDone: make(chan struct{}),
	}
	owner.state.Store(1)
	return owner, &physicalTransportLease{owner: owner, generation: 1}
}

// Transfer atomically hands the physical transport to the next logical Conn
// generation. Holding writeMu ensures every send admitted by the old lease has
// returned before the generation changes. CloseAny may still interrupt such a
// send and make the CAS fail, which is the required shutdown ordering.
func (l *physicalTransportLease) Transfer() (*physicalTransportLease, bool) {
	if l == nil || l.owner == nil || l.generation == 0 {
		return nil, false
	}
	owner := l.owner
	owner.writeMu.Lock()
	defer owner.writeMu.Unlock()

	if l.generation >= physicalTransportGenerationMax {
		return nil, false
	}
	if !owner.state.CompareAndSwap(l.generation, l.generation+1) {
		return nil, false
	}
	owner.bindingMu.Lock()
	if owner.boundGeneration == l.generation {
		owner.boundGeneration = l.generation + 1
		owner.boundLogicalConn = nil
	}
	owner.bindingMu.Unlock()
	return &physicalTransportLease{owner: owner, generation: l.generation + 1}, true
}

// IsCurrentOpen reports whether this lease is still the unique open owner.
// Callers use it at protocol publication barriers after a required write.
func (l *physicalTransportLease) IsCurrentOpen() bool {
	return l != nil && l.owner != nil && l.generation != 0 &&
		l.owner.state.Load() == l.generation
}

// bindLogicalConn publishes the logical Conn for this exact generation. If
// physical close already won, the new Conn is terminally fenced immediately
// and can never pass SessionManager publication checks.
func (l *physicalTransportLease) bindLogicalConn(c *Conn) bool {
	if l == nil || l.owner == nil || c == nil {
		if c != nil {
			c.beginTerminalShutdown()
		}
		return false
	}
	owner := l.owner
	owner.bindingMu.Lock()
	open := owner.state.Load() == l.generation
	if open {
		owner.boundGeneration = l.generation
		owner.boundLogicalConn = c
	}
	owner.bindingMu.Unlock()
	if !open {
		c.beginTerminalShutdown()
	}
	return open
}

// Send admits a write only while this lease is the current open generation.
// The lock also serializes quick ACK/protocol writes with the outbound actor.
func (l *physicalTransportLease) Send(ctx context.Context, b *bin.Buffer) error {
	return l.withCurrentWriter(func(raw transport.Conn) error {
		return raw.Send(ctx, b)
	})
}

// SendDeadline preserves the allocation-free fast path of compatTransportConn.
func (l *physicalTransportLease) SendDeadline(deadline time.Time, b *bin.Buffer) error {
	return l.withCurrentWriter(func(raw transport.Conn) error {
		if writer, ok := raw.(deadlineOutboundWriter); ok {
			return writer.SendDeadline(deadline, b)
		}
		ctx := context.Background()
		cancel := func() {}
		if !deadline.IsZero() {
			ctx, cancel = context.WithDeadline(ctx, deadline)
		}
		defer cancel()
		return raw.Send(ctx, b)
	})
}

// SendDeadlineWithScratch forwards the globally budgeted codec scratch through the generation
// lease while preserving the same write-ownership barrier as SendDeadline.
func (l *physicalTransportLease) SendDeadlineWithScratch(deadline time.Time, b *bin.Buffer, scratch *[]byte) error {
	return l.withCurrentWriter(func(raw transport.Conn) error {
		if writer, ok := raw.(deadlineOutboundScratchWriter); ok {
			return writer.SendDeadlineWithScratch(deadline, b, scratch)
		}
		if writer, ok := raw.(deadlineOutboundWriter); ok {
			return writer.SendDeadline(deadline, b)
		}
		ctx := context.Background()
		cancel := func() {}
		if !deadline.IsZero() {
			ctx, cancel = context.WithDeadline(ctx, deadline)
		}
		defer cancel()
		return raw.Send(ctx, b)
	})
}

// SendDeadlineWithScratchGuarded evaluates guard while holding the physical
// write-ownership lock, immediately before entering the raw writer. Conn-level
// checks performed before this call are insufficient: a quick ACK or protocol
// write may hold writeMu across a temporary-key expiry boundary. The guard must
// not close/fence the connection itself because that would re-enter transport
// shutdown while writeMu is held; callers handle its error after the lock drops.
func (l *physicalTransportLease) SendDeadlineWithScratchGuarded(deadline time.Time, b *bin.Buffer, scratch *[]byte, guard func() error) error {
	return l.withCurrentWriterGuarded(guard, func(raw transport.Conn) error {
		if writer, ok := raw.(deadlineOutboundScratchWriter); ok {
			return writer.SendDeadlineWithScratch(deadline, b, scratch)
		}
		if writer, ok := raw.(deadlineOutboundWriter); ok {
			return writer.SendDeadline(deadline, b)
		}
		ctx := context.Background()
		cancel := func() {}
		if !deadline.IsZero() {
			ctx, cancel = context.WithDeadline(ctx, deadline)
		}
		defer cancel()
		return raw.Send(ctx, b)
	})
}

func (l *physicalTransportLease) withCurrentWriter(send func(transport.Conn) error) error {
	return l.withCurrentWriterGuarded(nil, send)
}

func (l *physicalTransportLease) withCurrentWriterGuarded(guard func() error, send func(transport.Conn) error) error {
	if l == nil || l.owner == nil || l.owner.raw == nil {
		return ErrConnClosed
	}
	owner := l.owner
	owner.writeMu.Lock()
	defer owner.writeMu.Unlock()
	if owner.state.Load() != l.generation {
		return ErrConnClosed
	}
	if guard != nil {
		if err := guard(); err != nil {
			return err
		}
	}
	return send(owner.raw)
}

// Recv is owned by serveConn rather than a logical Conn generation. It is a
// direct proxy so the read loop remains valid after ownership transfers.
func (l *physicalTransportLease) Recv(ctx context.Context, b *bin.Buffer) error {
	if l == nil || l.owner == nil || l.owner.raw == nil {
		return ErrConnClosed
	}
	return l.owner.raw.Recv(ctx, b)
}

// RecvDeadline preserves serveConn's direct-deadline fast path.
func (l *physicalTransportLease) RecvDeadline(deadline time.Time, b *bin.Buffer) error {
	if l == nil || l.owner == nil || l.owner.raw == nil {
		return ErrConnClosed
	}
	if receiver, ok := l.owner.raw.(deadlineReceiver); ok {
		return receiver.RecvDeadline(deadline, b)
	}
	ctx := context.Background()
	cancel := func() {}
	if !deadline.IsZero() {
		ctx, cancel = context.WithDeadline(ctx, deadline)
	}
	defer cancel()
	return l.owner.raw.Recv(ctx, b)
}

// Close closes the physical transport only if this lease still owns the
// current generation. A stale logical Conn therefore cannot close a socket
// already transferred to its replacement.
func (l *physicalTransportLease) Close() error {
	if l == nil || l.owner == nil || l.generation == 0 {
		return nil
	}
	owner := l.owner
	for {
		state := owner.state.Load()
		if state&physicalTransportClosedBit != 0 {
			return owner.waitClosed()
		}
		if state != l.generation {
			return nil
		}
		if owner.state.CompareAndSwap(state, state|physicalTransportClosedBit) {
			owner.fenceBoundGeneration(l.generation)
			return owner.closeRaw()
		}
	}
}

// startCloseAlreadyFenced is the non-reentrant close capability for the logical
// Conn that has already published terminal/lifecycle gates itself. It
// synchronously publishes the physical closed bit (so Transfer cannot win), then
// runs the potentially pathological raw.Close outside the RPC worker/flight
// handoff. It neither calls fenceBoundGeneration nor waits for a CloseAny that
// already won, avoiding both lifecycle reentry and close cycles.
func (l *physicalTransportLease) startCloseAlreadyFenced() {
	if l == nil || l.owner == nil || l.generation == 0 {
		return
	}
	owner := l.owner
	for {
		state := owner.state.Load()
		if state&physicalTransportClosedBit != 0 || state != l.generation {
			return
		}
		if owner.state.CompareAndSwap(state, state|physicalTransportClosedBit) {
			owner.bindingMu.Lock()
			if owner.boundGeneration == l.generation {
				owner.boundLogicalConn = nil
			}
			owner.bindingMu.Unlock()
			go func() { _ = owner.closeRaw() }()
			return
		}
	}
}

// CloseAny is the unconditional physical-socket capability retained by
// serveConn. It marks the owner closed before calling raw.Close and never waits
// for writeMu, so it can break a blocked Send or Recv.
func (o *physicalTransportOwner) CloseAny() error {
	if o == nil {
		return nil
	}
	for {
		state := o.state.Load()
		if state&physicalTransportClosedBit != 0 {
			return o.waitClosed()
		}
		if o.state.CompareAndSwap(state, state|physicalTransportClosedBit) {
			o.fenceBoundGeneration(state)
			return o.closeRaw()
		}
	}
}

func (o *physicalTransportOwner) fenceBoundGeneration(generation uint64) {
	o.bindingMu.Lock()
	var c *Conn
	if o.boundGeneration == generation {
		c = o.boundLogicalConn
		o.boundLogicalConn = nil
	}
	o.bindingMu.Unlock()
	if c != nil {
		c.beginTerminalShutdown()
	}
}

func (o *physicalTransportOwner) closeRaw() error {
	if o.raw != nil {
		o.closeErr = o.raw.Close()
	}
	close(o.closeDone)
	return o.closeErr
}

func (o *physicalTransportOwner) waitClosed() error {
	<-o.closeDone
	return o.closeErr
}

// Forward the optional compat-transport capabilities hidden by the lease.
// These keep frame-budget ownership and quick-ack semantics unchanged while
// serveConn operates on the initial lease instead of the raw transport.
func (l *physicalTransportLease) releaseInboundFrame() {
	if l == nil || l.owner == nil {
		return
	}
	if releaser, ok := l.owner.raw.(inboundFrameOwnershipReleaser); ok {
		releaser.releaseInboundFrame()
	}
}

func (l *physicalTransportLease) retainInboundFrameBytes(n int64) bool {
	if l == nil || l.owner == nil {
		return true
	}
	if retainer, ok := l.owner.raw.(inboundFrameBackingRetainer); ok {
		return retainer.retainInboundFrameBytes(n)
	}
	return true
}

func (l *physicalTransportLease) ConsumeQuickAckRequested() bool {
	if l == nil || l.owner == nil {
		return false
	}
	if quick, ok := l.owner.raw.(quickAckTransport); ok {
		return quick.ConsumeQuickAckRequested()
	}
	return false
}

func (l *physicalTransportLease) SendQuickAck(ctx context.Context, token uint32) error {
	return l.withCurrentWriter(func(raw transport.Conn) error {
		if quick, ok := raw.(quickAckTransport); ok {
			return quick.SendQuickAck(ctx, token)
		}
		return nil
	})
}

func (l *physicalTransportLease) SendQuickAckDeadline(deadline time.Time, token uint32) error {
	return l.withCurrentWriter(func(raw transport.Conn) error {
		if quick, ok := raw.(deadlineQuickAckTransport); ok {
			return quick.SendQuickAckDeadline(deadline, token)
		}
		if quick, ok := raw.(quickAckTransport); ok {
			ctx := context.Background()
			cancel := func() {}
			if !deadline.IsZero() {
				ctx, cancel = context.WithDeadline(ctx, deadline)
			}
			defer cancel()
			return quick.SendQuickAck(ctx, token)
		}
		return nil
	})
}