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
ignore gil and everything as this fior interview purpose to demonstrate
""
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()
"""