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

Commits:

5 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);
    
    ... ... @@ -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
     
    

  • rts/IOManager.h
    ... ... @@ -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"

  • rts/IOManagerInternals.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;
    

  • 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,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 */

  • 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