Summary
Context-switch from GC causes infinite GC loop
Metadata
- Id: 423d8950958bca54b4b7ca17613915bc7019a4a6
- Trac id: 1586
- Type: defect
- Reporter: megane
- Owner:
- Cc:
- Status: closed
- Component: compiler
- Estimated difficulty: insane
- Resolution: fixed
- Priority: major
- Milestone: 5.2
- Version: 5.0.0
- Changetime: 2019-09-14 16:10:40 UTC
- Created: 2019-02-16 13:48:52 UTC
- Keywords: garbage collector context-switch threads scheduler
Attachments
- 423d8950958bca54b4b7ca17613915bc7019a4a6/attachments/finalizer-fuzz.scm
Description
The following code causes the runtime to enter an infinite GC loop.
With help of Christian and Kon the cause was determined to be a re-entry to the GC while GC'ing.
In the example below the thread running a finalizer runs out of its quantum while running `spin` and does a context switch. The other thread then starts a new GC which causes the issue.
Using `disable-interrupts` makes the issue go away. I haven't tested if `disable-interrupts` is enough if the finalizer calls something that was compiled without `disable-interrupts`.
I'll attach a fuzzer that can be used instead if the example doesn't work.
;; (declare (disable-interrupts))
(cond-expand (chicken-5 (import (chicken base))
(import (chicken gc))
(import (srfi 18)))
(else (use srfi-18)))
(define (spin count) (let lp2 ([i 0]) (when (< i count) (lp2 (add1 i)))))
(define (add-finalized) (set-finalizer! (list 'foo) (lambda (o) (spin 100))))
(let lp ([i 0])
(while (< i 100)
(print* i " ")
(let ([threads (list (make-thread
(lambda ()
(add-finalized) (add-finalized)
(gc #t)
(add-finalized) (add-finalized) (add-finalized))
"thread-a")
(make-thread (lambda () (gc #t) (add-finalized) (add-finalized))
"thread-b"))])
(for-each thread-start! threads)
(for-each thread-join! threads))
(lp (add1 i))))
(print "dones")
;; Output:
;; $ csc gcloop2.scm && ./gcloop2
;; 0 1 2 3 4 5 6 7 8 9 10 11 12 <hangs here>
Changes and comments
[2019-02-16 13:49:23 UTC] megane attached finalizer-fuzz.scm (description=#f)
[2019-04-20 17:34:37 UTC] megane wrote:
The implication of this is that a program will have a greater than zero change of hanging with the gc-loop if these conditions are met:
- The program uses threads - The program has a finalizer that has been compiled without `disable-interrupts` - There is a `(gc #t)` call after a finalizer has been installed
[2019-09-14 16:10:04 UTC] sjamaan changed status from new to closed
[2019-09-14 16:10:04 UTC] sjamaan set resolution to fixed
[2019-09-14 16:10:04 UTC] sjamaan wrote:
Fixed with 63ebf0fa
[2019-09-14 16:10:40 UTC] sjamaan set milestone to 5.2
[2019-09-14 16:10:40 UTC] sjamaan wrote:
Setting milestone so we can generate a list of fixed tickets in 5.2