Duncan Coutts pushed to branch wip/dcoutts/io-manager-tidy at Glasgow Haskell Compiler / GHC

Commits:

13 changed files:

Changes:

  • rts/IOManager.c
    ... ... @@ -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);
    
    ... ... @@ -735,13 +739,13 @@ bool awaitCompletedTimeoutsOrIO(CapIOManager *iomgr)
    735 739
         switch (iomgr_type) {
    
    736 740
     #if defined(IOMGR_ENABLED_SELECT)
    
    737 741
             case IO_MANAGER_SELECT:
    
    738
    -          awaitCompletedTimeoutsOrIOSelect(iomgr, true);
    
    742
    +          completed = awaitCompletedTimeoutsOrIOSelect(iomgr, true);
    
    739 743
               break;
    
    740 744
     #endif
    
    741 745
     
    
    742 746
     #if defined(IOMGR_ENABLED_POLL)
    
    743 747
             case IO_MANAGER_POLL:
    
    744
    -          awaitCompletedTimeoutsOrIOPoll(iomgr);
    
    748
    +          completed = awaitCompletedTimeoutsOrIOPoll(iomgr);
    
    745 749
               break;
    
    746 750
     #endif
    
    747 751
     
    
    ... ... @@ -753,7 +757,7 @@ bool awaitCompletedTimeoutsOrIO(CapIOManager *iomgr)
    753 757
     #if defined(IOMGR_ENABLED_WINIO)
    
    754 758
             case IO_MANAGER_WINIO:
    
    755 759
     #endif
    
    756
    -          awaitCompletedTimeoutsOrIOWin32(iomgr->cap, true);
    
    760
    +          completed = awaitCompletedTimeoutsOrIOWin32(iomgr->cap, true);
    
    757 761
               break;
    
    758 762
     #endif
    
    759 763
             default:
    
    ... ... @@ -774,6 +778,32 @@ void interruptIOManager(CapIOManager *iomgr)
    774 778
         debugTrace(DEBUG_iomanager, "Interrupting the I/O manager...");
    
    775 779
         switch (iomgr_type) {
    
    776 780
     
    
    781
    +#if defined(IOMGR_ENABLED_SELECT)
    
    782
    +        case IO_MANAGER_SELECT:
    
    783
    +            interruptIOManagerSelect(iomgr);
    
    784
    +            break;
    
    785
    +#endif
    
    786
    +
    
    787
    +#if defined(IOMGR_ENABLED_POLL)
    
    788
    +        case IO_MANAGER_POLL:
    
    789
    +            interruptIOManagerPoll(iomgr);
    
    790
    +            break;
    
    791
    +#endif
    
    792
    +
    
    793
    +#if defined(IOMGR_ENABLED_WIN32_LEGACY)
    
    794
    +        case IO_MANAGER_WIN32_LEGACY:
    
    795
    +            abandonRequestWait();
    
    796
    +            break;
    
    797
    +#endif
    
    798
    +
    
    799
    +#if defined(IOMGR_ENABLED_WINIO)
    
    800
    +        case IO_MANAGER_WINIO:
    
    801
    +            /* FIXME: no support yet for interrupting in WinIO I/O manager
    
    802
    +             * See issue #27403
    
    803
    +             */
    
    804
    +            break;
    
    805
    +#endif
    
    806
    +
    
    777 807
             default:
    
    778 808
                 break;
    
    779 809
         }
    

  • rts/IOManager.h
    ... ... @@ -319,7 +319,7 @@ void scavengeTSOIOManager(StgTSO *tso);
    319 319
     /* Several code paths are almost identical between read and write paths. In
    
    320 320
      * such cases we use a shared code path with an enum to say which we're doing.
    
    321 321
      */
    
    322
    -typedef enum { IORead, IOWrite } IOReadOrWrite;
    
    322
    +typedef enum { IORead = 0, IOWrite = 1 } IOReadOrWrite;
    
    323 323
     
    
    324 324
     /* Synchronous operations: I/O and delays. As synchronous operations they
    
    325 325
      * necessarily operate on threads. The thread is suspended until the operation
    

  • rts/IOManagerInternals.h
    ... ... @@ -46,6 +46,13 @@ struct _CapIOManager {
    46 46
         StgTSO *sleeping_queue;
    
    47 47
     #endif
    
    48 48
     
    
    49
    +#if defined(IOMGR_ENABLED_SELECT) || defined(IOMGR_ENABLED_POLL)
    
    50
    +#if defined(HAVE_PREEMPTION)
    
    51
    +    /* FDs for waking up the I/O manager when it is blocked waiting */
    
    52
    +    int interrupt_fd_r, interrupt_fd_w;
    
    53
    +#endif
    
    54
    +#endif
    
    55
    +
    
    49 56
     #if defined(IOMGR_ENABLED_POLL)
    
    50 57
         /* AIOP and timeout collections shared by several I/O manager impls */
    
    51 58
         ClosureTable     aiop_table;
    
    ... ... @@ -53,8 +60,11 @@ struct _CapIOManager {
    53 60
     #endif
    
    54 61
     
    
    55 62
     #if defined(IOMGR_ENABLED_POLL)
    
    56
    -    /* Auxiliary table with size and indexes matching the aiop_table */
    
    57
    -    struct pollfd *aiop_poll_table;
    
    63
    +    /* Auxiliary table with size and indexes matching the aiop_table. This is
    
    64
    +     * aliased to the tail of the full poll table, which has a head entry for
    
    65
    +     * the wakeup_fd_r above, so we can also poll that fd.
    
    66
    +     */
    
    67
    +    struct pollfd *aiop_poll_table, *full_poll_table;
    
    58 68
     #endif
    
    59 69
     
    
    60 70
     #if defined(IOMGR_ENABLED_WIN32_LEGACY)
    

  • rts/PrimOps.cmm
    ... ... @@ -2269,7 +2269,7 @@ stg_waitReadzh ( W_ fd )
    2269 2269
     
    
    2270 2270
         (ok) = ccall syncIOWaitReady(Capability_iomgr(MyCapability()) "ptr",
    
    2271 2271
                                      CurrentTSO "ptr",
    
    2272
    -                                 /* IORead */ 0::I32, fd);
    
    2272
    +                                 /* IORead */ 0::CInt, fd);
    
    2273 2273
         if (ok != 0::CBool) (likely: True) {
    
    2274 2274
             jump stg_block_noregs();
    
    2275 2275
         } else {
    
    ... ... @@ -2283,7 +2283,7 @@ stg_waitWritezh ( W_ fd )
    2283 2283
     
    
    2284 2284
         (ok) = ccall syncIOWaitReady(Capability_iomgr(MyCapability()) "ptr",
    
    2285 2285
                                      CurrentTSO "ptr",
    
    2286
    -                                 /* IOWrite */ 1::I32, fd);
    
    2286
    +                                 /* IOWrite */ 1::CInt, fd);
    
    2287 2287
         if (ok != 0::CBool) (likely: True) {
    
    2288 2288
             jump stg_block_noregs();
    
    2289 2289
         } else {
    

  • rts/posix/FdWakeup.h
    ... ... @@ -29,12 +29,14 @@
    29 29
     
    
    30 30
     #include "BeginPrivate.h"
    
    31 31
     
    
    32
    +#if defined(HAVE_PREEMPTION)
    
    32 33
     void newFdWakeup(int *fd_r, int *fd_w);
    
    33 34
     void closeFdWakeup(int fd_r, int fd_w);
    
    34 35
     
    
    35 36
     /* This is safe to use from a signal handler */
    
    36 37
     void sendFdWakeup(int fd_w);
    
    37 38
     void collectFdWakeup(int fd_r);
    
    39
    +#endif
    
    38 40
     
    
    39 41
     #include "EndPrivate.h"
    
    40 42
     

  • rts/posix/Poll.c
    ... ... @@ -41,6 +41,7 @@
    41 41
     
    
    42 42
     #include "IOManagerInternals.h"
    
    43 43
     #include "Timeout.h"
    
    44
    +#include "FdWakeup.h"
    
    44 45
     
    
    45 46
     /******************************************************************************
    
    46 47
     
    
    ... ... @@ -107,8 +108,9 @@ timeout (if any) as the poll() timeout parameter.
    107 108
     The CapIOManager structure for this I/O manager contains:
    
    108 109
     
    
    109 110
         ClosureTable     aiop_table;
    
    110
    -    struct pollfd   *aiop_poll_table;
    
    111
    +    struct pollfd   *aiop_poll_table, *full_poll_table;
    
    111 112
         StgTimeoutQueue *timeout_queue;
    
    113
    +    int interrupt_fd_r, interrupt_fd_w;
    
    112 114
     
    
    113 115
     We also support the Linux-specific ppoll API which supports higher resolution
    
    114 116
     time delays -- nanoseconds rather than milliseconds as in classic poll(). It
    
    ... ... @@ -117,6 +119,15 @@ also allows the signal mask to be adjusted, but we do not make use of this.
    117 119
        int ppoll(struct pollfd *fds, nfds_t nfds,
    
    118 120
                const struct timespec *tmo_p, const sigset_t *sigmask);
    
    119 121
     
    
    122
    +We have both aiop_poll_table and full_poll_table. This is to cope with needing
    
    123
    +to wait on the special extra file descriptor interrupt_fd_r. This fd is used to
    
    124
    +support waking the I/O manager when we are blocked in a poll call. This
    
    125
    +requires waiting on an extra fd that has no corresponding entry in the
    
    126
    +aiop_table. To manage this quirk, we alias the aiop_poll_table to be the tail
    
    127
    +of the full_poll_table and have the first entry of the full_poll_table be the
    
    128
    +interrupt_fd_r. This means the aiop_poll_table indicies match up exactly with
    
    129
    +the aiop_table, but still allows the full_poll_table to have an extra entry.
    
    130
    +
    
    120 131
     ******************************************************************************/
    
    121 132
     
    
    122 133
     /* Forward declarations */
    
    ... ... @@ -129,16 +140,34 @@ static void reportPollError(int res, nfds_t nfds) STG_NORETURN;
    129 140
     void initCapabilityIOManagerPoll(CapIOManager *iomgr)
    
    130 141
     {
    
    131 142
         initClosureTable(&iomgr->aiop_table, ClosureTableCompact);
    
    132
    -    iomgr->aiop_poll_table = NULL;
    
    133 143
         iomgr->timeout_queue = emptyTimeoutQueue();
    
    144
    +
    
    145
    +#if defined(HAVE_PREEMPTION)
    
    146
    +    newFdWakeup(&iomgr->interrupt_fd_r, &iomgr->interrupt_fd_w);
    
    147
    +#endif
    
    148
    +
    
    149
    +    iomgr->full_poll_table = stgMallocBytes(sizeof(struct pollfd) /* size 1 */,
    
    150
    +                                            "initCapabilityIOManagerPoll");
    
    151
    +    iomgr->full_poll_table[0] = (struct pollfd) {
    
    152
    +#if defined(HAVE_PREEMPTION)
    
    153
    +                                  .fd      = iomgr->interrupt_fd_r,
    
    154
    +                                  .events  = POLLIN,
    
    155
    +#else
    
    156
    +                                  .fd      = -1, // unused
    
    157
    +                                  .events  = 0,  // unused
    
    158
    +#endif
    
    159
    +                                  .revents = 0
    
    160
    +                                };
    
    161
    +    iomgr->aiop_poll_table = iomgr->full_poll_table+1; /* hence empty */
    
    134 162
     }
    
    135 163
     
    
    136 164
     
    
    137 165
     void freeCapabilityIOManagerPoll(CapIOManager *iomgr)
    
    138 166
     {
    
    139
    -    if (iomgr->aiop_poll_table) {
    
    140
    -        stgFree(iomgr->aiop_poll_table);
    
    141
    -    }
    
    167
    +    stgFree(iomgr->full_poll_table);
    
    168
    +#if defined(HAVE_PREEMPTION)
    
    169
    +    closeFdWakeup(iomgr->interrupt_fd_r, iomgr->interrupt_fd_w);
    
    170
    +#endif
    
    142 171
     }
    
    143 172
     
    
    144 173
     
    
    ... ... @@ -295,7 +324,7 @@ static void notifyIOCompletion(CapIOManager *iomgr, StgAsyncIOOp *aiop)
    295 324
     }
    
    296 325
     
    
    297 326
     
    
    298
    -static void processIOCompletions(CapIOManager *iomgr, int ncompletions)
    
    327
    +static bool processIOCompletions(CapIOManager *iomgr, int ncompletions)
    
    299 328
     {
    
    300 329
         /* The scheme we use with poll is that we have a dense poll table, and a
    
    301 330
          * corresponding table that maps to the closure table index. The poll
    
    ... ... @@ -305,6 +334,19 @@ static void processIOCompletions(CapIOManager *iomgr, int ncompletions)
    305 334
          */
    
    306 335
         debugTrace(DEBUG_iomanager, "processIOCompletions(ncompletions = %d)",
    
    307 336
                                     ncompletions);
    
    337
    +
    
    338
    +    bool interrupt = false;
    
    339
    +#if defined(HAVE_PREEMPTION)
    
    340
    +    /* If the interrupt_fd_r is ready, collect it */
    
    341
    +    if (iomgr->full_poll_table[0].revents) {
    
    342
    +        ASSERT(iomgr->full_poll_table[0].fd == iomgr->interrupt_fd_r);
    
    343
    +        collectFdWakeup(iomgr->interrupt_fd_r);
    
    344
    +        ncompletions--;
    
    345
    +        interrupt = true;
    
    346
    +        debugTrace(DEBUG_iomanager, "Received interrupt in poll I/O manager");
    
    347
    +    }
    
    348
    +#endif
    
    349
    +
    
    308 350
         struct pollfd *aiop_poll_table = iomgr->aiop_poll_table;
    
    309 351
         int n = ncompletions;
    
    310 352
         int i = 0;
    
    ... ... @@ -357,11 +399,14 @@ static void processIOCompletions(CapIOManager *iomgr, int ncompletions)
    357 399
                 i++;
    
    358 400
             }
    
    359 401
         }
    
    402
    +    return interrupt;
    
    360 403
     }
    
    361 404
     
    
    362 405
     
    
    363 406
     void pollCompletedTimeoutsOrIOPoll(CapIOManager *iomgr)
    
    364 407
     {
    
    408
    +    ASSERT(iomgr->aiop_poll_table == iomgr->full_poll_table+1);
    
    409
    +
    
    365 410
         if (!isEmptyTimeoutQueue(iomgr->timeout_queue)) {
    
    366 411
             Time now = getProcessElapsedTime();
    
    367 412
             processTimeoutCompletions(iomgr, now);
    
    ... ... @@ -369,20 +414,28 @@ void pollCompletedTimeoutsOrIOPoll(CapIOManager *iomgr)
    369 414
     
    
    370 415
         if (!isEmptyClosureTable(&iomgr->aiop_table)) {
    
    371 416
     
    
    372
    -        nfds_t nfds = sizeClosureTable(&iomgr->aiop_table);
    
    417
    +#if defined(HAVE_PREEMPTION)
    
    418
    +        /* the full_poll_table includes interrupt_fd_r */
    
    419
    +        nfds_t nfds = sizeClosureTable(&iomgr->aiop_table) + 1;
    
    420
    +        struct pollfd *poll_table = iomgr->full_poll_table;
    
    421
    +#else
    
    422
    +        /* the aiop_poll_table does not include interrupt_fd_r */
    
    423
    +        nfds_t nfds = sizeClosureTable(&iomgr->aiop_table) + 0;
    
    424
    +        struct pollfd *poll_table = iomgr->aiop_poll_table;
    
    425
    +#endif
    
    373 426
     
    
    374 427
             /* Poll for I/O readiness, without waiting. */
    
    375 428
     #if defined(HAVE_DECL_PPOLL) && HAVE_DECL_PPOLL == 1
    
    376 429
             /* We could use poll here, since we use no timeout, but for
    
    377 430
                consistency we use the same syscall as at the other call site. */
    
    378 431
             struct timespec tv = (struct timespec) { .tv_sec = 0, .tv_nsec = 0 };
    
    379
    -        int res = ppoll(iomgr->aiop_poll_table, nfds, &tv, NULL);
    
    432
    +        int res = ppoll(poll_table, nfds, &tv, NULL);
    
    380 433
     
    
    381 434
             debugTrace(DEBUG_iomanager,
    
    382 435
                        "ppoll(nfds = %d, timeout.sec = 0, timeout.nsec = 0) = %d",
    
    383 436
                        nfds, res);
    
    384 437
     #else
    
    385
    -        int res = poll(iomgr->aiop_poll_table, nfds, 0);
    
    438
    +        int res = poll(poll_table, nfds, 0);
    
    386 439
     
    
    387 440
             debugTrace(DEBUG_iomanager,
    
    388 441
                        "poll(nfds = %d, timeout_ms = 0) = %d",
    
    ... ... @@ -408,8 +461,12 @@ void pollCompletedTimeoutsOrIOPoll(CapIOManager *iomgr)
    408 461
     }
    
    409 462
     
    
    410 463
     
    
    411
    -void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr)
    
    464
    +bool awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr)
    
    412 465
     {
    
    466
    +    bool interrupt = false; /* got woken up via interruptIOManager */
    
    467
    +
    
    468
    +    ASSERT(iomgr->aiop_poll_table == iomgr->full_poll_table+1);
    
    469
    +
    
    413 470
         /* Loop until we've woken up some threads. This loop is needed because the
    
    414 471
          * poll() timing isn't accurate, we sometimes sleep for a while but not
    
    415 472
          * long enough to wake up a thread in a threadDelay. Or we may need to
    
    ... ... @@ -431,6 +488,16 @@ void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr)
    431 488
              */
    
    432 489
             bool wait = emptyRunQueue(iomgr->cap);
    
    433 490
     
    
    491
    +#if defined(HAVE_PREEMPTION)
    
    492
    +        /* the full_poll_table includes interrupt_fd_r */
    
    493
    +        nfds_t nfds = sizeClosureTable(&iomgr->aiop_table) + 1;
    
    494
    +        struct pollfd *poll_table = iomgr->full_poll_table;
    
    495
    +#else
    
    496
    +        /* the aiop_poll_table does not include interrupt_fd_r */
    
    497
    +        nfds_t nfds = sizeClosureTable(&iomgr->aiop_table) + 0;
    
    498
    +        struct pollfd *poll_table = iomgr->aiop_poll_table;
    
    499
    +#endif
    
    500
    +
    
    434 501
             /* Decide if we are going to wait if no I/O is ready, either:
    
    435 502
              * poll only, wait indefinitely, or wait until a timeout.
    
    436 503
              */
    
    ... ... @@ -442,9 +509,8 @@ void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr)
    442 509
     #endif
    
    443 510
     
    
    444 511
             /* Check for I/O readiness, possibly waiting. */
    
    445
    -        nfds_t nfds = sizeClosureTable(&iomgr->aiop_table);
    
    446 512
     #if defined(HAVE_DECL_PPOLL) && HAVE_DECL_PPOLL == 1
    
    447
    -        int res = ppoll(iomgr->aiop_poll_table, nfds, timeout_ns, NULL);
    
    513
    +        int res = ppoll(poll_table, nfds, timeout_ns, NULL);
    
    448 514
     
    
    449 515
             debugTrace(DEBUG_iomanager,
    
    450 516
                        "ppoll(nfds = %d, timeout.sec = %d, timeout.nsec = %d) = %d",
    
    ... ... @@ -452,7 +518,7 @@ void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr)
    452 518
                              timeout_ns == NULL ?  0 : timeout_ns->tv_nsec,
    
    453 519
                        res);
    
    454 520
     #else
    
    455
    -        int res = poll(iomgr->aiop_poll_table, nfds, timeout_ms);
    
    521
    +        int res = poll(poll_table, nfds, timeout_ms);
    
    456 522
     
    
    457 523
             debugTrace(DEBUG_iomanager,
    
    458 524
                        "poll(nfds = %d, timeout_ms = %d) = %d",
    
    ... ... @@ -474,7 +540,7 @@ void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr)
    474 540
             } else if (res > 0) {
    
    475 541
                 int ncompletions = res;
    
    476 542
                 ASSERT(ncompletions <= (int)nfds);
    
    477
    -            processIOCompletions(iomgr, ncompletions);
    
    543
    +            interrupt = processIOCompletions(iomgr, ncompletions);
    
    478 544
                 // FIXME: do we also need to check for timeout completions now?
    
    479 545
                 // we have a non-empty queue, but if !wait then we have also moved
    
    480 546
                 // on and so we sould check for timeouts.
    
    ... ... @@ -502,7 +568,9 @@ void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr)
    502 568
             }
    
    503 569
     
    
    504 570
         } while (emptyRunQueue(iomgr->cap)
    
    571
    +         && !interrupt
    
    505 572
              && (getSchedState() == SCHED_RUNNING));
    
    573
    +    return !interrupt;
    
    506 574
     }
    
    507 575
     
    
    508 576
     static void reportPollError(int res, nfds_t nfds)
    
    ... ... @@ -521,6 +589,14 @@ static void reportPollError(int res, nfds_t nfds)
    521 589
     }
    
    522 590
     
    
    523 591
     
    
    592
    +void interruptIOManagerPoll(CapIOManager *iomgr)
    
    593
    +{
    
    594
    +#if defined(HAVE_PREEMPTION)
    
    595
    +    sendFdWakeup(iomgr->interrupt_fd_w);
    
    596
    +#endif
    
    597
    +}
    
    598
    +
    
    599
    +
    
    524 600
     /* Helper function to double the size of the aiop_table and aiop_poll_table.
    
    525 601
      */
    
    526 602
     static bool enlargeTables(CapIOManager *iomgr)
    
    ... ... @@ -531,13 +607,17 @@ static bool enlargeTables(CapIOManager *iomgr)
    531 607
         bool ok = enlargeClosureTable(iomgr->cap, &iomgr->aiop_table, newcapacity);
    
    532 608
         if (RTS_UNLIKELY(!ok)) return false;
    
    533 609
     
    
    534
    -    /* Update the auxiliary aiop_poll_table to match */
    
    535
    -    struct pollfd *aiop_poll_table;
    
    536
    -    aiop_poll_table = stgReallocBytes(iomgr->aiop_poll_table,
    
    537
    -                                      sizeof(struct pollfd) * newcapacity,
    
    538
    -                                      "Poll.c: enlargeTables");
    
    539
    -    iomgr->aiop_poll_table = aiop_poll_table;
    
    610
    +    /* Update the auxiliary aiop_poll_table to match. The full_poll_table is
    
    611
    +     * one bigger than the aiop_poll_table, since it has an extra entry at the
    
    612
    +     * front for interrupt_fd_r, with no corresponding aiop. */
    
    613
    +    iomgr->full_poll_table =
    
    614
    +        stgReallocBytes(iomgr->full_poll_table,
    
    615
    +                        sizeof(struct pollfd) * (newcapacity+1),
    
    616
    +                        "Poll.c: enlargeTables");
    
    617
    +    iomgr->aiop_poll_table = iomgr->full_poll_table+1;
    
    618
    +
    
    540 619
         /* Initialise the new part of the aiop_poll_table */
    
    620
    +    struct pollfd *aiop_poll_table = iomgr->aiop_poll_table;
    
    541 621
         for (int i = oldcapacity; i < newcapacity; i++) {
    
    542 622
             aiop_poll_table[i] = (struct pollfd) {
    
    543 623
                                    .fd      = -1,
    

  • rts/posix/Poll.h
    ... ... @@ -32,7 +32,8 @@ void asyncIOCancelPoll(CapIOManager *iomgr, StgAsyncIOOp *aiop);
    32 32
     /* Scheduler operations */
    
    33 33
     bool anyPendingTimeoutsOrIOPoll(CapIOManager *iomgr);
    
    34 34
     void pollCompletedTimeoutsOrIOPoll(CapIOManager *iomgr);
    
    35
    -void awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr);
    
    35
    +bool awaitCompletedTimeoutsOrIOPoll(CapIOManager *iomgr);
    
    36
    +void interruptIOManagerPoll(CapIOManager *iomgr);
    
    36 37
     
    
    37 38
     #endif /* IOMGR_ENABLED_POLL */
    
    38 39
     
    

  • rts/posix/Select.c
    ... ... @@ -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,39 @@
    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
    +
    
    67
    +    /* Would never happen in a standalone process, but could plausibly happen
    
    68
    +     * if the RTS is used within another process that already has many open fds.
    
    69
    +     */
    
    70
    +    if (iomgr->interrupt_fd_r < 0 || iomgr->interrupt_fd_r >= (int)FD_SETSIZE ||
    
    71
    +        iomgr->interrupt_fd_w < 0 || iomgr->interrupt_fd_w >= (int)FD_SETSIZE) {
    
    72
    +        barf("initCapabilityIOManagerSelect: fds out of select range");
    
    73
    +    }
    
    74
    +#endif
    
    75
    +}
    
    76
    +
    
    77
    +void freeCapabilityIOManagerSelect(CapIOManager *iomgr)
    
    78
    +{
    
    79
    +#if defined(HAVE_PREEMPTION)
    
    80
    +    closeFdWakeup(iomgr->interrupt_fd_r, iomgr->interrupt_fd_w);
    
    81
    +#endif
    
    82
    +}
    
    83
    +
    
    84
    +void interruptIOManagerSelect(CapIOManager *iomgr)
    
    85
    +{
    
    86
    +#if defined(HAVE_PREEMPTION)
    
    87
    +    sendFdWakeup(iomgr->interrupt_fd_w);
    
    88
    +#endif
    
    89
    +}
    
    90
    +
    
    57 91
     /*
    
    58 92
      * Return the time since the program started, in LowResTime,
    
    59 93
      * rounded down.
    
    ... ... @@ -215,7 +249,7 @@ static enum FdState fdPollWriteState (int fd)
    215 249
      * not write handles.
    
    216 250
      *
    
    217 251
      */
    
    218
    -void
    
    252
    +bool
    
    219 253
     awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait)
    
    220 254
     {
    
    221 255
         StgTSO *tso, *prev, *next;
    
    ... ... @@ -225,6 +259,7 @@ awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait)
    225 259
         bool seen_bad_fd = false;
    
    226 260
         struct timeval tv, *ptv;
    
    227 261
         LowResTime now;
    
    262
    +    bool interrupt = false; /* got interrupted up via interruptIOManager */
    
    228 263
     
    
    229 264
         IF_DEBUG(scheduler,
    
    230 265
                  debugBelch("scheduler: checking for threads blocked on I/O");
    
    ... ... @@ -243,7 +278,7 @@ awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait)
    243 278
     
    
    244 279
           now = getLowResTimeOfDay();
    
    245 280
           if (wakeUpSleepingThreads(iomgr, now)) {
    
    246
    -          return;
    
    281
    +          return true;
    
    247 282
           }
    
    248 283
     
    
    249 284
           /*
    
    ... ... @@ -252,6 +287,16 @@ awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait)
    252 287
           FD_ZERO(&rfd);
    
    253 288
           FD_ZERO(&wfd);
    
    254 289
     
    
    290
    +#if defined(HAVE_PREEMPTION)
    
    291
    +      /* We're always interested in our interrupt fd */
    
    292
    +      {
    
    293
    +          int fd = iomgr->interrupt_fd_r;
    
    294
    +          maxfd = (fd > maxfd) ? fd : maxfd;
    
    295
    +          ASSERT(fd >= 0 && fd < (int)FD_SETSIZE); // checked during init
    
    296
    +          FD_SET(fd, &rfd);
    
    297
    +      }
    
    298
    +#endif
    
    299
    +
    
    255 300
           for(tso = iomgr->blocked_queue_hd;
    
    256 301
               tso != END_TSO_QUEUE;
    
    257 302
               tso = next) {
    
    ... ... @@ -354,14 +399,14 @@ awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait)
    354 399
     #if defined(RTS_USER_SIGNALS)
    
    355 400
               if (RtsFlags.MiscFlags.install_signal_handlers && signals_pending()) {
    
    356 401
                   startSignalHandlers(iomgr->cap);
    
    357
    -              return; /* still hold the lock */
    
    402
    +              return true; /* still hold the lock */
    
    358 403
               }
    
    359 404
     #endif
    
    360 405
     
    
    361 406
               /* we were interrupted, return to the scheduler immediately.
    
    362 407
                */
    
    363 408
               if (getSchedState() >= SCHED_INTERRUPTING) {
    
    364
    -              return; /* still hold the lock */
    
    409
    +              return true; /* still hold the lock */
    
    365 410
               }
    
    366 411
     
    
    367 412
               /* check for threads that need waking up
    
    ... ... @@ -372,10 +417,19 @@ awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait)
    372 417
                * I/O and run them.
    
    373 418
                */
    
    374 419
               if (!emptyRunQueue(iomgr->cap)) {
    
    375
    -              return; /* still hold the lock */
    
    420
    +              return true; /* still hold the lock */
    
    376 421
               }
    
    377 422
           }
    
    378 423
     
    
    424
    +#if defined(HAVE_PREEMPTION)
    
    425
    +      /* If the interrupt_fd_r is ready, collect it */
    
    426
    +      if (FD_ISSET(iomgr->interrupt_fd_r, &rfd)) {
    
    427
    +          collectFdWakeup(iomgr->interrupt_fd_r);
    
    428
    +          interrupt = true;
    
    429
    +          debugTrace(DEBUG_iomanager, "Received interrupt in select I/O manager");
    
    430
    +      }
    
    431
    +#endif
    
    432
    +
    
    379 433
           /* Step through the waiting queue, unblocking every thread that now has
    
    380 434
            * a file descriptor in a ready state.
    
    381 435
            */
    
    ... ... @@ -458,7 +512,9 @@ awaitCompletedTimeoutsOrIOSelect(CapIOManager *iomgr, bool wait)
    458 512
           }
    
    459 513
     
    
    460 514
         } while (wait && getSchedState() == SCHED_RUNNING
    
    461
    -                  && emptyRunQueue(iomgr->cap));
    
    515
    +                  && emptyRunQueue(iomgr->cap)
    
    516
    +                  && !interrupt);
    
    517
    +    return !interrupt;
    
    462 518
     }
    
    463 519
     
    
    464 520
     #endif /* IOMGR_ENABLED_SELECT */

  • rts/posix/Select.h
    ... ... @@ -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
     

  • rts/win32/AsyncMIO.c
    ... ... @@ -221,8 +221,12 @@ shutdownAsyncIO(bool wait_threads)
    221 221
      * requests to make further progress. In the latter scenario,
    
    222 222
      * awaitRequests() will simply block waiting for worker threads
    
    223 223
      * to complete if the 'completedTable' is empty.
    
    224
    + *
    
    225
    + * The result reports if the wait completed successfully (typically with some
    
    226
    + * work available), or was interrupted by abandonRequestWait(), with true
    
    227
    + * meaning completed, and false meaning interrupted.
    
    224 228
      */
    
    225
    -int
    
    229
    +bool
    
    226 230
     awaitRequests(bool wait)
    
    227 231
     {
    
    228 232
     #if !defined(THREADED_RTS)
    
    ... ... @@ -246,7 +250,7 @@ start:
    246 250
     #endif
    
    247 251
             ) {
    
    248 252
             OS_RELEASE_LOCK(&queue_lock);
    
    249
    -        return 0;
    
    253
    +        return true;
    
    250 254
         }
    
    251 255
         if (completed_hw == 0) {
    
    252 256
             // empty table, drop lock and wait
    
    ... ... @@ -259,22 +263,24 @@ start:
    259 263
                     // a request was completed
    
    260 264
                     break;
    
    261 265
                 case WAIT_OBJECT_0 + 1:
    
    266
    +                // abandon_req_wait signaled, by abandonRequestWait()
    
    267
    +                return false;
    
    262 268
                 case WAIT_TIMEOUT:
    
    263 269
                     // timeout (unlikely) or told to abandon waiting
    
    264
    -                return 0;
    
    270
    +                return true;
    
    265 271
                 case WAIT_FAILED: {
    
    266 272
                     DWORD dw = GetLastError();
    
    267 273
                     fprintf(stderr, "awaitRequests: wait failed -- "
    
    268 274
                                     "error code: %lu\n", dw); fflush(stderr);
    
    269
    -                return 0;
    
    275
    +                return true;
    
    270 276
                 }
    
    271 277
                 default:
    
    272 278
                     fprintf(stderr, "awaitRequests: unexpected wait return "
    
    273 279
                                     "code %lu\n", dwRes); fflush(stderr);
    
    274
    -                return 0;
    
    280
    +                return true;
    
    275 281
                 }
    
    276 282
             } else {
    
    277
    -            return 0;
    
    283
    +            return true;
    
    278 284
             }
    
    279 285
             goto start;
    
    280 286
         } else {
    
    ... ... @@ -352,7 +358,7 @@ start:
    352 358
             completed_hw = 0;
    
    353 359
             ResetEvent(completed_req_event);
    
    354 360
             OS_RELEASE_LOCK(&queue_lock);
    
    355
    -        return 1;
    
    361
    +        return true;
    
    356 362
         }
    
    357 363
     #endif /* !THREADED_RTS */
    
    358 364
     }
    
    ... ... @@ -383,12 +389,6 @@ abandonRequestWait( void )
    383 389
         interruptIOManagerEvent ();
    
    384 390
     }
    
    385 391
     
    
    386
    -void
    
    387
    -resetAbandonRequestWait( void )
    
    388
    -{
    
    389
    -    ResetEvent(abandon_req_wait);
    
    390
    -}
    
    391
    -
    
    392 392
     #endif /* !defined(THREADED_RTS) */
    
    393 393
     
    
    394 394
     HsInt rts_EINTR(void)
    

  • rts/win32/AsyncMIO.h
    ... ... @@ -25,7 +25,7 @@ extern unsigned int addDoProcRequest(void* proc, void* param);
    25 25
     extern int  startupAsyncIO(void);
    
    26 26
     extern void shutdownAsyncIO(bool wait_threads);
    
    27 27
     
    
    28
    -extern int awaitRequests(bool wait);
    
    28
    +extern bool awaitRequests(bool wait);
    
    29 29
     
    
    30 30
     extern void abandonRequestWait(void);
    
    31 31
     extern void resetAbandonRequestWait(void);
    

  • rts/win32/AwaitEvent.c
    ... ... @@ -28,17 +28,21 @@
    28 28
     // Protected by sched_mutex.
    
    29 29
     static bool workerWaitingForRequests = false;
    
    30 30
     
    
    31
    -void
    
    31
    +bool
    
    32 32
     awaitCompletedTimeoutsOrIOWin32(Capability *cap, bool wait)
    
    33 33
     {
    
    34
    +  bool interrupt = false;
    
    34 35
       do {
    
    35 36
         /* Try to de-queue completed IO requests
    
    36 37
          */
    
    37 38
         workerWaitingForRequests = true;
    
    38 39
         if (is_io_mng_native_p())
    
    39 40
           awaitAsyncRequests(wait);
    
    41
    +      /* FIXME: no support yet for interrupting in WinIO I/O manager
    
    42
    +       * See issue #27403
    
    43
    +       */
    
    40 44
         else
    
    41
    -      awaitRequests(wait);
    
    45
    +      interrupt = !awaitRequests(wait);
    
    42 46
         workerWaitingForRequests = false;
    
    43 47
     
    
    44 48
         // If a signal was raised, we need to service it
    
    ... ... @@ -47,11 +51,12 @@ awaitCompletedTimeoutsOrIOWin32(Capability *cap, bool wait)
    47 51
         // does it and I'm feeling too paranoid to refactor it today --SDM
    
    48 52
         if (stg_pending_events != 0) {
    
    49 53
             startSignalHandlers(cap);
    
    50
    -        return;
    
    54
    +        // This will normally cause emptyRunQueue to become false and
    
    55
    +        // thus we will drop out of the loop.
    
    51 56
         }
    
    52 57
     
    
    53
    -    // The return value from awaitRequests() is a red herring: ignore
    
    54
    -    // it.  Return to the scheduler if !wait, or
    
    58
    +    // The return value from awaitRequests() reports if it was interrupted by
    
    59
    +    // abandonRequestWait(). Return to the scheduler if !wait, or
    
    55 60
         //
    
    56 61
         //  - we were interrupted
    
    57 62
         //  - the run-queue is now non- empty
    
    ... ... @@ -59,6 +64,8 @@ awaitCompletedTimeoutsOrIOWin32(Capability *cap, bool wait)
    59 64
       } while (wait
    
    60 65
                && getSchedState() == SCHED_RUNNING
    
    61 66
                && emptyRunQueue(cap)
    
    67
    +           && !interrupt
    
    62 68
           );
    
    69
    +  return !interrupt;
    
    63 70
     }
    
    64 71
     #endif

  • rts/win32/AwaitEvent.h
    ... ... @@ -2,6 +2,6 @@
    2 2
     
    
    3 3
     #include "BeginPrivate.h"
    
    4 4
     
    
    5
    -void awaitCompletedTimeoutsOrIOWin32(Capability *cap, bool wait);
    
    5
    +bool awaitCompletedTimeoutsOrIOWin32(Capability *cap, bool wait);
    
    6 6
     
    
    7 7
     #include "EndPrivate.h"