Tombstone exhausted ObjectIds in the wire

It's possible though unlikely to overflow the generation for a given
ObjectId. If this happens, an object like a Buffer or Fence could begin
receiving callbacks for previously destructed objects. This CL makes it
so the client doesn't reuse ObjectIds once they've hit the max generation
number so overflow isn't possible.

Bug: dawn:381
Change-Id: I443c1c87d96614a95d1973e2bf18cd702c34b3f9
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/19240
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
diff --git a/src/dawn_wire/client/ObjectAllocator.h b/src/dawn_wire/client/ObjectAllocator.h
index 7caacbe..b8db86f 100644
--- a/src/dawn_wire/client/ObjectAllocator.h
+++ b/src/dawn_wire/client/ObjectAllocator.h
@@ -16,6 +16,7 @@
 #define DAWNWIRE_CLIENT_OBJECTALLOCATOR_H_
 
 #include "common/Assert.h"
+#include "common/Compiler.h"
 
 #include <memory>
 #include <vector>
@@ -46,24 +47,29 @@
 
         ObjectAndSerial* New(ObjectOwner* owner) {
             uint32_t id = GetNewId();
-            T* result = new T(owner, 1, id);
-            auto object = std::unique_ptr<T>(result);
+            auto object = std::make_unique<T>(owner, 1, id);
 
             if (id >= mObjects.size()) {
                 ASSERT(id == mObjects.size());
                 mObjects.emplace_back(std::move(object), 0);
             } else {
                 ASSERT(mObjects[id].object == nullptr);
-                // TODO(cwallez@chromium.org): investigate if overflows could cause bad things to
-                // happen
+
                 mObjects[id].serial++;
+                // The serial should never overflow. We don't recycle ObjectIds that would overflow
+                // their next serial.
+                ASSERT(mObjects[id].serial != 0);
+
                 mObjects[id].object = std::move(object);
             }
 
             return &mObjects[id];
         }
         void Free(T* obj) {
-            FreeId(obj->id);
+            if (DAWN_LIKELY(mObjects[obj->id].serial != std::numeric_limits<uint32_t>::max())) {
+                // Only recycle this ObjectId if the serial won't overflow on the next allocation.
+                FreeId(obj->id);
+            }
             mObjects[obj->id].object = nullptr;
         }