| //===-- lib/runtime/work-queue.cpp ------------------------------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "flang-rt/runtime/work-queue.h" |
| #include "flang-rt/runtime/environment.h" |
| #include "flang-rt/runtime/memory.h" |
| #include "flang-rt/runtime/type-info.h" |
| #include "flang/Common/visit.h" |
| |
| namespace Fortran::runtime { |
| |
| #if !defined(RT_DEVICE_COMPILATION) |
| // FLANG_RT_DEBUG code is disabled when false. |
| static constexpr bool enableDebugOutput{false}; |
| #endif |
| |
| RT_OFFLOAD_API_GROUP_BEGIN |
| |
| RT_API_ATTRS int Ticket::Continue(WorkQueue &workQueue) { |
| if (!begun) { |
| begun = true; |
| return common::visit( |
| [&workQueue]( |
| auto &specificTicket) { return specificTicket.Begin(workQueue); }, |
| u); |
| } else { |
| return common::visit( |
| [&workQueue](auto &specificTicket) { |
| return specificTicket.Continue(workQueue); |
| }, |
| u); |
| } |
| } |
| |
| RT_API_ATTRS WorkQueue::~WorkQueue() { |
| if (anyDynamicAllocation_) { |
| if (last_) { |
| if ((last_->next = firstFree_)) { |
| last_->next->previous = last_; |
| } |
| firstFree_ = first_; |
| first_ = last_ = nullptr; |
| } |
| while (firstFree_) { |
| TicketList *next{firstFree_->next}; |
| if (!firstFree_->isStatic) { |
| FreeMemory(firstFree_); |
| } |
| firstFree_ = next; |
| } |
| } |
| } |
| |
| RT_API_ATTRS Ticket &WorkQueue::StartTicket() { |
| if (!firstFree_) { |
| void *p{AllocateMemoryOrCrash(terminator_, sizeof(TicketList))}; |
| firstFree_ = new (p) TicketList; |
| firstFree_->isStatic = false; |
| anyDynamicAllocation_ = true; |
| } |
| TicketList *newTicket{firstFree_}; |
| if ((firstFree_ = newTicket->next)) { |
| firstFree_->previous = nullptr; |
| } |
| TicketList *after{insertAfter_ ? insertAfter_->next : nullptr}; |
| if ((newTicket->previous = insertAfter_ ? insertAfter_ : last_)) { |
| newTicket->previous->next = newTicket; |
| } else { |
| first_ = newTicket; |
| } |
| if ((newTicket->next = after)) { |
| after->previous = newTicket; |
| } else { |
| last_ = newTicket; |
| } |
| newTicket->ticket.begun = false; |
| #if !defined(RT_DEVICE_COMPILATION) |
| if (enableDebugOutput && |
| (executionEnvironment.internalDebugging & |
| ExecutionEnvironment::WorkQueue)) { |
| std::fprintf(stderr, "WQ: new ticket\n"); |
| } |
| #endif |
| return newTicket->ticket; |
| } |
| |
| RT_API_ATTRS int WorkQueue::Run() { |
| while (last_) { |
| TicketList *at{last_}; |
| insertAfter_ = last_; |
| #if !defined(RT_DEVICE_COMPILATION) |
| if (enableDebugOutput && |
| (executionEnvironment.internalDebugging & |
| ExecutionEnvironment::WorkQueue)) { |
| std::fprintf(stderr, "WQ: %zd %s\n", at->ticket.u.index(), |
| at->ticket.begun ? "Continue" : "Begin"); |
| } |
| #endif |
| int stat{at->ticket.Continue(*this)}; |
| #if !defined(RT_DEVICE_COMPILATION) |
| if (enableDebugOutput && |
| (executionEnvironment.internalDebugging & |
| ExecutionEnvironment::WorkQueue)) { |
| std::fprintf(stderr, "WQ: ... stat %d\n", stat); |
| } |
| #endif |
| insertAfter_ = nullptr; |
| if (stat == StatOk) { |
| if (at->previous) { |
| at->previous->next = at->next; |
| } else { |
| first_ = at->next; |
| } |
| if (at->next) { |
| at->next->previous = at->previous; |
| } else { |
| last_ = at->previous; |
| } |
| if ((at->next = firstFree_)) { |
| at->next->previous = at; |
| } |
| at->previous = nullptr; |
| firstFree_ = at; |
| } else if (stat != StatContinue) { |
| Stop(); |
| return stat; |
| } |
| } |
| return StatOk; |
| } |
| |
| RT_API_ATTRS void WorkQueue::Stop() { |
| if (last_) { |
| if ((last_->next = firstFree_)) { |
| last_->next->previous = last_; |
| } |
| firstFree_ = first_; |
| first_ = last_ = nullptr; |
| } |
| } |
| |
| RT_OFFLOAD_API_GROUP_END |
| |
| } // namespace Fortran::runtime |