1 /* 2 * Copyright 2016 Advanced Micro Devices, Inc. 3 * All Rights Reserved. 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining 6 * a copy of this software and associated documentation files (the 7 * "Software"), to deal in the Software without restriction, including 8 * without limitation the rights to use, copy, modify, merge, publish, 9 * distribute, sub license, and/or sell copies of the Software, and to 10 * permit persons to whom the Software is furnished to do so, subject to 11 * the following conditions: 12 * 13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 14 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 15 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 16 * NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS, AUTHORS 17 * AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20 * USE OR OTHER DEALINGS IN THE SOFTWARE. 21 * 22 * The above copyright notice and this permission notice (including the 23 * next paragraph) shall be included in all copies or substantial portions 24 * of the Software. 25 */ 26 27 /* Job queue with execution in a separate thread. 28 * 29 * Jobs can be added from any thread. After that, the wait call can be used 30 * to wait for completion of the job. 31 */ 32 33 #ifndef U_QUEUE_H 34 #define U_QUEUE_H 35 36 #include <string.h> 37 38 #include "util/futex.h" 39 #include "util/list.h" 40 #include "util/macros.h" 41 #include "util/os_time.h" 42 #include "util/u_atomic.h" 43 #include "util/u_thread.h" 44 45 #ifdef __cplusplus 46 extern "C" { 47 #endif 48 49 #define UTIL_QUEUE_INIT_USE_MINIMUM_PRIORITY (1 << 0) 50 #define UTIL_QUEUE_INIT_RESIZE_IF_FULL (1 << 1) 51 52 #if defined(__GNUC__) && defined(HAVE_LINUX_FUTEX_H) 53 #define UTIL_QUEUE_FENCE_FUTEX 54 #else 55 #define UTIL_QUEUE_FENCE_STANDARD 56 #endif 57 58 #ifdef UTIL_QUEUE_FENCE_FUTEX 59 /* Job completion fence. 60 * Put this into your job structure. 61 */ 62 struct util_queue_fence { 63 /* The fence can be in one of three states: 64 * 0 - signaled 65 * 1 - unsignaled 66 * 2 - unsignaled, may have waiters 67 */ 68 uint32_t val; 69 }; 70 71 static inline void 72 util_queue_fence_init(struct util_queue_fence *fence) 73 { 74 fence->val = 0; 75 } 76 77 static inline void 78 util_queue_fence_destroy(struct util_queue_fence *fence) 79 { 80 assert(fence->val == 0); 81 /* no-op */ 82 } 83 84 static inline void 85 util_queue_fence_signal(struct util_queue_fence *fence) 86 { 87 uint32_t val = p_atomic_xchg(&fence->val, 0); 88 89 assert(val != 0); 90 91 if (val == 2) 92 futex_wake(&fence->val, INT_MAX); 93 } 94 95 /** 96 * Move \p fence back into unsignalled state. 97 * 98 * \warning The caller must ensure that no other thread may currently be 99 * waiting (or about to wait) on the fence. 100 */ 101 static inline void 102 util_queue_fence_reset(struct util_queue_fence *fence) 103 { 104 #ifdef NDEBUG 105 fence->val = 1; 106 #else 107 uint32_t v = p_atomic_xchg(&fence->val, 1); 108 assert(v == 0); 109 #endif 110 } 111 112 static inline bool 113 util_queue_fence_is_signalled(struct util_queue_fence *fence) 114 { 115 return fence->val == 0; 116 } 117 #endif 118 119 #ifdef UTIL_QUEUE_FENCE_STANDARD 120 /* Job completion fence. 121 * Put this into your job structure. 122 */ 123 struct util_queue_fence { 124 mtx_t mutex; 125 cnd_t cond; 126 int signalled; 127 }; 128 129 void util_queue_fence_init(struct util_queue_fence *fence); 130 void util_queue_fence_destroy(struct util_queue_fence *fence); 131 void util_queue_fence_signal(struct util_queue_fence *fence); 132 133 /** 134 * Move \p fence back into unsignalled state. 135 * 136 * \warning The caller must ensure that no other thread may currently be 137 * waiting (or about to wait) on the fence. 138 */ 139 static inline void 140 util_queue_fence_reset(struct util_queue_fence *fence) 141 { 142 assert(fence->signalled); 143 fence->signalled = 0; 144 } 145 146 static inline bool 147 util_queue_fence_is_signalled(struct util_queue_fence *fence) 148 { 149 return fence->signalled != 0; 150 } 151 #endif 152 153 void 154 _util_queue_fence_wait(struct util_queue_fence *fence); 155 156 static inline void 157 util_queue_fence_wait(struct util_queue_fence *fence) 158 { 159 if (unlikely(!util_queue_fence_is_signalled(fence))) 160 _util_queue_fence_wait(fence); 161 } 162 163 bool 164 _util_queue_fence_wait_timeout(struct util_queue_fence *fence, 165 int64_t abs_timeout); 166 167 /** 168 * Wait for the fence to be signaled with a timeout. 169 * 170 * \param fence the fence 171 * \param abs_timeout the absolute timeout in nanoseconds, relative to the 172 * clock provided by os_time_get_nano. 173 * 174 * \return true if the fence was signaled, false if the timeout occurred. 175 */ 176 static inline bool 177 util_queue_fence_wait_timeout(struct util_queue_fence *fence, 178 int64_t abs_timeout) 179 { 180 if (util_queue_fence_is_signalled(fence)) 181 return true; 182 183 if (abs_timeout == (int64_t)OS_TIMEOUT_INFINITE) { 184 _util_queue_fence_wait(fence); 185 return true; 186 } 187 188 return _util_queue_fence_wait_timeout(fence, abs_timeout); 189 } 190 191 typedef void (*util_queue_execute_func)(void *job, int thread_index); 192 193 struct util_queue_job { 194 void *job; 195 struct util_queue_fence *fence; 196 util_queue_execute_func execute; 197 util_queue_execute_func cleanup; 198 }; 199 200 /* Put this into your context. */ 201 struct util_queue { 202 const char *name; 203 mtx_t finish_lock; /* only for util_queue_finish */ 204 mtx_t lock; 205 cnd_t has_queued_cond; 206 cnd_t has_space_cond; 207 thrd_t *threads; 208 unsigned flags; 209 int num_queued; 210 unsigned num_threads; 211 int kill_threads; 212 int max_jobs; 213 int write_idx, read_idx; /* ring buffer pointers */ 214 struct util_queue_job *jobs; 215 216 /* for cleanup at exit(), protected by exit_mutex */ 217 struct list_head head; 218 }; 219 220 bool util_queue_init(struct util_queue *queue, 221 const char *name, 222 unsigned max_jobs, 223 unsigned num_threads, 224 unsigned flags); 225 void util_queue_destroy(struct util_queue *queue); 226 227 /* optional cleanup callback is called after fence is signaled: */ 228 void util_queue_add_job(struct util_queue *queue, 229 void *job, 230 struct util_queue_fence *fence, 231 util_queue_execute_func execute, 232 util_queue_execute_func cleanup); 233 void util_queue_drop_job(struct util_queue *queue, 234 struct util_queue_fence *fence); 235 236 void util_queue_finish(struct util_queue *queue); 237 238 int64_t util_queue_get_thread_time_nano(struct util_queue *queue, 239 unsigned thread_index); 240 241 /* util_queue needs to be cleared to zeroes for this to work */ 242 static inline bool 243 util_queue_is_initialized(struct util_queue *queue) 244 { 245 return queue->threads != NULL; 246 } 247 248 /* Convenient structure for monitoring the queue externally and passing 249 * the structure between Mesa components. The queue doesn't use it directly. 250 */ 251 struct util_queue_monitoring 252 { 253 /* For querying the thread busyness. */ 254 struct util_queue *queue; 255 256 /* Counters updated by the user of the queue. */ 257 unsigned num_offloaded_items; 258 unsigned num_direct_items; 259 unsigned num_syncs; 260 }; 261 262 #ifdef __cplusplus 263 } 264 #endif 265 266 #endif 267