|  | //===-- runtime/unit-map.cpp ----------------------------------------------===// | 
|  | // | 
|  | // 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 | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "unit-map.h" | 
|  |  | 
|  | namespace Fortran::runtime::io { | 
|  |  | 
|  | void UnitMap::Initialize() { | 
|  | if (!isInitialized_) { | 
|  | freeNewUnits_.InitializeState(); | 
|  | // Unit number -1 is reserved. | 
|  | // The unit numbers are pushed in reverse order so that the first | 
|  | // ones to be popped will be small and suitable for use as kind=1 | 
|  | // integers. | 
|  | for (int j{freeNewUnits_.maxValue}; j > 1; --j) { | 
|  | freeNewUnits_.Add(j); | 
|  | } | 
|  | isInitialized_ = true; | 
|  | } | 
|  | } | 
|  |  | 
|  | // See 12.5.6.12 in Fortran 2018.  NEWUNIT= unit numbers are negative, | 
|  | // and not equal to -1 (or ERROR_UNIT, if it were negative, which it isn't.) | 
|  | ExternalFileUnit &UnitMap::NewUnit(const Terminator &terminator) { | 
|  | CriticalSection critical{lock_}; | 
|  | Initialize(); | 
|  | std::optional<int> n{freeNewUnits_.PopValue()}; | 
|  | if (!n) { | 
|  | n = emergencyNewUnit_++; | 
|  | } | 
|  | return Create(-*n, terminator); | 
|  | } | 
|  |  | 
|  | ExternalFileUnit *UnitMap::LookUpForClose(int n) { | 
|  | CriticalSection critical{lock_}; | 
|  | Chain *previous{nullptr}; | 
|  | int hash{Hash(n)}; | 
|  | for (Chain *p{bucket_[hash].get()}; p; previous = p, p = p->next.get()) { | 
|  | if (p->unit.unitNumber() == n) { | 
|  | if (previous) { | 
|  | previous->next.swap(p->next); | 
|  | } else { | 
|  | bucket_[hash].swap(p->next); | 
|  | } | 
|  | // p->next.get() == p at this point; the next swap pushes p on closing_ | 
|  | closing_.swap(p->next); | 
|  | return &p->unit; | 
|  | } | 
|  | } | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | void UnitMap::DestroyClosed(ExternalFileUnit &unit) { | 
|  | Chain *p{nullptr}; | 
|  | { | 
|  | CriticalSection critical{lock_}; | 
|  | Chain *previous{nullptr}; | 
|  | for (p = closing_.get(); p; previous = p, p = p->next.get()) { | 
|  | if (&p->unit == &unit) { | 
|  | int n{unit.unitNumber()}; | 
|  | if (n <= -2) { | 
|  | freeNewUnits_.Add(-n); | 
|  | } | 
|  | if (previous) { | 
|  | previous->next.swap(p->next); | 
|  | } else { | 
|  | closing_.swap(p->next); | 
|  | } | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | if (p) { | 
|  | p->unit.~ExternalFileUnit(); | 
|  | FreeMemory(p); | 
|  | } | 
|  | } | 
|  |  | 
|  | void UnitMap::CloseAll(IoErrorHandler &handler) { | 
|  | // Extract units from the map so they can be closed | 
|  | // without holding lock_. | 
|  | OwningPtr<Chain> closeList; | 
|  | { | 
|  | CriticalSection critical{lock_}; | 
|  | for (int j{0}; j < buckets_; ++j) { | 
|  | while (Chain * p{bucket_[j].get()}) { | 
|  | bucket_[j].swap(p->next); // pops p from head of bucket list | 
|  | closeList.swap(p->next); // pushes p to closeList | 
|  | } | 
|  | } | 
|  | } | 
|  | while (Chain * p{closeList.get()}) { | 
|  | closeList.swap(p->next); // pops p from head of closeList | 
|  | p->unit.CloseUnit(CloseStatus::Keep, handler); | 
|  | p->unit.~ExternalFileUnit(); | 
|  | FreeMemory(p); | 
|  | } | 
|  | } | 
|  |  | 
|  | void UnitMap::FlushAll(IoErrorHandler &handler) { | 
|  | CriticalSection critical{lock_}; | 
|  | for (int j{0}; j < buckets_; ++j) { | 
|  | for (Chain *p{bucket_[j].get()}; p; p = p->next.get()) { | 
|  | p->unit.FlushOutput(handler); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | ExternalFileUnit *UnitMap::Find(const char *path, std::size_t pathLen) { | 
|  | if (path) { | 
|  | // TODO: Faster data structure | 
|  | for (int j{0}; j < buckets_; ++j) { | 
|  | for (Chain *p{bucket_[j].get()}; p; p = p->next.get()) { | 
|  | if (p->unit.path() && p->unit.pathLength() == pathLen && | 
|  | std::memcmp(p->unit.path(), path, pathLen) == 0) { | 
|  | return &p->unit; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | ExternalFileUnit &UnitMap::Create(int n, const Terminator &terminator) { | 
|  | Chain &chain{*New<Chain>{terminator}(n).release()}; | 
|  | chain.next.reset(&chain); | 
|  | bucket_[Hash(n)].swap(chain.next); // pushes new node as list head | 
|  | return chain.unit; | 
|  | } | 
|  |  | 
|  | } // namespace Fortran::runtime::io |