@@ -2221,6 +2221,9 @@ top:
2221
2221
if _p_ .runSafePointFn != 0 {
2222
2222
runSafePointFn ()
2223
2223
}
2224
+
2225
+ now , pollUntil , _ := checkTimers (_p_ , 0 )
2226
+
2224
2227
if fingwait && fingwake {
2225
2228
if gp := wakefing (); gp != nil {
2226
2229
ready (gp , 0 , true )
@@ -2266,12 +2269,7 @@ top:
2266
2269
2267
2270
// Steal work from other P's.
2268
2271
procs := uint32 (gomaxprocs )
2269
- if atomic .Load (& sched .npidle ) == procs - 1 {
2270
- // Either GOMAXPROCS=1 or everybody, except for us, is idle already.
2271
- // New work can appear from returning syscall/cgocall, network or timers.
2272
- // Neither of that submits to local run queues, so no point in stealing.
2273
- goto stop
2274
- }
2272
+ ranTimer := false
2275
2273
// If number of spinning M's >= number of busy P's, block.
2276
2274
// This is necessary to prevent excessive CPU consumption
2277
2275
// when GOMAXPROCS>>1 but the program parallelism is low.
@@ -2288,11 +2286,48 @@ top:
2288
2286
goto top
2289
2287
}
2290
2288
stealRunNextG := i > 2 // first look for ready queues with more than 1 g
2291
- if gp := runqsteal (_p_ , allp [enum .position ()], stealRunNextG ); gp != nil {
2289
+ p2 := allp [enum .position ()]
2290
+ if _p_ == p2 {
2291
+ continue
2292
+ }
2293
+ if gp := runqsteal (_p_ , p2 , stealRunNextG ); gp != nil {
2292
2294
return gp , false
2293
2295
}
2296
+
2297
+ // Consider stealing timers from p2.
2298
+ // This call to checkTimers is the only place where
2299
+ // we hold a lock on a different P's timers.
2300
+ // Lock contention can be a problem here, so avoid
2301
+ // grabbing the lock if p2 is running and not marked
2302
+ // for preemption. If p2 is running and not being
2303
+ // preempted we assume it will handle its own timers.
2304
+ if i > 2 && shouldStealTimers (p2 ) {
2305
+ tnow , w , ran := checkTimers (p2 , now )
2306
+ now = tnow
2307
+ if w != 0 && (pollUntil == 0 || w < pollUntil ) {
2308
+ pollUntil = w
2309
+ }
2310
+ if ran {
2311
+ // Running the timers may have
2312
+ // made an arbitrary number of G's
2313
+ // ready and added them to this P's
2314
+ // local run queue. That invalidates
2315
+ // the assumption of runqsteal
2316
+ // that is always has room to add
2317
+ // stolen G's. So check now if there
2318
+ // is a local G to run.
2319
+ if gp , inheritTime := runqget (_p_ ); gp != nil {
2320
+ return gp , inheritTime
2321
+ }
2322
+ ranTimer = true
2323
+ }
2324
+ }
2294
2325
}
2295
2326
}
2327
+ if ranTimer {
2328
+ // Running a timer may have made some goroutine ready.
2329
+ goto top
2330
+ }
2296
2331
2297
2332
stop:
2298
2333
@@ -2309,6 +2344,12 @@ stop:
2309
2344
return gp , false
2310
2345
}
2311
2346
2347
+ delta := int64 (- 1 )
2348
+ if pollUntil != 0 {
2349
+ // checkTimers ensures that polluntil > now.
2350
+ delta = pollUntil - now
2351
+ }
2352
+
2312
2353
// wasm only:
2313
2354
// If a callback returned and no other goroutine is awake,
2314
2355
// then pause execution until a callback was triggered.
@@ -2400,14 +2441,16 @@ stop:
2400
2441
}
2401
2442
2402
2443
// poll network
2403
- if netpollinited () && atomic .Load (& netpollWaiters ) > 0 && atomic .Xchg64 (& sched .lastpoll , 0 ) != 0 {
2444
+ if netpollinited () && (atomic .Load (& netpollWaiters ) > 0 || pollUntil != 0 ) && atomic .Xchg64 (& sched .lastpoll , 0 ) != 0 {
2445
+ atomic .Store64 (& sched .pollUntil , uint64 (pollUntil ))
2404
2446
if _g_ .m .p != 0 {
2405
2447
throw ("findrunnable: netpoll with p" )
2406
2448
}
2407
2449
if _g_ .m .spinning {
2408
2450
throw ("findrunnable: netpoll with spinning" )
2409
2451
}
2410
- list := netpoll (- 1 ) // block until new work is available
2452
+ list := netpoll (delta ) // block until new work is available
2453
+ atomic .Store64 (& sched .pollUntil , 0 )
2411
2454
atomic .Store64 (& sched .lastpoll , uint64 (nanotime ()))
2412
2455
lock (& sched .lock )
2413
2456
_p_ = pidleget ()
@@ -2431,6 +2474,11 @@ stop:
2431
2474
}
2432
2475
goto top
2433
2476
}
2477
+ } else if pollUntil != 0 && netpollinited () {
2478
+ pollerPollUntil := int64 (atomic .Load64 (& sched .pollUntil ))
2479
+ if pollerPollUntil == 0 || pollerPollUntil > pollUntil {
2480
+ netpollBreak ()
2481
+ }
2434
2482
}
2435
2483
stopm ()
2436
2484
goto top
@@ -2457,6 +2505,22 @@ func pollWork() bool {
2457
2505
return false
2458
2506
}
2459
2507
2508
+ // wakeNetPoller wakes up the thread sleeping in the network poller,
2509
+ // if there is one, and if it isn't going to wake up anyhow before
2510
+ // the when argument.
2511
+ func wakeNetPoller (when int64 ) {
2512
+ if atomic .Load64 (& sched .lastpoll ) == 0 {
2513
+ // In findrunnable we ensure that when polling the pollUntil
2514
+ // field is either zero or the time to which the current
2515
+ // poll is expected to run. This can have a spurious wakeup
2516
+ // but should never miss a wakeup.
2517
+ pollerPollUntil := int64 (atomic .Load64 (& sched .pollUntil ))
2518
+ if pollerPollUntil == 0 || pollerPollUntil > when {
2519
+ netpollBreak ()
2520
+ }
2521
+ }
2522
+ }
2523
+
2460
2524
func resetspinning () {
2461
2525
_g_ := getg ()
2462
2526
if ! _g_ .m .spinning {
@@ -2525,10 +2589,20 @@ top:
2525
2589
gcstopm ()
2526
2590
goto top
2527
2591
}
2528
- if _g_ .m .p .ptr ().runSafePointFn != 0 {
2592
+ pp := _g_ .m .p .ptr ()
2593
+ if pp .runSafePointFn != 0 {
2529
2594
runSafePointFn ()
2530
2595
}
2531
2596
2597
+ // Sanity check: if we are spinning, the run queue should be empty.
2598
+ // Check this before calling checkTimers, as that might call
2599
+ // goready to put a ready goroutine on the local run queue.
2600
+ if _g_ .m .spinning && (pp .runnext != 0 || pp .runqhead != pp .runqtail ) {
2601
+ throw ("schedule: spinning with local work" )
2602
+ }
2603
+
2604
+ checkTimers (pp , 0 )
2605
+
2532
2606
var gp * g
2533
2607
var inheritTime bool
2534
2608
@@ -2560,9 +2634,8 @@ top:
2560
2634
}
2561
2635
if gp == nil {
2562
2636
gp , inheritTime = runqget (_g_ .m .p .ptr ())
2563
- if gp != nil && _g_ .m .spinning {
2564
- throw ("schedule: spinning with local work" )
2565
- }
2637
+ // We can see gp != nil here even if the M is spinning,
2638
+ // if checkTimers added a local goroutine via goready.
2566
2639
}
2567
2640
if gp == nil {
2568
2641
gp , inheritTime = findrunnable () // blocks until work is available
@@ -2623,6 +2696,60 @@ func dropg() {
2623
2696
setGNoWB (& _g_ .m .curg , nil )
2624
2697
}
2625
2698
2699
+ // checkTimers runs any timers for the P that are ready.
2700
+ // If now is not 0 it is the current time.
2701
+ // It returns the current time or 0 if it is not known,
2702
+ // and the time when the next timer should run or 0 if there is no next timer,
2703
+ // and reports whether it ran any timers.
2704
+ // If the time when the next timer should run is not 0,
2705
+ // it is always larger than the returned time.
2706
+ // We pass now in and out to avoid extra calls of nanotime.
2707
+ //go:yeswritebarrierrec
2708
+ func checkTimers (pp * p , now int64 ) (rnow , pollUntil int64 , ran bool ) {
2709
+ lock (& pp .timersLock )
2710
+
2711
+ adjusttimers (pp )
2712
+
2713
+ rnow = now
2714
+ if len (pp .timers ) > 0 {
2715
+ if rnow == 0 {
2716
+ rnow = nanotime ()
2717
+ }
2718
+ for len (pp .timers ) > 0 {
2719
+ if tw := runtimer (pp , rnow ); tw != 0 {
2720
+ if tw > 0 {
2721
+ pollUntil = tw
2722
+ }
2723
+ break
2724
+ }
2725
+ ran = true
2726
+ }
2727
+ }
2728
+
2729
+ unlock (& pp .timersLock )
2730
+
2731
+ return rnow , pollUntil , ran
2732
+ }
2733
+
2734
+ // shouldStealTimers reports whether we should try stealing the timers from p2.
2735
+ // We don't steal timers from a running P that is not marked for preemption,
2736
+ // on the assumption that it will run its own timers. This reduces
2737
+ // contention on the timers lock.
2738
+ func shouldStealTimers (p2 * p ) bool {
2739
+ if p2 .status != _Prunning {
2740
+ return true
2741
+ }
2742
+ mp := p2 .m .ptr ()
2743
+ if mp == nil || mp .locks > 0 {
2744
+ return false
2745
+ }
2746
+ gp := mp .curg
2747
+ if gp == nil || gp .atomicstatus != _Grunning || ! gp .preempt {
2748
+ return false
2749
+ }
2750
+ return true
2751
+ }
2752
+
2626
2753
func parkunlock_c (gp * g , lock unsafe.Pointer ) bool {
2627
2754
unlock ((* mutex )(lock ))
2628
2755
return true
@@ -4305,6 +4432,13 @@ func checkdead() {
4305
4432
return
4306
4433
}
4307
4434
4435
+ // There are no goroutines running, so we can look at the P's.
4436
+ for _ , _p_ := range allp {
4437
+ if len (_p_ .timers ) > 0 {
4438
+ return
4439
+ }
4440
+ }
4441
+
4308
4442
getg ().m .throwing = - 1 // do not dump full stacks
4309
4443
throw ("all goroutines are asleep - deadlock!" )
4310
4444
}
@@ -4392,6 +4526,12 @@ func sysmon() {
4392
4526
incidlelocked (1 )
4393
4527
}
4394
4528
}
4529
+ if timeSleepUntil () < now {
4530
+ // There are timers that should have already run,
4531
+ // perhaps because there is an unpreemptible P.
4532
+ // Try to start an M to run them.
4533
+ startm (nil , false )
4534
+ }
4395
4535
// retake P's blocked in syscalls
4396
4536
// and preempt long running G's
4397
4537
if retake (now ) != 0 {
0 commit comments