1 //===-- runtime/unit-map.cpp ----------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
11 namespace Fortran::runtime::io
{
13 void UnitMap::Initialize() {
14 if (!isInitialized_
) {
15 freeNewUnits_
.InitializeState();
16 // Unit number -1 is reserved.
17 // The unit numbers are pushed in reverse order so that the first
18 // ones to be popped will be small and suitable for use as kind=1
20 for (int j
{freeNewUnits_
.maxValue
}; j
> 1; --j
) {
23 isInitialized_
= true;
27 // See 12.5.6.12 in Fortran 2018. NEWUNIT= unit numbers are negative,
28 // and not equal to -1 (or ERROR_UNIT, if it were negative, which it isn't.)
29 ExternalFileUnit
&UnitMap::NewUnit(const Terminator
&terminator
) {
30 CriticalSection critical
{lock_
};
32 std::optional
<int> n
{freeNewUnits_
.PopValue()};
34 n
= emergencyNewUnit_
++;
36 return Create(-*n
, terminator
);
39 ExternalFileUnit
*UnitMap::LookUpForClose(int n
) {
40 CriticalSection critical
{lock_
};
41 Chain
*previous
{nullptr};
43 for (Chain
*p
{bucket_
[hash
].get()}; p
; previous
= p
, p
= p
->next
.get()) {
44 if (p
->unit
.unitNumber() == n
) {
46 previous
->next
.swap(p
->next
);
48 bucket_
[hash
].swap(p
->next
);
50 // p->next.get() == p at this point; the next swap pushes p on closing_
51 closing_
.swap(p
->next
);
58 void UnitMap::DestroyClosed(ExternalFileUnit
&unit
) {
61 CriticalSection critical
{lock_
};
62 Chain
*previous
{nullptr};
63 for (p
= closing_
.get(); p
; previous
= p
, p
= p
->next
.get()) {
64 if (&p
->unit
== &unit
) {
65 int n
{unit
.unitNumber()};
67 freeNewUnits_
.Add(-n
);
70 previous
->next
.swap(p
->next
);
72 closing_
.swap(p
->next
);
79 p
->unit
.~ExternalFileUnit();
84 void UnitMap::CloseAll(IoErrorHandler
&handler
) {
85 // Extract units from the map so they can be closed
86 // without holding lock_.
87 OwningPtr
<Chain
> closeList
;
89 CriticalSection critical
{lock_
};
90 for (int j
{0}; j
< buckets_
; ++j
) {
91 while (Chain
* p
{bucket_
[j
].get()}) {
92 bucket_
[j
].swap(p
->next
); // pops p from head of bucket list
93 closeList
.swap(p
->next
); // pushes p to closeList
97 while (Chain
* p
{closeList
.get()}) {
98 closeList
.swap(p
->next
); // pops p from head of closeList
99 p
->unit
.CloseUnit(CloseStatus::Keep
, handler
);
100 p
->unit
.~ExternalFileUnit();
105 void UnitMap::FlushAll(IoErrorHandler
&handler
) {
106 CriticalSection critical
{lock_
};
107 for (int j
{0}; j
< buckets_
; ++j
) {
108 for (Chain
*p
{bucket_
[j
].get()}; p
; p
= p
->next
.get()) {
109 p
->unit
.FlushOutput(handler
);
114 ExternalFileUnit
*UnitMap::Find(const char *path
, std::size_t pathLen
) {
116 // TODO: Faster data structure
117 for (int j
{0}; j
< buckets_
; ++j
) {
118 for (Chain
*p
{bucket_
[j
].get()}; p
; p
= p
->next
.get()) {
119 if (p
->unit
.path() && p
->unit
.pathLength() == pathLen
&&
120 std::memcmp(p
->unit
.path(), path
, pathLen
) == 0) {
129 ExternalFileUnit
&UnitMap::Create(int n
, const Terminator
&terminator
) {
130 Chain
&chain
{*New
<Chain
>{terminator
}(n
).release()};
131 chain
.next
.reset(&chain
);
132 bucket_
[Hash(n
)].swap(chain
.next
); // pushes new node as list head
136 } // namespace Fortran::runtime::io