layers: Consolidate thread-safety header files

Combine thread-safety.h and thread_safety_validation.h to allow for
more code-gen.

Change-Id: I5b6535d550efbc5c45c49c0058fd07b6dc895208
diff --git a/scripts/layer_chassis_generator.py b/scripts/layer_chassis_generator.py
index f36c132..4a9171f 100644
--- a/scripts/layer_chassis_generator.py
+++ b/scripts/layer_chassis_generator.py
@@ -373,7 +373,7 @@
 #include "object_lifetime_validation.h"
 #define OBJECT_LAYER_NAME "VK_LAYER_LUNARG_object_tracker"
 #elif BUILD_THREAD_SAFETY
-#include "thread_safety_validation.h"
+#include "thread_safety.h"
 #define OBJECT_LAYER_NAME "VK_LAYER_GOOGLE_threading"
 #elif BUILD_PARAMETER_VALIDATION
 #define OBJECT_LAYER_NAME "VK_LAYER_LUNARG_parameter_validation"
diff --git a/scripts/thread_safety_generator.py b/scripts/thread_safety_generator.py
index ae80b91..f3a434c 100644
--- a/scripts/thread_safety_generator.py
+++ b/scripts/thread_safety_generator.py
@@ -140,6 +140,413 @@
  * Author: Mark Lobodzinski <mark@lunarg.com>
  */"""
 
+    inline_custom_header_preamble = """
+#pragma once
+
+#include <condition_variable>
+#include <mutex>
+#include <vector>
+#include <unordered_set>
+#include <string>
+
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(DISTINCT_NONDISPATCHABLE_PHONY_HANDLE)
+// The following line must match the vulkan_core.h condition guarding VK_DEFINE_NON_DISPATCHABLE_HANDLE
+#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || \
+    defined(_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
+// If pointers are 64-bit, then there can be separate counters for each
+// NONDISPATCHABLE_HANDLE type.  Otherwise they are all typedef uint64_t.
+#define DISTINCT_NONDISPATCHABLE_HANDLES
+// Make sure we catch any disagreement between us and the vulkan definition
+static_assert(std::is_pointer<DISTINCT_NONDISPATCHABLE_PHONY_HANDLE>::value,
+              "Mismatched non-dispatchable handle handle, expected pointer type.");
+#else
+// Make sure we catch any disagreement between us and the vulkan definition
+static_assert(std::is_same<uint64_t, DISTINCT_NONDISPATCHABLE_PHONY_HANDLE>::value,
+              "Mismatched non-dispatchable handle handle, expected uint64_t.");
+#endif
+
+// Suppress unused warning on Linux
+#if defined(__GNUC__)
+#define DECORATE_UNUSED __attribute__((unused))
+#else
+#define DECORATE_UNUSED
+#endif
+
+// clang-format off
+static const char DECORATE_UNUSED *kVUID_Threading_Info = "UNASSIGNED-Threading-Info";
+static const char DECORATE_UNUSED *kVUID_Threading_MultipleThreads = "UNASSIGNED-Threading-MultipleThreads";
+static const char DECORATE_UNUSED *kVUID_Threading_SingleThreadReuse = "UNASSIGNED-Threading-SingleThreadReuse";
+// clang-format on
+
+#undef DECORATE_UNUSED
+
+struct object_use_data {
+    loader_platform_thread_id thread;
+    int reader_count;
+    int writer_count;
+};
+
+template <typename T>
+class counter {
+public:
+    const char *typeName;
+    VkDebugReportObjectTypeEXT objectType;
+    debug_report_data **report_data;
+    std::unordered_map<T, object_use_data> uses;
+    std::mutex counter_lock;
+    std::condition_variable counter_condition;
+
+
+    void StartWrite(T object) {
+        if (object == VK_NULL_HANDLE) {
+            return;
+        }
+        bool skip = false;
+        loader_platform_thread_id tid = loader_platform_get_thread_id();
+        std::unique_lock<std::mutex> lock(counter_lock);
+        if (uses.find(object) == uses.end()) {
+            // There is no current use of the object.  Record writer thread.
+            struct object_use_data *use_data = &uses[object];
+            use_data->reader_count = 0;
+            use_data->writer_count = 1;
+            use_data->thread = tid;
+        } else {
+            struct object_use_data *use_data = &uses[object];
+            if (use_data->reader_count == 0) {
+                // There are no readers.  Two writers just collided.
+                if (use_data->thread != tid) {
+                    skip |= log_msg(*report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, objectType, (uint64_t)(object),
+                        kVUID_Threading_MultipleThreads,
+                        "THREADING ERROR : object of type %s is simultaneously used in "
+                        "thread 0x%" PRIx64 " and thread 0x%" PRIx64,
+                        typeName, (uint64_t)use_data->thread, (uint64_t)tid);
+                    if (skip) {
+                        // Wait for thread-safe access to object instead of skipping call.
+                        while (uses.find(object) != uses.end()) {
+                            counter_condition.wait(lock);
+                        }
+                        // There is now no current use of the object.  Record writer thread.
+                        struct object_use_data *new_use_data = &uses[object];
+                        new_use_data->thread = tid;
+                        new_use_data->reader_count = 0;
+                        new_use_data->writer_count = 1;
+                    } else {
+                        // Continue with an unsafe use of the object.
+                        use_data->thread = tid;
+                        use_data->writer_count += 1;
+                    }
+                } else {
+                    // This is either safe multiple use in one call, or recursive use.
+                    // There is no way to make recursion safe.  Just forge ahead.
+                    use_data->writer_count += 1;
+                }
+            } else {
+                // There are readers.  This writer collided with them.
+                if (use_data->thread != tid) {
+                    skip |= log_msg(*report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, objectType, (uint64_t)(object),
+                        kVUID_Threading_MultipleThreads,
+                        "THREADING ERROR : object of type %s is simultaneously used in "
+                        "thread 0x%" PRIx64 " and thread 0x%" PRIx64,
+                        typeName, (uint64_t)use_data->thread, (uint64_t)tid);
+                    if (skip) {
+                        // Wait for thread-safe access to object instead of skipping call.
+                        while (uses.find(object) != uses.end()) {
+                            counter_condition.wait(lock);
+                        }
+                        // There is now no current use of the object.  Record writer thread.
+                        struct object_use_data *new_use_data = &uses[object];
+                        new_use_data->thread = tid;
+                        new_use_data->reader_count = 0;
+                        new_use_data->writer_count = 1;
+                    } else {
+                        // Continue with an unsafe use of the object.
+                        use_data->thread = tid;
+                        use_data->writer_count += 1;
+                    }
+                } else {
+                    // This is either safe multiple use in one call, or recursive use.
+                    // There is no way to make recursion safe.  Just forge ahead.
+                    use_data->writer_count += 1;
+                }
+            }
+        }
+    }
+
+    void FinishWrite(T object) {
+        if (object == VK_NULL_HANDLE) {
+            return;
+        }
+        // Object is no longer in use
+        std::unique_lock<std::mutex> lock(counter_lock);
+        uses[object].writer_count -= 1;
+        if ((uses[object].reader_count == 0) && (uses[object].writer_count == 0)) {
+            uses.erase(object);
+        }
+        // Notify any waiting threads that this object may be safe to use
+        lock.unlock();
+        counter_condition.notify_all();
+    }
+
+    void StartRead(T object) {
+        if (object == VK_NULL_HANDLE) {
+            return;
+        }
+        bool skip = false;
+        loader_platform_thread_id tid = loader_platform_get_thread_id();
+        std::unique_lock<std::mutex> lock(counter_lock);
+        if (uses.find(object) == uses.end()) {
+            // There is no current use of the object.  Record reader count
+            struct object_use_data *use_data = &uses[object];
+            use_data->reader_count = 1;
+            use_data->writer_count = 0;
+            use_data->thread = tid;
+        } else if (uses[object].writer_count > 0 && uses[object].thread != tid) {
+            // There is a writer of the object.
+            skip |= false;
+            log_msg(*report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, objectType, (uint64_t)(object), kVUID_Threading_MultipleThreads,
+                "THREADING ERROR : object of type %s is simultaneously used in "
+                "thread 0x%" PRIx64 " and thread 0x%" PRIx64,
+                typeName, (uint64_t)uses[object].thread, (uint64_t)tid);
+            if (skip) {
+                // Wait for thread-safe access to object instead of skipping call.
+                while (uses.find(object) != uses.end()) {
+                    counter_condition.wait(lock);
+                }
+                // There is no current use of the object.  Record reader count
+                struct object_use_data *use_data = &uses[object];
+                use_data->reader_count = 1;
+                use_data->writer_count = 0;
+                use_data->thread = tid;
+            } else {
+                uses[object].reader_count += 1;
+            }
+        } else {
+            // There are other readers of the object.  Increase reader count
+            uses[object].reader_count += 1;
+        }
+    }
+    void FinishRead(T object) {
+        if (object == VK_NULL_HANDLE) {
+            return;
+        }
+        std::unique_lock<std::mutex> lock(counter_lock);
+        uses[object].reader_count -= 1;
+        if ((uses[object].reader_count == 0) && (uses[object].writer_count == 0)) {
+            uses.erase(object);
+        }
+        // Notify any waiting threads that this object may be safe to use
+        lock.unlock();
+        counter_condition.notify_all();
+    }
+    counter(const char *name = "", VkDebugReportObjectTypeEXT type = VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, debug_report_data **rep_data = nullptr) {
+        typeName = name;
+        objectType = type;
+        report_data = rep_data;
+    }
+};
+
+
+
+class ThreadSafety : public ValidationObject {
+public:
+
+    // Override chassis read/write locks for this validation object
+    void write_lock() {}
+    void write_unlock() {}
+
+    std::mutex command_pool_lock;
+    std::unordered_map<VkCommandBuffer, VkCommandPool> command_pool_map;
+
+    counter<VkCommandBuffer> c_VkCommandBuffer;
+    counter<VkDevice> c_VkDevice;
+    counter<VkInstance> c_VkInstance;
+    counter<VkQueue> c_VkQueue;
+#ifdef DISTINCT_NONDISPATCHABLE_HANDLES
+
+    // Special entry to allow tracking of command pool Reset and Destroy
+    counter<VkCommandPool> c_VkCommandPoolContents;
+    counter<VkBuffer> c_VkBuffer;
+    counter<VkBufferView> c_VkBufferView;
+    counter<VkCommandPool> c_VkCommandPool;
+    counter<VkDescriptorPool> c_VkDescriptorPool;
+    counter<VkDescriptorSet> c_VkDescriptorSet;
+    counter<VkDescriptorSetLayout> c_VkDescriptorSetLayout;
+    counter<VkDeviceMemory> c_VkDeviceMemory;
+    counter<VkEvent> c_VkEvent;
+    counter<VkFence> c_VkFence;
+    counter<VkFramebuffer> c_VkFramebuffer;
+    counter<VkImage> c_VkImage;
+    counter<VkImageView> c_VkImageView;
+    counter<VkPipeline> c_VkPipeline;
+    counter<VkPipelineCache> c_VkPipelineCache;
+    counter<VkPipelineLayout> c_VkPipelineLayout;
+    counter<VkQueryPool> c_VkQueryPool;
+    counter<VkRenderPass> c_VkRenderPass;
+    counter<VkSampler> c_VkSampler;
+    counter<VkSemaphore> c_VkSemaphore;
+    counter<VkShaderModule> c_VkShaderModule;
+    counter<VkDebugReportCallbackEXT> c_VkDebugReportCallbackEXT;
+    counter<VkObjectTableNVX> c_VkObjectTableNVX;
+    counter<VkIndirectCommandsLayoutNVX> c_VkIndirectCommandsLayoutNVX;
+    counter<VkDisplayKHR> c_VkDisplayKHR;
+    counter<VkDisplayModeKHR> c_VkDisplayModeKHR;
+    counter<VkSurfaceKHR> c_VkSurfaceKHR;
+    counter<VkSwapchainKHR> c_VkSwapchainKHR;
+    counter<VkDescriptorUpdateTemplateKHR> c_VkDescriptorUpdateTemplateKHR;
+    counter<VkValidationCacheEXT> c_VkValidationCacheEXT;
+    counter<VkSamplerYcbcrConversionKHR> c_VkSamplerYcbcrConversionKHR;
+    counter<VkDebugUtilsMessengerEXT> c_VkDebugUtilsMessengerEXT;
+    counter<VkAccelerationStructureNV> c_VkAccelerationStructureNV;
+
+#else   // DISTINCT_NONDISPATCHABLE_HANDLES
+    // Special entry to allow tracking of command pool Reset and Destroy
+    counter<uint64_t> c_VkCommandPoolContents;
+
+    counter<uint64_t> c_uint64_t;
+#endif  // DISTINCT_NONDISPATCHABLE_HANDLES
+
+    ThreadSafety()
+        : c_VkCommandBuffer("VkCommandBuffer", VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, &report_data),
+          c_VkDevice("VkDevice", VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT, &report_data),
+          c_VkInstance("VkInstance", VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT, &report_data),
+          c_VkQueue("VkQueue", VK_DEBUG_REPORT_OBJECT_TYPE_QUEUE_EXT, &report_data),
+          c_VkCommandPoolContents("VkCommandPool", VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_POOL_EXT, &report_data),
+
+#ifdef DISTINCT_NONDISPATCHABLE_HANDLES
+          c_VkBuffer("VkBuffer", VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT, &report_data),
+          c_VkBufferView("VkBufferView", VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_VIEW_EXT, &report_data),
+          c_VkCommandPool("VkCommandPool", VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_POOL_EXT, &report_data),
+          c_VkDescriptorPool("VkDescriptorPool", VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_POOL_EXT, &report_data),
+          c_VkDescriptorSet("VkDescriptorSet", VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT, &report_data),
+          c_VkDescriptorSetLayout("VkDescriptorSetLayout", VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT_EXT, &report_data),
+          c_VkDeviceMemory("VkDeviceMemory", VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT, &report_data),
+          c_VkEvent("VkEvent", VK_DEBUG_REPORT_OBJECT_TYPE_EVENT_EXT, &report_data),
+          c_VkFence("VkFence", VK_DEBUG_REPORT_OBJECT_TYPE_FENCE_EXT, &report_data),
+          c_VkFramebuffer("VkFramebuffer", VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT, &report_data),
+          c_VkImage("VkImage", VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, &report_data),
+          c_VkImageView("VkImageView", VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_VIEW_EXT, &report_data),
+          c_VkPipeline("VkPipeline", VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT, &report_data),
+          c_VkPipelineCache("VkPipelineCache", VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_CACHE_EXT, &report_data),
+          c_VkPipelineLayout("VkPipelineLayout", VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_LAYOUT_EXT, &report_data),
+          c_VkQueryPool("VkQueryPool", VK_DEBUG_REPORT_OBJECT_TYPE_QUERY_POOL_EXT, &report_data),
+          c_VkRenderPass("VkRenderPass", VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT, &report_data),
+          c_VkSampler("VkSampler", VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_EXT, &report_data),
+          c_VkSemaphore("VkSemaphore", VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT, &report_data),
+          c_VkShaderModule("VkShaderModule", VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT, &report_data),
+          c_VkDebugReportCallbackEXT("VkDebugReportCallbackEXT", VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT, &report_data),
+          c_VkObjectTableNVX("VkObjectTableNVX", VK_DEBUG_REPORT_OBJECT_TYPE_OBJECT_TABLE_NVX_EXT, &report_data),
+          c_VkIndirectCommandsLayoutNVX("VkIndirectCommandsLayoutNVX",
+                                        VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT, &report_data),
+          c_VkDisplayKHR("VkDisplayKHR", VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_KHR_EXT, &report_data),
+          c_VkDisplayModeKHR("VkDisplayModeKHR", VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_MODE_KHR_EXT, &report_data),
+          c_VkSurfaceKHR("VkSurfaceKHR", VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT, &report_data),
+          c_VkSwapchainKHR("VkSwapchainKHR", VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT, &report_data),
+          c_VkDescriptorUpdateTemplateKHR("VkDescriptorUpdateTemplateKHR",
+                                          VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_KHR_EXT, &report_data),
+          c_VkSamplerYcbcrConversionKHR("VkSamplerYcbcrConversionKHR",
+                                        VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_KHR_EXT, &report_data),
+          c_VkDebugUtilsMessengerEXT("VkDebugUtilsMessengerEXT", VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, &report_data),
+          c_VkAccelerationStructureNV("VkAccelerationStructureNV", VK_DEBUG_REPORT_OBJECT_TYPE_ACCELERATION_STRUCTURE_NV_EXT, &report_data)
+
+#else   // DISTINCT_NONDISPATCHABLE_HANDLES
+          c_uint64_t("NON_DISPATCHABLE_HANDLE", VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, &report_data)
+#endif  // DISTINCT_NONDISPATCHABLE_HANDLES
+              {};
+
+#define WRAPPER(type)                                                \
+    void StartWriteObject(type object) {                             \
+        c_##type.StartWrite(object);                                 \
+    }                                                                \
+    void FinishWriteObject(type object) {                            \
+        c_##type.FinishWrite(object);                                \
+    }                                                                \
+    void StartReadObject(type object) {                              \
+        c_##type.StartRead(object);                                  \
+    }                                                                \
+    void FinishReadObject(type object) {                             \
+        c_##type.FinishRead(object);                                 \
+    }
+
+WRAPPER(VkDevice)
+WRAPPER(VkInstance)
+WRAPPER(VkQueue)
+#ifdef DISTINCT_NONDISPATCHABLE_HANDLES
+
+WRAPPER(VkBuffer)
+WRAPPER(VkBufferView)
+WRAPPER(VkCommandPool)
+WRAPPER(VkDescriptorPool)
+WRAPPER(VkDescriptorSet)
+WRAPPER(VkDescriptorSetLayout)
+WRAPPER(VkDeviceMemory)
+WRAPPER(VkEvent)
+WRAPPER(VkFence)
+WRAPPER(VkFramebuffer)
+WRAPPER(VkImage)
+WRAPPER(VkImageView)
+WRAPPER(VkPipeline)
+WRAPPER(VkPipelineCache)
+WRAPPER(VkPipelineLayout)
+WRAPPER(VkQueryPool)
+WRAPPER(VkRenderPass)
+WRAPPER(VkSampler)
+WRAPPER(VkSemaphore)
+WRAPPER(VkShaderModule)
+WRAPPER(VkDebugReportCallbackEXT)
+WRAPPER(VkObjectTableNVX)
+WRAPPER(VkIndirectCommandsLayoutNVX)
+WRAPPER(VkDisplayKHR)
+WRAPPER(VkDisplayModeKHR)
+WRAPPER(VkSurfaceKHR)
+WRAPPER(VkSwapchainKHR)
+WRAPPER(VkDescriptorUpdateTemplateKHR)
+WRAPPER(VkValidationCacheEXT)
+WRAPPER(VkSamplerYcbcrConversionKHR)
+WRAPPER(VkDebugUtilsMessengerEXT)
+WRAPPER(VkAccelerationStructureNV)
+
+#else   // DISTINCT_NONDISPATCHABLE_HANDLES
+WRAPPER(uint64_t)
+#endif  // DISTINCT_NONDISPATCHABLE_HANDLES
+
+    // VkCommandBuffer needs check for implicit use of command pool
+    void StartWriteObject(VkCommandBuffer object, bool lockPool = true) {
+        if (lockPool) {
+            std::unique_lock<std::mutex> lock(command_pool_lock);
+            VkCommandPool pool = command_pool_map[object];
+            lock.unlock();
+            StartWriteObject(pool);
+        }
+        c_VkCommandBuffer.StartWrite(object);
+    }
+    void FinishWriteObject(VkCommandBuffer object, bool lockPool = true) {
+        c_VkCommandBuffer.FinishWrite(object);
+        if (lockPool) {
+            std::unique_lock<std::mutex> lock(command_pool_lock);
+            VkCommandPool pool = command_pool_map[object];
+            lock.unlock();
+            FinishWriteObject(pool);
+        }
+    }
+    void StartReadObject(VkCommandBuffer object) {
+        std::unique_lock<std::mutex> lock(command_pool_lock);
+        VkCommandPool pool = command_pool_map[object];
+        lock.unlock();
+        // We set up a read guard against the "Contents" counter to catch conflict vs. vkResetCommandPool and vkDestroyCommandPool
+        // while *not* establishing a read guard against the command pool counter itself to avoid false postives for
+        // non-externally sync'd command buffers
+        c_VkCommandPoolContents.StartRead(pool);
+        c_VkCommandBuffer.StartRead(object);
+    }
+    void FinishReadObject(VkCommandBuffer object) {
+        c_VkCommandBuffer.FinishRead(object);
+        std::unique_lock<std::mutex> lock(command_pool_lock);
+        VkCommandPool pool = command_pool_map[object];
+        lock.unlock();
+        c_VkCommandPoolContents.FinishRead(pool);
+    }"""
+
+
     inline_custom_source_preamble = """
 void ThreadSafety::PreCallRecordAllocateCommandBuffers(VkDevice device, const VkCommandBufferAllocateInfo *pAllocateInfo,
                                                        VkCommandBuffer *pCommandBuffers) {
@@ -370,9 +777,7 @@
         #
         # TODO: LUGMAL -- remove this and add our copyright
         # User-supplied prefix text, if any (list of strings)
-        if (genOpts.prefixText):
-            for s in genOpts.prefixText:
-                write(s, file=self.outFile)
+        write(self.inline_copyright_message, file=self.outFile)
 
         self.header_file = (genOpts.filename == 'thread_safety.h')
         self.source_file = (genOpts.filename == 'thread_safety.cpp')
@@ -383,13 +788,20 @@
 
         if self.source_file:
             write('#include "chassis.h"', file=self.outFile)
-            write('#include "thread_safety_validation.h"', file=self.outFile)
+            write('#include "thread_safety.h"', file=self.outFile)
             self.newline()
             write(self.inline_custom_source_preamble, file=self.outFile)
+        else:
+            write(self.inline_custom_header_preamble, file=self.outFile)
+
 
     def endFile(self):
+        if self.header_file:
+            write('};', file=self.outFile)
+
         # Finish processing in superclass
         OutputGenerator.endFile(self)
+
     def beginFeature(self, interface, emit):
         #write('// starting beginFeature', file=self.outFile)
         # Start processing in superclass