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  }