Skip to content

Commit

Permalink
v1.0.1 process interrupt working fast and nicely
Browse files Browse the repository at this point in the history
  • Loading branch information
caph1993 committed Jun 23, 2020
1 parent c38633d commit e6d0f6c
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 37 deletions.
2 changes: 1 addition & 1 deletion _cpcp/downloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def download(self, UI, problem, platform):
tool = platform.name
else:
tool = 'fallback'
UI.print(f'\nUsing {tool} downloader...')
UI.print(f'\nUsing {tool} downloader... (ctrl+c to interrupt)')
UI.print('-'*15+'\n')
plugin = self.plugins[tool]
try:
Expand Down
65 changes: 44 additions & 21 deletions _cpcp/utils/_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from threading import Thread, Timer
from queue import Queue, Empty
import sys, time, io, asyncio, threading
from ._interrupt import terminate_thread

ON_POSIX = 'posix' in sys.builtin_module_names

Expand Down Expand Up @@ -74,22 +75,27 @@ def run(self, input=None, timeout=None, check=False,
kwargs.pop('self')
self.kwargs.update(kwargs)
self._start()
interrupt = Thread(target=self._kill,
args=['KeyboardInterrupt'])
try:
#self._sync.wait() was here before but
#the loop is needed for quick handling of
#terminate_thread(thread, KeyboardInterrupt)
while not self._sync.is_set():
time.sleep(1e-4)
except KeyboardInterrupt:
self._stop = True
self._process.kill()
self._error = 'KeyboardInterrupt'
interrupt.start()
except Exception as e:
self._error = str(e)
self._sync.wait()
if check and self.error:
kw = dict(returncode=self.returncode, cmd=self.kwargs.args)
raise CalledProcessError(**kw)
while 1:
try:
self._sync.wait()
if check and self.error:
kw = dict(returncode=self.returncode, cmd=self.kwargs.args)
raise CalledProcessError(**kw)
break
except KeyboardInterrupt:
pass
return self

async def async_run(self, input=None, timeout=None, check=False,
Expand Down Expand Up @@ -139,7 +145,8 @@ def _start(self):

self._threads['waiter'] = Thread(target=self._waiter)
if self._timeout:
self._threads['timer'] = Timer(self._timeout, self._terminate)
self._threads['timer'] = Timer(self._timeout,
self._kill, args=['TimeoutExpired'])

config = {
'out': {
Expand Down Expand Up @@ -211,6 +218,14 @@ def _start(self):
t.start()
return

def _silent_interrupt(func):
def wrapper(self, *args, **kwargs):
try: func(self, *args, **kwargs)
except KeyboardInterrupt: pass
return
wrapper.__name__=func.__name__
return wrapper

def _waiter(self):
try:
while not self._stop:
Expand Down Expand Up @@ -249,13 +264,28 @@ def get_value(buffer):
if self._async: self._async.set()
return

def _terminate(self):
self._threads['timer'].cancel()
self._process.kill()
self._stop = True
self.error = 'TimeoutExpired'
def kill(self):
self._kill('KilledByUser')
while not self._done:
time.sleep(1e-3)
return

def _kill(self, error):
if self.is_active():
self._error = error
if 'timer' in self._threads:
self._threads['timer'].cancel()
self._stop = True
self._process.kill()
for k,t in self._threads.items():
if k!='waiter' and k!='timer':
terminate_thread(t, KeyboardInterrupt)
for k,t in self._threads.items():
if k!='waiter' and k!='timer':
t.join()


@_silent_interrupt
def _non_blocking_reader(self, istream, queues, max_size):
#https://stackoverflow.com/a/4896288/3671939
for line in iter(istream.readline, b''):
Expand All @@ -268,6 +298,7 @@ def _non_blocking_reader(self, istream, queues, max_size):
for q in queues: q.put(line)
return istream.close()

@_silent_interrupt
def _live_handler(self, queue, ostream, flush, wait):
waiting = False
waiting_start = None
Expand Down Expand Up @@ -301,14 +332,6 @@ def _parse_time(self, x, ifNone):
def is_active(self):
return self._done==False

def kill(self):
if self.is_active():
self._stop = True
self._error = 'KilledByUser'
self._process.kill()
while not self._done:
time.sleep(1e-3)


class CustomOStream(io.BufferedWriter):
def __init__(self, write_function, flush_function=None):
Expand Down
24 changes: 10 additions & 14 deletions cpcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,6 @@ def main(self, UI):
self.UI = UI
self.lib = ProblemLibrary()
self.UI.print(f'\nRoot: {os.getcwd()}')
t = Thread(target=version_main, args=[
self.UI, META])
t.setDaemon(True)
t.start()
self.change_problem(escape=False)

self.limits = Dict(time_limit=10,
Expand Down Expand Up @@ -231,13 +227,13 @@ def f(shared, self, args, kwargs):
t = Thread(target=f, args=(shared,self,args,kwargs))
self.UI._interrupt.clear()
t.start()
while not shared['done'] and not self.UI._interrupt.is_set():
while not shared['done']:
time.sleep(1e-3)
if not shared['done']:
self.UI.print('Terminating...')
terminate_thread(t, KeyboardInterrupt)
t.join()
self.UI.print('Terinated')
if self.UI._interrupt.is_set():
self.UI.print('Keyboard interruption...')
terminate_thread(t, KeyboardInterrupt)
self.UI._interrupt.clear()
t.join()
return shared['ret']
wrapper.__name__=func.__name__
return wrapper
Expand Down Expand Up @@ -422,12 +418,9 @@ def xdg_open(self, path, touch=False):
return exists



def main():
global META

META = Dict()
META.version = 'v0.0.7'
META.version = 'v1.1.0'
META.source = os.path.realpath(__file__)
META.srcdir = os.path.dirname(os.path.realpath(__file__))
META.exetag = f'CPCP-{platform.system()}-{platform.machine()}'
Expand Down Expand Up @@ -464,6 +457,9 @@ def main():
t = Thread(target=cpcp.main, args=[UI])
t.setDaemon(True)
t.start()
t = Thread(target=version_main, args=[UI, META])
t.setDaemon(True)
t.start()
flx.run()
sys.exit(0)
return
Expand Down
5 changes: 4 additions & 1 deletion cpcp/cache/version.json
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
{}
{
"v0.0.7": "/home/carlos/Documents/cpcp/CPCP",
"v1.0.0": "/home/carlos/Documents/cpcp/CPCP"
}

0 comments on commit e6d0f6c

Please sign in to comment.