New event queue system in place, currently handles shutdown and restart calls.

Fixed ctrl-c issues with new event queue system.
Added a sleep timer to the NameParser class to help lower cpu usage spikes.
This commit is contained in:
echel0n 2014-07-08 15:17:34 -07:00
parent 0866bcc344
commit 74f73bcc34
8 changed files with 123 additions and 98 deletions

View File

@ -21,6 +21,7 @@
from __future__ import with_statement
import time
import signal
import sys
import shutil
import subprocess
@ -52,12 +53,12 @@ if sys.hexversion >= 0x020600F0:
import locale
import datetime
import threading
import signal
import traceback
import getopt
import sickbeard
from sickbeard.event_queue import Events
from sickbeard import db
from sickbeard.tv import TVShow
from sickbeard import logger, network_timezones, failed_history, name_cache
@ -68,14 +69,16 @@ from sickbeard.databases.mainDB import MAX_DB_VERSION
from lib.configobj import ConfigObj
throwaway = datetime.datetime.strptime('20110101', '%Y%m%d')
signal.signal(signal.SIGINT, sickbeard.sig_handler)
signal.signal(signal.SIGTERM, sickbeard.sig_handler)
throwaway = datetime.datetime.strptime('20110101', '%Y%m%d')
class SickRage(object):
def __init__(self):
sickbeard.events = Events(self.shutdown)
self.webserver = None
self.runAsDaemon = False
self.CREATEPID = False
@ -330,7 +333,8 @@ class SickRage(object):
if sickbeard.LAUNCH_BROWSER and not (self.noLaunch or self.runAsDaemon):
sickbeard.launchBrowser(self.startPort)
while(sickbeard.started):
# main loop
while(True):
time.sleep(1)
def daemonize(self):
@ -431,56 +435,54 @@ class SickRage(object):
except:
return False
def shutdown(self, type):
if sickbeard.started:
# stop all tasks
sickbeard.halt()
# shutdown web server
if self.webserver:
self.webserver.shutDown()
self.webserver = None
# save all shows to DB
sickbeard.saveAll()
# if run as daemon delete the pidfile
if self.runAsDaemon and self.CREATEPID:
self.remove_pid_file(self.PIDFILE)
if type == sickbeard.events.SystemEvent.RESTART:
install_type = sickbeard.versionCheckScheduler.action.install_type
popen_list = []
if install_type in ('git', 'source'):
popen_list = [sys.executable, sickbeard.MY_FULLNAME]
elif install_type == 'win':
if hasattr(sys, 'frozen'):
# c:\dir\to\updater.exe 12345 c:\dir\to\sickbeard.exe
popen_list = [os.path.join(sickbeard.PROG_DIR, 'updater.exe'), str(sickbeard.PID), sys.executable]
else:
logger.log(u"Unknown SB launch method, please file a bug report about this", logger.ERROR)
popen_list = [sys.executable, os.path.join(sickbeard.PROG_DIR, 'updater.py'), str(sickbeard.PID),
sys.executable,
sickbeard.MY_FULLNAME]
if popen_list:
popen_list += sickbeard.MY_ARGS
if '--nolaunch' not in popen_list:
popen_list += ['--nolaunch']
logger.log(u"Restarting SickRage with " + str(popen_list))
logger.close()
subprocess.Popen(popen_list, cwd=os.getcwd())
# system exit
os._exit(0)
if __name__ == "__main__":
if sys.hexversion >= 0x020600F0:
freeze_support()
sr = None
try:
# init sickrage
sr = SickRage()
# start sickrage
sr.start()
# shutdown web server
sr.webserver.shutDown()
sr.webserver.join()
sr.webserver = None
# if run as daemon delete the pidfile
if sr.runAsDaemon and sr.CREATEPID:
sr.remove_pid_file(sr.PIDFILE)
if not sickbeard.shutdown:
install_type = sickbeard.versionCheckScheduler.action.install_type
popen_list = []
if install_type in ('git', 'source'):
popen_list = [sys.executable, sickbeard.MY_FULLNAME]
elif install_type == 'win':
if hasattr(sys, 'frozen'):
# c:\dir\to\updater.exe 12345 c:\dir\to\sickbeard.exe
popen_list = [os.path.join(sickbeard.PROG_DIR, 'updater.exe'), str(sickbeard.PID), sys.executable]
else:
logger.log(u"Unknown SB launch method, please file a bug report about this", logger.ERROR)
popen_list = [sys.executable, os.path.join(sickbeard.PROG_DIR, 'updater.py'), str(sickbeard.PID), sys.executable,
sickbeard.MY_FULLNAME]
if popen_list:
popen_list += sickbeard.MY_ARGS
if '--nolaunch' not in popen_list:
popen_list += ['--nolaunch']
logger.log(u"Restarting SickRage with " + str(popen_list))
logger.close()
subprocess.Popen(popen_list, cwd=os.getcwd())
# exit process
os._exit(0)
except:
if sr:
logger.log(traceback.format_exc(), logger.ERROR)
else:
print(traceback.format_exc())
sys.exit(1)
# start sickrage
SickRage().start()

View File

@ -49,7 +49,6 @@ from sickbeard.common import SD, SKIPPED, NAMING_REPEAT
from sickbeard.databases import mainDB, cache_db, failed_db
from lib.configobj import ConfigObj
from tornado.ioloop import IOLoop
import xml.etree.ElementTree as ElementTree
PID = None
@ -75,6 +74,9 @@ PIDFILE = ''
DAEMON = None
NO_RESIZE = False
# system events
events = None
dailySearchScheduler = None
backlogSearchScheduler = None
showUpdateScheduler = None
@ -103,7 +105,6 @@ CUR_COMMIT_HASH = None
INIT_LOCK = Lock()
started = False
shutdown = False
ACTUAL_LOG_DIR = None
LOG_DIR = None
@ -1166,7 +1167,7 @@ def halt():
showUpdateScheduler, versionCheckScheduler, showQueueScheduler, \
properFinderScheduler, autoPostProcesserScheduler, searchQueueScheduler, \
subtitlesFinderScheduler, traktCheckerScheduler, \
dailySearchScheduler, started
dailySearchScheduler, events, started
with INIT_LOCK:
@ -1174,7 +1175,12 @@ def halt():
logger.log(u"Aborting all threads")
# abort all the threads
events.alive = False
logger.log(u"Waiting for the EVENTS thread to exit")
try:
events.join(10)
except:
pass
dailySearchScheduler.abort = True
logger.log(u"Waiting for the DAILYSEARCH thread to exit")
@ -1261,7 +1267,7 @@ def halt():
def sig_handler(signum=None, frame=None):
if type(signum) != type(None):
logger.log(u"Signal %i caught, saving and exiting..." % int(signum))
saveAndShutdown()
events.put(events.SystemEvent.SHUTDOWN)
def saveAll():
global showList
@ -1275,32 +1281,6 @@ def saveAll():
logger.log(u"Saving config file to disk")
save_config()
def saveAndShutdown(restart=False):
global shutdown, started
# flag restart/shutdown
if not restart:
shutdown = True
# proceed with shutdown
started = False
def invoke_command(to_call, *args, **kwargs):
def delegate():
to_call(*args, **kwargs)
logger.log(u"Placed invoked command: " + repr(delegate) + " for " + repr(to_call) + " with " + repr(
args) + " and " + repr(kwargs), logger.DEBUG)
IOLoop.current().add_callback(delegate)
def invoke_restart(soft=True):
invoke_command(restart, soft=soft)
def invoke_shutdown():
invoke_command(saveAndShutdown, False)
def restart(soft=True):
if soft:
halt()
@ -1308,7 +1288,7 @@ def restart(soft=True):
logger.log(u"Re-initializing all data")
initialize()
else:
saveAndShutdown(True)
events.put(events.SystemEvent.RESTART)
def save_config():

45
sickbeard/event_queue.py Normal file
View File

@ -0,0 +1,45 @@
from threading import Thread
from Queue import Queue, Empty
from tornado.ioloop import IOLoop
class Event:
def __init__(self, type):
self._type = type
@property
def type(self):
return self._type
class Events(Thread):
def __init__(self, callback):
super(Events, self).__init__()
self.queue = Queue()
self.daemon = True
self.alive = True
self.callback = callback
self.name = "EVENT-QUEUE"
# auto-start
self.start()
def put(self, type):
self.queue.put_nowait(type)
def run(self):
while(self.alive):
try:
# get event type
type = self.queue.get(True, 1)
# perform callback if we got a event type
self.callback(type)
# event completed
self.queue.task_done()
except Empty:
type = None
# System Events
class SystemEvent(Event):
RESTART = "RESTART"
SHUTDOWN = "SHUTDOWN"

View File

@ -16,6 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with SickRage. If not, see <http://www.gnu.org/licenses/>.
import time
import re
import datetime
import os.path
@ -133,6 +134,8 @@ class NameParser(object):
if self.showObj:
break
else:
time.sleep(0.05)
raise InvalidShowException(
"Unable to parse " + name.encode(sickbeard.SYS_ENCODING, 'xmlcharrefreplace'))
@ -251,6 +254,8 @@ class NameParser(object):
result.score += 1
matches.append(result)
time.sleep(0.05)
if len(matches):
result = max(sorted(matches, reverse=True, key=lambda x: x.which_regex), key=lambda x: x.score)

View File

@ -64,7 +64,7 @@ class CheckVersion():
if sickbeard.versionCheckScheduler.action.update():
logger.log(u"Update was successful!")
ui.notifications.message('Update was successful')
threading.Timer(2, sickbeard.invoke_restart, [False]).start()
sickbeard.events.put(sickbeard.events.SystemEvent.RESTART)
def find_install_type(self):
"""

View File

@ -1524,7 +1524,7 @@ class CMD_SickBeardRestart(ApiCall):
def run(self):
""" restart sickbeard """
threading.Timer(2, sickbeard.invoke_restart, [False]).start()
sickbeard.events.put(sickbeard.events.SystemEvent.RESTART)
return _responds(RESULT_SUCCESS, msg="SickRage is restarting...")
@ -1701,7 +1701,7 @@ class CMD_SickBeardShutdown(ApiCall):
def run(self):
""" shutdown sickbeard """
threading.Timer(2, sickbeard.invoke_shutdown).start()
sickbeard.events.put(sickbeard.events.SystemEvent.SHUTDOWN)
return _responds(RESULT_SUCCESS, msg="SickRage is shutting down...")

View File

@ -3434,7 +3434,7 @@ class Home(MainHandler):
if str(pid) != str(sickbeard.PID):
redirect("/home/")
threading.Timer(2, sickbeard.invoke_shutdown).start()
sickbeard.events.put(sickbeard.events.SystemEvent.SHUTDOWN)
title = "Shutting down"
message = "SickRage is shutting down..."
@ -3450,7 +3450,7 @@ class Home(MainHandler):
t.submenu = HomeMenu()
# restart
threading.Timer(5, sickbeard.invoke_restart, [False]).start()
sickbeard.events.put(sickbeard.events.SystemEvent.RESTART)
return _munge(t)
@ -3462,7 +3462,7 @@ class Home(MainHandler):
updated = sickbeard.versionCheckScheduler.action.update() # @UndefinedVariable
if updated:
# do a hard restart
threading.Timer(2, sickbeard.invoke_restart, [False]).start()
sickbeard.events.put(sickbeard.events.SystemEvent.RESTART)
t = PageTemplate(headers=self.request.headers, file="restart_bare.tmpl")
return _munge(t)

View File

@ -122,13 +122,6 @@ class SRWebServer(threading.Thread):
try:
self.io_loop.start()
self.io_loop.close(True)
# stop all tasks
sickbeard.halt()
# save all shows to DB
sickbeard.saveAll()
except ValueError:
# Ignore errors like "ValueError: I/O operation on closed kqueue fd". These might be thrown during a reload.
pass