Repository: pion/interceptor
Pull request #182 (2023-04-24 18:20:48)

Fix routine leak in TWCC Send Interceptor
Internally we read and process packets in a seperate routine. On close this routine would be destroyed. In some cases a packet could still be in flight to this thread. This would cause a routine to be leaked.

Resolves #181 

codecov[bot]: ## [Codecov](https://codecov.io/gh/pion/interceptor/pull/182?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion) Report
Patch coverage: **`100.00`**% and project coverage change: **`+0.18`** :tada:
> Comparison is base [(`6d63a76`)](https://codecov.io/gh/pion/interceptor/commit/6d63a76db72bac206e9dbff9f0c678f3ca2f0d39?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion) 79.20% compared to head [(`4ad4f11`)](https://codecov.io/gh/pion/interceptor/pull/182?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion) 79.39%.

> :exclamation: Current head 4ad4f11 differs from pull request most recent head fe0025f. Consider uploading reports for the commit fe0025f to get more accurate results

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master     #182      +/-   ##
==========================================
+ Coverage   79.20%   79.39%   +0.18%     
==========================================
  Files          63       63              
  Lines        3189     3193       +4     
==========================================
+ Hits         2526     2535       +9     
+ Misses        551      547       -4     
+ Partials      112      111       -1     
```

| Flag | Coverage Δ | |
|---|---|---|
| go | `79.39% <100.00%> (+0.18%)` | :arrow_up: |
| wasm | `77.10% <100.00%> (+0.09%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/pion/interceptor/pull/182?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion) | Coverage Δ | |
|---|---|---|
| [pkg/twcc/header\_extension\_interceptor.go](https://codecov.io/gh/pion/interceptor/pull/182?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion#diff-cGtnL3R3Y2MvaGVhZGVyX2V4dGVuc2lvbl9pbnRlcmNlcHRvci5nbw==) | `64.00% <100.00%> (ø)` | |
| [pkg/twcc/sender\_interceptor.go](https://codecov.io/gh/pion/interceptor/pull/182?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion#diff-cGtnL3R3Y2Mvc2VuZGVyX2ludGVyY2VwdG9yLmdv) | `82.30% <100.00%> (+2.48%)` | :arrow_up: |

... and [1 file with indirect coverage changes](https://codecov.io/gh/pion/interceptor/pull/182/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion)

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion)

</details>

[:umbrella: View full report in Codecov by Sentry](https://codecov.io/gh/pion/interceptor/pull/182?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion).   
:loudspeaker: Do you have feedback about the report comment? [Let us know in this issue](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion).

Sean-Der: @ziminghua @braams thank you so much for the PR!

I added a test. hopefully this fixes the issues you were having. If you don't mind testing again I would really appreciate it :) 

Patch:
diff --git a/AUTHORS.txt b/AUTHORS.txt
index 42752d4..94bc6df 100644
--- a/AUTHORS.txt
+++ b/AUTHORS.txt
@@ -15,8 +15,10 @@ boks1971 <raja.gobi@tutanota.com>
 David Zhao <david@davidzhao.com>
 Jonathan Müller <jonathan@fotokite.com>
 Kevin Caffrey <kcaffrey@gmail.com>
+Maksim Nesterov <msnesterov@avito.ru>
 Mathis Engelbart <mathis.engelbart@gmail.com>
 Sean DuBois <sean@siobud.com>
+ziminghua <565209960@qq.com>
 
 # List of contributors not appearing in Git history
 
diff --git a/examples/nack/main.go b/examples/nack/main.go
index 316809c..9cdb674 100644
--- a/examples/nack/main.go
+++ b/examples/nack/main.go
@@ -1,3 +1,4 @@
+// Package demonstrates how to use the NACK interceptor
 package main
 
 import (
diff --git a/go.mod b/go.mod
index 59cda13..f4d83c5 100644
--- a/go.mod
+++ b/go.mod
@@ -6,5 +6,6 @@ require (
 	github.com/pion/logging v0.2.2
 	github.com/pion/rtcp v1.2.10
 	github.com/pion/rtp v1.7.13
+	github.com/pion/transport/v2 v2.2.0
 	github.com/stretchr/testify v1.8.2
 )
diff --git a/go.sum b/go.sum
index 4d5c42b..5124e5b 100644
--- a/go.sum
+++ b/go.sum
@@ -9,6 +9,8 @@ github.com/pion/rtcp v1.2.10 h1:nkr3uj+8Sp97zyItdN60tE/S6vk4al5CPRR6Gejsdjc=
 github.com/pion/rtcp v1.2.10/go.mod h1:ztfEwXZNLGyF1oQDttz/ZKIBaeeg/oWbRYqzBM9TL1I=
 github.com/pion/rtp v1.7.13 h1:qcHwlmtiI50t1XivvoawdCGTP4Uiypzfrsap+bijcoA=
 github.com/pion/rtp v1.7.13/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko=
+github.com/pion/transport/v2 v2.2.0 h1:u5lFqFHkXLMXMzai8tixZDfVjb8eOjH35yCunhPeb1c=
+github.com/pion/transport/v2 v2.2.0/go.mod h1:AdSw4YBZVDkZm8fpoz+fclXyQwANWmZAlDuQdctTThQ=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -18,6 +20,42 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
 github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
+golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
+golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
+golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/internal/cc/feedback_adapter.go b/internal/cc/feedback_adapter.go
index fe94ff0..30ff1ac 100644
--- a/internal/cc/feedback_adapter.go
+++ b/internal/cc/feedback_adapter.go
@@ -136,7 +136,7 @@ func (f *FeedbackAdapter) unpackStatusVectorChunk(start uint16, refTime time.Tim
 
 // OnTransportCCFeedback converts incoming TWCC RTCP packet feedback to
 // Acknowledgments.
-func (f *FeedbackAdapter) OnTransportCCFeedback(ts time.Time, feedback *rtcp.TransportLayerCC) ([]Acknowledgment, error) {
+func (f *FeedbackAdapter) OnTransportCCFeedback(_ time.Time, feedback *rtcp.TransportLayerCC) ([]Acknowledgment, error) {
 	f.lock.Lock()
 	defer f.lock.Unlock()
 
@@ -175,7 +175,7 @@ func (f *FeedbackAdapter) OnTransportCCFeedback(ts time.Time, feedback *rtcp.Tra
 
 // OnRFC8888Feedback converts incoming Congestion Control Feedback RTCP packet
 // to Acknowledgments.
-func (f *FeedbackAdapter) OnRFC8888Feedback(ts time.Time, feedback *rtcp.CCFeedbackReport) []Acknowledgment {
+func (f *FeedbackAdapter) OnRFC8888Feedback(_ time.Time, feedback *rtcp.CCFeedbackReport) []Acknowledgment {
 	f.lock.Lock()
 	defer f.lock.Unlock()
 
diff --git a/pkg/gcc/adaptive_threshold.go b/pkg/gcc/adaptive_threshold.go
index c7a6012..e72ce38 100644
--- a/pkg/gcc/adaptive_threshold.go
+++ b/pkg/gcc/adaptive_threshold.go
@@ -57,7 +57,7 @@ func newAdaptiveThreshold(opts ...adaptiveThresholdOption) *adaptiveThreshold {
 	return at
 }
 
-func (a *adaptiveThreshold) compare(estimate, dt time.Duration) (usage, time.Duration, time.Duration) {
+func (a *adaptiveThreshold) compare(estimate, _ time.Duration) (usage, time.Duration, time.Duration) {
 	a.numDeltas++
 	if a.numDeltas < 2 {
 		return usageNormal, estimate, a.max
diff --git a/pkg/gcc/send_side_bwe.go b/pkg/gcc/send_side_bwe.go
index c1a28e5..70c6b31 100644
--- a/pkg/gcc/send_side_bwe.go
+++ b/pkg/gcc/send_side_bwe.go
@@ -153,7 +153,7 @@ func (e *SendSideBWE) AddStream(info *interceptor.StreamInfo, writer interceptor
 }
 
 // WriteRTCP adds some RTCP feedback to the bandwidth estimator
-func (e *SendSideBWE) WriteRTCP(pkts []rtcp.Packet, attributes interceptor.Attributes) error {
+func (e *SendSideBWE) WriteRTCP(pkts []rtcp.Packet, _ interceptor.Attributes) error {
 	now := time.Now()
 	e.closeLock.RLock()
 	defer e.closeLock.RUnlock()
diff --git a/pkg/nack/generator_interceptor.go b/pkg/nack/generator_interceptor.go
index f55f4d7..5622db7 100644
--- a/pkg/nack/generator_interceptor.go
+++ b/pkg/nack/generator_interceptor.go
@@ -16,7 +16,7 @@ type GeneratorInterceptorFactory struct {
 }
 
 // NewInterceptor constructs a new ReceiverInterceptor
-func (g *GeneratorInterceptorFactory) NewInterceptor(id string) (interceptor.Interceptor, error) {
+func (g *GeneratorInterceptorFactory) NewInterceptor(string) (interceptor.Interceptor, error) {
 	i := &GeneratorInterceptor{
 		size:        512,
 		skipLastN:   0,
diff --git a/pkg/nack/receive_log.go b/pkg/nack/receive_log.go
index 8107f59..14dc00b 100644
--- a/pkg/nack/receive_log.go
+++ b/pkg/nack/receive_log.go
@@ -127,8 +127,11 @@ func (s *receiveLog) getReceived(seq uint16) bool {
 
 func (s *receiveLog) fixLastConsecutive() {
 	i := s.lastConsecutive + 1
+
+	// nolint:revive
 	for ; i != s.end+1 && s.getReceived(i); i++ {
 		// find all consecutive packets
 	}
+
 	s.lastConsecutive = i - 1
 }
diff --git a/pkg/nack/responder_interceptor.go b/pkg/nack/responder_interceptor.go
index 7f3123f..e992090 100644
--- a/pkg/nack/responder_interceptor.go
+++ b/pkg/nack/responder_interceptor.go
@@ -19,7 +19,7 @@ type packetFactory interface {
 }
 
 // NewInterceptor constructs a new ResponderInterceptor
-func (r *ResponderInterceptorFactory) NewInterceptor(id string) (interceptor.Interceptor, error) {
+func (r *ResponderInterceptorFactory) NewInterceptor(string) (interceptor.Interceptor, error) {
 	i := &ResponderInterceptor{
 		size:    1024,
 		log:     logging.NewDefaultLoggerFactory().NewLogger("nack_responder"),
diff --git a/pkg/nack/retainable_packet.go b/pkg/nack/retainable_packet.go
index fa0afea..07a3e8b 100644
--- a/pkg/nack/retainable_packet.go
+++ b/pkg/nack/retainable_packet.go
@@ -80,7 +80,7 @@ func (f *noOpPacketFactory) NewPacket(header *rtp.Header, payload []byte) (*reta
 	}, nil
 }
 
-func (f *noOpPacketFactory) releasePacket(header *rtp.Header, payload *[]byte) {
+func (f *noOpPacketFactory) releasePacket(*rtp.Header, *[]byte) {
 	// no-op
 }
 
diff --git a/pkg/packetdump/receiver_interceptor.go b/pkg/packetdump/receiver_interceptor.go
index bc7b6f2..670f80e 100644
--- a/pkg/packetdump/receiver_interceptor.go
+++ b/pkg/packetdump/receiver_interceptor.go
@@ -17,7 +17,7 @@ func NewReceiverInterceptor(opts ...PacketDumperOption) (*ReceiverInterceptorFac
 }
 
 // NewInterceptor returns a new ReceiverInterceptor interceptor.
-func (r *ReceiverInterceptorFactory) NewInterceptor(id string) (interceptor.Interceptor, error) {
+func (r *ReceiverInterceptorFactory) NewInterceptor(string) (interceptor.Interceptor, error) {
 	dumper, err := NewPacketDumper(r.opts...)
 	if err != nil {
 		return nil, err
@@ -38,7 +38,7 @@ type ReceiverInterceptor struct {
 
 // BindRemoteStream lets you modify any incoming RTP packets. It is called once for per RemoteStream. The returned method
 // will be called once per rtp packet.
-func (r *ReceiverInterceptor) BindRemoteStream(info *interceptor.StreamInfo, reader interceptor.RTPReader) interceptor.RTPReader {
+func (r *ReceiverInterceptor) BindRemoteStream(_ *interceptor.StreamInfo, reader interceptor.RTPReader) interceptor.RTPReader {
 	return interceptor.RTPReaderFunc(func(bytes []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
 		i, attr, err := reader.Read(bytes, attributes)
 		if err != nil {
diff --git a/pkg/packetdump/sender_interceptor.go b/pkg/packetdump/sender_interceptor.go
index ae65466..a15c30e 100644
--- a/pkg/packetdump/sender_interceptor.go
+++ b/pkg/packetdump/sender_interceptor.go
@@ -19,7 +19,7 @@ func NewSenderInterceptor(opts ...PacketDumperOption) (*SenderInterceptorFactory
 }
 
 // NewInterceptor returns a new SenderInterceptor interceptor
-func (s *SenderInterceptorFactory) NewInterceptor(id string) (interceptor.Interceptor, error) {
+func (s *SenderInterceptorFactory) NewInterceptor(string) (interceptor.Interceptor, error) {
 	dumper, err := NewPacketDumper(s.opts...)
 	if err != nil {
 		return nil, err
@@ -47,7 +47,7 @@ func (s *SenderInterceptor) BindRTCPWriter(writer interceptor.RTCPWriter) interc
 
 // BindLocalStream lets you modify any outgoing RTP packets. It is called once for per LocalStream. The returned method
 // will be called once per rtp packet.
-func (s *SenderInterceptor) BindLocalStream(info *interceptor.StreamInfo, writer interceptor.RTPWriter) interceptor.RTPWriter {
+func (s *SenderInterceptor) BindLocalStream(_ *interceptor.StreamInfo, writer interceptor.RTPWriter) interceptor.RTPWriter {
 	return interceptor.RTPWriterFunc(func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
 		s.logRTPPacket(header, payload, attributes)
 		return writer.Write(header, payload, attributes)
diff --git a/pkg/report/receiver_interceptor.go b/pkg/report/receiver_interceptor.go
index eb7aff9..e4df8b4 100644
--- a/pkg/report/receiver_interceptor.go
+++ b/pkg/report/receiver_interceptor.go
@@ -15,7 +15,7 @@ type ReceiverInterceptorFactory struct {
 }
 
 // NewInterceptor constructs a new ReceiverInterceptor
-func (r *ReceiverInterceptorFactory) NewInterceptor(id string) (interceptor.Interceptor, error) {
+func (r *ReceiverInterceptorFactory) NewInterceptor(string) (interceptor.Interceptor, error) {
 	i := &ReceiverInterceptor{
 		interval: 1 * time.Second,
 		now:      time.Now,
diff --git a/pkg/report/sender_interceptor.go b/pkg/report/sender_interceptor.go
index d191392..bbcafde 100644
--- a/pkg/report/sender_interceptor.go
+++ b/pkg/report/sender_interceptor.go
@@ -16,7 +16,7 @@ type SenderInterceptorFactory struct {
 }
 
 // NewInterceptor constructs a new SenderInterceptor
-func (s *SenderInterceptorFactory) NewInterceptor(id string) (interceptor.Interceptor, error) {
+func (s *SenderInterceptorFactory) NewInterceptor(string) (interceptor.Interceptor, error) {
 	i := &SenderInterceptor{
 		interval: 1 * time.Second,
 		now:      time.Now,
diff --git a/pkg/rfc8888/interceptor.go b/pkg/rfc8888/interceptor.go
index 1529d5c..9adddb4 100644
--- a/pkg/rfc8888/interceptor.go
+++ b/pkg/rfc8888/interceptor.go
@@ -20,7 +20,7 @@ type SenderInterceptorFactory struct {
 }
 
 // NewInterceptor constructs a new SenderInterceptor
-func (s *SenderInterceptorFactory) NewInterceptor(id string) (interceptor.Interceptor, error) {
+func (s *SenderInterceptorFactory) NewInterceptor(string) (interceptor.Interceptor, error) {
 	i := &SenderInterceptor{
 		NoOp:          interceptor.NoOp{},
 		log:           logging.NewDefaultLoggerFactory().NewLogger("rfc8888_interceptor"),
@@ -90,7 +90,7 @@ func (s *SenderInterceptor) BindRTCPWriter(writer interceptor.RTCPWriter) interc
 
 // BindRemoteStream lets you modify any incoming RTP packets. It is called once for per RemoteStream. The returned method
 // will be called once per rtp packet.
-func (s *SenderInterceptor) BindRemoteStream(info *interceptor.StreamInfo, reader interceptor.RTPReader) interceptor.RTPReader {
+func (s *SenderInterceptor) BindRemoteStream(_ *interceptor.StreamInfo, reader interceptor.RTPReader) interceptor.RTPReader {
 	return interceptor.RTPReaderFunc(func(b []byte, a interceptor.Attributes) (int, interceptor.Attributes, error) {
 		i, attr, err := reader.Read(b, a)
 		if err != nil {
diff --git a/pkg/twcc/header_extension_interceptor.go b/pkg/twcc/header_extension_interceptor.go
index fa3fb81..b1d4d95 100644
--- a/pkg/twcc/header_extension_interceptor.go
+++ b/pkg/twcc/header_extension_interceptor.go
@@ -11,7 +11,7 @@ import (
 type HeaderExtensionInterceptorFactory struct{}
 
 // NewInterceptor constructs a new HeaderExtensionInterceptor
-func (h *HeaderExtensionInterceptorFactory) NewInterceptor(id string) (interceptor.Interceptor, error) {
+func (h *HeaderExtensionInterceptorFactory) NewInterceptor(string) (interceptor.Interceptor, error) {
 	return &HeaderExtensionInterceptor{}, nil
 }
 
diff --git a/pkg/twcc/sender_interceptor.go b/pkg/twcc/sender_interceptor.go
index 89df1ca..1f0d57f 100644
--- a/pkg/twcc/sender_interceptor.go
+++ b/pkg/twcc/sender_interceptor.go
@@ -1,6 +1,7 @@
 package twcc
 
 import (
+	"errors"
 	"math/rand"
 	"sync"
 	"time"
@@ -15,8 +16,10 @@ type SenderInterceptorFactory struct {
 	opts []Option
 }
 
+var errClosed = errors.New("interceptor is closed")
+
 // NewInterceptor constructs a new SenderInterceptor
-func (s *SenderInterceptorFactory) NewInterceptor(id string) (interceptor.Interceptor, error) {
+func (s *SenderInterceptorFactory) NewInterceptor(string) (interceptor.Interceptor, error) {
 	i := &SenderInterceptor{
 		log:        logging.NewDefaultLoggerFactory().NewLogger("twcc_sender_interceptor"),
 		packetChan: make(chan packet),
@@ -129,12 +132,17 @@ func (s *SenderInterceptor) BindRemoteStream(info *interceptor.StreamInfo, reade
 				return 0, nil, err
 			}
 
-			s.packetChan <- packet{
+			p := packet{
 				hdr:            header,
 				sequenceNumber: tccExt.TransportSequence,
 				arrivalTime:    time.Since(s.startTime).Microseconds(),
 				ssrc:           info.SSRC,
 			}
+			select {
+			case <-s.close:
+				return 0, nil, errClosed
+			case s.packetChan <- p:
+			}
 		}
 
 		return i, attr, nil


Test patch:
diff --git a/pkg/gcc/send_side_bwe_test.go b/pkg/gcc/send_side_bwe_test.go
index 34ddfa0..e66ddb1 100644
--- a/pkg/gcc/send_side_bwe_test.go
+++ b/pkg/gcc/send_side_bwe_test.go
@@ -19,7 +19,7 @@ type mockTWCCResponder struct {
 	rtpChan chan []byte
 }
 
-func (m *mockTWCCResponder) Read(out []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
+func (m *mockTWCCResponder) Read(out []byte, _ interceptor.Attributes) (int, interceptor.Attributes, error) {
 	pkt := <-m.rtpChan
 	copy(out, pkt)
 	return len(pkt), nil, nil
@@ -35,7 +35,7 @@ type mockGCCWriteStream struct {
 	twccResponder *mockTWCCResponder
 }
 
-func (m *mockGCCWriteStream) Write(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
+func (m *mockGCCWriteStream) Write(header *rtp.Header, payload []byte, _ interceptor.Attributes) (int, error) {
 	pkt, err := (&rtp.Packet{Header: *header, Payload: payload}).Marshal()
 	if err != nil {
 		panic(err)
@@ -125,12 +125,10 @@ func BenchmarkSendSideBWE_WriteRTCP(b *testing.B) {
 				for j := 0; j < seqs; j++ {
 					seq++
 
-					if rand.Intn(5) == 0 { //nolint:gosec,staticcheck
-						// skip this packet
+					if rand.Intn(5) != 0 { //nolint:gosec,staticcheck
+						arrivalTime += int64(rtcp.TypeTCCDeltaScaleFactor * (rand.Intn(128) + 1)) //nolint:gosec
+						r.Record(5000, seq, arrivalTime)
 					}
-
-					arrivalTime += int64(rtcp.TypeTCCDeltaScaleFactor * (rand.Intn(128) + 1)) //nolint:gosec
-					r.Record(5000, seq, arrivalTime)
 				}
 
 				rtcpPackets := r.BuildFeedbackPacket()
diff --git a/pkg/nack/responder_interceptor_test.go b/pkg/nack/responder_interceptor_test.go
index fa1d881..ecc5bb5 100644
--- a/pkg/nack/responder_interceptor_test.go
+++ b/pkg/nack/responder_interceptor_test.go
@@ -113,7 +113,7 @@ func TestResponderInterceptor_DisableCopy(t *testing.T) {
 
 // this test is only useful when being run with the race detector, it won't fail otherwise:
 //
-//     go test -race ./pkg/nack/
+// go test -race ./pkg/nack/
 func TestResponderInterceptor_Race(t *testing.T) {
 	f, err := NewResponderInterceptor(
 		ResponderSize(32768),
diff --git a/pkg/nack/send_buffer_test.go b/pkg/nack/send_buffer_test.go
index 751f6c0..7b00009 100644
--- a/pkg/nack/send_buffer_test.go
+++ b/pkg/nack/send_buffer_test.go
@@ -98,7 +98,7 @@ func TestSendBuffer_Overridden(t *testing.T) {
 
 // this test is only useful when being run with the race detector, it won't fail otherwise:
 //
-//     go test -race ./pkg/nack/
+// go test -race ./pkg/nack/
 func TestSendBuffer_Race(t *testing.T) {
 	pm := newPacketManager()
 	for _, start := range []uint16{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 511, 512, 513, 32767, 32768, 32769, 65527, 65528, 65529, 65530, 65531, 65532, 65533, 65534, 65535} {
diff --git a/pkg/report/receiver_interceptor_test.go b/pkg/report/receiver_interceptor_test.go
index 412122c..aa87a81 100644
--- a/pkg/report/receiver_interceptor_test.go
+++ b/pkg/report/receiver_interceptor_test.go
@@ -182,7 +182,7 @@ func TestReceiverInterceptor(t *testing.T) {
 		assert.Equal(t, 1, len(rr.Reports))
 		assert.Equal(t, rtcp.ReceptionReport{
 			SSRC:               uint32(123456),
-			LastSequenceNumber: 1<<16 | 0x0000,
+			LastSequenceNumber: 1 << 16,
 			LastSenderReport:   0,
 			FractionLost:       0,
 			TotalLost:          0,
diff --git a/pkg/twcc/sender_interceptor_test.go b/pkg/twcc/sender_interceptor_test.go
index 9a9c7f5..36860bb 100644
--- a/pkg/twcc/sender_interceptor_test.go
+++ b/pkg/twcc/sender_interceptor_test.go
@@ -8,6 +8,7 @@ import (
 	"github.com/pion/interceptor/internal/test"
 	"github.com/pion/rtcp"
 	"github.com/pion/rtp"
+	transportTest "github.com/pion/transport/v2/test"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -265,3 +266,37 @@ func TestSenderInterceptor(t *testing.T) {
 		}, cc.PacketChunks)
 	})
 }
+
+func TestSenderInterceptor_Leak(t *testing.T) {
+	lim := transportTest.TimeOut(time.Second * 10)
+	defer lim.Stop()
+
+	report := transportTest.CheckRoutines(t)
+	defer report()
+
+	f, err := NewSenderInterceptor(SendInterval(200 * time.Millisecond))
+	assert.NoError(t, err)
+
+	i, err := f.NewInterceptor("")
+	assert.NoError(t, err)
+
+	stream := test.NewMockStream(&interceptor.StreamInfo{RTPHeaderExtensions: []interceptor.RTPHeaderExtension{
+		{
+			URI: transportCCURI,
+			ID:  1,
+		},
+	}}, i)
+	defer func() {
+		assert.NoError(t, stream.Close())
+	}()
+
+	assert.NoError(t, i.Close())
+	for _, i := range []int{0, 1, 2, 3, 4, 5} {
+		hdr := rtp.Header{}
+		tcc, err := (&rtp.TransportCCExtension{TransportSequence: uint16(i)}).Marshal()
+		assert.NoError(t, err)
+
+		assert.NoError(t, hdr.SetExtension(1, tcc))
+		stream.ReceiveRTP(&rtp.Packet{Header: hdr})
+	}
+}