• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "memory.h"
17 #include "unit.h"
18 #include <cstdlib>
19 
20 namespace Fortran::runtime::io {
21 
22 class UnitMap {
23 public:
LookUp(int n)24   ExternalFileUnit *LookUp(int n) {
25     CriticalSection critical{lock_};
26     return Find(n);
27   }
28 
LookUpOrCreate(int n,const Terminator & terminator,bool & wasExtant)29   ExternalFileUnit &LookUpOrCreate(
30       int n, const Terminator &terminator, bool &wasExtant) {
31     CriticalSection critical{lock_};
32     auto *p{Find(n)};
33     wasExtant = p != nullptr;
34     return p ? *p : Create(n, terminator);
35   }
36 
37   // Unit look-up by name is needed for INQUIRE(FILE="...")
LookUp(const char * path)38   ExternalFileUnit *LookUp(const char *path) {
39     CriticalSection critical{lock_};
40     return Find(path);
41   }
42 
NewUnit(const Terminator & terminator)43   ExternalFileUnit &NewUnit(const Terminator &terminator) {
44     CriticalSection critical{lock_};
45     return Create(nextNewUnit_--, terminator);
46   }
47 
48   // To prevent races, the unit is removed from the map if it exists,
49   // and put on the closing_ list until DestroyClosed() is called.
50   ExternalFileUnit *LookUpForClose(int);
51 
52   void DestroyClosed(ExternalFileUnit &);
53   void CloseAll(IoErrorHandler &);
54   void FlushAll(IoErrorHandler &);
55 
56 private:
57   struct Chain {
ChainChain58     explicit Chain(int n) : unit{n} {}
59     ExternalFileUnit unit;
60     OwningPtr<Chain> next{nullptr};
61   };
62 
63   static constexpr int buckets_{1031}; // must be prime
Hash(int n)64   int Hash(int n) { return std::abs(n) % buckets_; }
65 
Find(int n)66   ExternalFileUnit *Find(int n) {
67     Chain *previous{nullptr};
68     int hash{Hash(n)};
69     for (Chain *p{bucket_[hash].get()}; p; previous = p, p = p->next.get()) {
70       if (p->unit.unitNumber() == n) {
71         if (previous) {
72           // Move found unit to front of chain for quicker lookup next time
73           previous->next.swap(p->next); // now p->next.get() == p
74           bucket_[hash].swap(p->next); // now bucket_[hash].get() == p
75         }
76         return &p->unit;
77       }
78     }
79     return nullptr;
80   }
81   ExternalFileUnit *Find(const char *path);
82 
83   ExternalFileUnit &Create(int, const Terminator &);
84 
85   Lock lock_;
86   OwningPtr<Chain> bucket_[buckets_]{}; // all owned by *this
87   int nextNewUnit_{-1000}; // see 12.5.6.12 in Fortran 2018
88   OwningPtr<Chain> closing_{nullptr}; // units during CLOSE statement
89 };
90 } // namespace Fortran::runtime::io
91 #endif // FORTRAN_RUNTIME_UNIT_MAP_H_
92