1 //===-- runtime/unit-map.h --------------------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 // Maps Fortran unit numbers to their ExternalFileUnit instances. 10 // A simple hash table with forward-linked chains per bucket. 11 12 #ifndef FORTRAN_RUNTIME_UNIT_MAP_H_ 13 #define FORTRAN_RUNTIME_UNIT_MAP_H_ 14 15 #include "lock.h" 16 #include "unit.h" 17 #include "flang/Common/fast-int-set.h" 18 #include "flang/Runtime/memory.h" 19 #include <cstdint> 20 #include <cstdlib> 21 22 namespace Fortran::runtime::io { 23 24 class UnitMap { 25 public: LookUp(int n)26 ExternalFileUnit *LookUp(int n) { 27 CriticalSection critical{lock_}; 28 return Find(n); 29 } 30 LookUpOrCreate(int n,const Terminator & terminator,bool & wasExtant)31 ExternalFileUnit *LookUpOrCreate( 32 int n, const Terminator &terminator, bool &wasExtant) { 33 CriticalSection critical{lock_}; 34 if (auto *p{Find(n)}) { 35 wasExtant = true; 36 return p; 37 } else { 38 wasExtant = false; 39 return n >= 0 ? &Create(n, terminator) : nullptr; 40 } 41 } 42 43 // Unit look-up by name is needed for INQUIRE(FILE="...") LookUp(const char * path,std::size_t pathLen)44 ExternalFileUnit *LookUp(const char *path, std::size_t pathLen) { 45 CriticalSection critical{lock_}; 46 return Find(path, pathLen); 47 } 48 49 ExternalFileUnit &NewUnit(const Terminator &); 50 51 // To prevent races, the unit is removed from the map if it exists, 52 // and put on the closing_ list until DestroyClosed() is called. 53 ExternalFileUnit *LookUpForClose(int); 54 55 void DestroyClosed(ExternalFileUnit &); 56 void CloseAll(IoErrorHandler &); 57 void FlushAll(IoErrorHandler &); 58 59 private: 60 struct Chain { ChainChain61 explicit Chain(int n) : unit{n} {} 62 ExternalFileUnit unit; 63 OwningPtr<Chain> next{nullptr}; 64 }; 65 66 static constexpr int buckets_{1031}; // must be prime 67 68 // The pool of recyclable new unit numbers uses the range that 69 // works even with INTEGER(kind=1). 0 and -1 are never used. 70 static constexpr int maxNewUnits_{129}; // [ -128 .. 0 ] 71 Hash(int n)72 int Hash(int n) { return std::abs(n) % buckets_; } 73 74 void Initialize(); 75 Find(int n)76 ExternalFileUnit *Find(int n) { 77 Chain *previous{nullptr}; 78 int hash{Hash(n)}; 79 for (Chain *p{bucket_[hash].get()}; p; previous = p, p = p->next.get()) { 80 if (p->unit.unitNumber() == n) { 81 if (previous) { 82 // Move found unit to front of chain for quicker lookup next time 83 previous->next.swap(p->next); // now p->next.get() == p 84 bucket_[hash].swap(p->next); // now bucket_[hash].get() == p 85 } 86 return &p->unit; 87 } 88 } 89 return nullptr; 90 } 91 ExternalFileUnit *Find(const char *path, std::size_t pathLen); 92 93 ExternalFileUnit &Create(int, const Terminator &); 94 95 Lock lock_; 96 bool isInitialized_{false}; 97 OwningPtr<Chain> bucket_[buckets_]{}; // all owned by *this 98 OwningPtr<Chain> closing_{nullptr}; // units during CLOSE statement 99 common::FastIntSet<maxNewUnits_> freeNewUnits_; 100 int emergencyNewUnit_{maxNewUnits_}; // not recycled 101 }; 102 } // namespace Fortran::runtime::io 103 #endif // FORTRAN_RUNTIME_UNIT_MAP_H_ 104