@@ -120,12 +120,54 @@ struct FileHandle: ~Copyable, Sendable {
120
120
_closeWhenDone = closeWhenDone
121
121
}
122
122
123
+ /// Initialize an instance of this type with an existing POSIX file descriptor
124
+ /// for reading.
125
+ ///
126
+ /// - Parameters:
127
+ /// - fd: The POSIX file descriptor to wrap. The caller is responsible for
128
+ /// ensuring that this file handle is open in the expected mode and that
129
+ /// another part of the system won't close it.
130
+ /// - mode: The mode `fd` was opened with, such as `"wb"`.
131
+ ///
132
+ /// - Throws: Any error preventing the stream from being opened.
133
+ ///
134
+ /// The resulting file handle takes ownership of `fd` and closes it when it is
135
+ /// deinitialized or if an error is thrown from this initializer.
136
+ init ( unsafePOSIXFileDescriptor fd: CInt , mode: String ) throws {
137
+ #if os(Windows)
138
+ let fileHandle = _fdopen ( fd, mode)
139
+ #else
140
+ let fileHandle = fdopen ( fd, mode)
141
+ #endif
142
+ guard let fileHandle else {
143
+ let errorCode = swt_errno ( )
144
+ #if os(Windows)
145
+ _close ( fd)
146
+ #else
147
+ _TestingInternals. close ( fd)
148
+ #endif
149
+ throw CError ( rawValue: errorCode)
150
+ }
151
+ self . init ( unsafeCFILEHandle: fileHandle, closeWhenDone: true )
152
+ }
153
+
123
154
deinit {
124
155
if _closeWhenDone {
125
156
fclose ( _fileHandle)
126
157
}
127
158
}
128
159
160
+ /// Close this file handle.
161
+ ///
162
+ /// This function effectively deinitializes the file handle.
163
+ ///
164
+ /// - Warning: This function closes the underlying C file handle even if
165
+ /// `closeWhenDone` was `false` when this instance was initialized. Callers
166
+ /// must take care not to close file handles they do not own.
167
+ consuming func close( ) {
168
+ _closeWhenDone = true
169
+ }
170
+
129
171
/// Call a function and pass the underlying C file handle to it.
130
172
///
131
173
/// - Parameters:
@@ -383,6 +425,77 @@ extension FileHandle {
383
425
}
384
426
}
385
427
428
+ // MARK: - Pipes
429
+
430
+ extension FileHandle {
431
+ /// A type representing a bidirectional pipe between two file handles.
432
+ struct Pipe : ~ Copyable, Sendable {
433
+ /// The end of the pipe capable of reading.
434
+ var readEnd : FileHandle
435
+
436
+ /// The end of the pipe capable of writing.
437
+ var writeEnd : FileHandle
438
+
439
+ /// Initialize a new anonymous pipe.
440
+ ///
441
+ /// - Throws: Any error that prevented creation of the pipe.
442
+ init ( ) throws {
443
+ let ( fdReadEnd, fdWriteEnd) = try withUnsafeTemporaryAllocation ( of: CInt . self, capacity: 2 ) { fds in
444
+ #if os(Windows)
445
+ guard 0 == _pipe ( fds. baseAddress, 0 , _O_BINARY) else {
446
+ throw CError ( rawValue: swt_errno ( ) )
447
+ }
448
+ #else
449
+ guard 0 == pipe ( fds. baseAddress) else {
450
+ throw CError ( rawValue: swt_errno ( ) )
451
+ }
452
+ #endif
453
+ return ( fds [ 0 ] , fds [ 1 ] )
454
+ }
455
+
456
+ // NOTE: Partial initialization of a move-only type is disallowed, as is
457
+ // conditional initialization of a local move-only value, which is why
458
+ // this section looks a little awkward.
459
+ let readEnd : FileHandle
460
+ do {
461
+ readEnd = try FileHandle ( unsafePOSIXFileDescriptor: fdReadEnd, mode: " rb " )
462
+ } catch {
463
+ #if os(Windows)
464
+ _close ( fdWriteEnd)
465
+ #else
466
+ _TestingInternals. close ( fdWriteEnd)
467
+ #endif
468
+ throw error
469
+ }
470
+ let writeEnd = try FileHandle ( unsafePOSIXFileDescriptor: fdWriteEnd, mode: " wb " )
471
+ self . readEnd = readEnd
472
+ self . writeEnd = writeEnd
473
+ }
474
+
475
+ /// Close the read end of this pipe.
476
+ ///
477
+ /// - Returns: The remaining open end of the pipe.
478
+ ///
479
+ /// After calling this function, the read end is closed and the write end
480
+ /// remains open.
481
+ consuming func closeReadEnd( ) -> FileHandle {
482
+ readEnd. close ( )
483
+ return writeEnd
484
+ }
485
+
486
+ /// Close the write end of this pipe.
487
+ ///
488
+ /// - Returns: The remaining open end of the pipe.
489
+ ///
490
+ /// After calling this function, the write end is closed and the read end
491
+ /// remains open.
492
+ consuming func closeWriteEnd( ) -> FileHandle {
493
+ writeEnd. close ( )
494
+ return readEnd
495
+ }
496
+ }
497
+ }
498
+
386
499
// MARK: - Attributes
387
500
388
501
extension FileHandle {
0 commit comments