xref: /llvm-project/flang/runtime/unit-map.cpp (revision c7f4c333af2bd7b21dfb58df6cea59b30f6576d7)
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 
11 namespace Fortran::runtime::io {
12 
13 // See 12.5.6.12 in Fortran 2018.  NEWUNIT= unit numbers are negative,
14 // and not equal -1 (or ERROR_UNIT, if it were negative, which it isn't.)
15 ExternalFileUnit &UnitMap::NewUnit(const Terminator &terminator) {
16   CriticalSection critical{lock_};
17   std::optional<std::size_t> n;
18   n = (~busyNewUnits_).LeastElement();
19   if (!n.has_value()) {
20     terminator.Crash(
21         "No available unit number for NEWUNIT= or internal child I/O");
22   }
23   busyNewUnits_.set(*n);
24   // bit position 0 <-> unit -2; kind=1 units are in [-65..-2]
25   return Create(static_cast<int>(-2 - *n), terminator);
26 }
27 
28 ExternalFileUnit *UnitMap::LookUpForClose(int n) {
29   CriticalSection critical{lock_};
30   Chain *previous{nullptr};
31   int hash{Hash(n)};
32   for (Chain *p{bucket_[hash].get()}; p; previous = p, p = p->next.get()) {
33     if (p->unit.unitNumber() == n) {
34       if (previous) {
35         previous->next.swap(p->next);
36       } else {
37         bucket_[hash].swap(p->next);
38       }
39       // p->next.get() == p at this point; the next swap pushes p on closing_
40       closing_.swap(p->next);
41       return &p->unit;
42     }
43   }
44   return nullptr;
45 }
46 
47 void UnitMap::DestroyClosed(ExternalFileUnit &unit) {
48   Chain *p{nullptr};
49   {
50     CriticalSection critical{lock_};
51     Chain *previous{nullptr};
52     for (p = closing_.get(); p; previous = p, p = p->next.get()) {
53       if (&p->unit == &unit) {
54         int n{unit.unitNumber()};
55         if (n <= -2) {
56           busyNewUnits_.reset(static_cast<std::size_t>(-2 - n));
57         }
58         if (previous) {
59           previous->next.swap(p->next);
60         } else {
61           closing_.swap(p->next);
62         }
63         break;
64       }
65     }
66   }
67   if (p) {
68     p->unit.~ExternalFileUnit();
69     FreeMemory(p);
70   }
71 }
72 
73 void UnitMap::CloseAll(IoErrorHandler &handler) {
74   // Extract units from the map so they can be closed
75   // without holding lock_.
76   OwningPtr<Chain> closeList;
77   {
78     CriticalSection critical{lock_};
79     for (int j{0}; j < buckets_; ++j) {
80       while (Chain * p{bucket_[j].get()}) {
81         bucket_[j].swap(p->next); // pops p from head of bucket list
82         closeList.swap(p->next); // pushes p to closeList
83       }
84     }
85   }
86   while (Chain * p{closeList.get()}) {
87     closeList.swap(p->next); // pops p from head of closeList
88     p->unit.CloseUnit(CloseStatus::Keep, handler);
89     p->unit.~ExternalFileUnit();
90     FreeMemory(p);
91   }
92 }
93 
94 void UnitMap::FlushAll(IoErrorHandler &handler) {
95   CriticalSection critical{lock_};
96   for (int j{0}; j < buckets_; ++j) {
97     for (Chain *p{bucket_[j].get()}; p; p = p->next.get()) {
98       p->unit.FlushOutput(handler);
99     }
100   }
101 }
102 
103 ExternalFileUnit *UnitMap::Find(const char *path) {
104   if (path) {
105     // TODO: Faster data structure
106     for (int j{0}; j < buckets_; ++j) {
107       for (Chain *p{bucket_[j].get()}; p; p = p->next.get()) {
108         if (p->unit.path() && std::strcmp(p->unit.path(), path) == 0) {
109           return &p->unit;
110         }
111       }
112     }
113   }
114   return nullptr;
115 }
116 
117 ExternalFileUnit &UnitMap::Create(int n, const Terminator &terminator) {
118   Chain &chain{*New<Chain>{terminator}(n).release()};
119   chain.next.reset(&chain);
120   bucket_[Hash(n)].swap(chain.next); // pushes new node as list head
121   return chain.unit;
122 }
123 
124 } // namespace Fortran::runtime::io
125