xref: /llvm-project/flang/runtime/external-unit.cpp (revision 2326a02357c74a1a913a3d572bf789d4d48af7f0)
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 = &in;
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