github.com/pygolin/runtime@v0.0.0-20201208210830-a62e3cd39798/threading.go (about)

     1  // Copyright 2016 Google Inc. All Rights Reserved.
     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 runtime
    16  
    17  import (
    18  	"sync"
    19  	"sync/atomic"
    20  	"unsafe"
    21  )
    22  
    23  const (
    24  	argsCacheSize = 16
    25  	argsCacheArgc = 6
    26  )
    27  
    28  type threadState struct {
    29  	reprState    map[*Object]bool
    30  	excValue     *BaseException
    31  	excTraceback *Traceback
    32  	// argsCache is a small, per-thread LIFO cache for arg lists. Entries
    33  	// have a fixed capacity so calls to functions with larger parameter
    34  	// lists will be allocated afresh each time. Args freed when the cache
    35  	// is full are dropped. If the cache is empty then a new args slice
    36  	// will be allocated.
    37  	argsCache []Args
    38  
    39  	// frameCache is a local cache of allocated frames almost ready for
    40  	// reuse. The cache is maintained through the Frame `back` pointer as a
    41  	// singly linked list.
    42  	frameCache *Frame
    43  }
    44  
    45  func newThreadState() *threadState {
    46  	return &threadState{argsCache: make([]Args, 0, argsCacheSize)}
    47  }
    48  
    49  // recursiveMutex implements a typical reentrant lock, similar to Python's
    50  // RLock. Lock can be called multiple times for the same frame stack.
    51  type recursiveMutex struct {
    52  	mutex       sync.Mutex
    53  	threadState *threadState
    54  	count       int
    55  }
    56  
    57  func (m *recursiveMutex) Lock(f *Frame) {
    58  	p := (*unsafe.Pointer)(unsafe.Pointer(&m.threadState))
    59  	if (*threadState)(atomic.LoadPointer(p)) != f.threadState {
    60  		// m.threadState != f.threadState implies m is not held by this
    61  		// thread and therefore we won't deadlock acquiring the mutex.
    62  		m.mutex.Lock()
    63  		// m.threadState is now guaranteed to be empty (otherwise we
    64  		// couldn't have acquired m.mutex) so store our own thread ID.
    65  		atomic.StorePointer(p, unsafe.Pointer(f.threadState))
    66  		m.count++
    67  	} else {
    68  		m.count++
    69  	}
    70  }
    71  
    72  func (m *recursiveMutex) Unlock(f *Frame) {
    73  	p := (*unsafe.Pointer)(unsafe.Pointer(&m.threadState))
    74  	if (*threadState)(atomic.LoadPointer(p)) != f.threadState {
    75  		logFatal("recursiveMutex.Unlock: frame did not match that passed to Lock")
    76  	}
    77  	// Since we're unlocking, we must hold m.mutex, so this is safe.
    78  	if m.count <= 0 {
    79  		logFatal("recursiveMutex.Unlock: Unlock called too many times")
    80  	}
    81  	m.count--
    82  	if m.count == 0 {
    83  		atomic.StorePointer(p, unsafe.Pointer(nil))
    84  		m.mutex.Unlock()
    85  	}
    86  }
    87  
    88  // TryableMutex is a mutex-like object that also supports TryLock().
    89  type TryableMutex struct {
    90  	c chan bool
    91  }
    92  
    93  // NewTryableMutex returns a new TryableMutex.
    94  func NewTryableMutex() *TryableMutex {
    95  	m := &TryableMutex{make(chan bool, 1)}
    96  	m.Unlock()
    97  	return m
    98  }
    99  
   100  // Lock blocks until the mutex is available and then acquires a lock.
   101  func (m *TryableMutex) Lock() {
   102  	<-m.c
   103  }
   104  
   105  // TryLock returns true and acquires a lock if the mutex is available, otherwise
   106  // it returns false.
   107  func (m *TryableMutex) TryLock() bool {
   108  	select {
   109  	case <-m.c:
   110  		return true
   111  	default:
   112  		return false
   113  	}
   114  }
   115  
   116  // Unlock releases the mutex's lock.
   117  func (m *TryableMutex) Unlock() {
   118  	m.c <- true
   119  }