//===-- runtime/unit.h ------------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // Fortran external I/O units #ifndef FORTRAN_RUNTIME_IO_UNIT_H_ #define FORTRAN_RUNTIME_IO_UNIT_H_ #include "buffer.h" #include "connection.h" #include "environment.h" #include "file.h" #include "format.h" #include "io-error.h" #include "io-stmt.h" #include "lock.h" #include "terminator.h" #include "flang/Common/constexpr-bitset.h" #include "flang/Common/optional.h" #include "flang/Runtime/memory.h" #include #include #include namespace Fortran::runtime::io { class UnitMap; class ChildIo; class ExternalFileUnit; RT_OFFLOAD_VAR_GROUP_BEGIN // Predefined file units. extern RT_VAR_ATTRS ExternalFileUnit *defaultInput; // unit 5 extern RT_VAR_ATTRS ExternalFileUnit *defaultOutput; // unit 6 extern RT_VAR_ATTRS ExternalFileUnit *errorOutput; // unit 0 extension RT_OFFLOAD_VAR_GROUP_END #if defined(RT_USE_PSEUDO_FILE_UNIT) // A flavor of OpenFile class that pretends to be a terminal, // and only provides basic buffering of the output // in an internal buffer, and Write's the output // using std::printf(). Since it does not rely on file system // APIs, it can be used to implement external output // for offload devices. class PseudoOpenFile { public: using FileOffset = std::int64_t; RT_API_ATTRS const char *path() const { return nullptr; } RT_API_ATTRS std::size_t pathLength() const { return 0; } RT_API_ATTRS void set_path(OwningPtr &&, std::size_t bytes) {} RT_API_ATTRS bool mayRead() const { return false; } RT_API_ATTRS bool mayWrite() const { return true; } RT_API_ATTRS bool mayPosition() const { return false; } RT_API_ATTRS bool mayAsynchronous() const { return false; } RT_API_ATTRS void set_mayAsynchronous(bool yes); // Pretend to be a terminal to force the output // at the end of IO statement. RT_API_ATTRS bool isTerminal() const { return true; } RT_API_ATTRS bool isWindowsTextFile() const { return false; } RT_API_ATTRS Fortran::common::optional knownSize() const; RT_API_ATTRS bool IsConnected() const { return false; } RT_API_ATTRS void Open(OpenStatus, Fortran::common::optional, Position, IoErrorHandler &); RT_API_ATTRS void Predefine(int fd) {} RT_API_ATTRS void Close(CloseStatus, IoErrorHandler &); RT_API_ATTRS std::size_t Read(FileOffset, char *, std::size_t minBytes, std::size_t maxBytes, IoErrorHandler &); RT_API_ATTRS std::size_t Write( FileOffset, const char *, std::size_t, IoErrorHandler &); RT_API_ATTRS void Truncate(FileOffset, IoErrorHandler &); RT_API_ATTRS int ReadAsynchronously( FileOffset, char *, std::size_t, IoErrorHandler &); RT_API_ATTRS int WriteAsynchronously( FileOffset, const char *, std::size_t, IoErrorHandler &); RT_API_ATTRS void Wait(int id, IoErrorHandler &); RT_API_ATTRS void WaitAll(IoErrorHandler &); RT_API_ATTRS Position InquirePosition() const; }; #endif // defined(RT_USE_PSEUDO_FILE_UNIT) #if !defined(RT_USE_PSEUDO_FILE_UNIT) using OpenFileClass = OpenFile; using FileFrameClass = FileFrame; #else // defined(RT_USE_PSEUDO_FILE_UNIT) using OpenFileClass = PseudoOpenFile; // Use not so big buffer for the pseudo file unit frame. using FileFrameClass = FileFrame; #endif // defined(RT_USE_PSEUDO_FILE_UNIT) class ExternalFileUnit : public ConnectionState, public OpenFileClass, public FileFrameClass { public: static constexpr int maxAsyncIds{64 * 16}; explicit RT_API_ATTRS ExternalFileUnit(int unitNumber) : unitNumber_{unitNumber} { isUTF8 = executionEnvironment.defaultUTF8; for (int j{0}; 64 * j < maxAsyncIds; ++j) { asyncIdAvailable_[j].set(); } asyncIdAvailable_[0].reset(0); } RT_API_ATTRS ~ExternalFileUnit() {} RT_API_ATTRS int unitNumber() const { return unitNumber_; } RT_API_ATTRS bool swapEndianness() const { return swapEndianness_; } RT_API_ATTRS bool createdForInternalChildIo() const { return createdForInternalChildIo_; } static RT_API_ATTRS ExternalFileUnit *LookUp(int unit); static RT_API_ATTRS ExternalFileUnit *LookUpOrCreate( int unit, const Terminator &, bool &wasExtant); static RT_API_ATTRS ExternalFileUnit *LookUpOrCreateAnonymous(int unit, Direction, Fortran::common::optional isUnformatted, IoErrorHandler &); static RT_API_ATTRS ExternalFileUnit *LookUp( const char *path, std::size_t pathLen); static RT_API_ATTRS ExternalFileUnit &CreateNew(int unit, const Terminator &); static RT_API_ATTRS ExternalFileUnit *LookUpForClose(int unit); static RT_API_ATTRS ExternalFileUnit &NewUnit( const Terminator &, bool forChildIo); static RT_API_ATTRS void CloseAll(IoErrorHandler &); static RT_API_ATTRS void FlushAll(IoErrorHandler &); // Returns true if an existing unit was closed RT_API_ATTRS bool OpenUnit(Fortran::common::optional, Fortran::common::optional, Position, OwningPtr &&path, std::size_t pathLength, Convert, IoErrorHandler &); RT_API_ATTRS bool OpenAnonymousUnit(Fortran::common::optional, Fortran::common::optional, Position, Convert, IoErrorHandler &); RT_API_ATTRS void CloseUnit(CloseStatus, IoErrorHandler &); RT_API_ATTRS void DestroyClosed(); RT_API_ATTRS Iostat SetDirection(Direction); template RT_API_ATTRS IoStatementState &BeginIoStatement( const Terminator &terminator, X &&...xs) { // Take lock_ and hold it until EndIoStatement(). #if USE_PTHREADS if (!lock_.TakeIfNoDeadlock()) { terminator.Crash("Recursive I/O attempted on unit %d", unitNumber_); } #else lock_.Take(); #endif A &state{u_.emplace(std::forward(xs)...)}; if constexpr (!std::is_same_v) { state.mutableModes() = ConnectionState::modes; } directAccessRecWasSet_ = false; io_.emplace(state); return *io_; } RT_API_ATTRS bool Emit( const char *, std::size_t, std::size_t elementBytes, IoErrorHandler &); RT_API_ATTRS bool Receive( char *, std::size_t, std::size_t elementBytes, IoErrorHandler &); RT_API_ATTRS std::size_t GetNextInputBytes(const char *&, IoErrorHandler &); RT_API_ATTRS std::size_t ViewBytesInRecord(const char *&, bool forward) const; RT_API_ATTRS bool BeginReadingRecord(IoErrorHandler &); RT_API_ATTRS void FinishReadingRecord(IoErrorHandler &); RT_API_ATTRS bool AdvanceRecord(IoErrorHandler &); RT_API_ATTRS void BackspaceRecord(IoErrorHandler &); RT_API_ATTRS void FlushOutput(IoErrorHandler &); RT_API_ATTRS void FlushIfTerminal(IoErrorHandler &); RT_API_ATTRS void Endfile(IoErrorHandler &); RT_API_ATTRS void Rewind(IoErrorHandler &); RT_API_ATTRS void EndIoStatement(); RT_API_ATTRS bool SetStreamPos( std::int64_t, IoErrorHandler &); // one-based, for POS= RT_API_ATTRS bool SetDirectRec( std::int64_t, IoErrorHandler &); // one-based, for REC= RT_API_ATTRS std::int64_t InquirePos() const { // 12.6.2.11 defines POS=1 as the beginning of file return frameOffsetInFile_ + recordOffsetInFrame_ + positionInRecord + 1; } RT_API_ATTRS ChildIo *GetChildIo() { return child_.get(); } RT_API_ATTRS ChildIo &PushChildIo(IoStatementState &); RT_API_ATTRS void PopChildIo(ChildIo &); RT_API_ATTRS int GetAsynchronousId(IoErrorHandler &); RT_API_ATTRS bool Wait(int); private: static RT_API_ATTRS UnitMap &CreateUnitMap(); static RT_API_ATTRS UnitMap &GetUnitMap(); RT_API_ATTRS const char *FrameNextInput(IoErrorHandler &, std::size_t); RT_API_ATTRS void SetPosition(std::int64_t, IoErrorHandler &); // zero-based RT_API_ATTRS void BeginSequentialVariableUnformattedInputRecord( IoErrorHandler &); RT_API_ATTRS void BeginVariableFormattedInputRecord(IoErrorHandler &); RT_API_ATTRS void BackspaceFixedRecord(IoErrorHandler &); RT_API_ATTRS void BackspaceVariableUnformattedRecord(IoErrorHandler &); RT_API_ATTRS void BackspaceVariableFormattedRecord(IoErrorHandler &); RT_API_ATTRS bool SetVariableFormattedRecordLength(); RT_API_ATTRS void DoImpliedEndfile(IoErrorHandler &); template RT_API_ATTRS void DoEndfile(IoErrorHandler &); RT_API_ATTRS void CommitWrites(); RT_API_ATTRS bool CheckDirectAccess(IoErrorHandler &); RT_API_ATTRS void HitEndOnRead(IoErrorHandler &); RT_API_ATTRS std::int32_t ReadHeaderOrFooter(std::int64_t frameOffset); Lock lock_; int unitNumber_{-1}; Direction direction_{Direction::Output}; bool impliedEndfile_{false}; // sequential/stream output has taken place bool beganReadingRecord_{false}; bool anyWriteSinceLastPositioning_{false}; bool directAccessRecWasSet_{false}; // REC= appeared // Subtle: The beginning of the frame can't be allowed to advance // during a single list-directed READ due to the possibility of a // multi-record CHARACTER value with a "r*" repeat count. So we // manage the frame and the current record therein separately. std::int64_t frameOffsetInFile_{0}; std::size_t recordOffsetInFrame_{0}; // of currentRecordNumber bool swapEndianness_{false}; bool createdForInternalChildIo_{false}; common::BitSet<64> asyncIdAvailable_[maxAsyncIds / 64]; // When a synchronous I/O statement is in progress on this unit, holds its // state. std::variant, ExternalFormattedIoStatementState, ExternalListIoStatementState, ExternalListIoStatementState, ExternalUnformattedIoStatementState, ExternalUnformattedIoStatementState, InquireUnitState, ExternalMiscIoStatementState, ErroneousIoStatementState> u_; // Points to the active alternative (if any) in u_ for use as a Cookie Fortran::common::optional io_; // A stack of child I/O pseudo-units for defined I/O that have this // unit number. OwningPtr child_; }; // A pseudo-unit for child I/O statements in defined I/O subroutines; // it forwards operations to the parent I/O statement, which might also // be a child I/O statement. class ChildIo { public: RT_API_ATTRS ChildIo(IoStatementState &parent, OwningPtr &&previous) : parent_{parent}, previous_{std::move(previous)} {} RT_API_ATTRS IoStatementState &parent() const { return parent_; } RT_API_ATTRS void EndIoStatement(); template RT_API_ATTRS IoStatementState &BeginIoStatement(X &&...xs) { A &state{u_.emplace(std::forward(xs)...)}; io_.emplace(state); return *io_; } RT_API_ATTRS OwningPtr AcquirePrevious() { return std::move(previous_); } RT_API_ATTRS Iostat CheckFormattingAndDirection(bool unformatted, Direction); private: IoStatementState &parent_; OwningPtr previous_; std::variant, ChildFormattedIoStatementState, ChildListIoStatementState, ChildListIoStatementState, ChildUnformattedIoStatementState, ChildUnformattedIoStatementState, InquireUnitState, ErroneousIoStatementState, ExternalMiscIoStatementState> u_; Fortran::common::optional io_; }; } // namespace Fortran::runtime::io #endif // FORTRAN_RUNTIME_IO_UNIT_H_