1 | //===-- runtime/unit.h ------------------------------------------*- C++ -*-===// |
2 | // |
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 |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | // Fortran external I/O units |
10 | |
11 | #ifndef FORTRAN_RUNTIME_IO_UNIT_H_ |
12 | #define FORTRAN_RUNTIME_IO_UNIT_H_ |
13 | |
14 | #include "buffer.h" |
15 | #include "connection.h" |
16 | #include "environment.h" |
17 | #include "file.h" |
18 | #include "format.h" |
19 | #include "io-error.h" |
20 | #include "io-stmt.h" |
21 | #include "lock.h" |
22 | #include "terminator.h" |
23 | #include "flang/Common/constexpr-bitset.h" |
24 | #include "flang/Common/optional.h" |
25 | #include "flang/Runtime/memory.h" |
26 | #include <cstdlib> |
27 | #include <cstring> |
28 | #include <flang/Common/variant.h> |
29 | |
30 | namespace Fortran::runtime::io { |
31 | |
32 | class UnitMap; |
33 | class ChildIo; |
34 | class ExternalFileUnit; |
35 | |
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 |
42 | |
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 { |
51 | public: |
52 | using FileOffset = std::int64_t; |
53 | |
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; |
84 | }; |
85 | #endif // defined(RT_USE_PSEUDO_FILE_UNIT) |
86 | |
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) |
95 | |
96 | class ExternalFileUnit : public ConnectionState, |
97 | public OpenFileClass, |
98 | public FileFrameClass { |
99 | public: |
100 | static constexpr int maxAsyncIds{64 * 16}; |
101 | |
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(); |
107 | } |
108 | asyncIdAvailable_[0].reset(0); |
109 | } |
110 | RT_API_ATTRS ~ExternalFileUnit() {} |
111 | |
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_; |
116 | } |
117 | |
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, |
123 | const Terminator &); |
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 &); |
132 | |
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 void 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(); |
141 | |
142 | RT_API_ATTRS Iostat SetDirection(Direction); |
143 | |
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(). |
148 | #if USE_PTHREADS |
149 | if (!lock_.TakeIfNoDeadlock()) { |
150 | terminator.Crash("Recursive I/O attempted on unit %d" , unitNumber_); |
151 | } |
152 | #else |
153 | lock_.Take(); |
154 | #endif |
155 | A &state{u_.emplace<A>(std::forward<X>(xs)...)}; |
156 | if constexpr (!std::is_same_v<A, OpenStatementState>) { |
157 | state.mutableModes() = ConnectionState::modes; |
158 | } |
159 | directAccessRecWasSet_ = false; |
160 | io_.emplace(state); |
161 | return *io_; |
162 | } |
163 | |
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 bool BeginReadingRecord(IoErrorHandler &); |
170 | RT_API_ATTRS void FinishReadingRecord(IoErrorHandler &); |
171 | RT_API_ATTRS bool AdvanceRecord(IoErrorHandler &); |
172 | RT_API_ATTRS void BackspaceRecord(IoErrorHandler &); |
173 | RT_API_ATTRS void FlushOutput(IoErrorHandler &); |
174 | RT_API_ATTRS void FlushIfTerminal(IoErrorHandler &); |
175 | RT_API_ATTRS void Endfile(IoErrorHandler &); |
176 | RT_API_ATTRS void Rewind(IoErrorHandler &); |
177 | RT_API_ATTRS void EndIoStatement(); |
178 | RT_API_ATTRS bool SetStreamPos( |
179 | std::int64_t, IoErrorHandler &); // one-based, for POS= |
180 | RT_API_ATTRS bool SetDirectRec( |
181 | std::int64_t, IoErrorHandler &); // one-based, for REC= |
182 | RT_API_ATTRS std::int64_t InquirePos() const { |
183 | // 12.6.2.11 defines POS=1 as the beginning of file |
184 | return frameOffsetInFile_ + recordOffsetInFrame_ + positionInRecord + 1; |
185 | } |
186 | |
187 | RT_API_ATTRS ChildIo *GetChildIo() { return child_.get(); } |
188 | RT_API_ATTRS ChildIo &PushChildIo(IoStatementState &); |
189 | RT_API_ATTRS void PopChildIo(ChildIo &); |
190 | |
191 | RT_API_ATTRS int GetAsynchronousId(IoErrorHandler &); |
192 | RT_API_ATTRS bool Wait(int); |
193 | |
194 | private: |
195 | static RT_API_ATTRS UnitMap &CreateUnitMap(); |
196 | static RT_API_ATTRS UnitMap &GetUnitMap(); |
197 | RT_API_ATTRS const char *FrameNextInput(IoErrorHandler &, std::size_t); |
198 | RT_API_ATTRS void SetPosition(std::int64_t, IoErrorHandler &); // zero-based |
199 | RT_API_ATTRS void BeginSequentialVariableUnformattedInputRecord( |
200 | IoErrorHandler &); |
201 | RT_API_ATTRS void BeginVariableFormattedInputRecord(IoErrorHandler &); |
202 | RT_API_ATTRS void BackspaceFixedRecord(IoErrorHandler &); |
203 | RT_API_ATTRS void BackspaceVariableUnformattedRecord(IoErrorHandler &); |
204 | RT_API_ATTRS void BackspaceVariableFormattedRecord(IoErrorHandler &); |
205 | RT_API_ATTRS bool SetVariableFormattedRecordLength(); |
206 | RT_API_ATTRS void DoImpliedEndfile(IoErrorHandler &); |
207 | template <bool ANY_DIR = true, Direction DIR = Direction::Output> |
208 | RT_API_ATTRS void DoEndfile(IoErrorHandler &); |
209 | RT_API_ATTRS void CommitWrites(); |
210 | RT_API_ATTRS bool CheckDirectAccess(IoErrorHandler &); |
211 | RT_API_ATTRS void HitEndOnRead(IoErrorHandler &); |
212 | RT_API_ATTRS std::int32_t ReadHeaderOrFooter(std::int64_t frameOffset); |
213 | |
214 | Lock lock_; |
215 | |
216 | int unitNumber_{-1}; |
217 | Direction direction_{Direction::Output}; |
218 | bool impliedEndfile_{false}; // sequential/stream output has taken place |
219 | bool beganReadingRecord_{false}; |
220 | bool anyWriteSinceLastPositioning_{false}; |
221 | bool directAccessRecWasSet_{false}; // REC= appeared |
222 | // Subtle: The beginning of the frame can't be allowed to advance |
223 | // during a single list-directed READ due to the possibility of a |
224 | // multi-record CHARACTER value with a "r*" repeat count. So we |
225 | // manage the frame and the current record therein separately. |
226 | std::int64_t frameOffsetInFile_{0}; |
227 | std::size_t recordOffsetInFrame_{0}; // of currentRecordNumber |
228 | bool swapEndianness_{false}; |
229 | bool createdForInternalChildIo_{false}; |
230 | common::BitSet<64> asyncIdAvailable_[maxAsyncIds / 64]; |
231 | |
232 | // When a synchronous I/O statement is in progress on this unit, holds its |
233 | // state. |
234 | std::variant<std::monostate, OpenStatementState, CloseStatementState, |
235 | ExternalFormattedIoStatementState<Direction::Output>, |
236 | ExternalFormattedIoStatementState<Direction::Input>, |
237 | ExternalListIoStatementState<Direction::Output>, |
238 | ExternalListIoStatementState<Direction::Input>, |
239 | ExternalUnformattedIoStatementState<Direction::Output>, |
240 | ExternalUnformattedIoStatementState<Direction::Input>, InquireUnitState, |
241 | ExternalMiscIoStatementState, ErroneousIoStatementState> |
242 | u_; |
243 | |
244 | // Points to the active alternative (if any) in u_ for use as a Cookie |
245 | Fortran::common::optional<IoStatementState> io_; |
246 | |
247 | // A stack of child I/O pseudo-units for defined I/O that have this |
248 | // unit number. |
249 | OwningPtr<ChildIo> child_; |
250 | }; |
251 | |
252 | // A pseudo-unit for child I/O statements in defined I/O subroutines; |
253 | // it forwards operations to the parent I/O statement, which might also |
254 | // be a child I/O statement. |
255 | class ChildIo { |
256 | public: |
257 | RT_API_ATTRS ChildIo(IoStatementState &parent, OwningPtr<ChildIo> &&previous) |
258 | : parent_{parent}, previous_{std::move(previous)} {} |
259 | |
260 | RT_API_ATTRS IoStatementState &parent() const { return parent_; } |
261 | |
262 | RT_API_ATTRS void EndIoStatement(); |
263 | |
264 | template <typename A, typename... X> |
265 | RT_API_ATTRS IoStatementState &BeginIoStatement(X &&...xs) { |
266 | A &state{u_.emplace<A>(std::forward<X>(xs)...)}; |
267 | io_.emplace(state); |
268 | return *io_; |
269 | } |
270 | |
271 | RT_API_ATTRS OwningPtr<ChildIo> AcquirePrevious() { |
272 | return std::move(previous_); |
273 | } |
274 | |
275 | RT_API_ATTRS Iostat CheckFormattingAndDirection(bool unformatted, Direction); |
276 | |
277 | private: |
278 | IoStatementState &parent_; |
279 | OwningPtr<ChildIo> previous_; |
280 | std::variant<std::monostate, |
281 | ChildFormattedIoStatementState<Direction::Output>, |
282 | ChildFormattedIoStatementState<Direction::Input>, |
283 | ChildListIoStatementState<Direction::Output>, |
284 | ChildListIoStatementState<Direction::Input>, |
285 | ChildUnformattedIoStatementState<Direction::Output>, |
286 | ChildUnformattedIoStatementState<Direction::Input>, InquireUnitState, |
287 | ErroneousIoStatementState, ExternalMiscIoStatementState> |
288 | u_; |
289 | Fortran::common::optional<IoStatementState> io_; |
290 | }; |
291 | |
292 | } // namespace Fortran::runtime::io |
293 | #endif // FORTRAN_RUNTIME_IO_UNIT_H_ |
294 | |