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: 26 ExternalFileUnit *LookUp(int n) { 27 CriticalSection critical{lock_}; 28 return Find(n); 29 } 30 31 ExternalFileUnit &LookUpOrCreate( 32 int n, const Terminator &terminator, bool &wasExtant) { 33 CriticalSection critical{lock_}; 34 auto *p{Find(n)}; 35 wasExtant = p != nullptr; 36 return p ? *p : Create(n, terminator); 37 } 38 39 // Unit look-up by name is needed for INQUIRE(FILE="...") 40 ExternalFileUnit *LookUp(const char *path, std::size_t pathLen) { 41 CriticalSection critical{lock_}; 42 return Find(path, pathLen); 43 } 44 45 ExternalFileUnit &NewUnit(const Terminator &); 46 47 // To prevent races, the unit is removed from the map if it exists, 48 // and put on the closing_ list until DestroyClosed() is called. 49 ExternalFileUnit *LookUpForClose(int); 50 51 void DestroyClosed(ExternalFileUnit &); 52 void CloseAll(IoErrorHandler &); 53 void FlushAll(IoErrorHandler &); 54 55 private: 56 struct Chain { 57 explicit Chain(int n) : unit{n} {} 58 ExternalFileUnit unit; 59 OwningPtr<Chain> next{nullptr}; 60 }; 61 62 static constexpr int buckets_{1031}; // must be prime 63 64 // The pool of recyclable new unit numbers uses the range that 65 // works even with INTEGER(kind=1). 0 and -1 are never used. 66 static constexpr int maxNewUnits_{129}; // [ -128 .. 0 ] 67 68 int Hash(int n) { return std::abs(n) % buckets_; } 69 70 void Initialize(); 71 72 ExternalFileUnit *Find(int n) { 73 Chain *previous{nullptr}; 74 int hash{Hash(n)}; 75 for (Chain *p{bucket_[hash].get()}; p; previous = p, p = p->next.get()) { 76 if (p->unit.unitNumber() == n) { 77 if (previous) { 78 // Move found unit to front of chain for quicker lookup next time 79 previous->next.swap(p->next); // now p->next.get() == p 80 bucket_[hash].swap(p->next); // now bucket_[hash].get() == p 81 } 82 return &p->unit; 83 } 84 } 85 return nullptr; 86 } 87 ExternalFileUnit *Find(const char *path, std::size_t pathLen); 88 89 ExternalFileUnit &Create(int, const Terminator &); 90 91 Lock lock_; 92 bool isInitialized_{false}; 93 OwningPtr<Chain> bucket_[buckets_]{}; // all owned by *this 94 OwningPtr<Chain> closing_{nullptr}; // units during CLOSE statement 95 common::FastIntSet<maxNewUnits_> freeNewUnits_; 96 int emergencyNewUnit_{maxNewUnits_}; // not recycled 97 }; 98 } // namespace Fortran::runtime::io 99 #endif // FORTRAN_RUNTIME_UNIT_MAP_H_ 100