github.com/matrixorigin/matrixone@v1.2.0/pkg/txn/service/service_event.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 service
    16  
    17  import (
    18  	"context"
    19  	"runtime"
    20  	"sync"
    21  
    22  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    23  	"github.com/matrixorigin/matrixone/pkg/pb/txn"
    24  )
    25  
    26  var (
    27  	waiterPool = sync.Pool{
    28  		New: func() any {
    29  			w := &waiter{c: make(chan txn.TxnStatus, 1)}
    30  			w.setFinalizer()
    31  			return w
    32  		},
    33  	}
    34  
    35  	notifierPool = sync.Pool{
    36  		New: func() any {
    37  			n := &notifier{}
    38  			n.waiters = make([][]*waiter, len(txn.TxnStatus_name))
    39  			return n
    40  		},
    41  	}
    42  )
    43  
    44  func acquireWaiter() *waiter {
    45  	w := waiterPool.Get().(*waiter)
    46  	w.ref()
    47  	w.reuse++
    48  	return w
    49  }
    50  
    51  type waiter struct {
    52  	c     chan txn.TxnStatus
    53  	reuse uint64
    54  
    55  	mu struct {
    56  		sync.RWMutex
    57  		closed bool
    58  		// A waiter can only be notified once, regardless of how many transactions it watches.
    59  		notified bool
    60  		// The wait will held by notifier and the place to use waiter. The waiter can only be recycled to
    61  		// sync.Pool if it is no longer referenced by any.
    62  		ref int
    63  	}
    64  }
    65  
    66  func (w *waiter) ref() {
    67  	w.mu.Lock()
    68  	defer w.mu.Unlock()
    69  
    70  	w.mu.ref++
    71  }
    72  
    73  func (w *waiter) unref() {
    74  	w.mu.Lock()
    75  	defer w.mu.Unlock()
    76  
    77  	w.unrefLocked()
    78  	w.maybeReleaseLocked()
    79  }
    80  
    81  func (w *waiter) unrefLocked() {
    82  	w.mu.ref--
    83  	if w.mu.ref < 0 {
    84  		panic("invalid ref value")
    85  	}
    86  }
    87  
    88  func (w *waiter) notify(status txn.TxnStatus) bool {
    89  	w.mu.Lock()
    90  	defer w.mu.Unlock()
    91  
    92  	if w.mu.notified {
    93  		panic("already notified")
    94  	}
    95  
    96  	w.mu.notified = true
    97  	if w.mu.closed {
    98  		return false
    99  	}
   100  	select {
   101  	case w.c <- status:
   102  	default:
   103  		panic("BUG")
   104  	}
   105  	return true
   106  }
   107  
   108  func (w *waiter) close() {
   109  	w.mu.Lock()
   110  	defer w.mu.Unlock()
   111  
   112  	w.mu.closed = true
   113  	w.unrefLocked()
   114  	w.maybeReleaseLocked()
   115  }
   116  
   117  func (w *waiter) maybeReleaseLocked() {
   118  	if w.mu.ref == 0 {
   119  		w.mu.closed = false
   120  		w.mu.notified = false
   121  		select {
   122  		case <-w.c:
   123  		default:
   124  		}
   125  		waiterPool.Put(w)
   126  	}
   127  }
   128  
   129  // wait only 3 results:
   130  // 1. want status, nil
   131  // 2. any, context.Done error
   132  // 3. final status(committed or aborted), nil
   133  func (w *waiter) wait(ctx context.Context) (txn.TxnStatus, error) {
   134  	select {
   135  	case <-ctx.Done():
   136  		return txn.TxnStatus_Aborted, moerr.ConvertGoError(ctx, ctx.Err())
   137  	case status := <-w.c:
   138  		return status, nil
   139  	}
   140  }
   141  
   142  func (w *waiter) setFinalizer() {
   143  	runtime.SetFinalizer(w, func(w *waiter) {
   144  		close(w.c)
   145  	})
   146  }
   147  
   148  func acquireNotifier() *notifier {
   149  	return notifierPool.Get().(*notifier)
   150  }
   151  
   152  // notifier a transaction corresponds to a notifier, for other transactions that need to be notified that
   153  // the current transaction has reached the specified status.
   154  type notifier struct {
   155  	sync.Mutex
   156  	waiters [][]*waiter
   157  }
   158  
   159  func (s *notifier) close(status txn.TxnStatus) {
   160  	s.Lock()
   161  	defer s.Unlock()
   162  
   163  	for i, waiters := range s.waiters {
   164  		for j := range waiters {
   165  			waiters[j].notify(status)
   166  			waiters[j].unref()
   167  			waiters[j] = nil
   168  		}
   169  		s.waiters[i] = waiters[:0]
   170  	}
   171  	notifierPool.Put(s)
   172  }
   173  
   174  func (s *notifier) notify(status txn.TxnStatus) int {
   175  	s.Lock()
   176  	defer s.Unlock()
   177  
   178  	if !s.hasWaitersLocked(status) {
   179  		return 0
   180  	}
   181  
   182  	waiters := s.waiters[status]
   183  	n := 0
   184  	for idx, w := range waiters {
   185  		if w.notify(status) {
   186  			n++
   187  		}
   188  		w.unref()
   189  		waiters[idx] = nil
   190  	}
   191  	s.waiters[status] = waiters[:0]
   192  	return n
   193  }
   194  
   195  func (s *notifier) addWaiter(w *waiter, status txn.TxnStatus) {
   196  	s.Lock()
   197  	defer s.Unlock()
   198  
   199  	w.ref()
   200  	s.waiters[status] = append(s.waiters[status], w)
   201  }
   202  
   203  func (s *notifier) hasWaitersLocked(status txn.TxnStatus) bool {
   204  	return len(s.waiters[status]) > 0
   205  }