diff --git a/Source/Engine/Core/Delegate.h b/Source/Engine/Core/Delegate.h index 4a483a2e1..7e80504e6 100644 --- a/Source/Engine/Core/Delegate.h +++ b/Source/Engine/Core/Delegate.h @@ -4,6 +4,7 @@ #include "Engine/Core/Memory/Allocation.h" #include "Engine/Threading/Threading.h" +#include "Engine/Core/Collections/HashSet.h" /// /// The function object that supports binding static, member and lambda functions. @@ -12,6 +13,9 @@ template class Function { friend Delegate; + + template + friend uint32 GetHash(const Function& key); public: /// /// Signature of the function to call. @@ -192,7 +196,7 @@ public: public: /// - /// Returns true if any function has been binded. + /// Returns true if any function has been bound. /// FORCE_INLINE bool IsBinded() const { @@ -200,7 +204,7 @@ public: } /// - /// Calls the binded function (it must be assigned). + /// Calls the bound function (it must be assigned). /// /// A list of parameters for the function invocation. /// Function result @@ -247,7 +251,7 @@ public: }; /// -/// Delegate object that can be used to bind and call multiply functions. Thread-safe to register/unregister during the call. +/// Delegate object that can be used to bind and call multiply functions. Thread-safe to register/unregister during the call. Execution order of bound functions is not stable. /// template class Delegate @@ -264,52 +268,65 @@ public: using FunctionType = Function; protected: - // Single allocation for list of binded functions. Thread-safe access via atomic operations. Removing binded function simply clears the entry to handle function unregister during invocation. - intptr _ptr = 0; - intptr _size = 0; - CriticalSection _locker; + HashSet* _functions = nullptr; + CriticalSection* _locker; + int32* _lockerRefCount; typedef void (*StubSignature)(void*, Params ...); public: Delegate() { + _locker = New(); + _lockerRefCount = New(); + *_lockerRefCount = 1; } Delegate(const Delegate& other) { - const intptr newSize = other._size; - auto newBindings = (FunctionType*)Allocator::Allocate(newSize * sizeof(FunctionType)); - Platform::MemoryCopy((void*)newBindings, (const void*)other._ptr, newSize * sizeof(FunctionType)); - for (intptr i = 0; i < newSize; i++) + if (other._functions == nullptr) + return; + + _functions = New>(*other._functions); + for (auto i = _functions->Begin(); i.IsNotEnd(); ++i) { - FunctionType& f = newBindings[i]; - if (f._function && f._lambda) - f.LambdaCtor(); + if (i->Item._function && i->Item._lambda) + i->Item.LambdaCtor(); } - _ptr = (intptr)newBindings; - _size = newSize; + _locker = other._locker; + _lockerRefCount = other._lockerRefCount; + *_lockerRefCount = *_lockerRefCount + 1; } Delegate(Delegate&& other) noexcept { - _ptr = other._ptr; - _size = other._size; - other._ptr = 0; - other._size = 0; + _functions = other._functions; + _locker = other._locker; + _lockerRefCount = other._lockerRefCount; + other._functions = nullptr; + other._locker = nullptr; + other._lockerRefCount = nullptr; } ~Delegate() { - auto ptr = (FunctionType*)_ptr; - if (ptr) + int32& lockerRefCount = *_lockerRefCount; + lockerRefCount--; + if (lockerRefCount == 0) { - while (_size--) + Allocator::Free(_locker); + Allocator::Free(_lockerRefCount); + _locker = nullptr; + _lockerRefCount = nullptr; + } + + if (_functions != nullptr) + { + for (auto i = _functions->Begin(); i.IsNotEnd(); ++i) { - if (ptr->_lambda) - ptr->LambdaDtor(); - ++ptr; + if (i->Item._lambda) + i->Item.LambdaCtor(); } - Allocator::Free((void*)_ptr); + Allocator::Free(_functions); } } @@ -318,10 +335,8 @@ public: if (this != &other) { UnbindAll(); - const intptr size = Platform::AtomicRead((intptr volatile*)&other._size); - FunctionType* bindings = (FunctionType*)Platform::AtomicRead((intptr volatile*)&other._ptr); - for (intptr i = 0; i < size; i++) - Bind(bindings[i]); + for (auto i = other._functions->Begin(); i.IsNotEnd(); ++i) + Bind(i->Item); } return *this; } @@ -330,10 +345,12 @@ public: { if (this != &other) { - _ptr = other._ptr; - _size = other._size; - other._ptr = 0; - other._size = 0; + _functions = other._functions; + _locker = other._locker; + _lockerRefCount = other._lockerRefCount; + other._functions = nullptr; + other._locker = nullptr; + other._lockerRefCount = nullptr; } return *this; } @@ -390,44 +407,14 @@ public: /// The function to bind. void Bind(const FunctionType& f) { - ScopeLock lock(_locker); - - const intptr size = _size; - FunctionType* bindings = (FunctionType*)_ptr; - if (bindings) - { - // Find a first free slot - for (intptr i = 0; i < size; i++) - { - if (bindings[i]._function == 0) - { - bindings[i]._function = f._function; - bindings[i]._callee = f._callee; - bindings[i]._lambda = f._lambda; - if (f._lambda) - f.LambdaCtor(); - return; - } - } - } - - // Failed to find an empty slot in the list (empty or too small) so perform reallocation - const intptr newSize = size ? size * 2 : 32; - auto newBindings = (FunctionType*)Allocator::Allocate(newSize * sizeof(FunctionType)); - Platform::MemoryCopy(newBindings, bindings, size * sizeof(FunctionType)); - Platform::MemoryClear(newBindings + size, (newSize - size) * sizeof(FunctionType)); - - // Insert function into a first slot after the old list - newBindings[size] = f; - - // Set the new list - _ptr = (intptr)newBindings; - _size = newSize; - Allocator::Free(bindings); + ScopeLock lock(*_locker); + if (_functions == nullptr) + _functions = New>(32); + _functions->Add(f); } /// - /// Binds a static function (if not binded yet). + /// Binds a static function (if not bound yet). /// template void BindUnique() @@ -438,7 +425,7 @@ public: } /// - /// Binds a member function (if not binded yet). + /// Binds a member function (if not bound yet). /// /// The object instance. template @@ -450,7 +437,7 @@ public: } /// - /// Binds a function (if not binded yet). + /// Binds a function (if not bound yet). /// /// The method. void BindUnique(Signature method) @@ -460,22 +447,17 @@ public: } /// - /// Binds a function (if not binded yet). + /// Binds a function (if not bound yet). /// /// The function to bind. void BindUnique(const FunctionType& f) { - ScopeLock lock(_locker); - const intptr size = _size; - FunctionType* bindings = (FunctionType*)_ptr; - if (bindings) + ScopeLock lock(*_locker); + if (_functions != nullptr) { - // Skip if already binded - for (intptr i = 0; i < size; i++) - { - if (bindings[i]._callee == f._callee && bindings[i]._function == f._function) - return; - } + // Skip if already bound + if (_functions->Contains(f)) + return; } Bind(f); } @@ -519,24 +501,10 @@ public: /// The function to unbind. void Unbind(const FunctionType& f) { - ScopeLock lock(_locker); - // Find slot with that function - const intptr size = _size; - FunctionType* bindings = (FunctionType*)_ptr; - for (intptr i = 0; i < size; i++) - { - if (bindings[i]._callee == f._callee && bindings[i]._function == f._function) - { - if (bindings[i]._lambda) - { - bindings[i].LambdaDtor(); - bindings[i]._lambda = nullptr; - } - bindings[i]._callee = 0; - bindings[i]._function = 0; - break; - } - } + if (_functions == nullptr) + return; + ScopeLock lock(*_locker); + _functions->Remove(f); } /// @@ -544,37 +512,27 @@ public: /// void UnbindAll() { - ScopeLock lock(_locker); - const intptr size = _size; - FunctionType* bindings = (FunctionType*)_ptr; - for (intptr i = 0; i < size; i++) + if (_functions == nullptr) + return; + ScopeLock lock(*_locker); + for (auto i = _functions->Begin(); i.IsNotEnd(); ++i) { - if (bindings[i]._lambda) - { - bindings[i].LambdaDtor(); - bindings[i]._lambda = nullptr; - } - bindings[i]._callee = 0; - bindings[i]._function = 0; + if (i->Item._lambda) + i->Item.LambdaDtor(); } + _functions->Clear(); } /// - /// Counts the amount of binded functions. + /// Counts the amount of bound functions. /// - /// The binded functions count. + /// The bound functions count. int32 Count() const { - ScopeLock lock(_locker); - int32 count = 0; - const intptr size = _size; - FunctionType* bindings = (FunctionType*)_ptr; - for (intptr i = 0; i < size; i++) - { - if (bindings[i]._function != 0) - count++; - } - return count; + if (_functions == nullptr) + return 0; + ScopeLock lock(*_locker); + return _functions->Count(); } /// @@ -582,45 +540,40 @@ public: /// int32 Capacity() const { - return (int32)Platform::AtomicRead((intptr volatile*)&_size); + if (_functions == nullptr) + return 0; + ScopeLock lock(*_locker); + return _functions->Capacity(); } /// - /// Determines whether any function is binded. + /// Determines whether any function is bound. /// - /// true if any function is binded; otherwise, false. + /// true if any function is bound; otherwise, false. bool IsBinded() const { - ScopeLock lock(_locker); - const intptr size = _size; - FunctionType* bindings = (FunctionType*)_ptr; - for (intptr i = 0; i < size; i++) - { - if (bindings[i]._function != 0) - return true; - } - return false; + ScopeLock lock(*_locker); + return _functions != nullptr && _functions->Count() > 0; } /// - /// Gets all the binded functions. + /// Gets all the bound functions. /// /// The result bindings functions memory. /// The result bindings functions memory size. /// The amount of written items into the output bindings buffer. Can be equal or less than input bindingsCount capacity. int32 GetBindings(FunctionType* buffer, int32 bufferSize) const { - ScopeLock lock(_locker); + ScopeLock lock(*_locker); + int32 count = 0; - const intptr size = _size; - FunctionType* bindings = (FunctionType*)_ptr; - for (intptr i = 0; i < size && i < bufferSize; i++) + for (auto i = _functions->Begin(); i.IsNotEnd(); ++i) { - buffer[count]._function = (StubSignature)bindings[i]._function; - if (buffer[count]._function != nullptr) + if (i->Item._function != nullptr) { - buffer[count]._callee = (void*)bindings[i]._callee; - buffer[count]._lambda = (typename FunctionType::Lambda*)bindings[i]._lambda; + 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(); count++; @@ -630,25 +583,32 @@ public: } /// - /// Calls all the binded functions. Supports unbinding of the called functions during invocation. + /// Calls all the bound functions. Supports unbinding of the called functions during invocation. /// /// A list of parameters for the function invocation. void operator()(Params ... params) const { - ScopeLock lock(_locker); // TODO: We probably don't need to hold the lock during the function calls, only when iterating through the bindings? - const intptr size = _size; - FunctionType* bindings = (FunctionType*)_ptr; - for (intptr i = 0; i < size; i++) + if (_functions == nullptr) + return; + ScopeLock lock(*_locker); + for (auto i = _functions->Begin(); i.IsNotEnd(); ++i) { - auto function = (StubSignature)bindings->_function; - auto callee = (void*)bindings->_callee; + auto function = (StubSignature)(i->Item._function); + auto callee = (void*)(i->Item._callee); if (function != nullptr) function(callee, Forward(params)...); - ++bindings; } } }; +template +inline uint32 GetHash(const Function& key) +{ + uint32 hash = GetHash((void*)key._callee); + CombineHash(hash, GetHash((void*)key._function)); + return hash; +} + /// /// Action delegate. /// diff --git a/Source/Engine/Scripting/Scripting.cpp b/Source/Engine/Scripting/Scripting.cpp index 6210063b8..c78bc01d0 100644 --- a/Source/Engine/Scripting/Scripting.cpp +++ b/Source/Engine/Scripting/Scripting.cpp @@ -128,11 +128,6 @@ bool ScriptingService::Init() { const auto startTime = DateTime::NowUTC(); - // Link for assemblies events - auto engineAssembly = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly; - engineAssembly->Loaded.Bind(onEngineLoaded); - engineAssembly->Unloading.Bind(onEngineUnloading); - // Initialize managed runtime if (MCore::LoadEngine()) { @@ -483,6 +478,8 @@ bool Scripting::Load() return true; } + onEngineLoaded(flaxEngineModule->Assembly); + // Insert type aliases for vector types that don't exist in C++ but are just typedef (properly redirect them to actual types) // TODO: add support for automatic typedef aliases setup for scripting module to properly lookup type from the alias typename #if USE_LARGE_WORLDS @@ -585,6 +582,9 @@ void Scripting::Release() asset->DeleteObjectNow(); } + auto* flaxEngineModule = (NativeBinaryModule*)GetBinaryModuleFlaxEngine(); + onEngineUnloading(flaxEngineModule->Assembly); + // Unload assemblies (from back to front) { LOG(Info, "Unloading binary modules");