dontnuse threading anywher alwaus usecustom approach
ps: Let's say you are running the lock/unlock in a multi-core machine. Now you want to let multiple threads run lock() simultaneously. As we saw in part I, locking a node has multiple validations inside. While doing lock on two nodes causes a race condition. If yes, how will you solve it? In short, how do make the lock() function thread safe?

Multiple threads running it simultaneously shouldn't not affect the correctness.
Try to make the critical sections more granular. ie. don't create any big atomic/synchronized blocks that will make parallelism suffer

use set and test non mutex method and and mutiple reads and exclusive write approach on theLOCK method only 
""
import sys

class Node:
    def __init__(self, name):
        self.name = name
        self.parent = None
        self.children = []
        self.isLocked = False
        self.locked_uid = None
        self.locked_descendant_count = 0  # Number of locked descendants

class Tree:
    def __init__(self, m, n, name_list):
        self.node_map = {}
        self.node_list = [Node(name) for name in name_list]
        for node in self.node_list:
            self.node_map[node.name] = node
        
        # Building an m-ary tree assuming breadth-first insertion
        for i in range(n):
            current = self.node_list[i]
            for j in range(1, m+1):
                child_index = m * i + j
                if child_index >= n:
                    break
                child = self.node_list[child_index]
                current.children.append(child)
                child.parent = current
        
    def 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
        
        if node.locked_descendant_count > 0:
            return False
        
        # Lock the node
        node.isLocked = True
        node.locked_uid = uid
        
        # Update locked_descendant_count for all ancestors
        current = node.parent
        while current:
            current.locked_descendant_count += 1
            current = current.parent
        
        return True
    
    def unlock(self, node, uid):
        if not node.isLocked or node.locked_uid != uid:
            return False
        
        # Unlock the node
        node.isLocked = False
        node.locked_uid = None
        
        # Update locked_descendant_count for all ancestors
        current = node.parent
        while current:
            current.locked_descendant_count -= 1
            current = current.parent
        
        return True
    
    def upgrade(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
        
        if node.locked_descendant_count == 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 locked_node in locked_nodes:
            if locked_node.locked_uid != uid:
                return False
        
        # Unlock all locked descendants
        for locked_node in locked_nodes:
            locked_node.isLocked = False
            locked_node.locked_uid = None
            # Update locked_descendant_count for ancestors of each unlocked node
            ancestor = locked_node.parent
            while ancestor:
                ancestor.locked_descendant_count -= 1
                ancestor = ancestor.parent
        
        # Lock the current node
        node.isLocked = True
        node.locked_uid = uid
        
        # Update locked_descendant_count for all ancestors
        current = node.parent
        while current:
            current.locked_descendant_count += 1
            current = current.parent
        
        return True
    
    def _collect_locked_descendants(self, node, locked_nodes):
    # DFS to collect all locked descendants
        stack = []
        stack.append(node)
        while stack:
            current = stack.pop()
            for child in current.children:
                if child.locked_descendant_count == 0 and not child.isLocked:
                    continue  # No need to traverse this subtree
                if child.isLocked:
                    locked_nodes.append(child)
                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_str = parts
            try:
                uid = int(uid_str)
            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, uid)
            elif operation == "2":
                result = self.unlock(node, uid)
            elif operation == "3":
                result = self.upgrade(node, uid)
            else:
                result = False  # Invalid operation
            results.append('true' if result else 'false')
        return results

def main():
    import sys
    import sys
    input = sys.stdin.read
    data = input().splitlines()
    ptr = 0
    if ptr >= len(data):
        sys.exit()
    n = int(data[ptr]); ptr +=1
    if ptr >= len(data):
        sys.exit()
    m = int(data[ptr]); ptr +=1
    if ptr >= len(data):
        sys.exit()
    q = int(data[ptr]); ptr +=1
    name_list = []
    for _ in range(n):
        if ptr >= len(data):
            break
        name_list.append(data[ptr].strip())
        ptr +=1
    queries = []
    for _ in range(q):
        if ptr >= len(data):
            break
        queries.append(data[ptr].strip())
        ptr +=1
    tree = Tree(m, n, name_list)
    results = tree.process_queries(queries)
    for res in results:
        print(res)

if __name__ == "__main__":
    main()
"""