gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/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 "time" 20 21 "gvisor.dev/gvisor/pkg/atomicbitops" 22 ) 23 24 func Example_ioReadinessInterrputible() { 25 const ( 26 evReady = Set(1 << iota) 27 evInterrupt 28 ) 29 errNotReady := fmt.Errorf("not ready for I/O") 30 31 // State of some I/O object. 32 var ( 33 br Broadcaster 34 ready atomicbitops.Uint32 35 ) 36 doIO := func() error { 37 if ready.Load() == 0 { 38 return errNotReady 39 } 40 return nil 41 } 42 go func() { 43 // The I/O object eventually becomes ready for I/O. 44 time.Sleep(100 * time.Millisecond) 45 // When it does, it first ensures that future calls to isReady() return 46 // true, then broadcasts the readiness event to Receivers. 47 ready.Store(1) 48 br.Broadcast(evReady) 49 }() 50 51 // Each user of the I/O object owns a Waiter. 52 var w Waiter 53 w.Init() 54 // The Waiter may be asynchronously interruptible, e.g. for signal 55 // handling in the sentry. 56 go func() { 57 time.Sleep(200 * time.Millisecond) 58 w.Receiver().Notify(evInterrupt) 59 }() 60 61 // To use the I/O object: 62 // 63 // Optionally, if the I/O object is likely to be ready, attempt I/O first. 64 err := doIO() 65 if err == nil { 66 // Success, we're done. 67 return /* nil */ 68 } 69 if err != errNotReady { 70 // Failure, I/O failed for some reason other than readiness. 71 return /* err */ 72 } 73 // Subscribe for readiness events from the I/O object. 74 id := br.SubscribeEvents(w.Receiver(), evReady) 75 // When we are finished blocking, unsubscribe from readiness events and 76 // remove readiness events from the pending event set. 77 defer UnsubscribeAndAck(&br, w.Receiver(), evReady, id) 78 for { 79 // Attempt I/O again. This must be done after the call to SubscribeEvents, 80 // since the I/O object might have become ready between the previous call 81 // to doIO and the call to SubscribeEvents. 82 err = doIO() 83 if err == nil { 84 return /* nil */ 85 } 86 if err != errNotReady { 87 return /* err */ 88 } 89 // Block until either the I/O object indicates it is ready, or we are 90 // interrupted. 91 events := w.Wait() 92 if events&evInterrupt != 0 { 93 // In the specific case of sentry signal handling, signal delivery 94 // is handled by another system, so we aren't responsible for 95 // acknowledging evInterrupt. 96 return /* errInterrupted */ 97 } 98 // Note that, in a concurrent context, the I/O object might become 99 // ready and then not ready again. To handle this: 100 // 101 // - evReady must be acknowledged before calling doIO() again (rather 102 // than after), so that if the I/O object becomes ready *again* after 103 // the call to doIO(), the readiness event is not lost. 104 // 105 // - We must loop instead of just calling doIO() once after receiving 106 // evReady. 107 w.Ack(evReady) 108 } 109 }