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)(
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
// 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.
IostatRealInputOverflow,
IostatOpenAlreadyConnected,
IostatCannotReposition,
+ IostatBadWaitId,
+ IostatTooManyAsyncOps,
};
const char *IostatErrorString(int);
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=*/);
}
"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 "
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);
case Rewind:
ext.Rewind(*this);
break;
+ case Wait:
+ break; // handled in io-api.cpp BeginWait
}
return IoStatementBase::CompleteOperation();
}
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>
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} {}
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:
"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;
}
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>();
#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>
public:
explicit ExternalFileUnit(int unitNumber) : unitNumber_{unitNumber} {
isUTF8 = executionEnvironment.defaultUTF8;
+ asyncIdAvailable_.set();
}
~ExternalFileUnit() {}
ChildIo &PushChildIo(IoStatementState &);
void PopChildIo(ChildIo &);
+ int GetAsynchronousId(IoErrorHandler &);
+ bool Wait(int);
+
private:
static UnitMap &GetUnitMap();
const char *FrameNextInput(IoErrorHandler &, std::size_t);
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.
// 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_;