# ICE Revision: $Id$
"""Thread wrappers for OpenFOAM"""
import sys
from threading import Thread,Lock,Timer
from PyFoam.ThirdParty.six import print_
from PyFoam.Error import warning,error
from PyFoam import configuration as config
from PyFoam.FoamInformation import shellExecutionPrefix
if sys.version_info<(2,4):
from popen2 import Popen4
else:
import subprocess
from time import time,sleep
try:
from resource import getrusage,getpagesize,RUSAGE_CHILDREN
except ImportError:
try:
from PyFoam.ThirdParty.winhacks import getrusage,getpagesize,RUSAGE_CHILDREN
except ImportError:
error("Unable to import working getrusage,getpagesize,RUSAGE_CHILDREN functions.")
from os import kill,path,unlink
from platform import uname
import signal
from PyFoam.Basics.LineReader import LineReader
from PyFoam.Infrastructure.Logging import foamLogger
[docs]def checkForStopFile(thrd):
"""Checks for the file 'stop' in the directory of the FoamRun. If
it exists it is removed and the run is stopped gracefully
If a file 'write' is found then the next timestep is written
File 'stopWrite' stops the run at the next write
'kill' stops without writing"""
fName=path.join(thrd.runner.dir,"stop")
if path.exists(fName):
unlink(fName)
thrd.runner.stopGracefully()
return
fName=path.join(thrd.runner.dir,"write")
if path.exists(fName):
unlink(fName)
thrd.runner.writeResults()
fName=path.join(thrd.runner.dir,"stopWrite")
if path.exists(fName):
unlink(fName)
thrd.runner.stopAtNextWrite()
fName=path.join(thrd.runner.dir,"kill")
if path.exists(fName):
unlink(fName)
thrd.runner.stopWithoutWrite()
thrd.timer2=Timer(thrd.timerTime,checkForStopFile,args=[thrd])
thrd.timer2.start()
[docs]def getLinuxMem(thrd):
"""Reads the Memory usage of a thread on a linux-System
:param thrd: the thread object in question"""
# print "Timer called"
if not thrd.isLinux or thrd.threadPid<0:
return
mem=0
try:
import psutil
me=psutil.Process(thrd.threadPid)
procs=[me]+me.children(True)
try:
for p in procs:
try:
mInfo=p.memory_full_info()
mem+=mInfo.uss+mInfo.pss # Unique-memory and proportional
# of shared memory. smaller than
# RSS
except AttributeError:
mInfo=p.memory_info()
mem+=mInfo.rss
except psutil.NoSuchProcess:
pass
except ImportError:
try:
t=open('/proc/%d/status' % thrd.threadPid)
v=t.read()
t.close()
# f=open('/tmp/test%dstatus' % thrd.threadPid,'w')
# f.write(v)
# f.close()
i=v.index('VmRSS')
tmp=v[i:].split()
if len(tmp)>=3:
mem=int(tmp[1])
if tmp[2].lower()=='kb':
mem*=1024
elif tmp[2].lower()=='mb':
mem*=1024*1024
else:
mem=-1
except Exception:
e = sys.exc_info()[1] # compatible with 2.x and 3.x
print_("Getting LinuxMem:",e)
mem=-1
if mem>thrd.linuxMaxMem:
# print "Setting Memory to: ",mem
thrd.linuxMaxMem=mem
# print "Restarting Timer"
thrd.timer=Timer(thrd.timerTime,getLinuxMem,args=[thrd])
thrd.timer.start()
[docs]class FoamThread(Thread):
"""Thread running an OpenFOAM command
The output of the command can be accessed in a thread-safe manner,
line by line
Designed to be used by the BasicRunner-class"""
def __init__(self,cmdline,runner):
""":param cmdline:cmdline - Command line of the OpenFOAM command
:param runner: the Runner-object that started this thread"""
Thread.__init__(self)
self.cmdline=cmdline
self.runner=runner
self.output=None
self.reader=LineReader(config().getboolean("SolverOutput","stripSpaces"))
self.keyboardInterupted=False
self.isLinux=False
self.isDarwin=False
self.isWindows=False
self.threadPid=-1
self.who=RUSAGE_CHILDREN
if uname()[0]=="Linux":
self.isLinux=True
self.linuxMaxMem=0
elif uname()[0]=="Darwin":
self.isDarwin=True
elif uname()[0]=="Windows":
self.isWindows=True
self.resStart=None
self.resEnd=None
self.timeStart=None
self.timeEnd=None
self.timerTime=5.
self.stateLock=Lock()
self.setState(False)
self.status=None
self.returncode=None
self.lineLock=Lock()
self.line=""
self.stateLock.acquire()
[docs] def run(self):
"""start the command"""
# print "Starting ",self.cmdline
self.resStart=getrusage(self.who)
self.timeStart=time()
if sys.version_info<(2,4):
run=Popen4(self.cmdline)
self.output=run.fromchild
else:
run=subprocess.Popen(shellExecutionPrefix()+self.cmdline,
shell=True,
bufsize=0,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
close_fds=True)
self.output=run.stdout
self.run=run
self.threadPid=run.pid
foamLogger().info("Started with PID %d" % self.threadPid)
if self.isLinux:
# print "Starting Timer"
self.timer=Timer(0.1*self.timerTime,getLinuxMem,args=[self])
self.timer.start()
# print "Starting Timer"
self.timer2=Timer(0.5*self.timerTime,checkForStopFile,args=[self])
self.timer2.start()
self.hasSomethingToSay=True
self.stateLock.release()
try:
# print "Waiting",time()
self.status=run.wait()
# Python 2.3 on Mac OS X never seems to reach this point
# print "After wait",time()
# print "Status:",self.status
# to give a chance to read the remaining output
if self.hasSomethingToSay:
sleep(2.)
while self.reader.read(self.output):
print_("Unused output:",self.reader.line)
except OSError:
e = sys.exc_info()[1] # compatible with 2.x and 3.x
print_("Exeption caught:",e)
self.stopTimer()
self.threadPid=-1
self.resEnd=getrusage(self.who)
self.timeEnd=time()
# print "End:",self.timeEnd
# print "Returned",self.status
self.getReturnCode()
[docs] def getReturnCode(self):
if sys.version_info<(2,4):
# Don't know how to get the returncode from a Popen4-object
self.returncode=0
else:
self.returncode=self.run.returncode
return self.returncode
[docs] def stopTimer(self):
if self.isLinux:
self.timer.cancel()
self.timer2.cancel()
[docs] def read(self):
"""read another line from the output"""
self.setState(self.reader.read(self.output))
self.lineLock.acquire()
self.line=self.reader.line
self.lineLock.release()
[docs] def getLine(self):
"""gets the last line from the output"""
self.lineLock.acquire()
val=self.line
self.lineLock.release()
return val
[docs] def interrupt(self):
"""A keyboard-interrupt is reported"""
# print "Interrupt"
self.reader.wasInterupted=True
self.setState(False)
[docs] def setState(self,state):
"""sets the state of the thread (is there any more output)"""
self.stateLock.acquire()
self.hasSomethingToSay=state
if not self.hasSomethingToSay and self.timeStart and self.reader.wasInterupted:
self.keyboardInterupted=self.reader.keyboardInterupted
if self.threadPid>0:
msg="Killing PID %d" % self.threadPid
print_(msg)
foamLogger().warning(msg)
try:
kill(self.threadPid,signal.SIGKILL)
except OSError:
warning("Process",self.threadPid,"was already dead")
# print "Set: ",state
self.stateLock.release()
[docs] def check(self):
""":return: False if there is no more output of the command"""
self.stateLock.acquire()
state=self.hasSomethingToSay
# print "Get: ",state
self.stateLock.release()
return state
[docs] def cpuTime(self):
""":return: number of seconds CPU-Time used"""
return self.cpuUserTime()+self.cpuSystemTime()
[docs] def cpuUserTime(self):
""":return: number of seconds CPU-Time used in user mode"""
if self.resEnd==None: # and self.isDarwin:
# Mac OS X needs this (Ubuntu too?)
self.resEnd=getrusage(self.who)
if self.resStart==None or self.resEnd==None:
return 0
else:
return self.resEnd.ru_utime-self.resStart.ru_utime
[docs] def cpuSystemTime(self):
""":return: number of seconds CPU-Time used in system mode"""
if self.resEnd==None: # and self.isDarwin:
# Mac OS X needs this (Ubuntu too?)
self.resEnd=getrusage(self.who)
if self.resStart==None or self.resEnd==None:
return 0
else:
return self.resEnd.ru_stime-self.resStart.ru_stime
[docs] def usedMemory(self):
""":return: maximum resident set size in MegaByte"""
scale=1024.*1024.
if self.isLinux:
return self.linuxMaxMem/scale
if self.resStart==None or self.resEnd==None:
return 0.
else:
return getpagesize()*(self.resEnd.ru_maxrss-self.resStart.ru_maxrss)/scale
[docs] def wallTime(self):
""":return: the wall-clock-time needed by the process"""
if self.timeEnd==None: # and self.isDarwin:
# Mac OS X needs this (Ubuntu too?)
self.timeEnd=time()
self.timeEnd=time()
# print "Wall:",self.timeEnd,self.timeStart
if self.timeStart==None or self.timeEnd==None:
return 0
else:
return self.timeEnd-self.timeStart