Skip to content

Potential concurrency bugs in FunctionsView #604

Closed

Description

FunctionsView appears to be trying to be thread-safe, as its SemanticFunctions and NativeFunctions are both ConcurrentDictionary. However, in that case there are a couple of concurrency bugs lurking.

First, while the dictionaries are ConcurrentDictionary, the values stored for each key are List<T>. The code for AddFunction looks like this:

            if (!this.SemanticFunctions.ContainsKey(view.SkillName))
            {
                this.SemanticFunctions[view.SkillName] = new();
            }

            this.SemanticFunctions[view.SkillName].Add(view);

If multiple threads concurrently try to add a FunctionView for the same skill, they could end up both mutating a List<T> concurrently, which is not safe.

Second, with that same code, if multiple threads both try to add a FunctionView for the same skill, and if both see that the SemanticFunctions doesn't yet contain that skill name, they will race to create the list to store for that skill. While the dictionary itself won't be corrupted from a data structure perspective, one thread might add its list and then proceed to add to the list, and the other thread might then (having already checked ContainsKey and found it to return false) overwrite the original list with a new one, there by losing the data the other thread wrote. Such initialization should be done using either TryAdd or GetOrAdd so that the dictionary provides the synchronization to ensure nothing is overwritten.

These issues can easily be fixed by using GetOrAdd and locking on the List<>... except the collections are exposed publicly from the FunctionsView, which means anyone else accessing these would also need to employ the same synchronization scheme. That suggests the API should be designed differently.

(And related to these being exposed publicly, the properties also have public setters... if someone were to set these concurrently with other operations being performed, that could lead to other failure modes, e.g. because lookups are being done first by calling ContainsKey and then separately by using the indexer, the collection could actually be changed to a completely different instance between those calls.)

cc: Devis Lucato (@dluc), Shawn Callegari (@shawncal)

Activity

  1. dluc commented on Apr 26, 2023

    @dluc
    Contributor

    I think we should change FunctionsView to protect the internal data, allowing only to add and list, not remove, and potentially handle conflicts. Plus needs testing and clear specs about multithreaded access and expectations

  2. moved this to Backlog - Code Style in Semantic Kernelon Jul 19, 2023
  3. moved this from Backlog - Code Style to Bugs in Semantic Kernelon Jul 21, 2023
  4. added
    sk team issueA tag to denote issues that where created by the Semantic Kernel team (i.e., not the community)
    on Aug 2, 2023
  5. added this to the R3 : Cycle 2 milestone on Aug 4, 2023
  6. added
    kernelIssues or pull requests impacting the core kernel
    on Aug 24, 2023
  7. modified the milestones: v1, R3 : Cycle 2 on Sep 14, 2023
  8. moved this from Bugs to Sprint: Done in Semantic Kernelon Sep 19, 2023
  9. added a commit that references this issue on Sep 20, 2023
    9d638e5
  10. added 2 commits that reference this issue on Nov 1, 2023
    f2304cd
    80a31ae
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

kernelIssues or pull requests impacting the core kernelsk team issueA tag to denote issues that where created by the Semantic Kernel team (i.e., not the community)

Type

No type

Projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions