fixed cron deadlock on windows, thanks Helko Besemann

This commit is contained in:
Massimo
2013-09-03 11:53:18 -05:00
parent fd000fe35b
commit e724e13564
4 changed files with 74 additions and 23 deletions
+1 -1
View File
@@ -1 +1 @@
Version 2.6.0-development+timestamp.2013.09.03.09.52.37
Version 2.6.0-development+timestamp.2013.09.03.11.52.13
+9 -5
View File
@@ -48,12 +48,12 @@ _DEBUG = True
_HISTORY_KIND = '_Shell_History'
# Types that can't be pickled.
UNPICKLABLE_TYPES = (
UNPICKLABLE_TYPES = [
types.ModuleType,
types.TypeType,
types.ClassType,
types.FunctionType,
)
]
# Unpicklable statements to seed new historys with.
INITIAL_UNPICKLABLES = [
@@ -244,8 +244,8 @@ def run(history, statement, env={}):
for name, val in statement_module.__dict__.items():
if name not in old_globals or represent(val) != old_globals[name]:
new_globals[name] = val
if True in [isinstance(val, UNPICKLABLE_TYPES)
if True in [isinstance(val, tuple(UNPICKLABLE_TYPES))
for val in new_globals.values()]:
# this statement added an unpicklable global. store the statement and
# the names of all of the globals it added in the unpicklables.
@@ -256,7 +256,11 @@ def run(history, statement, env={}):
# new globals back into the datastore.
for name, val in new_globals.items():
if not name.startswith('__'):
history.set_global(name, val)
try:
history.set_global(name, val)
except TypeError, ex:
UNPICKLABLE_TYPES.append(type(val))
history.add_unpicklable(statement, new_globals.keys())
finally:
sys.modules['__main__'] = old_main
+11 -2
View File
@@ -117,6 +117,10 @@ class Token(object):
if a cron job started before 60 seconds and did not stop,
a warning is issue "Stale cron.master detected"
"""
if sys.platform == 'win32':
locktime = 59.5
else:
locktime = 59.99
if portalocker.LOCK_EX is None:
logger.warning('WEB2PY CRON: Disabled because no file locking')
return None
@@ -128,7 +132,7 @@ class Token(object):
(start, stop) = cPickle.load(self.master)
except:
(start, stop) = (0, 1)
if startup or self.now - start > 59.99:
if startup or self.now - start > locktime:
ret = self.now
if not stop:
# this happens if previous cron job longer than 1 minute
@@ -136,6 +140,7 @@ class Token(object):
logger.debug('WEB2PY CRON: Acquiring lock')
self.master.seek(0)
cPickle.dump((self.now, 0), self.master)
self.master.flush()
finally:
portalocker.unlock(self.master)
if not ret:
@@ -304,7 +309,11 @@ def crondance(applications_parent, ctype='soft', startup=False, apps=None):
for task in tasks:
if _cron_stopping:
break
commands = [sys.executable]
if sys.executable.lower().endswith('pythonservice.exe'):
_python_exe = os.path.join(sys.exec_prefix, 'python.exe')
else:
_python_exe = sys.executable
commands = [_python_exe]
w2p_path = fileutils.abspath('web2py.py', gluon=True)
if os.path.exists(w2p_path):
commands.append(w2p_path)
+53 -15
View File
@@ -36,6 +36,7 @@ class Service(win32serviceutil.ServiceFramework):
_svc_name_ = '_unNamed'
_svc_display_name_ = '_Service Template'
_svc_description_ = '_Service Template description'
def __init__(self, *args):
win32serviceutil.ServiceFramework.__init__(self, *args)
@@ -44,6 +45,12 @@ class Service(win32serviceutil.ServiceFramework):
def log(self, msg):
servicemanager.LogInfoMsg(str(msg))
def log_error(self, msg):
"""
Log an error message to windows application event log.
"""
servicemanager.LogErrorMsg(str(msg))
def SvcDoRun(self):
self.ReportServiceStatus(win32service.SERVICE_START_PENDING)
try:
@@ -80,6 +87,7 @@ class Web2pyService(Service):
_svc_name_ = 'web2py'
_svc_display_name_ = 'web2py Service'
_svc_description_ = 'Web2py application framework service'
_exe_args_ = 'options'
server = None
@@ -132,17 +140,16 @@ class Web2pyService(Service):
server_name=options.server_name,
request_queue_size=options.request_queue_size,
timeout=options.timeout,
socket_timeout=options.socket_timeout,
shutdown_timeout=options.shutdown_timeout,
path=options.folder
path=options.folder,
interfaces=options.interfaces
)
try:
from rewrite import load
load()
self.server.start()
except:
# self.server.stop()
self.server = None
raise
@@ -159,12 +166,18 @@ class Web2pyCronService(Web2pyService):
_svc_name_ = 'web2py_cron'
_svc_display_name_ = 'web2py Cron Service'
_svc_description_ = 'web2py Windows cron service for scheduled tasks'
_exe_args_ = 'options'
def start(self):
import newcron
import global_settings
self.log('web2py server starting')
import logging
import logging.config
from settings import global_settings
from fileutils import abspath
from os.path import exists, join
self.log('web2py Cron service starting')
if not self.chdir():
return
if len(sys.argv) == 2:
@@ -172,25 +185,50 @@ class Web2pyCronService(Web2pyService):
else:
opt_mod = self._exe_args_
options = __import__(opt_mod, [], [], '')
global_settings.global_settings.web2py_crontype = 'external'
logpath = abspath(join(options.folder, "logging.conf"))
if exists(logpath):
logging.config.fileConfig(logpath)
else:
logging.basicConfig()
logger = logging.getLogger("web2py.cron")
global_settings.web2py_crontype = 'external'
if options.scheduler: # -K
apps = [app.strip() for app in options.scheduler.split(
',') if check_existent_app(options, app.strip())]
else:
apps = None
self.extcron = newcron.extcron(options.folder, apps=apps)
try:
self.extcron.start()
except:
# self.server.stop()
self.extcron = None
raise
misfire_gracetime = float(options.misfire_gracetime) if 'misfire_gracetime' in dir(options) else 0.0
logger.info('Starting Window cron service with %0.2f secs gracetime.' % misfire_gracetime)
self._started = True
wait_full_min = lambda: 60 - time.time() % 60
while True:
try:
if wait_full_min() >= misfire_gracetime: # an offset of max. 5 secs before full minute (e.g. time.sleep(60) == 58.99 secs)
self.extcron = newcron.extcron(options.folder, apps=apps)
self.extcron.start()
time.sleep(wait_full_min())
else:
logger.debug('time.sleep() offset detected: %0.3f s' % wait_full_min())
while wait_full_min() <= misfire_gracetime:
pass
if apps != None:
break
if not self._started:
break
except Exception, ex:
self.extcron = None
self.log_error('%s, restarting service.' % ex)
logger.exception('Exception! Restarting Windows cron service.' % ex)
self.start()
def stop(self):
self.log('web2py cron stopping')
self.log('web2py Cron service stopping')
if not self.chdir():
return
if self.extcron:
self._started = False
self.extcron.join()
def register_service_handler(argv=None, opt_file='options', cls=Web2pyService):