100f3454bSSlava Zakharin //===-- runtime/external-unit.cpp -----------------------------------------===// 200f3454bSSlava Zakharin // 300f3454bSSlava Zakharin // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 400f3454bSSlava Zakharin // See https://llvm.org/LICENSE.txt for license information. 500f3454bSSlava Zakharin // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 600f3454bSSlava Zakharin // 700f3454bSSlava Zakharin //===----------------------------------------------------------------------===// 800f3454bSSlava Zakharin // 900f3454bSSlava Zakharin // Implemenation of ExternalFileUnit for RT_USE_PSEUDO_FILE_UNIT=0. 1000f3454bSSlava Zakharin // 1100f3454bSSlava Zakharin //===----------------------------------------------------------------------===// 1200f3454bSSlava Zakharin 1300f3454bSSlava Zakharin #include "io-error.h" 1400f3454bSSlava Zakharin #include "lock.h" 158ebf7411SSlava Zakharin #include "tools.h" 1600f3454bSSlava Zakharin #include "unit-map.h" 1700f3454bSSlava Zakharin #include "unit.h" 188ebf7411SSlava Zakharin 198ebf7411SSlava Zakharin // NOTE: the header files above may define OpenMP declare target 208ebf7411SSlava Zakharin // variables, so they have to be included unconditionally 218ebf7411SSlava Zakharin // so that the offload entries are consistent between host and device. 228ebf7411SSlava Zakharin #if !defined(RT_USE_PSEUDO_FILE_UNIT) 238ebf7411SSlava Zakharin 2400f3454bSSlava Zakharin #include <cstdio> 2500f3454bSSlava Zakharin #include <limits> 2600f3454bSSlava Zakharin 2700f3454bSSlava Zakharin namespace Fortran::runtime::io { 2800f3454bSSlava Zakharin 2900f3454bSSlava Zakharin // The per-unit data structures are created on demand so that Fortran I/O 3000f3454bSSlava Zakharin // should work without a Fortran main program. 3100f3454bSSlava Zakharin static Lock unitMapLock; 3200f3454bSSlava Zakharin static Lock createOpenLock; 3300f3454bSSlava Zakharin static UnitMap *unitMap{nullptr}; 3400f3454bSSlava Zakharin 3500f3454bSSlava Zakharin void FlushOutputOnCrash(const Terminator &terminator) { 3600f3454bSSlava Zakharin if (!defaultOutput && !errorOutput) { 3700f3454bSSlava Zakharin return; 3800f3454bSSlava Zakharin } 3900f3454bSSlava Zakharin IoErrorHandler handler{terminator}; 4000f3454bSSlava Zakharin handler.HasIoStat(); // prevent nested crash if flush has error 4100f3454bSSlava Zakharin CriticalSection critical{unitMapLock}; 4200f3454bSSlava Zakharin if (defaultOutput) { 4300f3454bSSlava Zakharin defaultOutput->FlushOutput(handler); 4400f3454bSSlava Zakharin } 4500f3454bSSlava Zakharin if (errorOutput) { 4600f3454bSSlava Zakharin errorOutput->FlushOutput(handler); 4700f3454bSSlava Zakharin } 4800f3454bSSlava Zakharin } 4900f3454bSSlava Zakharin 5000f3454bSSlava Zakharin ExternalFileUnit *ExternalFileUnit::LookUp(int unit) { 5100f3454bSSlava Zakharin return GetUnitMap().LookUp(unit); 5200f3454bSSlava Zakharin } 5300f3454bSSlava Zakharin 5400f3454bSSlava Zakharin ExternalFileUnit *ExternalFileUnit::LookUpOrCreate( 5500f3454bSSlava Zakharin int unit, const Terminator &terminator, bool &wasExtant) { 5600f3454bSSlava Zakharin return GetUnitMap().LookUpOrCreate(unit, terminator, wasExtant); 5700f3454bSSlava Zakharin } 5800f3454bSSlava Zakharin 5900f3454bSSlava Zakharin ExternalFileUnit *ExternalFileUnit::LookUpOrCreateAnonymous(int unit, 6000f3454bSSlava Zakharin Direction dir, Fortran::common::optional<bool> isUnformatted, 61a8f2d185SPeter Klausler IoErrorHandler &handler) { 62a8f2d185SPeter Klausler // Make sure that the returned anonymous unit has been opened, 6300f3454bSSlava Zakharin // not just created in the unitMap. 6400f3454bSSlava Zakharin CriticalSection critical{createOpenLock}; 6500f3454bSSlava Zakharin bool exists{false}; 66a8f2d185SPeter Klausler ExternalFileUnit *result{GetUnitMap().LookUpOrCreate(unit, handler, exists)}; 6700f3454bSSlava Zakharin if (result && !exists) { 68*2326a023SPeter Klausler common::optional<Action> action; 69*2326a023SPeter Klausler if (dir == Direction::Output) { 70*2326a023SPeter Klausler action = Action::ReadWrite; 71*2326a023SPeter Klausler } 72eac925fbSPeter Klausler if (!result->OpenAnonymousUnit( 7300f3454bSSlava Zakharin dir == Direction::Input ? OpenStatus::Unknown : OpenStatus::Replace, 74*2326a023SPeter Klausler action, Position::Rewind, Convert::Unknown, handler)) { 75eac925fbSPeter Klausler // fort.N isn't a writable file 76eac925fbSPeter Klausler if (ExternalFileUnit * closed{LookUpForClose(result->unitNumber())}) { 77eac925fbSPeter Klausler closed->DestroyClosed(); 78eac925fbSPeter Klausler } 79eac925fbSPeter Klausler result = nullptr; 80eac925fbSPeter Klausler } else { 8100f3454bSSlava Zakharin result->isUnformatted = isUnformatted; 8200f3454bSSlava Zakharin } 83eac925fbSPeter Klausler } 8400f3454bSSlava Zakharin return result; 8500f3454bSSlava Zakharin } 8600f3454bSSlava Zakharin 8700f3454bSSlava Zakharin ExternalFileUnit *ExternalFileUnit::LookUp( 8800f3454bSSlava Zakharin const char *path, std::size_t pathLen) { 8900f3454bSSlava Zakharin return GetUnitMap().LookUp(path, pathLen); 9000f3454bSSlava Zakharin } 9100f3454bSSlava Zakharin 9200f3454bSSlava Zakharin ExternalFileUnit &ExternalFileUnit::CreateNew( 9300f3454bSSlava Zakharin int unit, const Terminator &terminator) { 9400f3454bSSlava Zakharin bool wasExtant{false}; 9500f3454bSSlava Zakharin ExternalFileUnit *result{ 9600f3454bSSlava Zakharin GetUnitMap().LookUpOrCreate(unit, terminator, wasExtant)}; 9700f3454bSSlava Zakharin RUNTIME_CHECK(terminator, result && !wasExtant); 9800f3454bSSlava Zakharin return *result; 9900f3454bSSlava Zakharin } 10000f3454bSSlava Zakharin 10100f3454bSSlava Zakharin ExternalFileUnit *ExternalFileUnit::LookUpForClose(int unit) { 10200f3454bSSlava Zakharin return GetUnitMap().LookUpForClose(unit); 10300f3454bSSlava Zakharin } 10400f3454bSSlava Zakharin 10500f3454bSSlava Zakharin ExternalFileUnit &ExternalFileUnit::NewUnit( 10600f3454bSSlava Zakharin const Terminator &terminator, bool forChildIo) { 10700f3454bSSlava Zakharin ExternalFileUnit &unit{GetUnitMap().NewUnit(terminator)}; 10800f3454bSSlava Zakharin unit.createdForInternalChildIo_ = forChildIo; 10900f3454bSSlava Zakharin return unit; 11000f3454bSSlava Zakharin } 11100f3454bSSlava Zakharin 11200f3454bSSlava Zakharin bool ExternalFileUnit::OpenUnit(Fortran::common::optional<OpenStatus> status, 11300f3454bSSlava Zakharin Fortran::common::optional<Action> action, Position position, 11400f3454bSSlava Zakharin OwningPtr<char> &&newPath, std::size_t newPathLength, Convert convert, 11500f3454bSSlava Zakharin IoErrorHandler &handler) { 11600f3454bSSlava Zakharin if (convert == Convert::Unknown) { 11700f3454bSSlava Zakharin convert = executionEnvironment.conversion; 11800f3454bSSlava Zakharin } 11900f3454bSSlava Zakharin swapEndianness_ = convert == Convert::Swap || 12000f3454bSSlava Zakharin (convert == Convert::LittleEndian && !isHostLittleEndian) || 12100f3454bSSlava Zakharin (convert == Convert::BigEndian && isHostLittleEndian); 12200f3454bSSlava Zakharin bool impliedClose{false}; 12300f3454bSSlava Zakharin if (IsConnected()) { 12400f3454bSSlava Zakharin bool isSamePath{newPath.get() && path() && pathLength() == newPathLength && 12500f3454bSSlava Zakharin std::memcmp(path(), newPath.get(), newPathLength) == 0}; 12600f3454bSSlava Zakharin if (status && *status != OpenStatus::Old && isSamePath) { 12700f3454bSSlava Zakharin handler.SignalError("OPEN statement for connected unit may not have " 12800f3454bSSlava Zakharin "explicit STATUS= other than 'OLD'"); 12900f3454bSSlava Zakharin return impliedClose; 13000f3454bSSlava Zakharin } 13100f3454bSSlava Zakharin if (!newPath.get() || isSamePath) { 13200f3454bSSlava Zakharin // OPEN of existing unit, STATUS='OLD' or unspecified, not new FILE= 13300f3454bSSlava Zakharin newPath.reset(); 13400f3454bSSlava Zakharin return impliedClose; 13500f3454bSSlava Zakharin } 13600f3454bSSlava Zakharin // Otherwise, OPEN on open unit with new FILE= implies CLOSE 13700f3454bSSlava Zakharin DoImpliedEndfile(handler); 13800f3454bSSlava Zakharin FlushOutput(handler); 13900f3454bSSlava Zakharin TruncateFrame(0, handler); 14000f3454bSSlava Zakharin Close(CloseStatus::Keep, handler); 14100f3454bSSlava Zakharin impliedClose = true; 14200f3454bSSlava Zakharin } 14300f3454bSSlava Zakharin if (newPath.get() && newPathLength > 0) { 14400f3454bSSlava Zakharin if (const auto *already{ 14500f3454bSSlava Zakharin GetUnitMap().LookUp(newPath.get(), newPathLength)}) { 14600f3454bSSlava Zakharin handler.SignalError(IostatOpenAlreadyConnected, 14700f3454bSSlava Zakharin "OPEN(UNIT=%d,FILE='%.*s'): file is already connected to unit %d", 14800f3454bSSlava Zakharin unitNumber_, static_cast<int>(newPathLength), newPath.get(), 14900f3454bSSlava Zakharin already->unitNumber_); 15000f3454bSSlava Zakharin return impliedClose; 15100f3454bSSlava Zakharin } 15200f3454bSSlava Zakharin } 15300f3454bSSlava Zakharin set_path(std::move(newPath), newPathLength); 15400f3454bSSlava Zakharin Open(status.value_or(OpenStatus::Unknown), action, position, handler); 155a8f2d185SPeter Klausler if (handler.InError()) { 156a8f2d185SPeter Klausler return impliedClose; 157a8f2d185SPeter Klausler } 15800f3454bSSlava Zakharin auto totalBytes{knownSize()}; 15900f3454bSSlava Zakharin if (access == Access::Direct) { 16000f3454bSSlava Zakharin if (!openRecl) { 16100f3454bSSlava Zakharin handler.SignalError(IostatOpenBadRecl, 16200f3454bSSlava Zakharin "OPEN(UNIT=%d,ACCESS='DIRECT'): record length is not known", 16300f3454bSSlava Zakharin unitNumber()); 16400f3454bSSlava Zakharin } else if (*openRecl <= 0) { 16500f3454bSSlava Zakharin handler.SignalError(IostatOpenBadRecl, 16600f3454bSSlava Zakharin "OPEN(UNIT=%d,ACCESS='DIRECT',RECL=%jd): record length is invalid", 16700f3454bSSlava Zakharin unitNumber(), static_cast<std::intmax_t>(*openRecl)); 16800f3454bSSlava Zakharin } else if (totalBytes && (*totalBytes % *openRecl != 0)) { 16900f3454bSSlava Zakharin handler.SignalError(IostatOpenBadRecl, 17000f3454bSSlava Zakharin "OPEN(UNIT=%d,ACCESS='DIRECT',RECL=%jd): record length is not an " 17100f3454bSSlava Zakharin "even divisor of the file size %jd", 17200f3454bSSlava Zakharin unitNumber(), static_cast<std::intmax_t>(*openRecl), 17300f3454bSSlava Zakharin static_cast<std::intmax_t>(*totalBytes)); 17400f3454bSSlava Zakharin } 17500f3454bSSlava Zakharin recordLength = openRecl; 17600f3454bSSlava Zakharin } 17700f3454bSSlava Zakharin endfileRecordNumber.reset(); 17800f3454bSSlava Zakharin currentRecordNumber = 1; 17900f3454bSSlava Zakharin if (totalBytes && access == Access::Direct && openRecl.value_or(0) > 0) { 18000f3454bSSlava Zakharin endfileRecordNumber = 1 + (*totalBytes / *openRecl); 18100f3454bSSlava Zakharin } 18200f3454bSSlava Zakharin if (position == Position::Append) { 18300f3454bSSlava Zakharin if (totalBytes) { 18400f3454bSSlava Zakharin frameOffsetInFile_ = *totalBytes; 18500f3454bSSlava Zakharin } 18600f3454bSSlava Zakharin if (access != Access::Stream) { 18700f3454bSSlava Zakharin if (!endfileRecordNumber) { 18800f3454bSSlava Zakharin // Fake it so that we can backspace relative from the end 18900f3454bSSlava Zakharin endfileRecordNumber = std::numeric_limits<std::int64_t>::max() - 2; 19000f3454bSSlava Zakharin } 19100f3454bSSlava Zakharin currentRecordNumber = *endfileRecordNumber; 19200f3454bSSlava Zakharin } 19300f3454bSSlava Zakharin } 19400f3454bSSlava Zakharin return impliedClose; 19500f3454bSSlava Zakharin } 19600f3454bSSlava Zakharin 197eac925fbSPeter Klausler bool ExternalFileUnit::OpenAnonymousUnit( 19800f3454bSSlava Zakharin Fortran::common::optional<OpenStatus> status, 19900f3454bSSlava Zakharin Fortran::common::optional<Action> action, Position position, 20000f3454bSSlava Zakharin Convert convert, IoErrorHandler &handler) { 20100f3454bSSlava Zakharin // I/O to an unconnected unit reads/creates a local file, e.g. fort.7 20200f3454bSSlava Zakharin std::size_t pathMaxLen{32}; 20300f3454bSSlava Zakharin auto path{SizedNew<char>{handler}(pathMaxLen)}; 20400f3454bSSlava Zakharin std::snprintf(path.get(), pathMaxLen, "fort.%d", unitNumber_); 20500f3454bSSlava Zakharin OpenUnit(status, action, position, std::move(path), std::strlen(path.get()), 20600f3454bSSlava Zakharin convert, handler); 207eac925fbSPeter Klausler return IsConnected(); 20800f3454bSSlava Zakharin } 20900f3454bSSlava Zakharin 21000f3454bSSlava Zakharin void ExternalFileUnit::CloseUnit(CloseStatus status, IoErrorHandler &handler) { 21100f3454bSSlava Zakharin DoImpliedEndfile(handler); 21200f3454bSSlava Zakharin FlushOutput(handler); 21300f3454bSSlava Zakharin Close(status, handler); 21400f3454bSSlava Zakharin } 21500f3454bSSlava Zakharin 21600f3454bSSlava Zakharin void ExternalFileUnit::DestroyClosed() { 21700f3454bSSlava Zakharin GetUnitMap().DestroyClosed(*this); // destroys *this 21800f3454bSSlava Zakharin } 21900f3454bSSlava Zakharin 22000f3454bSSlava Zakharin Iostat ExternalFileUnit::SetDirection(Direction direction) { 22100f3454bSSlava Zakharin if (direction == Direction::Input) { 22200f3454bSSlava Zakharin if (mayRead()) { 22300f3454bSSlava Zakharin direction_ = Direction::Input; 22400f3454bSSlava Zakharin return IostatOk; 22500f3454bSSlava Zakharin } else { 22600f3454bSSlava Zakharin return IostatReadFromWriteOnly; 22700f3454bSSlava Zakharin } 22800f3454bSSlava Zakharin } else { 22900f3454bSSlava Zakharin if (mayWrite()) { 230975579bdSPeter Klausler if (direction_ == Direction::Input) { 231975579bdSPeter Klausler // Don't retain any input data from previous record, like a 232975579bdSPeter Klausler // variable-length unformatted record footer, in the frame, 233975579bdSPeter Klausler // since we're going start writing frames. 234975579bdSPeter Klausler frameOffsetInFile_ += recordOffsetInFrame_; 235975579bdSPeter Klausler recordOffsetInFrame_ = 0; 236975579bdSPeter Klausler } 23700f3454bSSlava Zakharin direction_ = Direction::Output; 23800f3454bSSlava Zakharin return IostatOk; 23900f3454bSSlava Zakharin } else { 24000f3454bSSlava Zakharin return IostatWriteToReadOnly; 24100f3454bSSlava Zakharin } 24200f3454bSSlava Zakharin } 24300f3454bSSlava Zakharin } 24400f3454bSSlava Zakharin 24500f3454bSSlava Zakharin UnitMap &ExternalFileUnit::CreateUnitMap() { 24600f3454bSSlava Zakharin Terminator terminator{__FILE__, __LINE__}; 24700f3454bSSlava Zakharin IoErrorHandler handler{terminator}; 24800f3454bSSlava Zakharin UnitMap &newUnitMap{*New<UnitMap>{terminator}().release()}; 24900f3454bSSlava Zakharin 25000f3454bSSlava Zakharin bool wasExtant{false}; 25100f3454bSSlava Zakharin ExternalFileUnit &out{*newUnitMap.LookUpOrCreate( 25200f3454bSSlava Zakharin FORTRAN_DEFAULT_OUTPUT_UNIT, terminator, wasExtant)}; 25300f3454bSSlava Zakharin RUNTIME_CHECK(terminator, !wasExtant); 25400f3454bSSlava Zakharin out.Predefine(1); 25500f3454bSSlava Zakharin handler.SignalError(out.SetDirection(Direction::Output)); 25600f3454bSSlava Zakharin out.isUnformatted = false; 25700f3454bSSlava Zakharin defaultOutput = &out; 25800f3454bSSlava Zakharin 25900f3454bSSlava Zakharin ExternalFileUnit &in{*newUnitMap.LookUpOrCreate( 26000f3454bSSlava Zakharin FORTRAN_DEFAULT_INPUT_UNIT, terminator, wasExtant)}; 26100f3454bSSlava Zakharin RUNTIME_CHECK(terminator, !wasExtant); 26200f3454bSSlava Zakharin in.Predefine(0); 26300f3454bSSlava Zakharin handler.SignalError(in.SetDirection(Direction::Input)); 26400f3454bSSlava Zakharin in.isUnformatted = false; 26500f3454bSSlava Zakharin defaultInput = ∈ 26600f3454bSSlava Zakharin 26700f3454bSSlava Zakharin ExternalFileUnit &error{ 26800f3454bSSlava Zakharin *newUnitMap.LookUpOrCreate(FORTRAN_ERROR_UNIT, terminator, wasExtant)}; 26900f3454bSSlava Zakharin RUNTIME_CHECK(terminator, !wasExtant); 27000f3454bSSlava Zakharin error.Predefine(2); 27100f3454bSSlava Zakharin handler.SignalError(error.SetDirection(Direction::Output)); 27200f3454bSSlava Zakharin error.isUnformatted = false; 27300f3454bSSlava Zakharin errorOutput = &error; 27400f3454bSSlava Zakharin 27500f3454bSSlava Zakharin return newUnitMap; 27600f3454bSSlava Zakharin } 27700f3454bSSlava Zakharin 27800f3454bSSlava Zakharin // A back-up atexit() handler for programs that don't terminate with a main 27900f3454bSSlava Zakharin // program END or a STOP statement or other Fortran-initiated program shutdown, 28000f3454bSSlava Zakharin // such as programs with a C main() that terminate normally. It flushes all 28100f3454bSSlava Zakharin // external I/O units. It is registered once the first time that any external 28200f3454bSSlava Zakharin // I/O is attempted. 28300f3454bSSlava Zakharin static void CloseAllExternalUnits() { 28400f3454bSSlava Zakharin IoErrorHandler handler{"Fortran program termination"}; 28500f3454bSSlava Zakharin ExternalFileUnit::CloseAll(handler); 28600f3454bSSlava Zakharin } 28700f3454bSSlava Zakharin 28800f3454bSSlava Zakharin UnitMap &ExternalFileUnit::GetUnitMap() { 28900f3454bSSlava Zakharin if (unitMap) { 29000f3454bSSlava Zakharin return *unitMap; 29100f3454bSSlava Zakharin } 29200f3454bSSlava Zakharin { 29300f3454bSSlava Zakharin CriticalSection critical{unitMapLock}; 29400f3454bSSlava Zakharin if (unitMap) { 29500f3454bSSlava Zakharin return *unitMap; 29600f3454bSSlava Zakharin } 29700f3454bSSlava Zakharin unitMap = &CreateUnitMap(); 29800f3454bSSlava Zakharin } 29900f3454bSSlava Zakharin std::atexit(CloseAllExternalUnits); 30000f3454bSSlava Zakharin return *unitMap; 30100f3454bSSlava Zakharin } 30200f3454bSSlava Zakharin 30300f3454bSSlava Zakharin void ExternalFileUnit::CloseAll(IoErrorHandler &handler) { 30400f3454bSSlava Zakharin CriticalSection critical{unitMapLock}; 30500f3454bSSlava Zakharin if (unitMap) { 30600f3454bSSlava Zakharin unitMap->CloseAll(handler); 30700f3454bSSlava Zakharin FreeMemoryAndNullify(unitMap); 30800f3454bSSlava Zakharin } 30900f3454bSSlava Zakharin defaultOutput = nullptr; 31000f3454bSSlava Zakharin defaultInput = nullptr; 31100f3454bSSlava Zakharin errorOutput = nullptr; 31200f3454bSSlava Zakharin } 31300f3454bSSlava Zakharin 31400f3454bSSlava Zakharin void ExternalFileUnit::FlushAll(IoErrorHandler &handler) { 31500f3454bSSlava Zakharin CriticalSection critical{unitMapLock}; 31600f3454bSSlava Zakharin if (unitMap) { 31700f3454bSSlava Zakharin unitMap->FlushAll(handler); 31800f3454bSSlava Zakharin } 31900f3454bSSlava Zakharin } 32000f3454bSSlava Zakharin 32100f3454bSSlava Zakharin int ExternalFileUnit::GetAsynchronousId(IoErrorHandler &handler) { 32200f3454bSSlava Zakharin if (!mayAsynchronous()) { 32300f3454bSSlava Zakharin handler.SignalError(IostatBadAsynchronous); 32400f3454bSSlava Zakharin return -1; 32500f3454bSSlava Zakharin } else { 32600f3454bSSlava Zakharin for (int j{0}; 64 * j < maxAsyncIds; ++j) { 32700f3454bSSlava Zakharin if (auto least{asyncIdAvailable_[j].LeastElement()}) { 32800f3454bSSlava Zakharin asyncIdAvailable_[j].reset(*least); 32900f3454bSSlava Zakharin return 64 * j + static_cast<int>(*least); 33000f3454bSSlava Zakharin } 33100f3454bSSlava Zakharin } 33200f3454bSSlava Zakharin handler.SignalError(IostatTooManyAsyncOps); 33300f3454bSSlava Zakharin return -1; 33400f3454bSSlava Zakharin } 33500f3454bSSlava Zakharin } 33600f3454bSSlava Zakharin 33700f3454bSSlava Zakharin bool ExternalFileUnit::Wait(int id) { 33800f3454bSSlava Zakharin if (static_cast<std::size_t>(id) >= maxAsyncIds || 33900f3454bSSlava Zakharin asyncIdAvailable_[id / 64].test(id % 64)) { 34000f3454bSSlava Zakharin return false; 34100f3454bSSlava Zakharin } else { 34200f3454bSSlava Zakharin if (id == 0) { // means "all IDs" 34300f3454bSSlava Zakharin for (int j{0}; 64 * j < maxAsyncIds; ++j) { 34400f3454bSSlava Zakharin asyncIdAvailable_[j].set(); 34500f3454bSSlava Zakharin } 34600f3454bSSlava Zakharin asyncIdAvailable_[0].reset(0); 34700f3454bSSlava Zakharin } else { 34800f3454bSSlava Zakharin asyncIdAvailable_[id / 64].set(id % 64); 34900f3454bSSlava Zakharin } 35000f3454bSSlava Zakharin return true; 35100f3454bSSlava Zakharin } 35200f3454bSSlava Zakharin } 35300f3454bSSlava Zakharin 35400f3454bSSlava Zakharin } // namespace Fortran::runtime::io 35500f3454bSSlava Zakharin #endif // !defined(RT_USE_PSEUDO_FILE_UNIT) 356