diff --git a/fence_ring.c b/fence_ring.c
new file mode 100644
index 0000000..f49f704
--- /dev/null
+++ b/fence_ring.c
@@ -0,0 +1,145 @@
+/* Wayland compositor running on top of an X server.
+
+Copyright (C) 2022 to various contributors.
+
+This file is part of 12to11.
+
+12to11 is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation, either version 3 of the License, or (at your
+option) any later version.
+
+12to11 is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with 12to11. If not, see . */
+
+#include
+
+#include
+
+#include "compositor.h"
+
+#include
+#include
+
+#include
+
+#define FenceFree (1U << 31)
+
+struct _Fence
+{
+ /* The next and last fences on this list. */
+ Fence *next;
+
+ /* The xshmfence. */
+ struct xshmfence *fence;
+
+ /* The sync fence. High bit is a flag meaning that the fence is on
+ the free list. */
+ XSyncFence fence_id;
+
+ /* The number of references to this fence. Incremented by
+ FenceRetain, decremented by FenceAwait. */
+ int refcount;
+};
+
+/* Chain of all free fences. */
+static Fence *all_fences;
+
+Fence *
+GetFence (void)
+{
+ Fence *fence;
+ int fd;
+ Window drawable;
+
+ drawable = DefaultRootWindow (compositor.display);
+
+ /* Get one free fence. */
+
+ for (fence = all_fences; fence; fence = fence->next)
+ {
+ /* Unlink this fence. */
+ all_fences = fence->next;
+ fence->next = NULL;
+
+ /* Mark the fence as used. */
+ fence->fence_id &= ~FenceFree;
+
+ /* Return it. */
+ return fence;
+ }
+
+ /* Otherwise, allocate a new fence. */
+ fence = XLCalloc (1, sizeof *fence);
+ fd = xshmfence_alloc_shm ();
+
+ if (fd < 0)
+ {
+ perror ("xshmfence_alloc_shm");
+ abort ();
+ }
+
+ /* Map it. */
+ fence->fence = xshmfence_map_shm (fd);
+
+ if (!fence->fence)
+ {
+ perror ("xshmfence_map_shm");
+ abort ();
+ }
+
+ /* Upload the fence to the X server. XCB will close the file
+ descriptor. */
+ fence->fence_id = xcb_generate_id (compositor.conn);
+
+ /* Make the file descriptor CLOEXEC, since it isn't closed
+ immediately. */
+ XLAddFdFlag (fd, FD_CLOEXEC, False);
+ xcb_dri3_fence_from_fd (compositor.conn, drawable,
+ fence->fence_id, 0, fd);
+
+ /* Return the fence. */
+ return fence;
+}
+
+void
+FenceAwait (Fence *fence)
+{
+ XLAssert (fence->refcount);
+ fence->refcount -= 1;
+
+ if (!(fence->fence_id & FenceFree))
+ {
+ /* Wait for the fence to be triggered. */
+ xshmfence_await (fence->fence);
+
+ /* Reset the fence. */
+ xshmfence_reset (fence->fence);
+ fence->fence_id |= FenceFree;
+ }
+
+ if (fence->refcount)
+ return;
+
+ /* Now that the fence is no longer referenced, it can be put back on
+ the list of free fences. */
+ fence->next = all_fences;
+ all_fences = fence;
+}
+
+void
+FenceRetain (Fence *fence)
+{
+ fence->refcount++;
+}
+
+XSyncFence
+FenceToXFence (Fence *fence)
+{
+ return fence->fence_id & ~FenceFree;
+}