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