[flang-commits] [flang] 166d6ed - [flang][runtime] Better (but still synchronous) support for asynchronous I/O
Peter Klausler via flang-commits
flang-commits at lists.llvm.org
Mon Jun 13 10:43:25 PDT 2022
Author: Peter Klausler
Date: 2022-06-13T10:43:14-07:00
New Revision: 166d6ed5c70a509a266a942f6247ed64ecc6e232
URL: https://github.com/llvm/llvm-project/commit/166d6ed5c70a509a266a942f6247ed64ecc6e232
DIFF: https://github.com/llvm/llvm-project/commit/166d6ed5c70a509a266a942f6247ed64ecc6e232.diff
LOG: [flang][runtime] Better (but still synchronous) support for asynchronous I/O
Track pending "asynchronous" I/O operation IDs so that WAIT statements can
report errors about bad ID numbers.
Lowering will need to extended to call GetAsynchronousId() for a READ or
WRITE statement with ID=n.
Differential Revision: https://reviews.llvm.org/D127421
Added:
Modified:
flang/include/flang/Runtime/io-api.h
flang/include/flang/Runtime/iostat.h
flang/runtime/io-api.cpp
flang/runtime/io-stmt.cpp
flang/runtime/io-stmt.h
flang/runtime/iostat.cpp
flang/runtime/unit.cpp
flang/runtime/unit.h
Removed:
################################################################################
diff --git a/flang/include/flang/Runtime/io-api.h b/flang/include/flang/Runtime/io-api.h
index 66dd1a7a18b7b..27cffdad42c13 100644
--- a/flang/include/flang/Runtime/io-api.h
+++ b/flang/include/flang/Runtime/io-api.h
@@ -144,9 +144,11 @@ Cookie IONAME(BeginUnformattedInput)(ExternalUnit = DefaultUnit,
const char *sourceFile = nullptr, int sourceLine = 0);
// WAIT(ID=)
-Cookie IONAME(BeginWait)(ExternalUnit, AsynchronousId);
+Cookie IONAME(BeginWait)(ExternalUnit, AsynchronousId,
+ const char *sourceFile = nullptr, int sourceLine = 0);
// WAIT(no ID=)
-Cookie IONAME(BeginWaitAll)(ExternalUnit);
+Cookie IONAME(BeginWaitAll)(
+ ExternalUnit, const char *sourceFile = nullptr, int sourceLine = 0);
// Other I/O statements
Cookie IONAME(BeginClose)(
@@ -194,7 +196,8 @@ Cookie IONAME(BeginInquireIoLength)(
void IONAME(EnableHandlers)(Cookie, bool hasIoStat = false, bool hasErr = false,
bool hasEnd = false, bool hasEor = false, bool hasIoMsg = false);
-// ASYNCHRONOUS='YES' or 'NO' with no ID= on READ/WRITE/OPEN
+// ASYNCHRONOUS='YES' or 'NO' on READ/WRITE/OPEN
+// Use GetAsynchronousId() to handle ID=.
bool IONAME(SetAsynchronous)(Cookie, const char *, std::size_t);
// Control list options. These return false on a error that the
@@ -306,8 +309,8 @@ std::size_t IONAME(GetIoLength)(Cookie);
// end-of-record/file condition is present.
void IONAME(GetIoMsg)(Cookie, char *, std::size_t); // IOMSG=
-// TODO: for ID= on READ/WRITE(ASYNCHRONOUS='YES')
-// int IONAME(GetAsynchronousId)(Cookie);
+// Defines ID= on READ/WRITE(ASYNCHRONOUS='YES')
+int IONAME(GetAsynchronousId)(Cookie);
// INQUIRE() specifiers are mostly identified by their NUL-terminated
// case-insensitive names.
diff --git a/flang/include/flang/Runtime/iostat.h b/flang/include/flang/Runtime/iostat.h
index 9453c12fc0a83..cfde0448fbf28 100644
--- a/flang/include/flang/Runtime/iostat.h
+++ b/flang/include/flang/Runtime/iostat.h
@@ -77,6 +77,8 @@ enum Iostat {
IostatRealInputOverflow,
IostatOpenAlreadyConnected,
IostatCannotReposition,
+ IostatBadWaitId,
+ IostatTooManyAsyncOps,
};
const char *IostatErrorString(int);
diff --git a/flang/runtime/io-api.cpp b/flang/runtime/io-api.cpp
index 99ff2f01ab4e9..4373dcde29b40 100644
--- a/flang/runtime/io-api.cpp
+++ b/flang/runtime/io-api.cpp
@@ -337,18 +337,30 @@ Cookie IONAME(BeginOpenNewUnit)( // OPEN(NEWUNIT=j)
unit, false /*was an existing file*/, sourceFile, sourceLine);
}
-Cookie IONAME(BeginWait)(ExternalUnit unitNumber, AsynchronousId id) {
- // TODO: add and use sourceFile & sourceLine here
- Terminator oom;
- // TODO: add and use sourceFile & sourceLine here
- auto &io{
- New<NoopStatementState>{oom}(nullptr, 0).release()->ioStatementState()};
- if (id != 0 && !ExternalFileUnit::LookUp(unitNumber)) {
- io.GetIoErrorHandler().SetPendingError(IostatBadWaitUnit);
- }
- return &io;
-}
-Cookie IONAME(BeginWaitAll)(ExternalUnit unitNumber) {
+Cookie IONAME(BeginWait)(ExternalUnit unitNumber, AsynchronousId id,
+ const char *sourceFile, int sourceLine) {
+ Terminator terminator{sourceFile, sourceLine};
+ if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(unitNumber)}) {
+ if (unit->Wait(id)) {
+ return &unit->BeginIoStatement<ExternalMiscIoStatementState>(
+ *unit, ExternalMiscIoStatementState::Wait, sourceFile, sourceLine);
+ } else {
+ return &unit->BeginIoStatement<ErroneousIoStatementState>(
+ IostatBadWaitId, unit, sourceFile, sourceLine);
+ }
+ } else {
+ auto &io{
+ New<NoopStatementState>{terminator}(sourceFile, sourceLine, unitNumber)
+ .release()
+ ->ioStatementState()};
+ if (id != 0) {
+ io.GetIoErrorHandler().SetPendingError(IostatBadWaitUnit);
+ }
+ return &io;
+ }
+}
+Cookie IONAME(BeginWaitAll)(
+ ExternalUnit unitNumber, const char *sourceFile, int sourceLine) {
return IONAME(BeginWait)(unitNumber, 0 /*no ID=*/);
}
@@ -737,9 +749,13 @@ bool IONAME(SetAsynchronous)(
"SetAsynchronous() called after GetNewUnit() for an OPEN statement");
}
open->unit().set_mayAsynchronous(isYes);
- } else if (ExternalFileUnit * unit{io.GetExternalFileUnit()}) {
- if (isYes && !unit->mayAsynchronous()) {
- handler.SignalError(IostatBadAsynchronous);
+ } else if (auto *ext{io.get_if<ExternalIoStatementBase>()}) {
+ if (isYes) {
+ if (ext->unit().mayAsynchronous()) {
+ ext->SetAsynchronous();
+ } else {
+ handler.SignalError(IostatBadAsynchronous);
+ }
}
} else {
handler.Crash("SetAsynchronous() called when not in an OPEN or external "
diff --git a/flang/runtime/io-stmt.cpp b/flang/runtime/io-stmt.cpp
index 98456766ecb93..a73012dab6169 100644
--- a/flang/runtime/io-stmt.cpp
+++ b/flang/runtime/io-stmt.cpp
@@ -206,6 +206,10 @@ int ExternalIoStatementBase::EndIoStatement() {
return result;
}
+void ExternalIoStatementBase::SetAsynchronous() {
+ asynchronousID_ = unit().GetAsynchronousId(*this);
+}
+
void OpenStatementState::set_path(const char *path, std::size_t length) {
pathLength_ = TrimTrailingSpaces(path, length);
path_ = SaveDefaultCharacter(path, pathLength_, *this);
@@ -1023,6 +1027,8 @@ void ExternalMiscIoStatementState::CompleteOperation() {
case Rewind:
ext.Rewind(*this);
break;
+ case Wait:
+ break; // handled in io-api.cpp BeginWait
}
return IoStatementBase::CompleteOperation();
}
diff --git a/flang/runtime/io-stmt.h b/flang/runtime/io-stmt.h
index 5a5bef5d4e61e..5b5b60e4a97f4 100644
--- a/flang/runtime/io-stmt.h
+++ b/flang/runtime/io-stmt.h
@@ -407,11 +407,14 @@ class ExternalIoStatementBase : public IoStatementBase {
ExternalFileUnit &unit() { return unit_; }
MutableModes &mutableModes();
ConnectionState &GetConnectionState();
+ int asynchronousID() const { return asynchronousID_; }
int EndIoStatement();
ExternalFileUnit *GetExternalFileUnit() const { return &unit_; }
+ void SetAsynchronous();
private:
ExternalFileUnit &unit_;
+ int asynchronousID_{-1};
};
template <Direction DIR>
@@ -698,7 +701,7 @@ class InquireIOLengthState : public NoUnitIoStatementState,
class ExternalMiscIoStatementState : public ExternalIoStatementBase {
public:
- enum Which { Flush, Backspace, Endfile, Rewind };
+ enum Which { Flush, Backspace, Endfile, Rewind, Wait };
ExternalMiscIoStatementState(ExternalFileUnit &unit, Which which,
const char *sourceFile = nullptr, int sourceLine = 0)
: ExternalIoStatementBase{unit, sourceFile, sourceLine}, which_{which} {}
diff --git a/flang/runtime/iostat.cpp b/flang/runtime/iostat.cpp
index 0e6c162b54e4f..3008c6e763d5a 100644
--- a/flang/runtime/iostat.cpp
+++ b/flang/runtime/iostat.cpp
@@ -87,7 +87,7 @@ const char *IostatErrorString(int iostat) {
return "READ/WRITE(ASYNCHRONOUS='YES') on unit without "
"OPEN(ASYNCHRONOUS='YES')";
case IostatBadWaitUnit:
- return "WAIT(ID=nonzero) for a bad unit number";
+ return "WAIT(UNIT=) for a bad unit number";
case IostatBOZInputOverflow:
return "B/O/Z input value overflows variable";
case IostatIntegerInputOverflow:
@@ -99,6 +99,10 @@ const char *IostatErrorString(int iostat) {
"only be processed sequentially";
case IostatOpenAlreadyConnected:
return "OPEN of file already connected to another unit";
+ case IostatBadWaitId:
+ return "WAIT(ID=nonzero) for an ID value that is not a pending operation";
+ case IostatTooManyAsyncOps:
+ return "Too many asynchronous operations pending on unit";
default:
return nullptr;
}
diff --git a/flang/runtime/unit.cpp b/flang/runtime/unit.cpp
index 1a50b79235827..c7df78a1ed0db 100644
--- a/flang/runtime/unit.cpp
+++ b/flang/runtime/unit.cpp
@@ -911,6 +911,32 @@ void ExternalFileUnit::PopChildIo(ChildIo &child) {
child_.reset(child.AcquirePrevious().release()); // deletes top child
}
+int ExternalFileUnit::GetAsynchronousId(IoErrorHandler &handler) {
+ if (!mayAsynchronous()) {
+ handler.SignalError(IostatBadAsynchronous);
+ return -1;
+ } else if (auto least{asyncIdAvailable_.LeastElement()}) {
+ asyncIdAvailable_.reset(*least);
+ return static_cast<int>(*least);
+ } else {
+ handler.SignalError(IostatTooManyAsyncOps);
+ return -1;
+ }
+}
+
+bool ExternalFileUnit::Wait(int id) {
+ if (id < 0 || asyncIdAvailable_.test(id)) {
+ return false;
+ } else {
+ if (id == 0) {
+ asyncIdAvailable_.set();
+ } else {
+ asyncIdAvailable_.set(id);
+ }
+ return true;
+ }
+}
+
void ChildIo::EndIoStatement() {
io_.reset();
u_.emplace<std::monostate>();
diff --git a/flang/runtime/unit.h b/flang/runtime/unit.h
index e47fd5d5c3bc5..3d024ba28800f 100644
--- a/flang/runtime/unit.h
+++ b/flang/runtime/unit.h
@@ -20,6 +20,7 @@
#include "io-stmt.h"
#include "lock.h"
#include "terminator.h"
+#include "flang/Common/constexpr-bitset.h"
#include "flang/Runtime/memory.h"
#include <cstdlib>
#include <cstring>
@@ -37,6 +38,7 @@ class ExternalFileUnit : public ConnectionState,
public:
explicit ExternalFileUnit(int unitNumber) : unitNumber_{unitNumber} {
isUTF8 = executionEnvironment.defaultUTF8;
+ asyncIdAvailable_.set();
}
~ExternalFileUnit() {}
@@ -102,6 +104,9 @@ class ExternalFileUnit : public ConnectionState,
ChildIo &PushChildIo(IoStatementState &);
void PopChildIo(ChildIo &);
+ int GetAsynchronousId(IoErrorHandler &);
+ bool Wait(int);
+
private:
static UnitMap &GetUnitMap();
const char *FrameNextInput(IoErrorHandler &, std::size_t);
@@ -117,13 +122,22 @@ class ExternalFileUnit : public ConnectionState,
bool CheckDirectAccess(IoErrorHandler &);
void HitEndOnRead(IoErrorHandler &);
+ Lock lock_;
+
int unitNumber_{-1};
Direction direction_{Direction::Output};
bool impliedEndfile_{false}; // sequential/stream output has taken place
bool beganReadingRecord_{false};
bool directAccessRecWasSet_{false}; // REC= appeared
-
- Lock lock_;
+ // 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_;
// When a synchronous I/O statement is in progress on this unit, holds its
// state.
@@ -140,17 +154,6 @@ class ExternalFileUnit : public ConnectionState,
// Points to the active alternative (if any) in u_ for use as a Cookie
std::optional<IoStatementState> io_;
- // 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};
-
// A stack of child I/O pseudo-units for user-defined derived type
// I/O that have this unit number.
OwningPtr<ChildIo> child_;
More information about the flang-commits
mailing list