1 | //===-- runtime/unit-map.cpp ----------------------------------------------===// |
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 | #include "unit-map.h" |
10 | #include "flang/Common/optional.h" |
11 | |
12 | namespace Fortran::runtime::io { |
13 | |
14 | void UnitMap::Initialize() { |
15 | if (!isInitialized_) { |
16 | freeNewUnits_.InitializeState(); |
17 | // Unit number -1 is reserved. |
18 | // The unit numbers are pushed in reverse order so that the first |
19 | // ones to be popped will be small and suitable for use as kind=1 |
20 | // integers. |
21 | for (int j{freeNewUnits_.maxValue}; j > 1; --j) { |
22 | freeNewUnits_.Add(j); |
23 | } |
24 | isInitialized_ = true; |
25 | } |
26 | } |
27 | |
28 | // See 12.5.6.12 in Fortran 2018. NEWUNIT= unit numbers are negative, |
29 | // and not equal to -1 (or ERROR_UNIT, if it were negative, which it isn't.) |
30 | ExternalFileUnit &UnitMap::NewUnit(const Terminator &terminator) { |
31 | CriticalSection critical{lock_}; |
32 | Initialize(); |
33 | Fortran::common::optional<int> n{freeNewUnits_.PopValue()}; |
34 | if (!n) { |
35 | n = emergencyNewUnit_++; |
36 | } |
37 | return Create(-*n, terminator); |
38 | } |
39 | |
40 | ExternalFileUnit *UnitMap::LookUpForClose(int n) { |
41 | CriticalSection critical{lock_}; |
42 | Chain *previous{nullptr}; |
43 | int hash{Hash(n)}; |
44 | for (Chain *p{bucket_[hash].get()}; p; previous = p, p = p->next.get()) { |
45 | if (p->unit.unitNumber() == n) { |
46 | if (previous) { |
47 | previous->next.swap(p->next); |
48 | } else { |
49 | bucket_[hash].swap(p->next); |
50 | } |
51 | // p->next.get() == p at this point; the next swap pushes p on closing_ |
52 | closing_.swap(p->next); |
53 | return &p->unit; |
54 | } |
55 | } |
56 | return nullptr; |
57 | } |
58 | |
59 | void UnitMap::DestroyClosed(ExternalFileUnit &unit) { |
60 | Chain *p{nullptr}; |
61 | { |
62 | CriticalSection critical{lock_}; |
63 | Chain *previous{nullptr}; |
64 | for (p = closing_.get(); p; previous = p, p = p->next.get()) { |
65 | if (&p->unit == &unit) { |
66 | int n{unit.unitNumber()}; |
67 | if (n <= -2) { |
68 | freeNewUnits_.Add(-n); |
69 | } |
70 | if (previous) { |
71 | previous->next.swap(p->next); |
72 | } else { |
73 | closing_.swap(p->next); |
74 | } |
75 | break; |
76 | } |
77 | } |
78 | } |
79 | if (p) { |
80 | p->unit.~ExternalFileUnit(); |
81 | FreeMemory(p); |
82 | } |
83 | } |
84 | |
85 | void UnitMap::CloseAll(IoErrorHandler &handler) { |
86 | // Extract units from the map so they can be closed |
87 | // without holding lock_. |
88 | OwningPtr<Chain> closeList; |
89 | { |
90 | CriticalSection critical{lock_}; |
91 | for (int j{0}; j < buckets_; ++j) { |
92 | while (Chain * p{bucket_[j].get()}) { |
93 | bucket_[j].swap(p->next); // pops p from head of bucket list |
94 | closeList.swap(p->next); // pushes p to closeList |
95 | } |
96 | } |
97 | } |
98 | while (Chain * p{closeList.get()}) { |
99 | closeList.swap(p->next); // pops p from head of closeList |
100 | p->unit.CloseUnit(CloseStatus::Keep, handler); |
101 | p->unit.~ExternalFileUnit(); |
102 | FreeMemory(p); |
103 | } |
104 | } |
105 | |
106 | void UnitMap::FlushAll(IoErrorHandler &handler) { |
107 | CriticalSection critical{lock_}; |
108 | for (int j{0}; j < buckets_; ++j) { |
109 | for (Chain *p{bucket_[j].get()}; p; p = p->next.get()) { |
110 | p->unit.FlushOutput(handler); |
111 | } |
112 | } |
113 | } |
114 | |
115 | ExternalFileUnit *UnitMap::Find(const char *path, std::size_t pathLen) { |
116 | if (path) { |
117 | // TODO: Faster data structure |
118 | for (int j{0}; j < buckets_; ++j) { |
119 | for (Chain *p{bucket_[j].get()}; p; p = p->next.get()) { |
120 | if (p->unit.path() && p->unit.pathLength() == pathLen && |
121 | std::memcmp(s1: p->unit.path(), s2: path, n: pathLen) == 0) { |
122 | return &p->unit; |
123 | } |
124 | } |
125 | } |
126 | } |
127 | return nullptr; |
128 | } |
129 | |
130 | ExternalFileUnit &UnitMap::Create(int n, const Terminator &terminator) { |
131 | Chain &chain{*New<Chain>{terminator}(n).release()}; |
132 | chain.next.reset(&chain); |
133 | bucket_[Hash(n)].swap(chain.next); // pushes new node as list head |
134 | return chain.unit; |
135 | } |
136 | |
137 | } // namespace Fortran::runtime::io |
138 | |