cs: better handling for contended hash-table locks
When the number of places approaches the number of available processing cores, then a spin lock isn't good enough for a small number of contended hash tables (maybe just one of them). When contention is discovered, fall back to a mutex-based lock.
This commit is contained in:
parent
1101461434
commit
9268dcaad3
|
@ -56,25 +56,99 @@
|
||||||
;; threads, but not at Chez threads. Blocking a Chez thread might
|
;; threads, but not at Chez threads. Blocking a Chez thread might
|
||||||
;; block the Racket scheduler itself, so we just don't support it.
|
;; block the Racket scheduler itself, so we just don't support it.
|
||||||
|
|
||||||
;; Assume low contention on `eq?`- and `eqv?`-based tables across
|
|
||||||
;; Chez Scheme threads, in which case a compare-and-set spinlock is
|
|
||||||
;; good enough.
|
|
||||||
;; Taking a lock disables interrupts, which ensures that the GC
|
;; Taking a lock disables interrupts, which ensures that the GC
|
||||||
;; callback or other atomic actions can use hash tables without
|
;; callback or other atomic actions can use hash tables without
|
||||||
;; deadlocking.
|
;; deadlocking.
|
||||||
(define (make-spinlock) (box #f))
|
|
||||||
|
;; Assume low contention on `eq?`- and `eqv?`-based tables across
|
||||||
|
;; Chez Scheme threads, in which case a compare-and-set spinlock is
|
||||||
|
;; usually good enough. But if not, transition to a real lock; use a
|
||||||
|
;; mutex, but transitioning requires using an inintermediate
|
||||||
|
;; semaphore.
|
||||||
|
(define (make-spinlock)
|
||||||
|
;; Box content: #f (unlocked), #t (locked), sema (transitioning), or mutex
|
||||||
|
(box #f))
|
||||||
(define (spinlock? v) (#%box? v))
|
(define (spinlock? v) (#%box? v))
|
||||||
(define (spinlock-acquire q)
|
(define (spinlock-acquire q)
|
||||||
(let loop ()
|
(let loop ([n 0])
|
||||||
(disable-interrupts)
|
(disable-interrupts)
|
||||||
(unless (#%box-cas! q #f #t)
|
(cond
|
||||||
|
[(#%box-cas! q #f #t)
|
||||||
|
;; Took lock
|
||||||
|
(#%void)]
|
||||||
|
[(eq? #t (#%unbox q))
|
||||||
|
;; Spin..
|
||||||
(enable-interrupts)
|
(enable-interrupts)
|
||||||
(loop))))
|
(cond
|
||||||
|
[(fx= n 1000)
|
||||||
|
;; There's contention after all, so trasition to a semaphore,
|
||||||
|
;; where the current lock holder implicitly owns the semaphore.
|
||||||
|
;; That lock holder can replace the semaphore with a mutex,
|
||||||
|
;; which is cheaper to acquire and release.
|
||||||
|
(let ([lk (new-sema)])
|
||||||
|
(#%box-cas! q #t lk)
|
||||||
|
(loop 0))]
|
||||||
|
[else
|
||||||
|
(loop (fx+ n 1))])]
|
||||||
|
[else
|
||||||
|
(let ([l (#%unbox q)])
|
||||||
|
(cond
|
||||||
|
[(sema? l)
|
||||||
|
;; Transitioning to slower lock; wait on semaphore, then
|
||||||
|
;; try again
|
||||||
|
(enable-interrupts)
|
||||||
|
(sema-wait l)
|
||||||
|
(loop 0)]
|
||||||
|
[(mutex? l)
|
||||||
|
;; Using (permanent) mutex as lock
|
||||||
|
(mutex-acquire l)]
|
||||||
|
[else
|
||||||
|
(enable-interrupts)
|
||||||
|
(loop 0)]))])))
|
||||||
|
|
||||||
(define (spinlock-release q)
|
(define (spinlock-release q)
|
||||||
(#%set-box! q #f)
|
(unless (#%box-cas! q #t #f)
|
||||||
|
;; Contention must have promoted to a semaphore or mutex...
|
||||||
|
(let ([l (#%unbox q)])
|
||||||
|
(cond
|
||||||
|
[(mutex? l)
|
||||||
|
;; Must have been acquired as a plain mutex
|
||||||
|
(mutex-release l)]
|
||||||
|
[else
|
||||||
|
;; Transitioning, so finish transition to a plain mutex
|
||||||
|
(#%set-box! q (make-mutex))
|
||||||
|
(sema-post-all l)])))
|
||||||
(enable-interrupts)
|
(enable-interrupts)
|
||||||
(#%void))
|
(#%void))
|
||||||
|
|
||||||
|
;; Semaphores that include a "post all" operation
|
||||||
|
(define-record sema (v m c))
|
||||||
|
(define (new-sema)
|
||||||
|
(make-sema 0 (make-mutex) (make-condition)))
|
||||||
|
(define (sema-wait l)
|
||||||
|
(mutex-acquire (sema-m l))
|
||||||
|
(let loop ()
|
||||||
|
(let ([v (sema-v l)])
|
||||||
|
(cond
|
||||||
|
[(eqv? v #t) ; posted all
|
||||||
|
(mutex-release (sema-m l))]
|
||||||
|
[(eqv? 0 v)
|
||||||
|
(condition-wait (sema-c l) (sema-m l))
|
||||||
|
(loop)]
|
||||||
|
[else
|
||||||
|
(set-sema-v! l (sub1 v))
|
||||||
|
(mutex-release (sema-m l))]))))
|
||||||
|
(define (sema-post l)
|
||||||
|
(mutex-acquire (sema-m l))
|
||||||
|
(set-sema-v! l (add1 (sema-v l)))
|
||||||
|
(condition-signal (sema-c l))
|
||||||
|
(mutex-release (sema-m l)))
|
||||||
|
(define (sema-post-all l)
|
||||||
|
(mutex-acquire (sema-m l))
|
||||||
|
(set-sema-v! l #t)
|
||||||
|
(condition-broadcast (sema-c l))
|
||||||
|
(mutex-release (sema-m l)))
|
||||||
|
|
||||||
(define (make-lock for-kind)
|
(define (make-lock for-kind)
|
||||||
(cond
|
(cond
|
||||||
[(eq? for-kind 'equal?)
|
[(eq? for-kind 'equal?)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user