github.com/matrixorigin/matrixone@v0.7.0/pkg/common/morpc/future.go (about)

     1  // Copyright 2021 - 2022 Matrix Origin
     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 morpc
    16  
    17  import (
    18  	"runtime"
    19  	"sync"
    20  	"sync/atomic"
    21  )
    22  
    23  func newFuture(releaseFunc func(f *Future)) *Future {
    24  	f := &Future{
    25  		c:           make(chan Message, 1),
    26  		errC:        make(chan error, 1),
    27  		writtenC:    make(chan error, 1),
    28  		releaseFunc: releaseFunc,
    29  	}
    30  	f.setFinalizer()
    31  	return f
    32  }
    33  
    34  // Future is used to obtain response data synchronously.
    35  type Future struct {
    36  	send RPCMessage
    37  	c    chan Message
    38  	errC chan error
    39  	// used to check error for sending message
    40  	writtenC    chan error
    41  	sended      atomic.Uint32
    42  	releaseFunc func(*Future)
    43  	mu          struct {
    44  		sync.Mutex
    45  		closed bool
    46  		ref    int
    47  		cb     func()
    48  	}
    49  }
    50  
    51  func (f *Future) init(send RPCMessage) {
    52  	if _, ok := send.Ctx.Deadline(); !ok {
    53  		panic("context deadline not set")
    54  	}
    55  	f.sended.Store(0)
    56  	f.send = send
    57  	f.mu.Lock()
    58  	f.mu.closed = false
    59  	f.mu.Unlock()
    60  }
    61  
    62  // Get get the response data synchronously, blocking until `context.Done` or the response is received.
    63  // This method cannot be called more than once. After calling `Get`, `Close` must be called to close
    64  // `Future`.
    65  func (f *Future) Get() (Message, error) {
    66  	// we have to wait until the message is written, otherwise it will result in the message still
    67  	// waiting in the send queue after the Get returns, causing concurrent reading and writing on the
    68  	// request.
    69  	if err := f.waitSendCompleted(); err != nil {
    70  		return nil, err
    71  	}
    72  	select {
    73  	case <-f.send.Ctx.Done():
    74  		return nil, f.send.Ctx.Err()
    75  	case resp := <-f.c:
    76  		return resp, nil
    77  	case err := <-f.errC:
    78  		return nil, err
    79  	}
    80  }
    81  
    82  // Close close the future.
    83  func (f *Future) Close() {
    84  	f.mu.Lock()
    85  	defer f.mu.Unlock()
    86  
    87  	f.mu.closed = true
    88  	if f.mu.cb != nil {
    89  		f.mu.cb()
    90  	}
    91  	f.maybeReleaseLocked()
    92  }
    93  
    94  func (f *Future) waitSendCompleted() error {
    95  	err := <-f.writtenC
    96  	return err
    97  }
    98  
    99  func (f *Future) messageSended(err error) {
   100  	if f.sended.CompareAndSwap(0, 1) {
   101  		f.writtenC <- err
   102  		f.unRef()
   103  	}
   104  }
   105  
   106  func (f *Future) maybeReleaseLocked() {
   107  	if f.mu.closed && f.mu.ref == 0 && f.releaseFunc != nil {
   108  		f.releaseFunc(f)
   109  	}
   110  }
   111  
   112  func (f *Future) getSendMessageID() uint64 {
   113  	return f.send.Message.GetID()
   114  }
   115  
   116  func (f *Future) done(response Message, cb func()) {
   117  	f.mu.Lock()
   118  	defer f.mu.Unlock()
   119  
   120  	if !f.mu.closed && !f.timeout() {
   121  		if response.GetID() != f.getSendMessageID() {
   122  			return
   123  		}
   124  		f.mu.cb = cb
   125  		f.c <- response
   126  	} else if cb != nil {
   127  		cb()
   128  	}
   129  }
   130  
   131  func (f *Future) error(id uint64, err error, cb func()) {
   132  	f.mu.Lock()
   133  	defer f.mu.Unlock()
   134  
   135  	if !f.mu.closed && !f.timeout() {
   136  		if id != f.getSendMessageID() {
   137  			return
   138  		}
   139  		f.mu.cb = cb
   140  		f.errC <- err
   141  	} else if cb != nil {
   142  		cb()
   143  	}
   144  }
   145  
   146  func (f *Future) ref() {
   147  	f.mu.Lock()
   148  	defer f.mu.Unlock()
   149  
   150  	f.mu.ref++
   151  }
   152  
   153  func (f *Future) unRef() {
   154  	f.mu.Lock()
   155  	defer f.mu.Unlock()
   156  
   157  	f.mu.ref--
   158  	if f.mu.ref < 0 {
   159  		panic("BUG")
   160  	}
   161  	f.maybeReleaseLocked()
   162  }
   163  
   164  func (f *Future) reset() {
   165  	select {
   166  	case <-f.c:
   167  	default:
   168  	}
   169  	f.send = RPCMessage{}
   170  	f.mu.cb = nil
   171  }
   172  
   173  func (f *Future) timeout() bool {
   174  	select {
   175  	case <-f.send.Ctx.Done():
   176  		return true
   177  	default:
   178  		return false
   179  	}
   180  }
   181  
   182  func (f *Future) setFinalizer() {
   183  	// when we need to reuse, we need to keep chan from being closed to avoid
   184  	// repeated creation. When Future is released by sync.Pool and is GC'd, we
   185  	// need to close chan to avoid resource leaks.
   186  	runtime.SetFinalizer(f, func(f *Future) {
   187  		close(f.c)
   188  		close(f.errC)
   189  		close(f.writtenC)
   190  	})
   191  }