From 6d07a7c2fa547ff5ad24d1b192e9fc84eadfe845 Mon Sep 17 00:00:00 2001
From: Player <sfPlayer1@users.noreply.github.com>
Date: Sun, 14 Feb 2021 17:41:08 +0000
Subject: [PATCH] Make event registration thread safe (#1305)

(cherry picked from commit be996d2566ad2f23321b9343ac08a06080d57ab2)
---
 .../net/fabricmc/fabric/api/event/Event.java  |  2 +-
 .../impl/base/event/ArrayBackedEvent.java     | 29 ++++++++++++-------
 2 files changed, 20 insertions(+), 11 deletions(-)

diff --git a/fabric-api-base/src/main/java/net/fabricmc/fabric/api/event/Event.java b/fabric-api-base/src/main/java/net/fabricmc/fabric/api/event/Event.java
index c3c652f6f..8016d5db2 100644
--- a/fabric-api-base/src/main/java/net/fabricmc/fabric/api/event/Event.java
+++ b/fabric-api-base/src/main/java/net/fabricmc/fabric/api/event/Event.java
@@ -28,7 +28,7 @@ public abstract class Event<T> {
 	 * always refer to an instance containing all code that should be
 	 * executed upon event emission.
 	 */
-	protected T invoker;
+	protected volatile T invoker;
 
 	/**
 	 * Returns the invoker instance.
diff --git a/fabric-api-base/src/main/java/net/fabricmc/fabric/impl/base/event/ArrayBackedEvent.java b/fabric-api-base/src/main/java/net/fabricmc/fabric/impl/base/event/ArrayBackedEvent.java
index bc62e358b..99c1ecebf 100644
--- a/fabric-api-base/src/main/java/net/fabricmc/fabric/impl/base/event/ArrayBackedEvent.java
+++ b/fabric-api-base/src/main/java/net/fabricmc/fabric/impl/base/event/ArrayBackedEvent.java
@@ -18,6 +18,8 @@ package net.fabricmc.fabric.impl.base.event;
 
 import java.lang.reflect.Array;
 import java.util.Arrays;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 import java.util.function.Function;
 
 import net.fabricmc.fabric.api.event.Event;
@@ -26,6 +28,7 @@ class ArrayBackedEvent<T> extends Event<T> {
 	private final Class<? super T> type;
 	private final Function<T[], T> invokerFactory;
 	private final T dummyInvoker;
+	private final Lock lock = new ReentrantLock();
 	private T[] handlers;
 
 	ArrayBackedEvent(Class<? super T> type, T dummyInvoker, Function<T[], T> invokerFactory) {
@@ -35,12 +38,12 @@ class ArrayBackedEvent<T> extends Event<T> {
 		update();
 	}
 
+	@SuppressWarnings("unchecked")
 	void update() {
 		if (handlers == null) {
 			if (dummyInvoker != null) {
 				invoker = dummyInvoker;
 			} else {
-				//noinspection unchecked
 				invoker = invokerFactory.apply((T[]) Array.newInstance(type, 0));
 			}
 		} else if (handlers.length == 1) {
@@ -50,21 +53,27 @@ class ArrayBackedEvent<T> extends Event<T> {
 		}
 	}
 
+	@SuppressWarnings("unchecked")
 	@Override
 	public void register(T listener) {
 		if (listener == null) {
 			throw new NullPointerException("Tried to register a null listener!");
 		}
 
-		if (handlers == null) {
-			//noinspection unchecked
-			handlers = (T[]) Array.newInstance(type, 1);
-			handlers[0] = listener;
-		} else {
-			handlers = Arrays.copyOf(handlers, handlers.length + 1);
-			handlers[handlers.length - 1] = listener;
-		}
+		lock.lock();
 
-		update();
+		try {
+			if (handlers == null) {
+				handlers = (T[]) Array.newInstance(type, 1);
+				handlers[0] = listener;
+			} else {
+				handlers = Arrays.copyOf(handlers, handlers.length + 1);
+				handlers[handlers.length - 1] = listener;
+			}
+
+			update();
+		} finally {
+			lock.unlock();
+		}
 	}
 }