Wolfgang Jeltsch pushed to branch wip/jeltsch/obtaining-os-handles at Glasgow Haskell Compiler / GHC

Commits:

1 changed file:

Changes:

  • libraries/base/src/GHC/IO/Handle.hs
    ... ... @@ -76,10 +76,10 @@ module GHC.IO.Handle
    76 76
          hPutBufNonBlocking,
    
    77 77
     
    
    78 78
          -- * Obtaining file descriptors and Windows handles
    
    79
    -     withReadingFileDescriptor,
    
    80
    -     withWritingFileDescriptor,
    
    81
    -     withReadingWindowsHandle,
    
    82
    -     withWritingWindowsHandle
    
    79
    +     withFileDescriptorReadingBiased,
    
    80
    +     withFileDescriptorWritingBiased,
    
    81
    +     withWindowsHandleReadingBiased,
    
    82
    +     withWindowsHandleWritingBiased
    
    83 83
     
    
    84 84
          -- ** Caveats
    
    85 85
          -- $with-ref-caveats
    
    ... ... @@ -88,6 +88,7 @@ module GHC.IO.Handle
    88 88
     import GHC.Internal.IO.Handle
    
    89 89
     
    
    90 90
     import GHC.Internal.Control.Monad (return)
    
    91
    +import GHC.Internal.Control.Concurrent.MVar (MVar)
    
    91 92
     import GHC.Internal.Control.Exception (mask)
    
    92 93
     import GHC.Internal.Data.Function ((.), ($))
    
    93 94
     import GHC.Internal.Data.Functor (fmap)
    
    ... ... @@ -112,13 +113,12 @@ import GHC.Internal.IO.Windows.Handle
    112 113
                toHANDLE
    
    113 114
            )
    
    114 115
     #endif
    
    115
    -import GHC.Internal.IO.Handle.Types (Handle__)
    
    116
    -import GHC.Internal.IO.Handle.Internals
    
    116
    +import GHC.Internal.IO.Handle.Types
    
    117 117
            (
    
    118
    -           wantReadableHandle_,
    
    119
    -           wantWritableHandle,
    
    120
    -           flushBuffer
    
    118
    +           Handle (FileHandle, DuplexHandle),
    
    119
    +           Handle__ (Handle__, haDevice)
    
    121 120
            )
    
    121
    +import GHC.Internal.IO.Handle.Internals (withHandle_', flushBuffer)
    
    122 122
     import GHC.Internal.IO.Exception
    
    123 123
            (
    
    124 124
                IOErrorType (IllegalOperation),
    
    ... ... @@ -128,82 +128,116 @@ import GHC.Internal.IO.Exception
    128 128
     import GHC.Internal.Foreign.Ptr (Ptr)
    
    129 129
     import GHC.Internal.Foreign.C.Types (CInt)
    
    130 130
     
    
    131
    --- * Obtaining file descriptors and Windows handles
    
    131
    +-- * Obtaining POSIX file descriptors and Windows handles
    
    132 132
     
    
    133 133
     {-|
    
    134
    -    Obtains from a handle an underlying operating-system reference for reading
    
    135
    -    or writing and executes a user-provided action on it. The Haskell-side
    
    136
    -    buffers of the handle are flushed before this action is run. While this
    
    137
    -    action is executed, further operations on the handle are blocked to a degree
    
    138
    -    that interference with this action is prevented.
    
    134
    +    Obtains an operating-system handle that underlies a Haskell handle and
    
    135
    +    executes a user-provided action on it. The Haskell-managed buffers related
    
    136
    +    to the operating-system handle are flushed before the user-provided action
    
    137
    +    is run. While this action is executed, further operations on the Haskell
    
    138
    +    handle are blocked to a degree that interference with this action is
    
    139
    +    prevented.
    
    139 140
     
    
    140 141
         See [below](#with-ref-caveats) for caveats regarding this operation.
    
    141 142
     -}
    
    142
    -withRef :: (Handle -> (Handle__ -> IO a) -> IO a)
    
    143
    -           -- ^ Obtaining of an appropriately prepared handle side from a handle
    
    144
    -        -> (forall d. Typeable d => d -> IO r)
    
    145
    -           -- ^ Conversion of a device into an operating-system reference
    
    146
    -        -> Handle
    
    147
    -           -- ^ The handle to use
    
    148
    -        -> (r -> IO a)
    
    149
    -           -- ^ The action to execute on the operating-system reference
    
    150
    -        -> IO a
    
    151
    -withRef withHandleSide getRef handle act
    
    143
    +withOSHandle :: String
    
    144
    +                -- ^ The name of the overall operation
    
    145
    +             -> (Handle -> MVar Handle__)
    
    146
    +                {-^
    
    147
    +                    Obtaining of the handle state variable that holds the
    
    148
    +                    operating-system handle
    
    149
    +                -}
    
    150
    +             -> (forall d. Typeable d => d -> IO a)
    
    151
    +                -- ^ Conversion of a device into an operating-system handle
    
    152
    +             -> Handle
    
    153
    +                -- ^ The Haskell handle to use
    
    154
    +             -> (a -> IO r)
    
    155
    +                -- ^ The action to execute on the operating-system handle
    
    156
    +             -> IO r
    
    157
    +withOSHandle opName handleStateVar getOSHandle handle act
    
    152 158
         = mask $ \ withOriginalMaskingState ->
    
    153
    -      withHandleSide handle $ \ handleSide -> do
    
    154
    -          ref <- getRef handleSide
    
    155
    -          flushBuffer handleSide
    
    156
    -          withOriginalMaskingState $ act ref
    
    159
    +      withHandleState $ \ handleState@Handle__ {haDevice = dev} -> do
    
    160
    +          osHandle <- getOSHandle dev
    
    161
    +          flushBuffer handleState
    
    162
    +          withOriginalMaskingState $ act osHandle
    
    163
    +      where
    
    164
    +
    
    165
    +      withHandleState = withHandle_' opName handle (handleStateVar handle)
    
    157 166
     {-
    
    158
    -    The public operations that use 'withRef' provide 'withHandleSide' arguments
    
    159
    -    that perform masking. Still, we have to use 'mask' here, in order do obtain
    
    160
    -    the operation that restores the original masking state. The user-provided
    
    161
    -    action should be executed with this original masking state, as there is no
    
    162
    -    inherent reason to generally perform it with masking in place. The masking
    
    163
    -    that the 'withHandleSide' arguments perform is only for safely accessing
    
    164
    -    internal handle data and thus constitutes an implementation detail; it has
    
    165
    -    nothing to do with the user-provided action.
    
    167
    +    The 'withHandle_'' operation, which we use here, already performs masking.
    
    168
    +    Still, we have to employ 'mask', in order do obtain the operation that
    
    169
    +    restores the original masking state. The user-provided action should be
    
    170
    +    executed with this original masking state, as there is no inherent reason to
    
    171
    +    generally perform it with masking in place. The masking that 'withHandle_''
    
    172
    +    performs is only for safely accessing handle state and thus constitutes an
    
    173
    +    implementation detail; it has nothing to do with the user-provided action.
    
    166 174
     -}
    
    167 175
     {-
    
    168
    -    The order of actions in 'withRef' is such that any exception from 'getRef'
    
    169
    -    is thrown before the flushing of the Haskell-side buffers.
    
    176
    +    The order of actions in 'withOSHandle' is such that any exception from
    
    177
    +    'getOSHandle' is thrown before the flushing of the Haskell-managed buffers.
    
    178
    +-}
    
    179
    +
    
    180
    +{-|
    
    181
    +    Obtains the handle state variable that underlies a handle or specifically
    
    182
    +    the handle state variable for reading if the handle uses different state
    
    183
    +    variables for reading and writing.
    
    184
    +-}
    
    185
    +handleStateVarReadingBiased :: Handle -> MVar Handle__
    
    186
    +handleStateVarReadingBiased (FileHandle _ var)            = var
    
    187
    +handleStateVarReadingBiased (DuplexHandle _ readingVar _) = readingVar
    
    188
    +
    
    189
    +{-|
    
    190
    +    Obtains the handle state variable that underlies a handle or specifically
    
    191
    +    the handle state variable for writing if the handle uses different state
    
    192
    +    variables for reading and writing.
    
    170 193
     -}
    
    194
    +handleStateVarWritingBiased :: Handle -> MVar Handle__
    
    195
    +handleStateVarWritingBiased (FileHandle _ var)            = var
    
    196
    +handleStateVarWritingBiased (DuplexHandle _ _ writingVar) = writingVar
    
    171 197
     
    
    172 198
     {-|
    
    173 199
         Yields the result of another operation if that operation succeeded, and
    
    174 200
         otherwise throws an exception that signals that the other operation failed
    
    175
    -    because a certain I/O subsystem is not in use.
    
    201
    +    because the operating-system handle that underlies some Haskell handle is
    
    202
    +    not of a required type.
    
    176 203
     -}
    
    177
    -requiringSubsystem :: String
    
    178
    -                      -- ^ The name of the required subsystem
    
    179
    -                   -> Maybe a
    
    180
    -                      -- ^ The result of the other operation if it succeeded
    
    181
    -                   -> IO a
    
    182
    -requiringSubsystem subsystemName
    
    183
    -    = maybe (ioException subsystemRequired) return
    
    204
    +requiringOSHandleType :: String
    
    205
    +                         {-^
    
    206
    +                            The name of the required operating-system handle
    
    207
    +                            type
    
    208
    +                         -}
    
    209
    +                      -> Maybe a
    
    210
    +                         -- ^ The result of the other operation if it succeeded
    
    211
    +                      -> IO a
    
    212
    +requiringOSHandleType osHandleTypeName
    
    213
    +    = maybe (ioException osHandleTypeRequired) return
    
    184 214
         where
    
    185 215
     
    
    186
    -    subsystemRequired :: IOException
    
    187
    -    subsystemRequired = IOError Nothing
    
    188
    -                                IllegalOperation
    
    189
    -                                ""
    
    190
    -                                (subsystemName ++ " I/O subsystem required")
    
    191
    -                                Nothing
    
    192
    -                                Nothing
    
    216
    +    osHandleTypeRequired :: IOException
    
    217
    +    osHandleTypeRequired
    
    218
    +        = IOError Nothing
    
    219
    +                  IllegalOperation
    
    220
    +                  ""
    
    221
    +                  ("handle does not use " ++ osHandleTypeName ++ "s")
    
    222
    +                  Nothing
    
    223
    +                  Nothing
    
    193 224
     
    
    194 225
     {-|
    
    195
    -    Obtains the POSIX file descriptor of a device if the POSIX I/O subsystem is
    
    196
    -    in use, and throws an exception otherwise.
    
    226
    +    Obtains the POSIX file descriptor of a device if the device contains one,
    
    227
    +    and throws an exception otherwise.
    
    197 228
     -}
    
    198 229
     getFileDescriptor :: Typeable d => d -> IO CInt
    
    199
    -getFileDescriptor = requiringSubsystem "POSIX" . fmap fdFD . cast
    
    230
    +getFileDescriptor = requiringOSHandleType "POSIX file descriptor" .
    
    231
    +                    fmap fdFD . cast
    
    200 232
     
    
    201 233
     {-|
    
    202
    -    Obtains the Windows handle of a device if the Windows I/O subsystem is in
    
    203
    -    use, and throws an exception otherwise.
    
    234
    +    Obtains the Windows handle of a device if the device contains one, and
    
    235
    +    throws an exception otherwise.
    
    204 236
     -}
    
    205 237
     getWindowsHandle :: Typeable d => d -> IO (Ptr ())
    
    206
    -getWindowsHandle = requiringSubsystem "native" . toMaybeWindowsHandle where
    
    238
    +getWindowsHandle = requiringOSHandleType "Windows handle" .
    
    239
    +                   toMaybeWindowsHandle
    
    240
    +    where
    
    207 241
     
    
    208 242
         toMaybeWindowsHandle :: Typeable d => d -> Maybe (Ptr ())
    
    209 243
     #if defined(mingw32_HOST_OS)
    
    ... ... @@ -223,68 +257,76 @@ getWindowsHandle = requiringSubsystem "native" . toMaybeWindowsHandle where
    223 257
     #endif
    
    224 258
     
    
    225 259
     {-|
    
    226
    -    Obtains from a handle a POSIX file descriptor for reading and executes a
    
    227
    -    user-provided action on it. The Haskell-side buffers of the handle are
    
    228
    -    flushed before this action is run. While this action is executed, further
    
    229
    -    operations on the handle are blocked to a degree that interference with this
    
    230
    -    action is prevented.
    
    260
    +    Obtains the POSIX file descriptor that underlies a handle or specifically
    
    261
    +    the POSIX file descriptor for reading if the handle uses different file
    
    262
    +    descriptors for reading and writing and executes a user-provided action on
    
    263
    +    it. The Haskell-managed buffers related to the file descriptor are flushed
    
    264
    +    before the user-provided action is run. While this action is executed,
    
    265
    +    further operations on the handle are blocked to a degree that interference
    
    266
    +    with this action is prevented.
    
    231 267
     
    
    232
    -    If the I/O subsystem in use is not the POSIX one, an exception is thrown.
    
    268
    +    If the handle does not use POSIX file descriptors, an exception is thrown.
    
    233 269
     
    
    234 270
         See [below](#with-ref-caveats) for caveats regarding this operation.
    
    235 271
     -}
    
    236
    -withReadingFileDescriptor :: Handle -> (CInt -> IO a) -> IO a
    
    237
    -withReadingFileDescriptor
    
    238
    -    = withRef (wantReadableHandle_ "withReadingFileDescriptor")
    
    239
    -              getFileDescriptor
    
    272
    +withFileDescriptorReadingBiased :: Handle -> (CInt -> IO r) -> IO r
    
    273
    +withFileDescriptorReadingBiased = withOSHandle "withFileDescriptorReadingBiased"
    
    274
    +                                               handleStateVarReadingBiased
    
    275
    +                                               getFileDescriptor
    
    240 276
     
    
    241 277
     {-|
    
    242
    -    Obtains from a handle a POSIX file descriptor for writing and executes a
    
    243
    -    user-provided action on it. The Haskell-side buffers of the handle are
    
    244
    -    flushed before this action is run. While this action is executed, further
    
    245
    -    operations on the handle are blocked to a degree that interference with this
    
    246
    -    action is prevented.
    
    278
    +    Obtains the POSIX file descriptor that underlies a handle or specifically
    
    279
    +    the POSIX file descriptor for writing if the handle uses different file
    
    280
    +    descriptors for reading and writing and executes a user-provided action on
    
    281
    +    it. The Haskell-managed buffers related to the file descriptor are flushed
    
    282
    +    before the user-provided action is run. While this action is executed,
    
    283
    +    further operations on the handle are blocked to a degree that interference
    
    284
    +    with this action is prevented.
    
    247 285
     
    
    248
    -    If the I/O subsystem in use is not the POSIX one, an exception is thrown.
    
    286
    +    If the handle does not use POSIX file descriptors, an exception is thrown.
    
    249 287
     
    
    250 288
         See [below](#with-ref-caveats) for caveats regarding this operation.
    
    251 289
     -}
    
    252
    -withWritingFileDescriptor :: Handle -> (CInt -> IO a) -> IO a
    
    253
    -withWritingFileDescriptor
    
    254
    -    = withRef (wantWritableHandle "withWritingFileDescriptor")
    
    255
    -              getFileDescriptor
    
    290
    +withFileDescriptorWritingBiased :: Handle -> (CInt -> IO r) -> IO r
    
    291
    +withFileDescriptorWritingBiased = withOSHandle "withFileDescriptorWritingBiased"
    
    292
    +                                               handleStateVarWritingBiased
    
    293
    +                                               getFileDescriptor
    
    256 294
     
    
    257 295
     {-|
    
    258
    -    Obtains from a Haskell handle a Windows handle for reading and executes a
    
    259
    -    user-provided action on it. The Haskell-side buffers of the Haskell handle
    
    260
    -    are flushed before this action is run. While this action is executed,
    
    261
    -    further operations on the handle are blocked to a degree that interference
    
    296
    +    Obtains the Windows handle that underlies a Haskell handle or specifically
    
    297
    +    the Windows handle for reading if the Haskell handle uses different Windows
    
    298
    +    handles for reading and writing and executes a user-provided action on it.
    
    299
    +    The Haskell-managed buffers related to the Windows handle are flushed before
    
    300
    +    the user-provided action is run. While this action is executed, further
    
    301
    +    operations on the Haskell handle are blocked to a degree that interference
    
    262 302
         with this action is prevented.
    
    263 303
     
    
    264
    -    If the I/O subsystem in use is not the Windows one, an exception is thrown.
    
    304
    +    If the Haskell handle does not use Windows handles, an exception is thrown.
    
    265 305
     
    
    266 306
         See [below](#with-ref-caveats) for caveats regarding this operation.
    
    267 307
     -}
    
    268
    -withReadingWindowsHandle :: Handle -> (Ptr () -> IO a) -> IO a
    
    269
    -withReadingWindowsHandle
    
    270
    -    = withRef (wantReadableHandle_ "withReadingWindowsHandle")
    
    271
    -              getWindowsHandle
    
    308
    +withWindowsHandleReadingBiased :: Handle -> (Ptr () -> IO r) -> IO r
    
    309
    +withWindowsHandleReadingBiased = withOSHandle "withWindowsHandleReadingBiased"
    
    310
    +                                              handleStateVarReadingBiased
    
    311
    +                                              getWindowsHandle
    
    272 312
     
    
    273 313
     {-|
    
    274
    -    Obtains from a Haskell handle a Windows handle for writing and executes a
    
    275
    -    user-provided action on it. The Haskell-side buffers of the Haskell handle
    
    276
    -    are flushed before this action is run. While this action is executed,
    
    277
    -    further operations on the handle are blocked to a degree that interference
    
    314
    +    Obtains the Windows handle that underlies a Haskell handle or specifically
    
    315
    +    the Windows handle for writing if the Haskell handle uses different Windows
    
    316
    +    handles for reading and writing and executes a user-provided action on it.
    
    317
    +    The Haskell-managed buffers related to the Windows handle are flushed before
    
    318
    +    the user-provided action is run. While this action is executed, further
    
    319
    +    operations on the Haskell handle are blocked to a degree that interference
    
    278 320
         with this action is prevented.
    
    279 321
     
    
    280
    -    If the I/O subsystem in use is not the Windows one, an exception is thrown.
    
    322
    +    If the Haskell handle does not use Windows handles, an exception is thrown.
    
    281 323
     
    
    282 324
         See [below](#with-ref-caveats) for caveats regarding this operation.
    
    283 325
     -}
    
    284
    -withWritingWindowsHandle :: Handle -> (Ptr () -> IO a) -> IO a
    
    285
    -withWritingWindowsHandle
    
    286
    -    = withRef (wantWritableHandle "withWritingWindowsHandle")
    
    287
    -              getWindowsHandle
    
    326
    +withWindowsHandleWritingBiased :: Handle -> (Ptr () -> IO r) -> IO r
    
    327
    +withWindowsHandleWritingBiased = withOSHandle "withWindowsHandleWritingBiased"
    
    328
    +                                              handleStateVarWritingBiased
    
    329
    +                                              getWindowsHandle
    
    288 330
     
    
    289 331
     -- ** Caveats
    
    290 332
     
    
    ... ... @@ -305,8 +347,8 @@ withWritingWindowsHandle
    305 347
     
    
    306 348
               - When the computation is later resumed due to another evaluation
    
    307 349
                 attempt, the blocking of handle operations is reinstantiated, the
    
    308
    -            Haskell-side buffers are flushed again, and the user-provided action
    
    309
    -            is run from the beginning.
    
    350
    +            Haskell-managed buffers are flushed again, and the user-provided
    
    351
    +            action is run from the beginning.
    
    310 352
     
    
    311 353
             Repeating the previously executed part of the user-provided action
    
    312 354
             cannot be avoided apparently. See the @[async]@ note in the source code