Duncan Coutts pushed to branch wip/dcoutts/io-manager-tidy at Glasgow Haskell Compiler / GHC
Commits:
-
b7568cd6
by Duncan Coutts at 2026-07-08T12:26:05+01:00
-
c2368cb2
by Duncan Coutts at 2026-07-08T12:26:05+01:00
5 changed files:
Changes:
| ... | ... | @@ -343,9 +343,7 @@ void initCapabilityIOManager(CapIOManager *iomgr) |
| 343 | 343 | switch (iomgr_type) {
|
| 344 | 344 | #if defined(IOMGR_ENABLED_SELECT)
|
| 345 | 345 | case IO_MANAGER_SELECT:
|
| 346 | - iomgr->blocked_queue_hd = END_TSO_QUEUE;
|
|
| 347 | - iomgr->blocked_queue_tl = END_TSO_QUEUE;
|
|
| 348 | - iomgr->sleeping_queue = END_TSO_QUEUE;
|
|
| 346 | + initCapabilityIOManagerSelect(iomgr);
|
|
| 349 | 347 | break;
|
| 350 | 348 | #endif
|
| 351 | 349 | |
| ... | ... | @@ -376,6 +374,12 @@ void initCapabilityIOManager(CapIOManager *iomgr) |
| 376 | 374 | void freeCapabilityIOManager(CapIOManager *iomgr)
|
| 377 | 375 | {
|
| 378 | 376 | switch (iomgr_type) {
|
| 377 | +#if defined(IOMGR_ENABLED_SELECT)
|
|
| 378 | + case IO_MANAGER_SELECT:
|
|
| 379 | + freeCapabilityIOManagerSelect(iomgr);
|
|
| 380 | + break;
|
|
| 381 | +#endif
|
|
| 382 | + |
|
| 379 | 383 | #if defined(IOMGR_ENABLED_POLL)
|
| 380 | 384 | case IO_MANAGER_POLL:
|
| 381 | 385 | freeCapabilityIOManagerPoll(iomgr);
|
| ... | ... | @@ -728,13 +732,14 @@ void pollCompletedTimeoutsOrIO(CapIOManager *iomgr) |
| 728 | 732 | }
|
| 729 | 733 | |
| 730 | 734 | |
| 731 | -void awaitCompletedTimeoutsOrIO(CapIOManager *iomgr)
|
|
| 735 | +bool awaitCompletedTimeoutsOrIO(CapIOManager *iomgr)
|
|
| 732 | 736 | {
|
| 733 | 737 | debugTrace(DEBUG_iomanager, "waiting for completed IO or timeouts");
|
| 738 | + bool completed = true; // wait completed or interrupted?
|
|
| 734 | 739 | switch (iomgr_type) {
|
| 735 | 740 | #if defined(IOMGR_ENABLED_SELECT)
|
| 736 | 741 | case IO_MANAGER_SELECT:
|
| 737 | - awaitCompletedTimeoutsOrIOSelect(iomgr, true);
|
|
| 742 | + completed = awaitCompletedTimeoutsOrIOSelect(iomgr, true);
|
|
| 738 | 743 | break;
|
| 739 | 744 | #endif
|
| 740 | 745 | |
| ... | ... | @@ -756,9 +761,32 @@ void awaitCompletedTimeoutsOrIO(CapIOManager *iomgr) |
| 756 | 761 | break;
|
| 757 | 762 | #endif
|
| 758 | 763 | default:
|
| 759 | - barf("pollCompletedTimeoutsOrIO not implemented");
|
|
| 764 | + barf("awaitCompletedTimeoutsOrIO not implemented");
|
|
| 765 | + }
|
|
| 766 | + ASSERT(!emptyRunQueue(iomgr->cap) ||
|
|
| 767 | + getSchedState() != SCHED_RUNNING ||
|
|
| 768 | + !completed);
|
|
| 769 | + return completed;
|
|
| 770 | +}
|
|
| 771 | + |
|
| 772 | + |
|
| 773 | +/* Interrupt the I/O manager if it is blocked in awaitCompletedTimeoutsOrIO,
|
|
| 774 | + * causing it to return early and return false.
|
|
| 775 | + */
|
|
| 776 | +void interruptIOManager(CapIOManager *iomgr)
|
|
| 777 | +{
|
|
| 778 | + debugTrace(DEBUG_iomanager, "Interrupting the I/O manager...");
|
|
| 779 | + switch (iomgr_type) {
|
|
| 780 | + |
|
| 781 | +#if defined(IOMGR_ENABLED_SELECT)
|
|
| 782 | + case IO_MANAGER_SELECT:
|
|
| 783 | + interruptIOManagerSelect(iomgr);
|
|
| 784 | + break;
|
|
| 785 | +#endif
|
|
| 786 | + |
|
| 787 | + default:
|
|
| 788 | + break;
|
|
| 760 | 789 | }
|
| 761 | - ASSERT(!emptyRunQueue(iomgr->cap) || getSchedState() != SCHED_RUNNING);
|
|
| 762 | 790 | }
|
| 763 | 791 | |
| 764 | 792 |
| ... | ... | @@ -365,20 +365,32 @@ bool anyPendingTimeoutsOrIO(CapIOManager *iomgr); |
| 365 | 365 | */
|
| 366 | 366 | void pollCompletedTimeoutsOrIO(CapIOManager *iomgr);
|
| 367 | 367 | |
| 368 | - /* If there are any completed I/O operations or expired timers, process the
|
|
| 368 | +/* If there are any completed I/O operations or expired timers, process the
|
|
| 369 | 369 | * completions as appropriate. If there are none, wait until I/O or a timer
|
| 370 | 370 | * does complete (or we get a signal with a handler) and process the
|
| 371 | 371 | * completions as appropriate.
|
| 372 | 372 | *
|
| 373 | - * Upon return this guarantees that the scheduler run queue is non-empty or
|
|
| 374 | - * that the scheduler is no longer in the running state. Succinctly, the
|
|
| 375 | - * post-condition is (!emptyRunQueue(cap) || getSchedState() != SCHED_RUNNING).
|
|
| 373 | + * Upon returning true this guarantees that the scheduler run queue is
|
|
| 374 | + * non-empty or that the scheduler is no longer in the running state.
|
|
| 375 | + * Succinctly, the post-condition in the return true case is
|
|
| 376 | + * (!emptyRunQueue(cap) || getSchedState() != SCHED_RUNNING).
|
|
| 377 | + * A false result means the wait was interrupted by interruptIOManager, and
|
|
| 378 | + * there is no post-condition in this case.
|
|
| 376 | 379 | *
|
| 377 | 380 | * This is only expected to be called if anyPendingTimeoutsOrIO() returns true,
|
| 378 | 381 | * i.e. there actually is something to wait for.
|
| 379 | 382 | *
|
| 380 | 383 | * Called from schedule() both *before* and *after* scheduleDetectDeadlock().
|
| 381 | 384 | */
|
| 382 | -void awaitCompletedTimeoutsOrIO(CapIOManager *iomgr);
|
|
| 385 | +bool awaitCompletedTimeoutsOrIO(CapIOManager *iomgr);
|
|
| 386 | + |
|
| 387 | +/* Interrupt the I/O manager if it is blocked in awaitCompletedTimeoutsOrIO,
|
|
| 388 | + * causing it to return early.
|
|
| 389 | + *
|
|
| 390 | + * Its use is inherently concurrent and racy: the interrupt races against any
|
|
| 391 | + * I/O or timer completion. This does not matter for the intended use case of
|
|
| 392 | + * returning control to the scheduler.
|
|
| 393 | + */
|
|
| 394 | +void interruptIOManager(CapIOManager *iomgr);
|
|
| 383 | 395 | |
| 384 | 396 | #include "EndPrivate.h" |
| ... | ... | @@ -46,6 +46,11 @@ struct _CapIOManager { |
| 46 | 46 | StgTSO *sleeping_queue;
|
| 47 | 47 | #endif
|
| 48 | 48 | |
| 49 | +#if defined(IOMGR_ENABLED_SELECT)
|
|
| 50 | + /* FDs for interrupting up the I/O manager when it is blocked waiting */
|
|
| 51 | + int interrupt_fd_r, interrupt_fd_w;
|
|
| 52 | +#endif
|
|
| 53 | + |
|
| 49 | 54 | #if defined(IOMGR_ENABLED_POLL)
|
| 50 | 55 | /* AIOP and timeout collections shared by several I/O manager impls */
|
| 51 | 56 | ClosureTable aiop_table;
|
| ... | ... | @@ -22,6 +22,7 @@ |
| 22 | 22 | #include "IOManagerInternals.h"
|
| 23 | 23 | #include "Stats.h"
|
| 24 | 24 | #include "GetTime.h"
|
| 25 | +#include "FdWakeup.h"
|
|
| 25 | 26 | |
| 26 | 27 | # if defined(HAVE_SYS_SELECT_H)
|
| 27 | 28 | # include <sys/select.h>
|
| ... | ... | @@ -54,6 +55,31 @@ |
| 54 | 55 | #define TimeToLowResTimeRoundUp(t) (t)
|
| 55 | 56 | #endif
|
| 56 | 57 | |
| 58 | +void initCapabilityIOManagerSelect(CapIOManager *iomgr)
|
|
| 59 | +{
|
|
| 60 | + iomgr->blocked_queue_hd = END_TSO_QUEUE;
|
|
| 61 | + iomgr->blocked_queue_tl = END_TSO_QUEUE;
|
|
| 62 | + iomgr->sleeping_queue = END_TSO_QUEUE;
|
|
| 63 | + |
|
| 64 | +#if defined(HAVE_PREEMPTION)
|
|
| 65 | + newFdWakeup(&iomgr->interrupt_fd_r, &iomgr->interrupt_fd_w);
|
|
| 66 | +#endif
|
|
| 67 | +}
|
|
| 68 | + |
|
| 69 | +void freeCapabilityIOManagerSelect(CapIOManager *iomgr)
|
|
| 70 | +{
|
|
| 71 | +#if defined(HAVE_PREEMPTION)
|
|
| 72 | + closeFdWakeup(iomgr->interrupt_fd_r, iomgr->interrupt_fd_w);
|
|
| 73 | +#endif
|
|
| 74 | +}
|
|
| 75 | + |
|
| 76 | +void interruptIOManagerSelect(CapIOManager *iomgr)
|
|
| 77 | +{
|
|
| 78 | +#if defined(HAVE_PREEMPTION)
|
|
| 79 | + sendFdWakeup(iomgr->interrupt_fd_w);
|
|
| 80 | +#endif
|
|
| 81 | +}
|
|
| 82 | + |
|
| 57 | 83 | /*
|
| 58 | 84 | * Return the time since the program started, in LowResTime,
|
| 59 | 85 | * rounded down.
|
| ... | ... | @@ -215,7 +241,7 @@ static enum FdState fdPollWriteState (int fd) |
| 215 | 241 | * not write handles.
|
| 216 | 242 | *
|
| 217 | 243 | */
|
| 218 | -void
|
|
| 244 | +bool
|
|
| 219 | 245 | awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait)
|
| 220 | 246 | {
|
| 221 | 247 | StgTSO *tso, *prev, *next;
|
| ... | ... | @@ -225,6 +251,7 @@ awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait) |
| 225 | 251 | bool seen_bad_fd = false;
|
| 226 | 252 | struct timeval tv, *ptv;
|
| 227 | 253 | LowResTime now;
|
| 254 | + bool interrupt = false; /* got interrupted up via interruptIOManager */
|
|
| 228 | 255 | |
| 229 | 256 | IF_DEBUG(scheduler,
|
| 230 | 257 | debugBelch("scheduler: checking for threads blocked on I/O");
|
| ... | ... | @@ -243,7 +270,7 @@ awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait) |
| 243 | 270 | |
| 244 | 271 | now = getLowResTimeOfDay();
|
| 245 | 272 | if (wakeUpSleepingThreads(iomgr, now)) {
|
| 246 | - return;
|
|
| 273 | + return true;
|
|
| 247 | 274 | }
|
| 248 | 275 | |
| 249 | 276 | /*
|
| ... | ... | @@ -252,6 +279,15 @@ awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait) |
| 252 | 279 | FD_ZERO(&rfd);
|
| 253 | 280 | FD_ZERO(&wfd);
|
| 254 | 281 | |
| 282 | +#if defined(HAVE_PREEMPTION)
|
|
| 283 | + /* We're always interested in our interrupt fd */
|
|
| 284 | + {
|
|
| 285 | + int fd = iomgr->interrupt_fd_r;
|
|
| 286 | + maxfd = (fd > maxfd) ? fd : maxfd;
|
|
| 287 | + FD_SET(fd, &rfd);
|
|
| 288 | + }
|
|
| 289 | +#endif
|
|
| 290 | + |
|
| 255 | 291 | for(tso = iomgr->blocked_queue_hd;
|
| 256 | 292 | tso != END_TSO_QUEUE;
|
| 257 | 293 | tso = next) {
|
| ... | ... | @@ -354,14 +390,14 @@ awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait) |
| 354 | 390 | #if defined(RTS_USER_SIGNALS)
|
| 355 | 391 | if (RtsFlags.MiscFlags.install_signal_handlers && signals_pending()) {
|
| 356 | 392 | startSignalHandlers(iomgr->cap);
|
| 357 | - return; /* still hold the lock */
|
|
| 393 | + return true; /* still hold the lock */
|
|
| 358 | 394 | }
|
| 359 | 395 | #endif
|
| 360 | 396 | |
| 361 | 397 | /* we were interrupted, return to the scheduler immediately.
|
| 362 | 398 | */
|
| 363 | 399 | if (getSchedState() >= SCHED_INTERRUPTING) {
|
| 364 | - return; /* still hold the lock */
|
|
| 400 | + return true; /* still hold the lock */
|
|
| 365 | 401 | }
|
| 366 | 402 | |
| 367 | 403 | /* check for threads that need waking up
|
| ... | ... | @@ -372,10 +408,19 @@ awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait) |
| 372 | 408 | * I/O and run them.
|
| 373 | 409 | */
|
| 374 | 410 | if (!emptyRunQueue(iomgr->cap)) {
|
| 375 | - return; /* still hold the lock */
|
|
| 411 | + return true; /* still hold the lock */
|
|
| 376 | 412 | }
|
| 377 | 413 | }
|
| 378 | 414 | |
| 415 | +#if defined(HAVE_PREEMPTION)
|
|
| 416 | + /* If the interrupt_fd_r is ready, collect it */
|
|
| 417 | + if (FD_ISSET(iomgr->interrupt_fd_r, &rfd)) {
|
|
| 418 | + collectFdWakeup(iomgr->interrupt_fd_r);
|
|
| 419 | + interrupt = true;
|
|
| 420 | + debugTrace(DEBUG_iomanager, "Received interrupt in select I/O manager");
|
|
| 421 | + }
|
|
| 422 | +#endif
|
|
| 423 | + |
|
| 379 | 424 | /* Step through the waiting queue, unblocking every thread that now has
|
| 380 | 425 | * a file descriptor in a ready state.
|
| 381 | 426 | */
|
| ... | ... | @@ -458,7 +503,9 @@ awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait) |
| 458 | 503 | }
|
| 459 | 504 | |
| 460 | 505 | } while (wait && getSchedState() == SCHED_RUNNING
|
| 461 | - && emptyRunQueue(iomgr->cap));
|
|
| 506 | + && emptyRunQueue(iomgr->cap)
|
|
| 507 | + && !interrupt);
|
|
| 508 | + return !interrupt;
|
|
| 462 | 509 | }
|
| 463 | 510 | |
| 464 | 511 | #endif /* IOMGR_ENABLED_SELECT */ |
| ... | ... | @@ -15,7 +15,12 @@ typedef StgWord LowResTime; |
| 15 | 15 | |
| 16 | 16 | LowResTime getDelayTarget (HsInt us);
|
| 17 | 17 | |
| 18 | -void awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait);
|
|
| 18 | +void initCapabilityIOManagerSelect(CapIOManager *iomgr);
|
|
| 19 | +void freeCapabilityIOManagerSelect(CapIOManager *iomgr);
|
|
| 20 | +void wakeupIOManagerSelect(CapIOManager *iomgr);
|
|
| 21 | + |
|
| 22 | +bool awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait);
|
|
| 23 | +void interruptIOManagerSelect(CapIOManager *iomgr);
|
|
| 19 | 24 | |
| 20 | 25 | #include "EndPrivate.h"
|
| 21 | 26 |