-
Notifications
You must be signed in to change notification settings - Fork 636
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dyn 3477 - Avoid unexpected DS function-not-found errors in CBNs, invalidate function definitions on opening new workspace #12002
Conversation
CustomNodeManager.AddUninitializedCustomNodesInPath(Path.GetDirectoryName(filePath), IsTestMode); | ||
|
||
var currentHomeSpace = Workspaces.OfType<HomeWorkspaceModel>().FirstOrDefault(); | ||
currentHomeSpace.UndefineCBNFunctionDefinitions(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we need to undefine func definitions before we call ReCompileCodeBlockNodesForFunctionDefinitions
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to undefine the function definitions if any in the previous workspace before we open a new workspace.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ReCompileCodeBlockForFunctionDefinitions
will be called after new function definitions have been compiled once the new workspace has been parsed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, one question then LGTM!
@@ -565,12 +565,23 @@ private void NodeOnRequestSilenceNodeModifiedEvents(NodeModel node, bool value) | |||
public override void Clear() | |||
{ | |||
WorkspaceClosed?.Invoke(); | |||
UndefineCBNFunctionDefinitions(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So we have never undefined function definitions before loading a new workspace until now? Where they still around after a new home workspace was loaded?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that is correct. There are two cores in the VM - one static and the other the runtime core. The runtime core was being reset every time a new workspace was opened but not the static core. I realized that if we reset the static core, we would also need to reload all libraries and packages again into the core, and we don't want to do that for every new workspace, so I decided to simply undefine the function definitions from the static core instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this called both when opening a new workspace and creating a new workspace?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When opening an existing workspace, this function is called in another place - when reading the JSON. There is another call to the same method in this PR.
So regarding |
We don't have multiple workspace support right now or a proposed design for how it should work w.r.t. function definitions. |
I agree - because it creates a situation where users can build broken graphs. |
} | ||
catch (Exception) | ||
{ | ||
buildStatus = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we log something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if we can log any useful information here that would let the user debug the node. For example, how would we identify this code block node for the user? We are catching a similar exception here and not logging anything there either.
Any compilation warnings should appear on the code block node in any case.
// | ||
// To check function redefinition, we need to check other | ||
// CBN to find out if it has been defined yet. Now just | ||
// skip this warning. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this be a todo/followup jira?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.ToList(); | ||
|
||
foreach (var symbol in localSymbols) | ||
procNodes = block.procedureTable.Procedures.Where(p => p.HashID == hash).ToList(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so what situation causes a procedure in the procedureTable to somehow have the same hashID, isn't it a hash table? How can there be more than one matching entry?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Procedures
is a list, not a hashtable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As long as the function name and the argument types and strings are the same, the same hash ID is generated. So if a function is deleted, then re-created, say if you delete a CBN and undo the deletion, a new entry for the same function with the same hash ID will be added, after the previous one has been marked inactive. The new one will be active.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- It's surprising that it's not a table at all... any idea why it's implemented with a list?
- Does it make sense to use some query method on the procedureTable instead of accessing the list directly?
- Did you see this case come up or it's just defensive coding?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh interesting.. so the procedure table will fill up with inactive functions - should it be cleaned up at some point? If we don't already do that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@pinzart90 had the same question. I don't have an answer to why a list was chosen and not a dictionary/hash table. I just think it was a poor choice of data structure. We do this even for the instruction stream where that is another list that keeps growing. Deleting instead of maintaining an ever-expanding list (and marking stale items inactive) is definitely the way to go but it's a bigger change and probably outside the scope of this PR. There needs to be a separate spike/epic to optimize these data structures.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason its name contains the word "table" is probably because it is aimed at representing that concept of a function table in traditional compiler design.
} | ||
} | ||
} | ||
|
||
if (DSExecutable == null) return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when does this happen?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the method is invoked on the static core. There is no DSExecutable
for this core.
@aparajit-pratap it looks good, I've got a few more questions in line - I guess we need to recompile only things that refer to those functions? ie, function calls, and function objects? Maybe this is an optimization we can skip for now, just wanted to start the discussion depending on if you have any performance concerns. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm
Yes, code now needs to be recompiled twice, which is a downside of this PR, however, for the most part compilation is relatively inexpensive as compared to actual execution (except for detecting cycles). I can distil the AST's down to only function call ASTs and recompile only those in the second pass as you suggested but I'm not sure whether that will make much of a difference as I would first need to walk each AST to check for which contain function calls on the rhs. Also, most of these function calls will most likely be FFI calls just because ZT methods are more common than DS function definitions. Definitely, something we can explore further. |
@aparajit-pratap - I'm happy with the state for now if you are, we can try to optimize it further if we need, we could watch the perf CI test to see if opening graphs is affected by a significant margin. |
Purpose
This PR fixes a bug where when a graph containing more than one CBN is opened, where one of those CBNs contains a function definition, while the other contains a function call, there can be a
function not found
warning if the CBN with the function call is pre-compiled first. This is obviously unexpected for the user as the function definition actually exists in another CBN.This is fixed by splitting the pre-compilation of CBNs into two passes. The first pass parses and compiles the CBN like normal except that in the first pass any
function not found
warnings are skipped. In the second pass of pre-compilation, the CBN code (except for function definitions) is compiled again turning back on those warnings as in the second pass it needs to take into account any function definitions that may have already been added tolibrary management core
in the first pass. Only after the second pass anyfunction not found
warnings will be real as by this time all function definitions should have been taken into account in the first pass.Note that this two-stage pre-compilation of CBNs only needs to happen on opening a workspace, not in other cases of CBN creation.
Additionally, any function definitions in CBNs from a previous workspace are invalidated (
procedureNode
is marked inactive) upon opening a new workspace so that they no longer continue to show up in autocomplete, andfunction not found
warnings appear if an attempt is made to call them.Declarations
Check these if you believe they are true
*.resx
filesall tests pass with the exception ofDynamo.Tests.CoreTests.TestHasUnsavedChangesOnNodeRemovalEdgeCase
, a fix is in progress ...