1 //===-- runtime/unit.h ------------------------------------------*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // Fortran external I/O units
11 #ifndef FORTRAN_RUNTIME_IO_UNIT_H_
12 #define FORTRAN_RUNTIME_IO_UNIT_H_
15 #include "connection.h"
16 #include "environment.h"
22 #include "terminator.h"
23 #include "flang/Common/constexpr-bitset.h"
24 #include "flang/Common/optional.h"
25 #include "flang/Runtime/memory.h"
28 #include <flang/Common/variant.h>
30 namespace Fortran::runtime::io
{
34 class ExternalFileUnit
;
36 RT_OFFLOAD_VAR_GROUP_BEGIN
37 // Predefined file units.
38 extern RT_VAR_ATTRS ExternalFileUnit
*defaultInput
; // unit 5
39 extern RT_VAR_ATTRS ExternalFileUnit
*defaultOutput
; // unit 6
40 extern RT_VAR_ATTRS ExternalFileUnit
*errorOutput
; // unit 0 extension
41 RT_OFFLOAD_VAR_GROUP_END
43 #if defined(RT_USE_PSEUDO_FILE_UNIT)
44 // A flavor of OpenFile class that pretends to be a terminal,
45 // and only provides basic buffering of the output
46 // in an internal buffer, and Write's the output
47 // using std::printf(). Since it does not rely on file system
48 // APIs, it can be used to implement external output
49 // for offload devices.
50 class PseudoOpenFile
{
52 using FileOffset
= std::int64_t;
54 RT_API_ATTRS
const char *path() const { return nullptr; }
55 RT_API_ATTRS
std::size_t pathLength() const { return 0; }
56 RT_API_ATTRS
void set_path(OwningPtr
<char> &&, std::size_t bytes
) {}
57 RT_API_ATTRS
bool mayRead() const { return false; }
58 RT_API_ATTRS
bool mayWrite() const { return true; }
59 RT_API_ATTRS
bool mayPosition() const { return false; }
60 RT_API_ATTRS
bool mayAsynchronous() const { return false; }
61 RT_API_ATTRS
void set_mayAsynchronous(bool yes
);
62 // Pretend to be a terminal to force the output
63 // at the end of IO statement.
64 RT_API_ATTRS
bool isTerminal() const { return true; }
65 RT_API_ATTRS
bool isWindowsTextFile() const { return false; }
66 RT_API_ATTRS
Fortran::common::optional
<FileOffset
> knownSize() const;
67 RT_API_ATTRS
bool IsConnected() const { return false; }
68 RT_API_ATTRS
void Open(OpenStatus
, Fortran::common::optional
<Action
>,
69 Position
, IoErrorHandler
&);
70 RT_API_ATTRS
void Predefine(int fd
) {}
71 RT_API_ATTRS
void Close(CloseStatus
, IoErrorHandler
&);
72 RT_API_ATTRS
std::size_t Read(FileOffset
, char *, std::size_t minBytes
,
73 std::size_t maxBytes
, IoErrorHandler
&);
74 RT_API_ATTRS
std::size_t Write(
75 FileOffset
, const char *, std::size_t, IoErrorHandler
&);
76 RT_API_ATTRS
void Truncate(FileOffset
, IoErrorHandler
&);
77 RT_API_ATTRS
int ReadAsynchronously(
78 FileOffset
, char *, std::size_t, IoErrorHandler
&);
79 RT_API_ATTRS
int WriteAsynchronously(
80 FileOffset
, const char *, std::size_t, IoErrorHandler
&);
81 RT_API_ATTRS
void Wait(int id
, IoErrorHandler
&);
82 RT_API_ATTRS
void WaitAll(IoErrorHandler
&);
83 RT_API_ATTRS Position
InquirePosition() const;
85 #endif // defined(RT_USE_PSEUDO_FILE_UNIT)
87 #if !defined(RT_USE_PSEUDO_FILE_UNIT)
88 using OpenFileClass
= OpenFile
;
89 using FileFrameClass
= FileFrame
<ExternalFileUnit
>;
90 #else // defined(RT_USE_PSEUDO_FILE_UNIT)
91 using OpenFileClass
= PseudoOpenFile
;
92 // Use not so big buffer for the pseudo file unit frame.
93 using FileFrameClass
= FileFrame
<ExternalFileUnit
, 1024>;
94 #endif // defined(RT_USE_PSEUDO_FILE_UNIT)
96 class ExternalFileUnit
: public ConnectionState
,
98 public FileFrameClass
{
100 static constexpr int maxAsyncIds
{64 * 16};
102 explicit RT_API_ATTRS
ExternalFileUnit(int unitNumber
)
103 : unitNumber_
{unitNumber
} {
104 isUTF8
= executionEnvironment
.defaultUTF8
;
105 for (int j
{0}; 64 * j
< maxAsyncIds
; ++j
) {
106 asyncIdAvailable_
[j
].set();
108 asyncIdAvailable_
[0].reset(0);
110 RT_API_ATTRS
~ExternalFileUnit() {}
112 RT_API_ATTRS
int unitNumber() const { return unitNumber_
; }
113 RT_API_ATTRS
bool swapEndianness() const { return swapEndianness_
; }
114 RT_API_ATTRS
bool createdForInternalChildIo() const {
115 return createdForInternalChildIo_
;
118 static RT_API_ATTRS ExternalFileUnit
*LookUp(int unit
);
119 static RT_API_ATTRS ExternalFileUnit
*LookUpOrCreate(
120 int unit
, const Terminator
&, bool &wasExtant
);
121 static RT_API_ATTRS ExternalFileUnit
*LookUpOrCreateAnonymous(int unit
,
122 Direction
, Fortran::common::optional
<bool> isUnformatted
,
124 static RT_API_ATTRS ExternalFileUnit
*LookUp(
125 const char *path
, std::size_t pathLen
);
126 static RT_API_ATTRS ExternalFileUnit
&CreateNew(int unit
, const Terminator
&);
127 static RT_API_ATTRS ExternalFileUnit
*LookUpForClose(int unit
);
128 static RT_API_ATTRS ExternalFileUnit
&NewUnit(
129 const Terminator
&, bool forChildIo
);
130 static RT_API_ATTRS
void CloseAll(IoErrorHandler
&);
131 static RT_API_ATTRS
void FlushAll(IoErrorHandler
&);
133 // Returns true if an existing unit was closed
134 RT_API_ATTRS
bool OpenUnit(Fortran::common::optional
<OpenStatus
>,
135 Fortran::common::optional
<Action
>, Position
, OwningPtr
<char> &&path
,
136 std::size_t pathLength
, Convert
, IoErrorHandler
&);
137 RT_API_ATTRS
bool OpenAnonymousUnit(Fortran::common::optional
<OpenStatus
>,
138 Fortran::common::optional
<Action
>, Position
, Convert
, IoErrorHandler
&);
139 RT_API_ATTRS
void CloseUnit(CloseStatus
, IoErrorHandler
&);
140 RT_API_ATTRS
void DestroyClosed();
142 RT_API_ATTRS Iostat
SetDirection(Direction
);
144 template <typename A
, typename
... X
>
145 RT_API_ATTRS IoStatementState
&BeginIoStatement(
146 const Terminator
&terminator
, X
&&...xs
) {
147 // Take lock_ and hold it until EndIoStatement().
149 if (!lock_
.TakeIfNoDeadlock()) {
150 terminator
.Crash("Recursive I/O attempted on unit %d", unitNumber_
);
155 A
&state
{u_
.emplace
<A
>(std::forward
<X
>(xs
)...)};
156 if constexpr (!std::is_same_v
<A
, OpenStatementState
>) {
157 state
.mutableModes() = ConnectionState::modes
;
159 directAccessRecWasSet_
= false;
164 RT_API_ATTRS
bool Emit(
165 const char *, std::size_t, std::size_t elementBytes
, IoErrorHandler
&);
166 RT_API_ATTRS
bool Receive(
167 char *, std::size_t, std::size_t elementBytes
, IoErrorHandler
&);
168 RT_API_ATTRS
std::size_t GetNextInputBytes(const char *&, IoErrorHandler
&);
169 RT_API_ATTRS
std::size_t ViewBytesInRecord(const char *&, bool forward
) const;
170 RT_API_ATTRS
bool BeginReadingRecord(IoErrorHandler
&);
171 RT_API_ATTRS
void FinishReadingRecord(IoErrorHandler
&);
172 RT_API_ATTRS
bool AdvanceRecord(IoErrorHandler
&);
173 RT_API_ATTRS
void BackspaceRecord(IoErrorHandler
&);
174 RT_API_ATTRS
void FlushOutput(IoErrorHandler
&);
175 RT_API_ATTRS
void FlushIfTerminal(IoErrorHandler
&);
176 RT_API_ATTRS
void Endfile(IoErrorHandler
&);
177 RT_API_ATTRS
void Rewind(IoErrorHandler
&);
178 RT_API_ATTRS
void EndIoStatement();
179 RT_API_ATTRS
bool SetStreamPos(
180 std::int64_t, IoErrorHandler
&); // one-based, for POS=
181 RT_API_ATTRS
bool SetDirectRec(
182 std::int64_t, IoErrorHandler
&); // one-based, for REC=
183 RT_API_ATTRS
std::int64_t InquirePos() const {
184 // 12.6.2.11 defines POS=1 as the beginning of file
185 return frameOffsetInFile_
+ recordOffsetInFrame_
+ positionInRecord
+ 1;
188 RT_API_ATTRS ChildIo
*GetChildIo() { return child_
.get(); }
189 RT_API_ATTRS ChildIo
&PushChildIo(IoStatementState
&);
190 RT_API_ATTRS
void PopChildIo(ChildIo
&);
192 RT_API_ATTRS
int GetAsynchronousId(IoErrorHandler
&);
193 RT_API_ATTRS
bool Wait(int);
196 static RT_API_ATTRS UnitMap
&CreateUnitMap();
197 static RT_API_ATTRS UnitMap
&GetUnitMap();
198 RT_API_ATTRS
const char *FrameNextInput(IoErrorHandler
&, std::size_t);
199 RT_API_ATTRS
void SetPosition(std::int64_t, IoErrorHandler
&); // zero-based
200 RT_API_ATTRS
void BeginSequentialVariableUnformattedInputRecord(
202 RT_API_ATTRS
void BeginVariableFormattedInputRecord(IoErrorHandler
&);
203 RT_API_ATTRS
void BackspaceFixedRecord(IoErrorHandler
&);
204 RT_API_ATTRS
void BackspaceVariableUnformattedRecord(IoErrorHandler
&);
205 RT_API_ATTRS
void BackspaceVariableFormattedRecord(IoErrorHandler
&);
206 RT_API_ATTRS
bool SetVariableFormattedRecordLength();
207 RT_API_ATTRS
void DoImpliedEndfile(IoErrorHandler
&);
208 template <bool ANY_DIR
= true, Direction
DIR = Direction::Output
>
209 RT_API_ATTRS
void DoEndfile(IoErrorHandler
&);
210 RT_API_ATTRS
void CommitWrites();
211 RT_API_ATTRS
bool CheckDirectAccess(IoErrorHandler
&);
212 RT_API_ATTRS
void HitEndOnRead(IoErrorHandler
&);
213 RT_API_ATTRS
std::int32_t ReadHeaderOrFooter(std::int64_t frameOffset
);
218 Direction direction_
{Direction::Output
};
219 bool impliedEndfile_
{false}; // sequential/stream output has taken place
220 bool beganReadingRecord_
{false};
221 bool anyWriteSinceLastPositioning_
{false};
222 bool directAccessRecWasSet_
{false}; // REC= appeared
223 // Subtle: The beginning of the frame can't be allowed to advance
224 // during a single list-directed READ due to the possibility of a
225 // multi-record CHARACTER value with a "r*" repeat count. So we
226 // manage the frame and the current record therein separately.
227 std::int64_t frameOffsetInFile_
{0};
228 std::size_t recordOffsetInFrame_
{0}; // of currentRecordNumber
229 bool swapEndianness_
{false};
230 bool createdForInternalChildIo_
{false};
231 common::BitSet
<64> asyncIdAvailable_
[maxAsyncIds
/ 64];
233 // When a synchronous I/O statement is in progress on this unit, holds its
235 std::variant
<std::monostate
, OpenStatementState
, CloseStatementState
,
236 ExternalFormattedIoStatementState
<Direction::Output
>,
237 ExternalFormattedIoStatementState
<Direction::Input
>,
238 ExternalListIoStatementState
<Direction::Output
>,
239 ExternalListIoStatementState
<Direction::Input
>,
240 ExternalUnformattedIoStatementState
<Direction::Output
>,
241 ExternalUnformattedIoStatementState
<Direction::Input
>, InquireUnitState
,
242 ExternalMiscIoStatementState
, ErroneousIoStatementState
>
245 // Points to the active alternative (if any) in u_ for use as a Cookie
246 Fortran::common::optional
<IoStatementState
> io_
;
248 // A stack of child I/O pseudo-units for defined I/O that have this
250 OwningPtr
<ChildIo
> child_
;
253 // A pseudo-unit for child I/O statements in defined I/O subroutines;
254 // it forwards operations to the parent I/O statement, which might also
255 // be a child I/O statement.
258 RT_API_ATTRS
ChildIo(IoStatementState
&parent
, OwningPtr
<ChildIo
> &&previous
)
259 : parent_
{parent
}, previous_
{std::move(previous
)} {}
261 RT_API_ATTRS IoStatementState
&parent() const { return parent_
; }
263 RT_API_ATTRS
void EndIoStatement();
265 template <typename A
, typename
... X
>
266 RT_API_ATTRS IoStatementState
&BeginIoStatement(X
&&...xs
) {
267 A
&state
{u_
.emplace
<A
>(std::forward
<X
>(xs
)...)};
272 RT_API_ATTRS OwningPtr
<ChildIo
> AcquirePrevious() {
273 return std::move(previous_
);
276 RT_API_ATTRS Iostat
CheckFormattingAndDirection(bool unformatted
, Direction
);
279 IoStatementState
&parent_
;
280 OwningPtr
<ChildIo
> previous_
;
281 std::variant
<std::monostate
,
282 ChildFormattedIoStatementState
<Direction::Output
>,
283 ChildFormattedIoStatementState
<Direction::Input
>,
284 ChildListIoStatementState
<Direction::Output
>,
285 ChildListIoStatementState
<Direction::Input
>,
286 ChildUnformattedIoStatementState
<Direction::Output
>,
287 ChildUnformattedIoStatementState
<Direction::Input
>, InquireUnitState
,
288 ErroneousIoStatementState
, ExternalMiscIoStatementState
>
290 Fortran::common::optional
<IoStatementState
> io_
;
293 } // namespace Fortran::runtime::io
294 #endif // FORTRAN_RUNTIME_IO_UNIT_H_