lab.nexedi.com/kirr/go123@v0.0.0-20240207185015-8299741fa871/tracing/tracetest/chan.go (about) 1 // Copyright (C) 2017-2021 Nexedi SA and Contributors. 2 // Kirill Smelkov <kirr@nexedi.com> 3 // 4 // This program is free software: you can Use, Study, Modify and Redistribute 5 // it under the terms of the GNU General Public License version 3, or (at your 6 // option) any later version, as published by the Free Software Foundation. 7 // 8 // You can also Link and Combine this program with other software covered by 9 // the terms of any of the Free Software licenses or any of the Open Source 10 // Initiative approved licenses and Convey the resulting work. Corresponding 11 // source of such a combination shall include the source code for all other 12 // software used. 13 // 14 // This program is distributed WITHOUT ANY WARRANTY; without even the implied 15 // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 16 // 17 // See COPYING file for full licensing terms. 18 // See https://www.nexedi.com/licensing for rationale and options. 19 20 package tracetest 21 // synchronous channels. 22 23 import ( 24 "errors" 25 "flag" 26 "fmt" 27 "reflect" 28 "time" 29 ) 30 31 var ( 32 chatty = flag.Bool("tracetest.v", false, "verbose: print events as they are sent on trace channels") 33 deadTime = flag.Duration("tracetest.deadtime", 3*time.Second, "time after which no events activity is considered to be a deadlock") 34 ) 35 36 // _Msg represents message with 1 event sent over _chan. 37 // 38 // The goroutine which sent the message will wait for Ack before continue. 39 type _Msg struct { 40 Event interface{} 41 ack chan<- error // nil on Ack; !nil on nak 42 } 43 44 // _chan provides synchronous channel associated with a stream. 45 // 46 // It comes with additional property that send blocks until receiving side 47 // explicitly acknowledges message was received and processed. 48 // 49 // New channels must be created via T.newChan. 50 // 51 // It is safe to use _chan from multiple goroutines simultaneously. 52 type _chan struct { 53 t *T // created for stream <.name> under <.t> 54 name string // name of the channel/stream 55 msgq chan *_Msg 56 down chan struct{} // becomes ready when closed 57 58 // messages that were not sent due to e.g. detected deadlock. 59 // T includes these in final printout for pending events 60 // protected by t.mu 61 unsentv []*_Msg 62 } 63 64 // Send sends event to a consumer and waits for ack. 65 // if main testing goroutine detects any problem Send panics. 66 func (ch *_chan) Send(event interface{}) { 67 t := ch.t 68 if *chatty { 69 fmt.Printf("%s <- %T %v\n", ch.name, event, event) 70 } 71 ack := make(chan error, 1) 72 msg := &_Msg{event, ack} 73 unsentWhy := "" 74 select { 75 case ch.msgq <- msg: 76 err := <-ack 77 if err != nil { 78 t.fatalfInNonMain("%s: send: %s", ch.name, err) 79 } 80 return 81 82 case <-ch.down: 83 unsentWhy = "channel was closed" 84 85 case <-time.After(*deadTime): 86 unsentWhy = "deadlock" 87 } 88 89 // remember event as still "send-pending" 90 t.mu.Lock() 91 ch.unsentv = append(ch.unsentv, msg) 92 t.mu.Unlock() 93 94 t.fatalfInNonMain("%s: send: %s", ch.name, unsentWhy) 95 } 96 97 // Close closes the sending side of the channel. 98 func (ch *_chan) Close() { 99 close(ch.down) // note - not .msgq 100 } 101 102 // Recv receives message from a producer. 103 // 104 // The consumer, after dealing with the message, must send back an ack. 105 // Must be called from main testing thread. 106 func (ch *_chan) Recv() *_Msg { 107 t := ch.t; t.Helper() 108 msg := ch.recv() 109 if msg == nil { 110 t.Fatalf("%s: recv: deadlock\n", ch.name) 111 } 112 return msg 113 } 114 115 // RecvInto receives message from a producer, verifies that event type is the 116 // same as type of *event, and saves received event there. 117 // 118 // Must be called from main testing thread. 119 func (ch *_chan) RecvInto(eventp interface{}) *_Msg { 120 t := ch.t; t.Helper() 121 msg := ch.recv() 122 if msg == nil { 123 t.Fatalf("%s: recv: deadlock waiting for %T\n", ch.name, eventp) 124 } 125 126 reventp := reflect.ValueOf(eventp) 127 if reventp.Type().Elem() != reflect.TypeOf(msg.Event) { 128 t.queuenak(msg, "unexpected event type") 129 t.Fatalf("%s: expect: %s: got %T %v", ch.name, reventp.Elem().Type(), msg.Event, msg.Event) 130 } 131 132 // *eventp = msg.Event 133 reventp.Elem().Set(reflect.ValueOf(msg.Event)) 134 135 return msg 136 } 137 138 func (ch *_chan) recv() *_Msg { 139 select { 140 case msg := <-ch.msgq: 141 return msg // ok 142 143 case <-time.After(*deadTime): 144 return nil // deadlock 145 } 146 } 147 148 149 // Ack acknowledges the event was processed and unblocks producer goroutine. 150 func (m *_Msg) Ack() { 151 m.ack <- nil 152 } 153 154 // nak tells sender that event verification failed and why. 155 // it is called only by tracetest internals. 156 func (m *_Msg) nak(why string) { 157 m.ack <- errors.New(why) 158 } 159 160 // nak represents scheduled call to `msg.nak(why)`. 161 type nak struct { 162 msg *_Msg 163 why string 164 } 165 166 // queuenak schedules call to `msg.nak(why)`. 167 func (t *T) queuenak(msg *_Msg, why string) { 168 t.nakq = append(t.nakq, nak{msg, why}) 169 } 170 171 // newChan creates new _chan channel. 172 func (t *T) newChan(name string) *_chan { 173 // NOTE T ensures not to create channels with duplicate names. 174 return &_chan{t: t, name: name, msgq: make(chan *_Msg), down: make(chan struct{})} 175 }