1651f58bfSDiana Picus //===-- runtime/unit-map.cpp ----------------------------------------------===//
23b635714Speter klausler //
33b635714Speter klausler // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43b635714Speter klausler // See https://llvm.org/LICENSE.txt for license information.
53b635714Speter klausler // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63b635714Speter klausler //
73b635714Speter klausler //===----------------------------------------------------------------------===//
83b635714Speter klausler 
93b635714Speter klausler #include "unit-map.h"
103b635714Speter klausler 
113b635714Speter klausler namespace Fortran::runtime::io {
123b635714Speter klausler 
Initialize()1373b193aeSPeter Klausler void UnitMap::Initialize() {
1473b193aeSPeter Klausler   if (!isInitialized_) {
1573b193aeSPeter Klausler     freeNewUnits_.InitializeState();
1673b193aeSPeter Klausler     // Unit number -1 is reserved.
1773b193aeSPeter Klausler     // The unit numbers are pushed in reverse order so that the first
1873b193aeSPeter Klausler     // ones to be popped will be small and suitable for use as kind=1
1973b193aeSPeter Klausler     // integers.
2073b193aeSPeter Klausler     for (int j{freeNewUnits_.maxValue}; j > 1; --j) {
2173b193aeSPeter Klausler       freeNewUnits_.Add(j);
2273b193aeSPeter Klausler     }
2373b193aeSPeter Klausler     isInitialized_ = true;
2473b193aeSPeter Klausler   }
2573b193aeSPeter Klausler }
2673b193aeSPeter Klausler 
27c7f4c333SPeter Klausler // See 12.5.6.12 in Fortran 2018.  NEWUNIT= unit numbers are negative,
2873b193aeSPeter Klausler // and not equal to -1 (or ERROR_UNIT, if it were negative, which it isn't.)
NewUnit(const Terminator & terminator)29c7f4c333SPeter Klausler ExternalFileUnit &UnitMap::NewUnit(const Terminator &terminator) {
30c7f4c333SPeter Klausler   CriticalSection critical{lock_};
3173b193aeSPeter Klausler   Initialize();
3273b193aeSPeter Klausler   std::optional<int> n{freeNewUnits_.PopValue()};
3373b193aeSPeter Klausler   if (!n) {
3473b193aeSPeter Klausler     n = emergencyNewUnit_++;
35c7f4c333SPeter Klausler   }
3673b193aeSPeter Klausler   return Create(-*n, terminator);
37c7f4c333SPeter Klausler }
38c7f4c333SPeter Klausler 
LookUpForClose(int n)393b635714Speter klausler ExternalFileUnit *UnitMap::LookUpForClose(int n) {
403b635714Speter klausler   CriticalSection critical{lock_};
413b635714Speter klausler   Chain *previous{nullptr};
423b635714Speter klausler   int hash{Hash(n)};
433b635714Speter klausler   for (Chain *p{bucket_[hash].get()}; p; previous = p, p = p->next.get()) {
443b635714Speter klausler     if (p->unit.unitNumber() == n) {
453b635714Speter klausler       if (previous) {
463b635714Speter klausler         previous->next.swap(p->next);
473b635714Speter klausler       } else {
483b635714Speter klausler         bucket_[hash].swap(p->next);
493b635714Speter klausler       }
503b635714Speter klausler       // p->next.get() == p at this point; the next swap pushes p on closing_
513b635714Speter klausler       closing_.swap(p->next);
523b635714Speter klausler       return &p->unit;
533b635714Speter klausler     }
543b635714Speter klausler   }
553b635714Speter klausler   return nullptr;
563b635714Speter klausler }
573b635714Speter klausler 
DestroyClosed(ExternalFileUnit & unit)583b635714Speter klausler void UnitMap::DestroyClosed(ExternalFileUnit &unit) {
593b635714Speter klausler   Chain *p{nullptr};
603b635714Speter klausler   {
613b635714Speter klausler     CriticalSection critical{lock_};
623b635714Speter klausler     Chain *previous{nullptr};
633b635714Speter klausler     for (p = closing_.get(); p; previous = p, p = p->next.get()) {
643b635714Speter klausler       if (&p->unit == &unit) {
65c7f4c333SPeter Klausler         int n{unit.unitNumber()};
66c7f4c333SPeter Klausler         if (n <= -2) {
6773b193aeSPeter Klausler           freeNewUnits_.Add(-n);
68c7f4c333SPeter Klausler         }
693b635714Speter klausler         if (previous) {
703b635714Speter klausler           previous->next.swap(p->next);
713b635714Speter klausler         } else {
723b635714Speter klausler           closing_.swap(p->next);
733b635714Speter klausler         }
743b635714Speter klausler         break;
753b635714Speter klausler       }
763b635714Speter klausler     }
773b635714Speter klausler   }
783b635714Speter klausler   if (p) {
793b635714Speter klausler     p->unit.~ExternalFileUnit();
803b635714Speter klausler     FreeMemory(p);
813b635714Speter klausler   }
823b635714Speter klausler }
833b635714Speter klausler 
CloseAll(IoErrorHandler & handler)843b635714Speter klausler void UnitMap::CloseAll(IoErrorHandler &handler) {
85c84616c3SPeter Klausler   // Extract units from the map so they can be closed
86c84616c3SPeter Klausler   // without holding lock_.
87c84616c3SPeter Klausler   OwningPtr<Chain> closeList;
88c84616c3SPeter Klausler   {
893b635714Speter klausler     CriticalSection critical{lock_};
903b635714Speter klausler     for (int j{0}; j < buckets_; ++j) {
913b635714Speter klausler       while (Chain * p{bucket_[j].get()}) {
92c84616c3SPeter Klausler         bucket_[j].swap(p->next); // pops p from head of bucket list
93c84616c3SPeter Klausler         closeList.swap(p->next); // pushes p to closeList
94c84616c3SPeter Klausler       }
95c84616c3SPeter Klausler     }
96c84616c3SPeter Klausler   }
97c84616c3SPeter Klausler   while (Chain * p{closeList.get()}) {
98c84616c3SPeter Klausler     closeList.swap(p->next); // pops p from head of closeList
993b635714Speter klausler     p->unit.CloseUnit(CloseStatus::Keep, handler);
1003b635714Speter klausler     p->unit.~ExternalFileUnit();
1013b635714Speter klausler     FreeMemory(p);
1023b635714Speter klausler   }
1033b635714Speter klausler }
1043b635714Speter klausler 
FlushAll(IoErrorHandler & handler)1050006354cSpeter klausler void UnitMap::FlushAll(IoErrorHandler &handler) {
1060006354cSpeter klausler   CriticalSection critical{lock_};
1070006354cSpeter klausler   for (int j{0}; j < buckets_; ++j) {
1080006354cSpeter klausler     for (Chain *p{bucket_[j].get()}; p; p = p->next.get()) {
109cd0a1226Speter klausler       p->unit.FlushOutput(handler);
1100006354cSpeter klausler     }
1110006354cSpeter klausler   }
1120006354cSpeter klausler }
1130006354cSpeter klausler 
Find(const char * path,std::size_t pathLen)114*03c066abSPeter Klausler ExternalFileUnit *UnitMap::Find(const char *path, std::size_t pathLen) {
115675ad1bcSpeter klausler   if (path) {
116675ad1bcSpeter klausler     // TODO: Faster data structure
117675ad1bcSpeter klausler     for (int j{0}; j < buckets_; ++j) {
118675ad1bcSpeter klausler       for (Chain *p{bucket_[j].get()}; p; p = p->next.get()) {
119*03c066abSPeter Klausler         if (p->unit.path() && p->unit.pathLength() == pathLen &&
120*03c066abSPeter Klausler             std::memcmp(p->unit.path(), path, pathLen) == 0) {
121675ad1bcSpeter klausler           return &p->unit;
122675ad1bcSpeter klausler         }
123675ad1bcSpeter klausler       }
124675ad1bcSpeter klausler     }
125675ad1bcSpeter klausler   }
126675ad1bcSpeter klausler   return nullptr;
127675ad1bcSpeter klausler }
128675ad1bcSpeter klausler 
Create(int n,const Terminator & terminator)1293b635714Speter klausler ExternalFileUnit &UnitMap::Create(int n, const Terminator &terminator) {
13098d576c7Speter klausler   Chain &chain{*New<Chain>{terminator}(n).release()};
1313b635714Speter klausler   chain.next.reset(&chain);
1323b635714Speter klausler   bucket_[Hash(n)].swap(chain.next); // pushes new node as list head
1333b635714Speter klausler   return chain.unit;
1343b635714Speter klausler }
13543fadefbSpeter klausler 
1361f879005STim Keith } // namespace Fortran::runtime::io
137