xref: /llvm-project/offload/plugins-nextgen/common/include/RPC.h (revision e7592d83e0ac58f61cfe8dcf61bcc8e7a8bd67b3)
1 //===- RPC.h - Interface for remote procedure calls from the GPU ----------===//
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 // This file provides the interface to support remote procedure calls (RPC) from
10 // the GPU. This is required to implement host services like printf or malloc.
11 // The interface to the RPC server is provided by the 'libc' project in LLVM.
12 // For more information visit https://libc.llvm.org/gpu/.
13 //
14 //===----------------------------------------------------------------------===//
15 
16 #ifndef OPENMP_LIBOMPTARGET_PLUGINS_NEXTGEN_COMMON_RPC_H
17 #define OPENMP_LIBOMPTARGET_PLUGINS_NEXTGEN_COMMON_RPC_H
18 
19 #include "llvm/ADT/DenseMap.h"
20 #include "llvm/Support/Error.h"
21 
22 #include <atomic>
23 #include <condition_variable>
24 #include <cstdint>
25 #include <mutex>
26 #include <thread>
27 
28 namespace llvm::omp::target {
29 namespace plugin {
30 struct GenericPluginTy;
31 struct GenericDeviceTy;
32 class GenericGlobalHandlerTy;
33 class DeviceImageTy;
34 } // namespace plugin
35 
36 /// A generic class implementing the interface between the RPC server provided
37 /// by the 'libc' project and 'libomptarget'. If the RPC server is not availible
38 /// these routines will perform no action.
39 struct RPCServerTy {
40 public:
41   /// Initializes the handles to the number of devices we may need to service.
42   RPCServerTy(plugin::GenericPluginTy &Plugin);
43 
44   /// Deinitialize the associated memory and resources.
45   llvm::Error shutDown();
46 
47   /// Initialize the worker thread.
48   llvm::Error startThread();
49 
50   /// Check if this device image is using an RPC server. This checks for the
51   /// precense of an externally visible symbol in the device image that will
52   /// be present whenever RPC code is called.
53   llvm::Expected<bool> isDeviceUsingRPC(plugin::GenericDeviceTy &Device,
54                                         plugin::GenericGlobalHandlerTy &Handler,
55                                         plugin::DeviceImageTy &Image);
56 
57   /// Initialize the RPC server for the given device. This will allocate host
58   /// memory for the internal server and copy the data to the client on the
59   /// device. The device must be loaded before this is valid.
60   llvm::Error initDevice(plugin::GenericDeviceTy &Device,
61                          plugin::GenericGlobalHandlerTy &Handler,
62                          plugin::DeviceImageTy &Image);
63 
64   /// Deinitialize the RPC server for the given device. This will free the
65   /// memory associated with the k
66   llvm::Error deinitDevice(plugin::GenericDeviceTy &Device);
67 
68 private:
69   /// Array from this device's identifier to its attached devices.
70   std::unique_ptr<void *[]> Buffers;
71 
72   /// Array of associated devices. These must be alive as long as the server is.
73   std::unique_ptr<plugin::GenericDeviceTy *[]> Devices;
74 
75   /// A helper class for running the user thread that handles the RPC interface.
76   /// Because we only need to check the RPC server while any kernels are
77   /// working, we track submission / completion events to allow the thread to
78   /// sleep when it is not needed.
79   struct ServerThread {
80     std::thread Worker;
81 
82     /// A boolean indicating whether or not the worker thread should continue.
83     std::atomic<bool> Running;
84 
85     /// The number of currently executing kernels across all devices that need
86     /// the server thread to be running.
87     std::atomic<uint32_t> NumUsers;
88 
89     /// The condition variable used to suspend the thread if no work is needed.
90     std::condition_variable CV;
91     std::mutex Mutex;
92 
93     /// A reference to all the RPC interfaces that the server is handling.
94     llvm::ArrayRef<void *> Buffers;
95 
96     /// A reference to the associated generic device for the buffer.
97     llvm::ArrayRef<plugin::GenericDeviceTy *> Devices;
98 
99     /// Initialize the worker thread to run in the background.
100     ServerThread(void *Buffers[], plugin::GenericDeviceTy *Devices[],
101                  size_t Length)
102         : Running(false), NumUsers(0), CV(), Mutex(), Buffers(Buffers, Length),
103           Devices(Devices, Length) {}
104 
105     ~ServerThread() { assert(!Running && "Thread not shut down explicitly\n"); }
106 
107     /// Notify the worker thread that there is a user that needs it.
108     void notify() {
109       std::lock_guard<decltype(Mutex)> Lock(Mutex);
110       NumUsers.fetch_add(1, std::memory_order_relaxed);
111       CV.notify_all();
112     }
113 
114     /// Indicate that one of the dependent users has finished.
115     void finish() {
116       [[maybe_unused]] uint32_t Old =
117           NumUsers.fetch_sub(1, std::memory_order_relaxed);
118       assert(Old > 0 && "Attempt to signal finish with no pending work");
119     }
120 
121     /// Destroy the worker thread and wait.
122     void shutDown();
123 
124     /// Initialize the worker thread.
125     void startThread();
126 
127     /// Run the server thread to continuously check the RPC interface for work
128     /// to be done for the device.
129     void run();
130   };
131 
132 public:
133   /// Pointer to the server thread instance.
134   std::unique_ptr<ServerThread> Thread;
135 };
136 
137 } // namespace llvm::omp::target
138 
139 #endif
140