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 //===----------------------------------------------------------------------===//
10 #include "flang/Common/optional.h"
12 namespace Fortran::runtime::io
{
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
21 for (int j
{freeNewUnits_
.maxValue
}; j
> 1; --j
) {
24 isInitialized_
= true;
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_
};
33 Fortran::common::optional
<int> n
{freeNewUnits_
.PopValue()};
35 n
= emergencyNewUnit_
++;
37 return Create(-*n
, terminator
);
40 ExternalFileUnit
*UnitMap::LookUpForClose(int n
) {
41 CriticalSection critical
{lock_
};
42 Chain
*previous
{nullptr};
44 for (Chain
*p
{bucket_
[hash
].get()}; p
; previous
= p
, p
= p
->next
.get()) {
45 if (p
->unit
.unitNumber() == n
) {
47 previous
->next
.swap(p
->next
);
49 bucket_
[hash
].swap(p
->next
);
51 // p->next.get() == p at this point; the next swap pushes p on closing_
52 closing_
.swap(p
->next
);
59 void UnitMap::DestroyClosed(ExternalFileUnit
&unit
) {
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()};
68 freeNewUnits_
.Add(-n
);
71 previous
->next
.swap(p
->next
);
73 closing_
.swap(p
->next
);
80 p
->unit
.~ExternalFileUnit();
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
;
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
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();
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
);
115 ExternalFileUnit
*UnitMap::Find(const char *path
, std::size_t pathLen
) {
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(p
->unit
.path(), path
, pathLen
) == 0) {
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
137 } // namespace Fortran::runtime::io