detached 993f038

fix(dgram): order reassembly diagnostic commits

commit
993f038ce4ddb3be3e40c6708eeabd4e1455f731
author
brice · · 2026-07-10 06:10 UTC
parent
b4de712
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
2 files · +23 -24
M internal/adapters/dgram/network_sim_test.go +1 -3
@@ -622,9 +622,7 @@ func TestInconsistentAuthenticatedFragmentDoesNotCountAsContact(t *testing.T) {
622622 if gotInflight != 0 {
623623 t.Fatalf("reassembly inflight after rejected inconsistent fragment=%d, want 0", gotInflight)
624624 }
625- b.diagnosticCh = make(chan Diagnostic, 1)
626- b.emitDiagnostic()
627- diagnostic := awaitResult(t, b.diagnosticCh, "diagnostic snapshot after inconsistent fragment")
625+ diagnostic := b.diagnosticSnapshot()
628626 if diagnostic.ReassemblyInflight != 0 {
629627 t.Fatalf("diagnostic reassembly inflight after rejected inconsistent fragment=%d, want 0", diagnostic.ReassemblyInflight)
630628 }
@@ -622,9 +622,7 @@ func TestInconsistentAuthenticatedFragmentDoesNotCountAsContact(t *testing.T) {
622 if gotInflight != 0 {622 if gotInflight != 0 {
623 t.Fatalf("reassembly inflight after rejected inconsistent fragment=%d, want 0", gotInflight)623 t.Fatalf("reassembly inflight after rejected inconsistent fragment=%d, want 0", gotInflight)
624 }624 }
625- b.diagnosticCh = make(chan Diagnostic, 1)625+ diagnostic := b.diagnosticSnapshot()
626- b.emitDiagnostic()
627- diagnostic := awaitResult(t, b.diagnosticCh, "diagnostic snapshot after inconsistent fragment")
628 if diagnostic.ReassemblyInflight != 0 {626 if diagnostic.ReassemblyInflight != 0 {
629 t.Fatalf("diagnostic reassembly inflight after rejected inconsistent fragment=%d, want 0", diagnostic.ReassemblyInflight)627 t.Fatalf("diagnostic reassembly inflight after rejected inconsistent fragment=%d, want 0", diagnostic.ReassemblyInflight)
630 }628 }
M internal/adapters/dgram/transport.go +22 -21
@@ -795,8 +795,20 @@ func (t *Transport) readLoop(pc net.PacketConn) {
795795 }
796796 payload, complete, err := t.reasm.Add(frag)
797797 inflight := t.reasm.Inflight()
798+ // Keep the diagnostic mirror ordered with reassembly mutations. Rebind can
799+ // overlap old and new read loops, so recvMu must remain held through commit.
800+ t.mu.Lock()
801+ t.reassemblyInflight = inflight
802+ var afterAuthenticated func()
803+ if err == nil {
804+ t.lastAuthenticatedPacket = t.clock.Now()
805+ afterAuthenticated = t.afterAuthenticatedPacket
806+ }
807+ t.mu.Unlock()
798808 t.recvMu.Unlock()
799- t.recordFragmentResult(inflight, err == nil)
809+ if afterAuthenticated != nil {
810+ afterAuthenticated()
811+ }
800812 if err != nil {
801813 t.notifyMalformedFragment()
802814 continue
@@ -819,21 +831,6 @@ func (t *Transport) notifyMalformedFragment() {
819831 }
820832 }
821833
822-func (t *Transport) recordFragmentResult(inflight int, accepted bool) {
823- t.mu.Lock()
824- t.reassemblyInflight = inflight
825- if !accepted {
826- t.mu.Unlock()
827- return
828- }
829- t.lastAuthenticatedPacket = t.clock.Now()
830- after := t.afterAuthenticatedPacket
831- t.mu.Unlock()
832- if after != nil {
833- after()
834- }
835-}
836-
837834 func (t *Transport) recordCompleteRecord() {
838835 t.mu.Lock()
839836 t.lastCompleteRecord = t.clock.Now()
@@ -1310,6 +1307,14 @@ func (t *Transport) emitDiagnostic() {
13101307 if t.diagnosticCh == nil {
13111308 return
13121309 }
1310+ d := t.diagnosticSnapshot()
1311+ select {
1312+ case t.diagnosticCh <- d:
1313+ default:
1314+ }
1315+}
1316+
1317+func (t *Transport) diagnosticSnapshot() Diagnostic {
13131318 t.mu.Lock()
13141319 now := t.clock.Now()
13151320 lastAuthenticatedPacket := t.lastAuthenticatedPacket
@@ -1327,7 +1332,7 @@ func (t *Transport) emitDiagnostic() {
13271332 reassemblyInflight := t.reassemblyInflight
13281333 t.mu.Unlock()
13291334
1330- d := Diagnostic{
1335+ return Diagnostic{
13311336 At: now,
13321337 State: state,
13331338 SinceAuthenticatedPacket: diagnosticAge(now, lastAuthenticatedPacket),
@@ -1338,10 +1343,6 @@ func (t *Transport) emitDiagnostic() {
13381343 Retransmits: retransmits,
13391344 ReassemblyInflight: reassemblyInflight,
13401345 }
1341- select {
1342- case t.diagnosticCh <- d:
1343- default:
1344- }
13451346 }
13461347
13471348 func diagnosticAge(now, then time.Time) time.Duration {
@@ -795,8 +795,20 @@ func (t *Transport) readLoop(pc net.PacketConn) {
795 }795 }
796 payload, complete, err := t.reasm.Add(frag)796 payload, complete, err := t.reasm.Add(frag)
797 inflight := t.reasm.Inflight()797 inflight := t.reasm.Inflight()
798+ // Keep the diagnostic mirror ordered with reassembly mutations. Rebind can
799+ // overlap old and new read loops, so recvMu must remain held through commit.
800+ t.mu.Lock()
801+ t.reassemblyInflight = inflight
802+ var afterAuthenticated func()
803+ if err == nil {
804+ t.lastAuthenticatedPacket = t.clock.Now()
805+ afterAuthenticated = t.afterAuthenticatedPacket
806+ }
807+ t.mu.Unlock()
798 t.recvMu.Unlock()808 t.recvMu.Unlock()
799- t.recordFragmentResult(inflight, err == nil)809+ if afterAuthenticated != nil {
810+ afterAuthenticated()
811+ }
800 if err != nil {812 if err != nil {
801 t.notifyMalformedFragment()813 t.notifyMalformedFragment()
802 continue814 continue
@@ -819,21 +831,6 @@ func (t *Transport) notifyMalformedFragment() {
819 }831 }
820 }832 }
821 833
822-func (t *Transport) recordFragmentResult(inflight int, accepted bool) {
823- t.mu.Lock()
824- t.reassemblyInflight = inflight
825- if !accepted {
826- t.mu.Unlock()
827- return
828- }
829- t.lastAuthenticatedPacket = t.clock.Now()
830- after := t.afterAuthenticatedPacket
831- t.mu.Unlock()
832- if after != nil {
833- after()
834- }
835-}
836-
837 func (t *Transport) recordCompleteRecord() {834 func (t *Transport) recordCompleteRecord() {
838 t.mu.Lock()835 t.mu.Lock()
839 t.lastCompleteRecord = t.clock.Now()836 t.lastCompleteRecord = t.clock.Now()
@@ -1310,6 +1307,14 @@ func (t *Transport) emitDiagnostic() {
1310 if t.diagnosticCh == nil {1307 if t.diagnosticCh == nil {
1311 return1308 return
1312 }1309 }
1310+ d := t.diagnosticSnapshot()
1311+ select {
1312+ case t.diagnosticCh <- d:
1313+ default:
1314+ }
1315+}
1316+
1317+func (t *Transport) diagnosticSnapshot() Diagnostic {
1313 t.mu.Lock()1318 t.mu.Lock()
1314 now := t.clock.Now()1319 now := t.clock.Now()
1315 lastAuthenticatedPacket := t.lastAuthenticatedPacket1320 lastAuthenticatedPacket := t.lastAuthenticatedPacket
@@ -1327,7 +1332,7 @@ func (t *Transport) emitDiagnostic() {
1327 reassemblyInflight := t.reassemblyInflight1332 reassemblyInflight := t.reassemblyInflight
1328 t.mu.Unlock()1333 t.mu.Unlock()
1329 1334
1330- d := Diagnostic{1335+ return Diagnostic{
1331 At: now,1336 At: now,
1332 State: state,1337 State: state,
1333 SinceAuthenticatedPacket: diagnosticAge(now, lastAuthenticatedPacket),1338 SinceAuthenticatedPacket: diagnosticAge(now, lastAuthenticatedPacket),
@@ -1338,10 +1343,6 @@ func (t *Transport) emitDiagnostic() {
1338 Retransmits: retransmits,1343 Retransmits: retransmits,
1339 ReassemblyInflight: reassemblyInflight,1344 ReassemblyInflight: reassemblyInflight,
1340 }1345 }
1341- select {
1342- case t.diagnosticCh <- d:
1343- default:
1344- }
1345 }1346 }
1346 1347
1347 func diagnosticAge(now, then time.Time) time.Duration {1348 func diagnosticAge(now, then time.Time) time.Duration {

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 12

Tags 11

↑↓ moveenter openesc close