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