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