Skip to content
This repository has been archived by the owner on Jul 28, 2022. It is now read-only.

Commit

Permalink
Improved error reporting for Event module
Browse files Browse the repository at this point in the history
  • Loading branch information
Sleitnick committed Feb 11, 2019
1 parent 06488d0 commit 59903dc
Showing 1 changed file with 27 additions and 97 deletions.
124 changes: 27 additions & 97 deletions src/ReplicatedStorage/Aero/Shared/Event.modulescript.lua
Original file line number Diff line number Diff line change
Expand Up @@ -23,42 +23,37 @@
-----------------------------------------------------------------------------
NOTE ON MEMORY LEAK PREVENTION:
Invoking 'Destroy' on an event will call 'DisconnectAll' and will prevent
further connections of functions. Use this if the event object is no
longer being used. Failure to call 'Destroy' when the event is no longer
in use could result in memory leaks due to connections still being
referenced. Trying to connect a function to a destroyed event will throw
an error.
If an event is no longer being used, be sure to invoke the 'Destroy' method
to ensure that all events are properly released. Failure to do so could
result in memory leaks due to connections still being referenced.
WHY NOT BINDABLE EVENTS:
This module passes by reference, whereas BindableEvents pass by value.
In other words, BindableEvents will create a copy of whatever is passed
rather than the original value itself. This becomes difficult when dealing
with tables, where passing by reference is usually most ideal.
--]]



local CO_WRAP = coroutine.wrap
local CO_RUNNING = coroutine.running
local CO_YIELD = coroutine.yield
local CO_RESUME = coroutine.resume
local BLANK_FUNC = function() end
local ASSERT = assert
local ASSERT = assert
local SELECT = select
local UNPACK = unpack
local TYPE = type


local Event = {}
Event.__index = Event

local Connection = {}
Connection.__index = Connection


------------------------------------------------------------------
-- Event


function Event.new()

local self = setmetatable({
_connections = {};
_destroyed = false;
_firing = false;
_bindable = Instance.new("BindableEvent");
}, Event)

return self
Expand All @@ -67,104 +62,39 @@ end


function Event:Fire(...)
self._firing = true
local connections = self._connections
for i = 1,#connections do
CO_WRAP(connections[i]._func)(...)
end
self._firing = false
self._args = {...}
self._numArgs = SELECT("#", ...)
self._bindable:Fire()
end


function Event:Wait()
local thread = CO_RUNNING()
local connection
connection = self:Connect(function(...)
connection:Disconnect()
ASSERT(CO_RESUME(thread, ...))
end)
return CO_YIELD()
self._bindable.Event:Wait()
return UNPACK(self._args, 1, self._numArgs)
end


function Event:Connect(func)
assert(not self._destroyed, "Cannot connect to destroyed event")
assert(type(func) == "function", "Argument must be function")
local connection = Connection.new(func, self)
table.insert(self._connections, connection)
return connection
ASSERT(not self._destroyed, "Cannot connect to destroyed event")
ASSERT(TYPE(func) == "function", "Argument must be function")
return self._bindable.Event:Connect(function()
func(UNPACK(self._args, 1, self._numArgs))
end)
end


function Event:DisconnectAll()
local function DisconnectAll()
for _,c in pairs(self._connections) do
c.IsConnected = false
end
self._connections = {}
end
if (self._firing) then
for _,c in pairs(self._connections) do
c._func = BLANK_FUNC
end
spawn(DisconnectAll)
else
DisconnectAll()
end
end


function Event:Disconnect(connection)
local function Disconnect()
local connections = self._connections
local numConnections = #connections
for i = 1,numConnections do
local c = connections[i]
if (c == connection) then
connections[i] = connections[numConnections]
connections[numConnections] = nil
break
end
end
end
if (self._firing) then
connection._func = BLANK_FUNC
spawn(Disconnect)
else
Disconnect()
end
self._bindable:Destroy()
self._bindable = Instance.new("BindableEvent")
end


function Event:Destroy()
if (self._destroyed) then return end
self._destroyed = true
self:DisconnectAll()
self._bindable:Destroy()
end


------------------------------------------------------------------
-- Connection


function Connection.new(func, event)
local self = setmetatable({
Connected = true;
_func = func;
_event = event;
}, Connection)
return self
end


function Connection:Disconnect()
if (not self.Connected) then return end
self.Connected = false
self._event:Disconnect(self)
end


------------------------------------------------------------------


return Event

1 comment on commit 59903dc

@Sleitnick
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The overlying change is to use BindableEvents internally instead of using coroutines. This allows proper error reporting for the developer.

Please sign in to comment.