Repository: godot
Issue: RefCounted objects disappear when accessed from multiple threads
Labels: discussion,topic:core,documentation

### Tested versions

Reproducible in v4.2.stable.official [46dc27791]

### System information

Godot v4.2.stable - Windows 10.0.19045 - GLES3 (Compatibility) - GeForce GTX 1050 Ti () - Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz (8 Threads)

### Issue description

When a field of `RefCounted` type is accessed from two threads, where one thread (main thread) reads it's value and another one (started as `Thread` object) both reads and writes it (but none sets it to `null`), sometimes any of thise threads may read it as `null`. Also, sometimes a local variable containing just allocated `RefCounted` object becomes `null` on secondary thread. This error seems to correlate with access to objects from multiple threads as well.

I've encountered this bug while debugging my [wave function collapse addon](https://github.com/AlexeyBond/godot-constraint-solving). It performs a lot of computations and allocations on secondary threads and I noticed that some demos fail once in a while with errors caused by variables being `null` just after they were `assert`ed to not be `null`.

<details>
<summary>Another error that may be or be not related tot his bug</summary>
<img src="https://github.com/godotengine/godot/assets/7383049/4db9d7fb-802c-4788-92ca-a67442148132" />
It's related to <a href="https://github.com/godotengine/godot/blob/master/core/variant/array.cpp#L55C43-L55C43">this line</a>:
<br>
<code>	ERR_FAIL_NULL(_fp); // Should NOT happen.
</code>
(but it does)
</details>


### Steps to reproduce

Open minimal reproduction project (attached below) or just create a node with following script:
```GDScript
extends Node2D

const TASKS: int = 7

class SomeObject extends RefCounted:
	var foo: Array[String]

	func copy() -> SomeObject:
		var o := SomeObject.new()
		assert(o != null) # This may fail sometimes as well !!!
		var a := self.foo.duplicate()
		o.foo = a

		return o

class Task extends RefCounted:
	var current_object: SomeObject

	func _init(x: Array[String]):
		current_object = SomeObject.new()
		current_object.foo = x

	func bar():
		assert(current_object != null)
		current_object = current_object.copy()
		assert(current_object != null)

var tasks: Array[Task]
var threads: Array[Thread]

func _main(task: Task):
	while true:
		assert(task.current_object != null)
		task.bar()
		assert(task.current_object != null)

func _ready():
	for i in range(TASKS):
		tasks.append(Task.new(["asd"]))

	for task in tasks:
		assert(task.current_object != null) # This one is always OK
		var thread := Thread.new()
		thread.start(_main.bind(task))
		threads.append(thread)

# With this function removed, everything seems to run without errors
func _process(_delta):
	var objs: Array[SomeObject] = []
	for task in tasks:
		# This assert fails sometimes. More frequently than code running in threads.
		#assert(task.current_object != null)
		objs.append(task.current_object)
	print(objs)
```

Run the project/scene for few minutes until one of assertions fails.

On my system it takes about 2-3 minutes for the project to crash. With `_process` removed (and thus only one thread accessing the variable) it ran for 12 minutes (until I stopped it manually) without errors.


### Minimal reproduction project (MRP)

[GodotReferenceBugTest.zip](https://github.com/godotengine/godot/files/13684586/GodotReferenceBugTest.zip)