diff --git a/Source/Engine/Core/Delegate.h b/Source/Engine/Core/Delegate.h
index 945dd6ce5..f0f8fdef0 100644
--- a/Source/Engine/Core/Delegate.h
+++ b/Source/Engine/Core/Delegate.h
@@ -226,7 +226,7 @@ public:
/// Function result
FORCE_INLINE ReturnType operator()(Params... params) const
{
- ASSERT(_function);
+ ASSERT_LOW_LAYER(_function);
return _function(_callee, Forward(params)...);
}
@@ -289,8 +289,13 @@ protected:
intptr volatile _ptr = 0;
intptr volatile _size = 0;
#else
- HashSet* _functions = nullptr;
- CriticalSection* _locker = nullptr;
+ struct Data
+ {
+ HashSet Functions;
+ CriticalSection Locker;
+ };
+ // Holds pointer to Data with Functions and Locker. Thread-safe access via atomic operations.
+ intptr volatile _data = 0;
#endif
typedef void (*StubSignature)(void*, Params...);
@@ -314,15 +319,12 @@ public:
_ptr = (intptr)newBindings;
_size = newSize;
#else
- if (other._functions == nullptr)
+ Data* otherData = (Data*)Platform::AtomicRead(&_data);
+ if (otherData == nullptr)
return;
- _functions = New>(*other._functions);
- for (auto i = _functions->Begin(); i.IsNotEnd(); ++i)
- {
- if (i->Item._function && i->Item._lambda)
- i->Item.LambdaCtor();
- }
- _locker = other._locker;
+ ScopeLock lock(otherData->Locker);
+ for (auto i = otherData->Functions.Begin(); i.IsNotEnd(); ++i)
+ Bind(i->Item);
#endif
}
@@ -334,10 +336,8 @@ public:
other._ptr = 0;
other._size = 0;
#else
- _functions = other._functions;
- _locker = other._locker;
- other._functions = nullptr;
- other._locker = nullptr;
+ _data = other._data;
+ other._data = 0;
#endif
}
@@ -356,20 +356,11 @@ public:
Allocator::Free((void*)_ptr);
}
#else
- if (_locker != nullptr)
+ Data* data = (Data*)_data;
+ if (data)
{
- Allocator::Free(_locker);
- _locker = nullptr;
- }
- if (_functions != nullptr)
- {
- for (auto i = _functions->Begin(); i.IsNotEnd(); ++i)
- {
- if (i->Item._lambda)
- i->Item.LambdaCtor();
- }
- Allocator::Free(_functions);
- _functions = nullptr;
+ _data = 0;
+ Delete(data);
}
#endif
}
@@ -385,8 +376,13 @@ public:
for (intptr i = 0; i < size; i++)
Bind(bindings[i]);
#else
- for (auto i = other._functions->Begin(); i.IsNotEnd(); ++i)
- Bind(i->Item);
+ Data* otherData = (Data*)Platform::AtomicRead(&_data);
+ if (otherData != nullptr)
+ {
+ ScopeLock lock(otherData->Locker);
+ for (auto i = otherData->Functions.Begin(); i.IsNotEnd(); ++i)
+ Bind(i->Item);
+ }
#endif
}
return *this;
@@ -402,10 +398,8 @@ public:
other._ptr = 0;
other._size = 0;
#else
- _functions = other._functions;
- _locker = other._locker;
- other._functions = nullptr;
- other._locker = nullptr;
+ _data = other._data;
+ other._data = 0;
#endif
}
return *this;
@@ -507,12 +501,20 @@ public:
Allocator::Free(bindings);
}
#else
- if (_locker == nullptr)
- _locker = New();
- ScopeLock lock(*_locker);
- if (_functions == nullptr)
- _functions = New>();
- _functions->Add(f);
+ Data* data = (Data*)Platform::AtomicRead(&_data);
+ while (!data)
+ {
+ Data* newData = New();
+ Data* oldData = (Data*)Platform::InterlockedCompareExchange(&_data, (intptr)newData, (intptr)data);
+ if (oldData != data)
+ {
+ // Other thread already set the new data so free it and try again
+ Delete(newData);
+ }
+ data = (Data*)Platform::AtomicRead(&_data);
+ }
+ ScopeLock lock(data->Locker);
+ data->Functions.Add(f);
#endif
}
@@ -568,13 +570,22 @@ public:
}
}
#else
- if (_locker == nullptr)
- _locker = New();
- ScopeLock lock(*_locker);
- if (_functions && _functions->Contains(f))
- return;
+ Data* data = (Data*)Platform::AtomicRead(&_data);
+ if (data)
+ {
+ data->Locker.Lock();
+ if (data->Functions.Contains(f))
+ {
+ data->Locker.Unlock();
+ return;
+ }
+ }
#endif
Bind(f);
+#if !DELEGATE_USE_ATOMIC
+ if (data)
+ data->Locker.Unlock();
+#endif
}
///
@@ -640,10 +651,11 @@ public:
Unbind(f);
}
#else
- if (_functions == nullptr)
+ Data* data = (Data*)Platform::AtomicRead(&_data);
+ if (!data)
return;
- ScopeLock lock(*_locker);
- _functions->Remove(f);
+ ScopeLock lock(data->Locker);
+ data->Functions.Remove(f);
#endif
}
@@ -666,15 +678,11 @@ public:
Platform::AtomicStore((intptr volatile*)&bindings[i]._callee, 0);
}
#else
- if (_functions == nullptr)
+ Data* data = (Data*)Platform::AtomicRead(&_data);
+ if (!data)
return;
- ScopeLock lock(*_locker);
- for (auto i = _functions->Begin(); i.IsNotEnd(); ++i)
- {
- if (i->Item._lambda)
- i->Item.LambdaDtor();
- }
- _functions->Clear();
+ ScopeLock lock(data->Locker);
+ data->Functions.Clear();
#endif
}
@@ -684,22 +692,24 @@ public:
/// The bound functions count.
int32 Count() const
{
+ int32 result = 0;
#if DELEGATE_USE_ATOMIC
- int32 count = 0;
const intptr size = Platform::AtomicRead((intptr volatile*)&_size);
FunctionType* bindings = (FunctionType*)Platform::AtomicRead((intptr volatile*)&_ptr);
for (intptr i = 0; i < size; i++)
{
if (Platform::AtomicRead((intptr volatile*)&bindings[i]._function) != 0)
- count++;
+ result++;
}
- return count;
#else
- if (_functions == nullptr)
- return 0;
- ScopeLock lock(*_locker);
- return _functions->Count();
+ Data* data = (Data*)Platform::AtomicRead((intptr volatile*)&_data);
+ if (data)
+ {
+ ScopeLock lock(data->Locker);
+ result = data->Functions.Count();
+ }
#endif
+ return result;
}
///
@@ -710,10 +720,14 @@ public:
#if DELEGATE_USE_ATOMIC
return (int32)Platform::AtomicRead((intptr volatile*)&_size);
#else
- if (_functions == nullptr)
- return 0;
- ScopeLock lock(*_locker);
- return _functions->Capacity();
+ int32 result = 0;
+ Data* data = (Data*)Platform::AtomicRead((intptr volatile*)&_data);
+ if (data)
+ {
+ ScopeLock lock(data->Locker);
+ result = data->Functions.Capacity();
+ }
+ return result;
#endif
}
@@ -733,10 +747,14 @@ public:
}
return false;
#else
- if (_functions == nullptr)
- return false;
- ScopeLock lock(*_locker);
- return _functions->Count() > 0;
+ bool result = false;
+ Data* data = (Data*)Platform::AtomicRead((intptr volatile*)&_data);
+ if (data)
+ {
+ ScopeLock lock(data->Locker);
+ result = data->Functions.Count() != 0;
+ }
+ return result;
#endif
}
@@ -765,18 +783,13 @@ public:
}
}
#else
- if (_functions == nullptr)
- return 0;
- ScopeLock lock(*_locker);
- for (auto i = _functions->Begin(); i.IsNotEnd(); ++i)
+ Data* data = (Data*)Platform::AtomicRead((intptr volatile*)&_data);
+ if (data)
{
- if (i->Item._function != nullptr)
+ ScopeLock lock(data->Locker);
+ for (auto i = data->Functions.Begin(); i.IsNotEnd(); ++i)
{
- buffer[count]._function = (StubSignature)i->Item._function;
- buffer[count]._callee = (void*)i->Item._callee;
- buffer[count]._lambda = (typename FunctionType::Lambda*)i->Item._lambda;
- if (buffer[count]._lambda)
- buffer[count].LambdaCtor();
+ new(buffer + count) FunctionType((const FunctionType&)i->Item);
count++;
}
}
@@ -802,15 +815,15 @@ public:
++bindings;
}
#else
- if (_functions == nullptr)
+ Data* data = (Data*)Platform::AtomicRead((intptr volatile*)&_data);
+ if (!data)
return;
- ScopeLock lock(*_locker);
- for (auto i = _functions->Begin(); i.IsNotEnd(); ++i)
+ ScopeLock lock(data->Locker);
+ for (auto i = data->Functions.Begin(); i.IsNotEnd(); ++i)
{
- auto function = (StubSignature)(i->Item._function);
- auto callee = (void*)(i->Item._callee);
- if (function != nullptr)
- function(callee, Forward(params)...);
+ const FunctionType& item = i->Item;
+ ASSERT_LOW_LAYER(item._function);
+ item._function(item._callee, Forward(params)...);
}
#endif
}