hey my interview question was this 
"""
You are given a complete, balanced M-Ary Tree and must support Q queries. There are 3 kinds of queries. Return true or false depending on whether the query was successful.


Lock(v, id) - Lock vertex v for user - id
Unlock(v, id) - If vertex v is locked by the same id, unlock it.
Upgrade(v, id) - If v is unlocked and has at least one locked vertex in it's subtree and every locked vertex in the subtree of v is locked by id, unlock them and lock v instead.
Further, here are some additional constraints


A vertex cannot be locked if it has any locked ancestors or descendants, by any ID.
When a vertex is upgraded, it's locked descendants are automatically unlocked.
An upgrade operation is not possible if the vertex is already locked or has any locked ancestors
An unlock operation is only possible if the vertex is already locked and locked by the same id

"""
"""
for which my answer was this 
"""
import sys

class Node:
    def __init__(self, val):
        self.val = val
        self.uid = None
        self.parent = None
        self.children = []
        self.isLocked = False
        self.lockedDescendantCount = 0  # Instead of a set, we use a count

class Tree:
    def __init__(self, m, n, name_list):
        self.node_list = []
        self.node_map = {}
        for name in name_list:
            node = Node(name)
            self.node_list.append(node)
            self.node_map[name] = node

        # Build the tree
        for i in range(n):
            for j in range(m):
                child_index = m * i + j + 1
                if child_index < n:
                    self.node_list[i].children.append(self.node_list[child_index])
                    self.node_list[child_index].parent = self.node_list[i]

        self.root = self.node_list[0]

    def lock_node(self, node, uid):
        if node.isLocked:
            return False

        # Check if any ancestor is locked
        current = node.parent
        while current:
            if current.isLocked:
                return False
            current = current.parent

        # Check if any descendant is locked
        if node.lockedDescendantCount > 0:
            return False

        # Lock the node
        node.isLocked  = True
        node.uid = uid

        # Update lockedDescendantCount for all ancestors
        current = node.parent
        while current:
            current.lockedDescendantCount += 1
            current = current.parent

        return True

    def unlock_node(self, node, uid):
        if not node.isLocked or node.uid != uid:
            return False

        # Unlock the node
        node.isLocked = False
        node.uid = None

        # Update lockedDescendantCount for all ancestors
        current = node.parent
        while current:
            current.lockedDescendantCount -= 1
            current = current.parent

        return True

    def upgrade_lock(self, node, uid):
        if node.isLocked:
            return False

        # Check if any ancestor is locked
        current = node.parent
        while current:
            if current.isLocked:
                return False
            current = current.parent

        # Check if there is at least one locked descendant
        if node.lockedDescendantCount == 0:
            return False

        # Collect all locked descendants
        locked_nodes = []
        self.collect_locked_descendants(node, locked_nodes)

        if not locked_nodes:
            return False

        # Ensure all locked descendants have the same uid
        for ln in locked_nodes:
            if ln.uid != uid:
                return False

        # Unlock all locked descendants
        for ln in locked_nodes:
            ln.isLocked = False
            ln.uid = None
            # Update lockedDescendantCount for ancestors
            ancestor = ln.parent
            while ancestor:
                ancestor.lockedDescendantCount -= 1
                ancestor = ancestor.parent

        # Lock the current node
        node.isLocked = True
        node.uid = uid
        # Update lockedDescendantCount for all ancestors
        current = node.parent
        while current:
            current.lockedDescendantCount += 1
            current = current.parent

        return True

    def collect_locked_descendants(self, node, locked_nodes):
        # DFS to collect all locked descendants
        stack = [node]
        while stack:
            current = stack.pop()
            for child in current.children:
                if child.isLocked:
                    locked_nodes.append(child)
                if child.lockedDescendantCount > 0:
                    stack.append(child)

    def process_queries(self, queries):
        results = []
        for query in queries:
            parts = query.strip().split()
            if len(parts) != 3:
                results.append('false')
                continue
            operation, node_name, uid = parts
            try:
                uid = int(uid)
            except ValueError:
                results.append('false')
                continue
            node = self.node_map.get(node_name, None)
            if not node:
                results.append('false')
                continue
            if operation == "1":
                result = self.lock_node(node, uid)
            elif operation == "2":
                result = self.unlock_node(node, uid)
            elif operation == "3":
                result = self.upgrade_lock(node, uid)
            else:
                result = False  # Invalid operation
            results.append('true' if result else 'false')
        return results

def main():
    import sys
    input = sys.stdin.readline
    try:
        n = int(input())
        m = int(input())
        q = int(input())
    except Exception:
        print("Invalid input for n, m, or q.")
        return

    name_list = []
    for _ in range(n):
        name = input()
        if not name:
            break
        name = name.strip()
        if name:
            name_list.append(name)
    if len(name_list) < n:
        print("Not enough node names provided.")
        return

    queries = []
    for _ in range(q):
        query = input()
        if not query:
            break
        query = query.strip()
        if query:
            queries.append(query)
    if len(queries) < q:
        print("Not enough queries provided.")
        return

    tree = Tree(m, n, name_list)
    results = tree.process_queries(queries)
    for res in results:
        print(res)

main()
"""
"""
now the interview is asking this 
"""
Let’s say you are running the lock/unlock in a multi core machine. Now you want to let multiple threads to run lock() As we saw in part A, locking a node has multiple validations inside. Will doing lock on two nodes cause a race condition. If yes, how will you solve it. In short,
with out using mutex/semaphore locks
"""
what all are the race conditions for LOCK method in a multithreading environment