1/* Copyright (c) 2015-2016 The Khronos Group Inc.
2 * Copyright (c) 2015-2016 Valve Corporation
3 * Copyright (c) 2015-2016 LunarG, Inc.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 * Author: Cody Northrop <cody@lunarg.com>
18 * Author: Mike Stroyan <mike@LunarG.com>
19 */
20
21#ifndef THREADING_H
22#define THREADING_H
23#include <condition_variable>
24#include <mutex>
25#include <vector>
26#include "vk_layer_config.h"
27#include "vk_layer_logging.h"
28
29#if defined(__LP64__) || defined(_WIN64) || defined(__x86_64__) || defined(_M_X64) || defined(__ia64) || defined(_M_IA64) ||       \
30    defined(__aarch64__) || defined(__powerpc64__)
31// If pointers are 64-bit, then there can be separate counters for each
32// NONDISPATCHABLE_HANDLE type.  Otherwise they are all typedef uint64_t.
33#define DISTINCT_NONDISPATCHABLE_HANDLES
34#endif
35
36// Draw State ERROR codes
37enum THREADING_CHECKER_ERROR {
38    THREADING_CHECKER_NONE,                // Used for INFO & other non-error messages
39    THREADING_CHECKER_MULTIPLE_THREADS,    // Object used simultaneously by multiple threads
40    THREADING_CHECKER_SINGLE_THREAD_REUSE, // Object used simultaneously by recursion in single thread
41};
42
43struct object_use_data {
44    loader_platform_thread_id thread;
45    int reader_count;
46    int writer_count;
47};
48
49struct layer_data;
50
51static std::mutex global_lock;
52static std::condition_variable global_condition;
53
54template <typename T> class counter {
55  public:
56    const char *typeName;
57    VkDebugReportObjectTypeEXT objectType;
58    std::unordered_map<T, object_use_data> uses;
59    void startWrite(debug_report_data *report_data, T object) {
60        bool skipCall = false;
61        loader_platform_thread_id tid = loader_platform_get_thread_id();
62        std::unique_lock<std::mutex> lock(global_lock);
63        if (uses.find(object) == uses.end()) {
64            // There is no current use of the object.  Record writer thread.
65            struct object_use_data *use_data = &uses[object];
66            use_data->reader_count = 0;
67            use_data->writer_count = 1;
68            use_data->thread = tid;
69        } else {
70            struct object_use_data *use_data = &uses[object];
71            if (use_data->reader_count == 0) {
72                // There are no readers.  Two writers just collided.
73                if (use_data->thread != tid) {
74                    skipCall |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, objectType, (uint64_t)(object),
75                                        /*location*/ 0, THREADING_CHECKER_MULTIPLE_THREADS, "THREADING",
76                                        "THREADING ERROR : object of type %s is simultaneously used in thread %ld and thread %ld",
77                                        typeName, use_data->thread, tid);
78                    if (skipCall) {
79                        // Wait for thread-safe access to object instead of skipping call.
80                        while (uses.find(object) != uses.end()) {
81                            global_condition.wait(lock);
82                        }
83                        // There is now no current use of the object.  Record writer thread.
84                        struct object_use_data *use_data = &uses[object];
85                        use_data->thread = tid;
86                        use_data->reader_count = 0;
87                        use_data->writer_count = 1;
88                    } else {
89                        // Continue with an unsafe use of the object.
90                        use_data->thread = tid;
91                        use_data->writer_count += 1;
92                    }
93                } else {
94                    // This is either safe multiple use in one call, or recursive use.
95                    // There is no way to make recursion safe.  Just forge ahead.
96                    use_data->writer_count += 1;
97                }
98            } else {
99                // There are readers.  This writer collided with them.
100                if (use_data->thread != tid) {
101                    skipCall |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, objectType, (uint64_t)(object),
102                                        /*location*/ 0, THREADING_CHECKER_MULTIPLE_THREADS, "THREADING",
103                                        "THREADING ERROR : object of type %s is simultaneously used in thread %ld and thread %ld",
104                                        typeName, use_data->thread, tid);
105                    if (skipCall) {
106                        // Wait for thread-safe access to object instead of skipping call.
107                        while (uses.find(object) != uses.end()) {
108                            global_condition.wait(lock);
109                        }
110                        // There is now no current use of the object.  Record writer thread.
111                        struct object_use_data *use_data = &uses[object];
112                        use_data->thread = tid;
113                        use_data->reader_count = 0;
114                        use_data->writer_count = 1;
115                    } else {
116                        // Continue with an unsafe use of the object.
117                        use_data->thread = tid;
118                        use_data->writer_count += 1;
119                    }
120                } else {
121                    // This is either safe multiple use in one call, or recursive use.
122                    // There is no way to make recursion safe.  Just forge ahead.
123                    use_data->writer_count += 1;
124                }
125            }
126        }
127    }
128
129    void finishWrite(T object) {
130        // Object is no longer in use
131        std::unique_lock<std::mutex> lock(global_lock);
132        uses[object].writer_count -= 1;
133        if ((uses[object].reader_count == 0) && (uses[object].writer_count == 0)) {
134            uses.erase(object);
135        }
136        // Notify any waiting threads that this object may be safe to use
137        lock.unlock();
138        global_condition.notify_all();
139    }
140
141    void startRead(debug_report_data *report_data, T object) {
142        bool skipCall = false;
143        loader_platform_thread_id tid = loader_platform_get_thread_id();
144        std::unique_lock<std::mutex> lock(global_lock);
145        if (uses.find(object) == uses.end()) {
146            // There is no current use of the object.  Record reader count
147            struct object_use_data *use_data = &uses[object];
148            use_data->reader_count = 1;
149            use_data->writer_count = 0;
150            use_data->thread = tid;
151        } else if (uses[object].writer_count > 0 && uses[object].thread != tid) {
152            // There is a writer of the object.
153            skipCall |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, objectType, (uint64_t)(object),
154                                /*location*/ 0, THREADING_CHECKER_MULTIPLE_THREADS, "THREADING",
155                                "THREADING ERROR : object of type %s is simultaneously used in thread %ld and thread %ld", typeName,
156                                uses[object].thread, tid);
157            if (skipCall) {
158                // Wait for thread-safe access to object instead of skipping call.
159                while (uses.find(object) != uses.end()) {
160                    global_condition.wait(lock);
161                }
162                // There is no current use of the object.  Record reader count
163                struct object_use_data *use_data = &uses[object];
164                use_data->reader_count = 1;
165                use_data->writer_count = 0;
166                use_data->thread = tid;
167            } else {
168                uses[object].reader_count += 1;
169            }
170        } else {
171            // There are other readers of the object.  Increase reader count
172            uses[object].reader_count += 1;
173        }
174    }
175    void finishRead(T object) {
176        std::unique_lock<std::mutex> lock(global_lock);
177        uses[object].reader_count -= 1;
178        if ((uses[object].reader_count == 0) && (uses[object].writer_count == 0)) {
179            uses.erase(object);
180        }
181        // Notify and waiting threads that this object may be safe to use
182        lock.unlock();
183        global_condition.notify_all();
184    }
185    counter(const char *name = "", VkDebugReportObjectTypeEXT type = VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT) {
186        typeName = name;
187        objectType = type;
188    }
189};
190
191struct layer_data {
192    VkInstance instance;
193
194    debug_report_data *report_data;
195    std::vector<VkDebugReportCallbackEXT> logging_callback;
196    VkLayerDispatchTable *device_dispatch_table;
197    VkLayerInstanceDispatchTable *instance_dispatch_table;
198    // The following are for keeping track of the temporary callbacks that can
199    // be used in vkCreateInstance and vkDestroyInstance:
200    uint32_t num_tmp_callbacks;
201    VkDebugReportCallbackCreateInfoEXT *tmp_dbg_create_infos;
202    VkDebugReportCallbackEXT *tmp_callbacks;
203    counter<VkCommandBuffer> c_VkCommandBuffer;
204    counter<VkDevice> c_VkDevice;
205    counter<VkInstance> c_VkInstance;
206    counter<VkQueue> c_VkQueue;
207#ifdef DISTINCT_NONDISPATCHABLE_HANDLES
208    counter<VkBuffer> c_VkBuffer;
209    counter<VkBufferView> c_VkBufferView;
210    counter<VkCommandPool> c_VkCommandPool;
211    counter<VkDescriptorPool> c_VkDescriptorPool;
212    counter<VkDescriptorSet> c_VkDescriptorSet;
213    counter<VkDescriptorSetLayout> c_VkDescriptorSetLayout;
214    counter<VkDeviceMemory> c_VkDeviceMemory;
215    counter<VkEvent> c_VkEvent;
216    counter<VkFence> c_VkFence;
217    counter<VkFramebuffer> c_VkFramebuffer;
218    counter<VkImage> c_VkImage;
219    counter<VkImageView> c_VkImageView;
220    counter<VkPipeline> c_VkPipeline;
221    counter<VkPipelineCache> c_VkPipelineCache;
222    counter<VkPipelineLayout> c_VkPipelineLayout;
223    counter<VkQueryPool> c_VkQueryPool;
224    counter<VkRenderPass> c_VkRenderPass;
225    counter<VkSampler> c_VkSampler;
226    counter<VkSemaphore> c_VkSemaphore;
227    counter<VkShaderModule> c_VkShaderModule;
228    counter<VkDebugReportCallbackEXT> c_VkDebugReportCallbackEXT;
229#else  // DISTINCT_NONDISPATCHABLE_HANDLES
230    counter<uint64_t> c_uint64_t;
231#endif // DISTINCT_NONDISPATCHABLE_HANDLES
232    layer_data()
233        : report_data(nullptr), num_tmp_callbacks(0), tmp_dbg_create_infos(nullptr), tmp_callbacks(nullptr),
234          c_VkCommandBuffer("VkCommandBuffer", VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT),
235          c_VkDevice("VkDevice", VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT),
236          c_VkInstance("VkInstance", VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT),
237          c_VkQueue("VkQueue", VK_DEBUG_REPORT_OBJECT_TYPE_QUEUE_EXT),
238#ifdef DISTINCT_NONDISPATCHABLE_HANDLES
239          c_VkBuffer("VkBuffer", VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT),
240          c_VkBufferView("VkBufferView", VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_VIEW_EXT),
241          c_VkCommandPool("VkCommandPool", VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_POOL_EXT),
242          c_VkDescriptorPool("VkDescriptorPool", VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_POOL_EXT),
243          c_VkDescriptorSet("VkDescriptorSet", VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT),
244          c_VkDescriptorSetLayout("VkDescriptorSetLayout", VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT_EXT),
245          c_VkDeviceMemory("VkDeviceMemory", VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT),
246          c_VkEvent("VkEvent", VK_DEBUG_REPORT_OBJECT_TYPE_EVENT_EXT), c_VkFence("VkFence", VK_DEBUG_REPORT_OBJECT_TYPE_FENCE_EXT),
247          c_VkFramebuffer("VkFramebuffer", VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT),
248          c_VkImage("VkImage", VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT),
249          c_VkImageView("VkImageView", VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_VIEW_EXT),
250          c_VkPipeline("VkPipeline", VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT),
251          c_VkPipelineCache("VkPipelineCache", VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_CACHE_EXT),
252          c_VkPipelineLayout("VkPipelineLayout", VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_LAYOUT_EXT),
253          c_VkQueryPool("VkQueryPool", VK_DEBUG_REPORT_OBJECT_TYPE_QUERY_POOL_EXT),
254          c_VkRenderPass("VkRenderPass", VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT),
255          c_VkSampler("VkSampler", VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_EXT),
256          c_VkSemaphore("VkSemaphore", VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT),
257          c_VkShaderModule("VkShaderModule", VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT),
258          c_VkDebugReportCallbackEXT("VkDebugReportCallbackEXT", VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT)
259#else  // DISTINCT_NONDISPATCHABLE_HANDLES
260          c_uint64_t("NON_DISPATCHABLE_HANDLE", VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT)
261#endif // DISTINCT_NONDISPATCHABLE_HANDLES
262              {};
263};
264
265#define WRAPPER(type)                                                                                                              \
266    static void startWriteObject(struct layer_data *my_data, type object) {                                                        \
267        my_data->c_##type.startWrite(my_data->report_data, object);                                                                \
268    }                                                                                                                              \
269    static void finishWriteObject(struct layer_data *my_data, type object) { my_data->c_##type.finishWrite(object); }              \
270    static void startReadObject(struct layer_data *my_data, type object) {                                                         \
271        my_data->c_##type.startRead(my_data->report_data, object);                                                                 \
272    }                                                                                                                              \
273    static void finishReadObject(struct layer_data *my_data, type object) { my_data->c_##type.finishRead(object); }
274
275WRAPPER(VkDevice)
276WRAPPER(VkInstance)
277WRAPPER(VkQueue)
278#ifdef DISTINCT_NONDISPATCHABLE_HANDLES
279WRAPPER(VkBuffer)
280WRAPPER(VkBufferView)
281WRAPPER(VkCommandPool)
282WRAPPER(VkDescriptorPool)
283WRAPPER(VkDescriptorSet)
284WRAPPER(VkDescriptorSetLayout)
285WRAPPER(VkDeviceMemory)
286WRAPPER(VkEvent)
287WRAPPER(VkFence)
288WRAPPER(VkFramebuffer)
289WRAPPER(VkImage)
290WRAPPER(VkImageView)
291WRAPPER(VkPipeline)
292WRAPPER(VkPipelineCache)
293WRAPPER(VkPipelineLayout)
294WRAPPER(VkQueryPool)
295WRAPPER(VkRenderPass)
296WRAPPER(VkSampler)
297WRAPPER(VkSemaphore)
298WRAPPER(VkShaderModule)
299WRAPPER(VkDebugReportCallbackEXT)
300#else  // DISTINCT_NONDISPATCHABLE_HANDLES
301WRAPPER(uint64_t)
302#endif // DISTINCT_NONDISPATCHABLE_HANDLES
303
304static std::unordered_map<void *, layer_data *> layer_data_map;
305static std::unordered_map<VkCommandBuffer, VkCommandPool> command_pool_map;
306
307// VkCommandBuffer needs check for implicit use of command pool
308static void startWriteObject(struct layer_data *my_data, VkCommandBuffer object, bool lockPool = true) {
309    if (lockPool) {
310        std::unique_lock<std::mutex> lock(global_lock);
311        VkCommandPool pool = command_pool_map[object];
312        lock.unlock();
313        startWriteObject(my_data, pool);
314    }
315    my_data->c_VkCommandBuffer.startWrite(my_data->report_data, object);
316}
317static void finishWriteObject(struct layer_data *my_data, VkCommandBuffer object, bool lockPool = true) {
318    my_data->c_VkCommandBuffer.finishWrite(object);
319    if (lockPool) {
320        std::unique_lock<std::mutex> lock(global_lock);
321        VkCommandPool pool = command_pool_map[object];
322        lock.unlock();
323        finishWriteObject(my_data, pool);
324    }
325}
326static void startReadObject(struct layer_data *my_data, VkCommandBuffer object) {
327    std::unique_lock<std::mutex> lock(global_lock);
328    VkCommandPool pool = command_pool_map[object];
329    lock.unlock();
330    startReadObject(my_data, pool);
331    my_data->c_VkCommandBuffer.startRead(my_data->report_data, object);
332}
333static void finishReadObject(struct layer_data *my_data, VkCommandBuffer object) {
334    my_data->c_VkCommandBuffer.finishRead(object);
335    std::unique_lock<std::mutex> lock(global_lock);
336    VkCommandPool pool = command_pool_map[object];
337    lock.unlock();
338    finishReadObject(my_data, pool);
339}
340#endif // THREADING_H
341