github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/syncevent/syncevent_example_test.go (about)

     1  // Copyright 2020 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package syncevent
    16  
    17  import (
    18  	"fmt"
    19  	"sync/atomic"
    20  	"time"
    21  )
    22  
    23  func Example_ioReadinessInterrputible() {
    24  	const (
    25  		evReady = Set(1 << iota)
    26  		evInterrupt
    27  	)
    28  	errNotReady := fmt.Errorf("not ready for I/O")
    29  
    30  	// State of some I/O object.
    31  	var (
    32  		br    Broadcaster
    33  		ready uint32
    34  	)
    35  	doIO := func() error {
    36  		if atomic.LoadUint32(&ready) == 0 {
    37  			return errNotReady
    38  		}
    39  		return nil
    40  	}
    41  	go func() {
    42  		// The I/O object eventually becomes ready for I/O.
    43  		time.Sleep(100 * time.Millisecond)
    44  		// When it does, it first ensures that future calls to isReady() return
    45  		// true, then broadcasts the readiness event to Receivers.
    46  		atomic.StoreUint32(&ready, 1)
    47  		br.Broadcast(evReady)
    48  	}()
    49  
    50  	// Each user of the I/O object owns a Waiter.
    51  	var w Waiter
    52  	w.Init()
    53  	// The Waiter may be asynchronously interruptible, e.g. for signal
    54  	// handling in the sentry.
    55  	go func() {
    56  		time.Sleep(200 * time.Millisecond)
    57  		w.Receiver().Notify(evInterrupt)
    58  	}()
    59  
    60  	// To use the I/O object:
    61  	//
    62  	// Optionally, if the I/O object is likely to be ready, attempt I/O first.
    63  	err := doIO()
    64  	if err == nil {
    65  		// Success, we're done.
    66  		return /* nil */
    67  	}
    68  	if err != errNotReady {
    69  		// Failure, I/O failed for some reason other than readiness.
    70  		return /* err */
    71  	}
    72  	// Subscribe for readiness events from the I/O object.
    73  	id := br.SubscribeEvents(w.Receiver(), evReady)
    74  	// When we are finished blocking, unsubscribe from readiness events and
    75  	// remove readiness events from the pending event set.
    76  	defer UnsubscribeAndAck(&br, w.Receiver(), evReady, id)
    77  	for {
    78  		// Attempt I/O again. This must be done after the call to SubscribeEvents,
    79  		// since the I/O object might have become ready between the previous call
    80  		// to doIO and the call to SubscribeEvents.
    81  		err = doIO()
    82  		if err == nil {
    83  			return /* nil */
    84  		}
    85  		if err != errNotReady {
    86  			return /* err */
    87  		}
    88  		// Block until either the I/O object indicates it is ready, or we are
    89  		// interrupted.
    90  		events := w.Wait()
    91  		if events&evInterrupt != 0 {
    92  			// In the specific case of sentry signal handling, signal delivery
    93  			// is handled by another system, so we aren't responsible for
    94  			// acknowledging evInterrupt.
    95  			return /* errInterrupted */
    96  		}
    97  		// Note that, in a concurrent context, the I/O object might become
    98  		// ready and then not ready again. To handle this:
    99  		//
   100  		// - evReady must be acknowledged before calling doIO() again (rather
   101  		// than after), so that if the I/O object becomes ready *again* after
   102  		// the call to doIO(), the readiness event is not lost.
   103  		//
   104  		// - We must loop instead of just calling doIO() once after receiving
   105  		// evReady.
   106  		w.Ack(evReady)
   107  	}
   108  }