DPC++ Runtime
Runtime libraries for oneAPI DPC++
graph_processor.cpp
Go to the documentation of this file.
1 //===-- graph_processor.cpp - SYCL Graph Processor --------------*- C++ -*-===//
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 <detail/event_impl.hpp>
10 #include <detail/queue_impl.hpp>
12 
13 #include <memory>
14 #include <vector>
15 
16 namespace sycl {
18 namespace detail {
19 
20 static Command *getCommand(const EventImplPtr &Event) {
21  return (Command *)Event->getCommand();
22 }
23 
24 void Scheduler::GraphProcessor::waitForEvent(const EventImplPtr &Event,
25  ReadLockT &GraphReadLock,
26  std::vector<Command *> &ToCleanUp,
27  bool LockTheLock) {
28  Command *Cmd = getCommand(Event);
29  // Command can be nullptr if user creates sycl::event explicitly or the
30  // event has been waited on by another thread
31  if (!Cmd)
32  return;
33 
34  EnqueueResultT Res;
35  bool Enqueued = enqueueCommand(Cmd, Res, ToCleanUp, BLOCKING);
36  if (!Enqueued && EnqueueResultT::SyclEnqueueFailed == Res.MResult)
37  // TODO: Reschedule commands.
38  throw runtime_error("Enqueue process failed.", PI_ERROR_INVALID_OPERATION);
39 
40  assert(Cmd->getEvent() == Event);
41 
42  GraphReadLock.unlock();
43  Event->waitInternal();
44 
45  if (LockTheLock)
46  GraphReadLock.lock();
47 }
48 
49 bool Scheduler::GraphProcessor::enqueueCommand(
50  Command *Cmd, EnqueueResultT &EnqueueResult,
51  std::vector<Command *> &ToCleanUp, BlockingT Blocking) {
52  if (!Cmd || Cmd->isSuccessfullyEnqueued())
53  return true;
54 
55  // Exit early if the command is blocked and the enqueue type is non-blocking
56  if (Cmd->isEnqueueBlocked() && !Blocking) {
57  EnqueueResult = EnqueueResultT(EnqueueResultT::SyclEnqueueBlocked, Cmd);
58  return false;
59  }
60 
61  // Recursively enqueue all the implicit + explicit backend level dependencies
62  // first and exit immediately if any of the commands cannot be enqueued.
63  for (const EventImplPtr &Event : Cmd->getPreparedDepsEvents()) {
64  if (Command *DepCmd = static_cast<Command *>(Event->getCommand()))
65  if (!enqueueCommand(DepCmd, EnqueueResult, ToCleanUp, Blocking))
66  return false;
67  }
68 
69  // Recursively enqueue all the implicit + explicit host dependencies and
70  // exit immediately if any of the commands cannot be enqueued.
71  // Host task execution is asynchronous. In current implementation enqueue for
72  // this command will wait till host task completion by waitInternal call on
73  // MHostDepsEvents. TO FIX: implement enqueue of blocked commands on host task
74  // completion stage and eliminate this event waiting in enqueue.
75  for (const EventImplPtr &Event : Cmd->getPreparedHostDepsEvents()) {
76  if (Command *DepCmd = static_cast<Command *>(Event->getCommand()))
77  if (!enqueueCommand(DepCmd, EnqueueResult, ToCleanUp, Blocking))
78  return false;
79  }
80 
81  // Only graph read lock is to be held here.
82  // Enqueue process of a command may last quite a time. Having graph locked can
83  // introduce some thread starving (i.e. when the other thread attempts to
84  // acquire write lock and add a command to graph). Releasing read lock without
85  // other safety measures isn't an option here as the other thread could go
86  // into graph cleanup process (due to some event complete) and remove some
87  // dependencies from dependencies of the user of this command.
88  // An example: command A depends on commands B and C. This thread wants to
89  // enqueue A. Hence, it needs to enqueue B and C. So this thread gets into
90  // dependency list and starts enqueueing B right away. The other thread waits
91  // on completion of C and starts cleanup process. This thread is still in the
92  // middle of enqueue of B. The other thread modifies dependency list of A by
93  // removing C out of it. Iterators become invalid.
94  return Cmd->enqueue(EnqueueResult, Blocking, ToCleanUp);
95 }
96 
97 } // namespace detail
98 } // __SYCL_INLINE_VER_NAMESPACE(_V1)
99 } // namespace sycl
The Command class represents some action that needs to be performed on one or more memory objects.
Definition: commands.hpp:95
bool isSuccessfullyEnqueued() const
Definition: commands.hpp:141
const std::vector< EventImplPtr > & getPreparedDepsEvents() const
Definition: commands.hpp:260
const std::vector< EventImplPtr > & getPreparedHostDepsEvents() const
Definition: commands.hpp:256
virtual bool enqueue(EnqueueResultT &EnqueueResult, BlockingT Blocking, std::vector< Command * > &ToCleanUp)
Checks if the command is enqueued, and calls enqueueImp.
Definition: commands.cpp:690
const EventImplPtr & getEvent() const
Definition: commands.hpp:151
bool isEnqueueBlocked() const
Definition: commands.hpp:145
std::shared_lock< RWLockT > ReadLockT
Definition: scheduler.hpp:453
#define __SYCL_INLINE_VER_NAMESPACE(X)
static Command * getCommand(const EventImplPtr &Event)
std::shared_ptr< event_impl > EventImplPtr
Definition: cg.hpp:42
---— Error handling, matching OpenCL plugin semantics.
Definition: access.hpp:14
Result of command enqueueing.
Definition: commands.hpp:50
ResultT MResult
Indicates the result of enqueueing.
Definition: commands.hpp:61