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/Runtime/memory.h" |
25 | #include <cstdlib> |
26 | #include <cstring> |
27 | #include <optional> |
28 | #include <variant> |
29 | |
30 | namespace Fortran::runtime::io { |
31 | |
32 | class UnitMap; |
33 | class ChildIo; |
34 | |
35 | class ExternalFileUnit : public ConnectionState, |
36 | public OpenFile, |
37 | public FileFrame<ExternalFileUnit> { |
38 | public: |
39 | explicit ExternalFileUnit(int unitNumber) : unitNumber_{unitNumber} { |
40 | isUTF8 = executionEnvironment.defaultUTF8; |
41 | asyncIdAvailable_.set(); |
42 | asyncIdAvailable_.reset(0); |
43 | } |
44 | ~ExternalFileUnit() {} |
45 | |
46 | int unitNumber() const { return unitNumber_; } |
47 | bool swapEndianness() const { return swapEndianness_; } |
48 | bool createdForInternalChildIo() const { return createdForInternalChildIo_; } |
49 | |
50 | static ExternalFileUnit *LookUp(int unit); |
51 | static ExternalFileUnit *LookUpOrCreate( |
52 | int unit, const Terminator &, bool &wasExtant); |
53 | static ExternalFileUnit *LookUpOrCreateAnonymous(int unit, Direction, |
54 | std::optional<bool> isUnformatted, const Terminator &); |
55 | static ExternalFileUnit *LookUp(const char *path, std::size_t pathLen); |
56 | static ExternalFileUnit &CreateNew(int unit, const Terminator &); |
57 | static ExternalFileUnit *LookUpForClose(int unit); |
58 | static ExternalFileUnit &NewUnit(const Terminator &, bool forChildIo); |
59 | static void CloseAll(IoErrorHandler &); |
60 | static void FlushAll(IoErrorHandler &); |
61 | |
62 | // Returns true if an existing unit was closed |
63 | bool OpenUnit(std::optional<OpenStatus>, std::optional<Action>, Position, |
64 | OwningPtr<char> &&path, std::size_t pathLength, Convert, |
65 | IoErrorHandler &); |
66 | void OpenAnonymousUnit(std::optional<OpenStatus>, std::optional<Action>, |
67 | Position, Convert, IoErrorHandler &); |
68 | void CloseUnit(CloseStatus, IoErrorHandler &); |
69 | void DestroyClosed(); |
70 | |
71 | Iostat SetDirection(Direction); |
72 | |
73 | template <typename A, typename... X> |
74 | IoStatementState &BeginIoStatement(const Terminator &terminator, X &&...xs) { |
75 | // Take lock_ and hold it until EndIoStatement(). |
76 | #if USE_PTHREADS |
77 | if (!lock_.TakeIfNoDeadlock()) { |
78 | terminator.Crash("Recursive I/O attempted on unit %d" , unitNumber_); |
79 | } |
80 | #else |
81 | lock_.Take(); |
82 | #endif |
83 | A &state{u_.emplace<A>(std::forward<X>(xs)...)}; |
84 | if constexpr (!std::is_same_v<A, OpenStatementState>) { |
85 | state.mutableModes() = ConnectionState::modes; |
86 | } |
87 | directAccessRecWasSet_ = false; |
88 | io_.emplace(state); |
89 | return *io_; |
90 | } |
91 | |
92 | bool Emit( |
93 | const char *, std::size_t, std::size_t elementBytes, IoErrorHandler &); |
94 | bool Receive(char *, std::size_t, std::size_t elementBytes, IoErrorHandler &); |
95 | std::size_t GetNextInputBytes(const char *&, IoErrorHandler &); |
96 | bool BeginReadingRecord(IoErrorHandler &); |
97 | void FinishReadingRecord(IoErrorHandler &); |
98 | bool AdvanceRecord(IoErrorHandler &); |
99 | void BackspaceRecord(IoErrorHandler &); |
100 | void FlushOutput(IoErrorHandler &); |
101 | void FlushIfTerminal(IoErrorHandler &); |
102 | void Endfile(IoErrorHandler &); |
103 | void Rewind(IoErrorHandler &); |
104 | void EndIoStatement(); |
105 | bool SetStreamPos(std::int64_t, IoErrorHandler &); // one-based, for POS= |
106 | bool SetDirectRec(std::int64_t, IoErrorHandler &); // one-based, for REC= |
107 | std::int64_t InquirePos() const { |
108 | // 12.6.2.11 defines POS=1 as the beginning of file |
109 | return frameOffsetInFile_ + recordOffsetInFrame_ + positionInRecord + 1; |
110 | } |
111 | |
112 | ChildIo *GetChildIo() { return child_.get(); } |
113 | ChildIo &PushChildIo(IoStatementState &); |
114 | void PopChildIo(ChildIo &); |
115 | |
116 | int GetAsynchronousId(IoErrorHandler &); |
117 | bool Wait(int); |
118 | |
119 | private: |
120 | static UnitMap &CreateUnitMap(); |
121 | static UnitMap &GetUnitMap(); |
122 | const char *FrameNextInput(IoErrorHandler &, std::size_t); |
123 | void SetPosition(std::int64_t, IoErrorHandler &); // zero-based |
124 | void BeginSequentialVariableUnformattedInputRecord(IoErrorHandler &); |
125 | void BeginVariableFormattedInputRecord(IoErrorHandler &); |
126 | void BackspaceFixedRecord(IoErrorHandler &); |
127 | void BackspaceVariableUnformattedRecord(IoErrorHandler &); |
128 | void BackspaceVariableFormattedRecord(IoErrorHandler &); |
129 | bool SetVariableFormattedRecordLength(); |
130 | void DoImpliedEndfile(IoErrorHandler &); |
131 | void DoEndfile(IoErrorHandler &); |
132 | void CommitWrites(); |
133 | bool CheckDirectAccess(IoErrorHandler &); |
134 | void HitEndOnRead(IoErrorHandler &); |
135 | std::int32_t (std::int64_t frameOffset); |
136 | |
137 | Lock lock_; |
138 | |
139 | int unitNumber_{-1}; |
140 | Direction direction_{Direction::Output}; |
141 | bool impliedEndfile_{false}; // sequential/stream output has taken place |
142 | bool beganReadingRecord_{false}; |
143 | bool anyWriteSinceLastPositioning_{false}; |
144 | bool directAccessRecWasSet_{false}; // REC= appeared |
145 | // Subtle: The beginning of the frame can't be allowed to advance |
146 | // during a single list-directed READ due to the possibility of a |
147 | // multi-record CHARACTER value with a "r*" repeat count. So we |
148 | // manage the frame and the current record therein separately. |
149 | std::int64_t frameOffsetInFile_{0}; |
150 | std::size_t recordOffsetInFrame_{0}; // of currentRecordNumber |
151 | bool swapEndianness_{false}; |
152 | bool createdForInternalChildIo_{false}; |
153 | common::BitSet<64> asyncIdAvailable_; |
154 | |
155 | // When a synchronous I/O statement is in progress on this unit, holds its |
156 | // state. |
157 | std::variant<std::monostate, OpenStatementState, CloseStatementState, |
158 | ExternalFormattedIoStatementState<Direction::Output>, |
159 | ExternalFormattedIoStatementState<Direction::Input>, |
160 | ExternalListIoStatementState<Direction::Output>, |
161 | ExternalListIoStatementState<Direction::Input>, |
162 | ExternalUnformattedIoStatementState<Direction::Output>, |
163 | ExternalUnformattedIoStatementState<Direction::Input>, InquireUnitState, |
164 | ExternalMiscIoStatementState, ErroneousIoStatementState> |
165 | u_; |
166 | |
167 | // Points to the active alternative (if any) in u_ for use as a Cookie |
168 | std::optional<IoStatementState> io_; |
169 | |
170 | // A stack of child I/O pseudo-units for defined I/O that have this |
171 | // unit number. |
172 | OwningPtr<ChildIo> child_; |
173 | }; |
174 | |
175 | // A pseudo-unit for child I/O statements in defined I/O subroutines; |
176 | // it forwards operations to the parent I/O statement, which might also |
177 | // be a child I/O statement. |
178 | class ChildIo { |
179 | public: |
180 | ChildIo(IoStatementState &parent, OwningPtr<ChildIo> &&previous) |
181 | : parent_{parent}, previous_{std::move(previous)} {} |
182 | |
183 | IoStatementState &parent() const { return parent_; } |
184 | |
185 | void EndIoStatement(); |
186 | |
187 | template <typename A, typename... X> |
188 | IoStatementState &BeginIoStatement(X &&...xs) { |
189 | A &state{u_.emplace<A>(std::forward<X>(xs)...)}; |
190 | io_.emplace(state); |
191 | return *io_; |
192 | } |
193 | |
194 | OwningPtr<ChildIo> AcquirePrevious() { return std::move(previous_); } |
195 | |
196 | Iostat CheckFormattingAndDirection(bool unformatted, Direction); |
197 | |
198 | private: |
199 | IoStatementState &parent_; |
200 | OwningPtr<ChildIo> previous_; |
201 | std::variant<std::monostate, |
202 | ChildFormattedIoStatementState<Direction::Output>, |
203 | ChildFormattedIoStatementState<Direction::Input>, |
204 | ChildListIoStatementState<Direction::Output>, |
205 | ChildListIoStatementState<Direction::Input>, |
206 | ChildUnformattedIoStatementState<Direction::Output>, |
207 | ChildUnformattedIoStatementState<Direction::Input>, InquireUnitState, |
208 | ErroneousIoStatementState, ExternalMiscIoStatementState> |
209 | u_; |
210 | std::optional<IoStatementState> io_; |
211 | }; |
212 | |
213 | } // namespace Fortran::runtime::io |
214 | #endif // FORTRAN_RUNTIME_IO_UNIT_H_ |
215 | |