Why two threads accessing one resource crashes one thread?

111 views Asked by At

I have created a client server program (in python), wherein the client(CMD for e.g.) establishes a TCP connection with the server, queries for the information regarding a particular process and if the process consumes memory more than the threshold, the server kills the process and restarts it,with the client connection being terminated. I am able to create multiple clients querying for different processes, the problem arises when two or more clients query the SAME PROCESS to the server. Ideally, the process should be killed, restarted and the both the client connections be terminated. The problem is only one of the client's connection gets terminated while the other clients continue to hang on the connection and even though the process is restarted, other client continue to hang and their connection never get terminated. Is it the problem of deadlock?

My code for reference(Any changes to the code would be appreciated to handle this problem)

# A CLIENT-SERVER ARCHITECTURE TO GET INFO ABOUT RUNNING PROCESSES ON THE SYSTEM
from thread import *
import threading
import time
import psutil
import itertools
import ctypes
import string
import os
import sys
import socket
import subprocess

exitFlag = 0
#function returning the list of all the valid partitions of the system...
def drives():
    drive_bitmask = ctypes.cdll.kernel32.GetLogicalDrives()
    return list(itertools.compress(string.ascii_uppercase,map(lambda x:ord(x) - ord('0'), bin(drive_bitmask)[:1:-1])))

def proc_info(conn,addr): # prints the process's info which match the keyword....... SERVER SIDE
    conn.send('Welcome to the server\n')
    name=[]
    c=drives()
    o=[]
    t=0
    m=0
    while(t<len(c)):
        o.append(str(c[t]+':\\'))
        t=t+1
    k=0
    #o.reverse()
    conn.send("Enter the key...\n") # for authentication purposes...e.g.here the key is 1
    t=conn.recv(8)
    if(t!='1'):  #WRONG KEY....INVALID USER
        conn.send("\nInvalid key..teminating the connection.....ABORT\n")
        conn.close()

    else:
        r=""
        conn.send("\nEnter the process keyword ..Press # @ the end ") # e.g. 'Sk' for Skype
        d=conn.recv(65536)
        while(d!='#'):
                    r=r+str(d)
            d=conn.recv(65536)
        for p in psutil.pids(): # iterates through all the pids of the processes obtained
                    try:
                        p1=psutil.Process(p)
            if(r in p1.name()):
                            p2=p1.get_memory_info()
                            t=p1.name()+' '
                            d=str(p)+' '
                            conn.send(d)# prints the pid of the process
                            conn.send(t),# prints the name of the process
                            d=str(p2[0]/(1024*1024))+' '
                            conn.send(d) # print memory in MB
                            conn.send('MB\t')
                            for connect in p1.connections(kind='tcp'):
                                d=str(connect.laddr[0])+' '
                conn.send(d) # prints ip
                                d=str(connect.laddr[1])+' '
                conn.send(d) # prints tcp ports                     
                    except psutil.AccessDenied: # won't show all the processes
                        pass

                    except psutil.NoSuchProcess:
                        pass
            else:
                        continue

        conn.send("    Enter the threshold...(in MB)(Press # at the end) ")
        d=""
        t=conn.recv(65536).decode('utf-8')
        while(t!='#'):
                    d=d+str(t)
                    t=conn.recv(65536).decode('utf-8')

                names=[]      # LIST OF PROCESSES TO BE KILLED...
#A RECORD IS KEPT SO THAT THEY CAN BE RESTARTED......
                for p in psutil.pids():
                    try:
                        p1=psutil.Process(p)
                        if(r in p1.name()):
                            if((p2[0]/(1024*1024))>=int(d)):
                                m=p1.name()
                                m=m.encode('ascii','ignore') # converting unicode object into string object

                                for l in o:
                                    f=0
                                    for root, dirs, files in os.walk(l): # walks through the entire file system of the Windows OS
                                        #f=0
                                        for name in files:
                                            if name==m:
                                                p1.kill()
                                                conn.send("     Finally the process is killed...... ")
                                                f=1
                                                conn.send(str(os.path.abspath(os.path.join(root,name))))
                                                names.append(os.path.abspath(os.path.join(root,name)))
                                                #pst=processThread(1,os.path.abspath(os.path.join(root,name)))
                                                #pst.start()
                                                #subprocess.Popen(str(os.path.abspath(os.path.join(root,name))))
                                                #if(p):
                                                break
                                        if(f==1):
                                            break

                                    if(f==1):
                                        break

                    except psutil.AccessDenied:
                        pass

                    except psutil.NoSuchProcess:
                        pass

                    else:
                        continue

        conn.send("    Now the processes will be restarted after the connection is terminated......" )
    conn.close()  # closes the connection...
        if(names):
            for l in names:
                subprocess.Popen(str(l))


class processThread(threading.Thread): # Thread class for Process Launch
    def __init__(self,threadID,name):
        threading.Thread.__init__(self)
        self.threadID=threadID
        self.name=name

    def run(self):
        subprocess.Popen(str(self.name))



class serverThread(threading.Thread): # Thread class for Server(FOR LISTENING)
    def __init__(self, threadID, name,):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name

    def run(self):   
        threadLock.acquire()
        host=raw_input("Enter the hostname.....")
        HOST=socket.gethostbyname(host)   
        PORT=60000 # specific port available
        s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        print 'Socket created' 
        #Bind socket to local host and port
        try:
            s.bind((HOST, PORT))
        except socket.error as msg:
            print 'Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]
            sys.exit()

        print 'Socket bind complete'
        s.listen(10) # no of connections @ one time
        print self.name+' now listening'
        threadLock.release()

        while 1:
            conn, addr = s.accept() # connection gets established here..
            print 'Connected with ' + addr[0] + ':' + str(addr[1])
            conn.send('Thank you for connecting   ')
            # creates a new thread for the client services and processes the parameter and sends it back to the client
            start_new_thread(proc_info,(conn,addr))

        s.close()

threadLock=threading.Lock()
thread1 =serverThread(1,"Server 1")
thread1.start() # starting the server thread...

Please explain the cause of the problem and its solution(if any).

1

There are 1 answers

0
div On

I am pretty new myself to concurrent programming, but reading the code I get the sense that the process can be killed only once and that happens only for one of the clients. So, an additional check should be introduced which closes the connection if no process linked to this connection is running.