xref: /llvm-project/flang/runtime/unit-map.h (revision cfbde7149d8647a4ff2eef892a30ffc55605df2c)
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