Commit cbd5d29e authored by Sidnei da Silva's avatar Sidnei da Silva

      - Applied patch for
        http://www.zope.org/Collectors/Zope/1527. The patch
        installs a windows 'control handler'. Ctrl+C/Break etc are
        delivered asynchronously (notwithstanding the GIL) to a new
        thread. This new thread triggers a shutdown, then tricks
        asyncore into breaking out of its 'select' call.
parent b4f66a21
......@@ -28,6 +28,13 @@ Zope Changes
Features added
- Applied patch for
http://www.zope.org/Collectors/Zope/1527. The patch
installs a windows "control handler". Ctrl+C/Break etc are
delivered asynchronously (notwithstanding the GIL) to a new
thread. This new thread triggers a shutdown, then tricks
asyncore into breaking out of its 'select' call.
- The ZEO server now records its PID to a file like the ZEO
client. Defaults to $INSTANCE_HOME/var/ZEO.pid, and its
configurable in $INSTANCE_HOME/etc/zeo.conf.
......
......@@ -38,6 +38,68 @@ def shutdown(exit_code,fast = 0):
# enough, but still clean.
_shutdown_timeout = 1.0
def setup_windows_control_handler():
try:
from win32api import SetConsoleCtrlHandler, \
GenerateConsoleCtrlEvent
from win32con import CTRL_C_EVENT, CTRL_BREAK_EVENT, \
CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT, \
CTRL_SHUTDOWN_EVENT
except ImportError:
pass
else:
def interrupt_select():
"""Interrupt a sleeping acyncore 'select' call"""
# What is the right thing to do here?
# asyncore.close_all() works, but I fear that would
# prevent the poll based graceful cleanup code from working.
# This seems to work :)
for fd, obj in asyncore.socket_map.items():
if hasattr(obj, "pull_trigger"):
obj.pull_trigger()
def ctrl_handler(ctrlType):
"""Called by Windows on a new thread whenever a
console control event is raised."""
result = 0
if ctrlType == CTRL_C_EVENT:
# user pressed Ctrl+C or someone did
# GenerateConsoleCtrlEvent
if _shutdown_phase == 0:
print "Shutting down Zope..."
shutdown(0)
interrupt_select()
elif _shutdown_timeout > 1.0:
print "Zope shutdown switching to 'fast'"
shutdown(0, 1)
else:
# Third time around - terminate via
# a CTRL_BREAK_EVENT
GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, 0)
result = 1
elif ctrlType == CTRL_BREAK_EVENT:
# Always let Ctrl+Break force it down.
# Default handler terminates process.
print "Terminating Zope (press Ctrl+C to shutdown cleanly)"
elif ctrlType == CTRL_CLOSE_EVENT:
# Console is about to die.
# CTRL_CLOSE_EVENT gives us 5 seconds before displaying
# the "End process" dialog - so switch directly to 'fast'
shutdown(0, 1)
interrupt_select()
result = 1
elif ctrlType in (CTRL_LOGOFF_EVENT, CTRL_SHUTDOWN_EVENT):
# MSDN says:
# "Note that this signal is received only by services.
# Interactive applications are terminated at logoff, so
# they are not present when the system sends this signal."
# We can therefore ignore it (our service framework
# manages shutdown in this case)
pass
return result
# Install our handler.
SetConsoleCtrlHandler(ctrl_handler)
def loop():
# Run the main loop until someone calls shutdown()
lifetime_loop()
......@@ -46,6 +108,9 @@ def loop():
graceful_shutdown_loop()
def lifetime_loop():
if sys.platform.startswith("win"):
setup_windows_control_handler()
# The main loop. Stay in here until we need to shutdown
map = asyncore.socket_map
timeout = 30.0
......
......@@ -186,6 +186,8 @@ class ZEOServer:
method is called without additional arguments.
"""
if os.name != "posix":
if os.name == "nt":
self.setup_win32_signals()
return
if hasattr(signal, 'SIGXFSZ'):
signal.signal(signal.SIGXFSZ, signal.SIG_IGN) # Special case
......@@ -197,6 +199,48 @@ class ZEOServer:
method()
signal.signal(sig, wrapper)
def setup_win32_signals(self):
try:
from win32api import SetConsoleCtrlHandler
import win32con # our handler uses this
except ImportError:
warn("no pywin32 extensions - can't install ctrl+c handler")
else:
SetConsoleCtrlHandler(self._win32_ctrl_handler)
# And borrow the Zope Signals module to get a log reopen handler.
from Signals.WinSignalHandler import SignalHandler
from Signals.Signals import logfileReopenHandler
SIGUSR2 = 12
SignalHandler.registerHandler(SIGUSR2, logfileReopenHandler)
def _win32_ctrl_handler(self, ctrlType):
"""Called by Windows on a new thread whenever a
console control event is raised."""
from win32con import CTRL_C_EVENT, CTRL_BREAK_EVENT, \
CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT, \
CTRL_SHUTDOWN_EVENT
import asyncore
result = 0
# Note we probably don't want to raise SystemExit from
# this thread - pywin32-203 at least calls PyErr_Print,
# which will still terminate us (but print a message
# about the callback failing)
if ctrlType == CTRL_C_EVENT:
# user pressed Ctrl+C or someone did
# GenerateConsoleCtrlEvent
info("terminated by CTRL_C_EVENT")
asyncore.close_all()
# Default will raise KeyboardInterrupt - we don't need that
elif ctrlType == CTRL_BREAK_EVENT:
info("terminated by CTRL_BREAK_EVENT")
asyncore.close_all()
# Default handler terminates process - result remains 0
elif ctrlType == CTRL_CLOSE_EVENT:
info("terminated by CTRL_CLOSE_EVENT")
asyncore.close_all()
result = 1
return result
def create_server(self):
from ZEO.StorageServer import StorageServer
self.server = StorageServer(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment