Author: tack
Date: Mon Feb 11 17:45:35 2008
New Revision: 3047
Log:
A bit of refactoring; kaa.utils.daemonize() will now create a new thread
notifier pipe if necessary; kaa.main.run() will not raise a 'Mainloop
already running' exception if we've forked and want to start a new loop.
Modified:
trunk/base/src/notifier/main.py
trunk/base/src/notifier/thread.py
trunk/base/src/utils.py
Modified: trunk/base/src/notifier/main.py
==============================================================================
--- trunk/base/src/notifier/main.py (original)
+++ trunk/base/src/notifier/main.py Mon Feb 11 17:45:35 2008
@@ -52,8 +52,9 @@
# get logging object
log = logging.getLogger('notifier')
-# variable to check if the notifier is running
-_running = False
+# pid of the process running the notifier loop. This lets run() know
+# if we've just forked and we want to run a new loop.
+_running_pid = None
# Set if currently in shutdown() (to prevent reentrancy)
_shutting_down = False
@@ -86,13 +87,12 @@
Notifier main loop function. It will loop until an exception
is raised or sys.exit is called.
"""
- global _running
unhandled_exception = None
if is_running():
raise RuntimeError('Mainthread is already running')
- _running = True
+ _set_running(True)
set_as_mainthread()
while True:
@@ -118,7 +118,7 @@
unhandled_exception = sys.exc_info()
break
- _running = False
+ _set_running(False)
stop()
if unhandled_exception:
# We aborted the main loop due to an unhandled exception. Now
@@ -135,7 +135,7 @@
"""
global _shutting_down
- if _running:
+ if is_running():
# notifier loop still running, send system exit
log.info('Stop notifier loop')
raise SystemExit
@@ -186,7 +186,7 @@
"""
Return if the main loop is currently running.
"""
- return _running
+ return _running_pid == os.getpid()
def is_shutting_down():
@@ -201,8 +201,11 @@
Set running status. This function is only for the thread based notifier
since it does not call run().
"""
- global _running
- _running = status
+ global _running_pid
+ if status:
+ _running_pid = os.getpid()
+ else:
+ _running_pid = None
def _shutdown_check(*args):
@@ -212,14 +215,13 @@
# can't call the shutdown handler. This is not a perfect
# solution, e.g. with the generic notifier you can do
# stuff after kaa.main.run() which is not possible with gtk
- global _running
- if _running:
+ if is_running():
# If the kaa mainthread (i.e. thread the mainloop is running in)
# is not the program's main thread, then is_mainthread() will be False
# and we don't need to set running=False since shutdown() will raise a
# SystemExit and things will exit normally.
if is_mainthread():
- _running = False
+ _set_running(False)
stop()
Modified: trunk/base/src/notifier/thread.py
==============================================================================
--- trunk/base/src/notifier/thread.py (original)
+++ trunk/base/src/notifier/thread.py Mon Feb 11 17:45:35 2008
@@ -47,7 +47,7 @@
# -----------------------------------------------------------------------------
__all__ = [ 'MainThreadCallback', 'ThreadCallback', 'is_mainthread',
- 'wakeup', 'set_as_mainthread' ]
+ 'wakeup', 'set_as_mainthread', '_create_thread_notifier_pipe' ]
# python imports
import sys
@@ -103,13 +103,7 @@
return in_progress
self.lock.acquire(False)
-
- _thread_notifier_lock.acquire()
- _thread_notifier_queue.insert(0, (self, args, kwargs, in_progress))
- if len(_thread_notifier_queue) == 1:
- if _thread_notifier_pipe:
- os.write(_thread_notifier_pipe[1], "1")
- _thread_notifier_lock.release()
+ _thread_notifier_queue_callback(self, args, kwargs, in_progress)
# TODO: this is deprecated, caller should use wait() on the InProgress
# we return (when set_async(False) isn't called). This is also broken
@@ -217,6 +211,7 @@
# For MainThread* callbacks. The pipe will be created when it is used the first
# time. This solves a nasty bug when you fork() into a second notifier based
# process without exec. If you have this pipe, communication will go wrong.
+# (kaa.utils.daemonize does not have this problem.)
_thread_notifier_pipe = None
@@ -228,26 +223,57 @@
os.write(_thread_notifier_pipe[1], "1")
+def _create_thread_notifier_pipe(new = True, purge = False):
+ """
+ Creates a new pipe for the thread notifier. If new is True, a new pipe
+ will always be created; if it is False, it will only be created if one
+ already exists. If purge is True, any previously queued work will be
+ discarded.
+
+ This is an internal function, but we export it for kaa.utils.daemonize.
+ """
+ global _thread_notifier_pipe
+ log.info('create thread notifier pipe')
+
+ if not _thread_notifier_pipe and not new:
+ return
+ elif _thread_notifier_pipe:
+ # There is an existing pipe already, so stop monitoring it.
+ notifier.socket_remove(_thread_notifier_pipe[0])
+
+ if purge:
+ del _thread_notifier_queue[:]
+
+ _thread_notifier_pipe = os.pipe()
+ fcntl.fcntl(_thread_notifier_pipe[0], fcntl.F_SETFL, os.O_NONBLOCK)
+ fcntl.fcntl(_thread_notifier_pipe[1], fcntl.F_SETFL, os.O_NONBLOCK)
+ notifier.socket_add(_thread_notifier_pipe[0], _thread_notifier_run_queue)
+
+ if _thread_notifier_queue:
+ # A thread is already running and wanted to run something in the
+ # mainloop before the mainloop is started. In that case we need
+ # to wakeup the loop ASAP to handle the requests.
+ os.write(_thread_notifier_pipe[1], "1")
+
+
def set_as_mainthread():
global _thread_notifier_mainthread
- global _thread_notifier_pipe
_thread_notifier_mainthread = threading.currentThread()
- # Make sure we have a pipe between the mainloop and threads. Since loop()
- # calls set_as_mainthread it is safe to assume the loop is
- # connected correctly. If someone calls step() without loop() and
- # without set_as_mainthread inter-thread communication does
- # not work.
if not _thread_notifier_pipe:
- log.info('create thread notifier pipe')
- _thread_notifier_pipe = os.pipe()
- fcntl.fcntl(_thread_notifier_pipe[0], fcntl.F_SETFL, os.O_NONBLOCK)
- fcntl.fcntl(_thread_notifier_pipe[1], fcntl.F_SETFL, os.O_NONBLOCK)
- notifier.socket_add(_thread_notifier_pipe[0],
_thread_notifier_run_queue)
- if _thread_notifier_queue:
- # A thread is already running and wanted to run something in the
- # mainloop before the mainloop is started. In that case we need
- # to wakeup the loop ASAP to handle the requests.
+ # Make sure we have a pipe between the mainloop and threads. Since
+ # loop() calls set_as_mainthread it is safe to assume the loop is
+ # connected correctly. If someone calls step() without loop() and
+ # without set_as_mainthread inter-thread communication does not work.
+ _create_thread_notifier_pipe()
+
+
+def _thread_notifier_queue_callback(callback, args, kwargs, in_progress):
+ _thread_notifier_lock.acquire()
+ _thread_notifier_queue.append((callback, args, kwargs, in_progress))
+ if len(_thread_notifier_queue) == 1:
+ if _thread_notifier_pipe:
os.write(_thread_notifier_pipe[1], "1")
+ _thread_notifier_lock.release()
def _thread_notifier_run_queue(fd):
@@ -265,7 +291,7 @@
while _thread_notifier_queue:
_thread_notifier_lock.acquire()
- callback, args, kwargs, in_progress = _thread_notifier_queue.pop()
+ callback, args, kwargs, in_progress = _thread_notifier_queue.pop(0)
_thread_notifier_lock.release()
try:
@@ -273,6 +299,12 @@
except:
in_progress.throw(*sys.exc_info())
+ # We must test if the in_progress is finished even though we called
+ # finished() or throw() on it above, because if the callback returns
+ # another InProgress object, in_progress is linked to it but it is
+ # not finished, and so we mustn't wake the caller.
+ # XXX: but this is needed only for deprecated functionality anyway
+ # (set_async) and can be removed in the future.
if in_progress.is_finished():
callback._wakeup()
Modified: trunk/base/src/utils.py
==============================================================================
--- trunk/base/src/utils.py (original)
+++ trunk/base/src/utils.py Mon Feb 11 17:45:35 2008
@@ -38,6 +38,7 @@
import kaa
import _utils
+from notifier.thread import _create_thread_notifier_pipe
# get logging object
log = logging.getLogger('kaa')
@@ -121,8 +122,7 @@
os.waitpid(pid, 0)
return pid
except OSError, e:
- log.error("Initial daemonize fork failed: %d, %s\n",
- e.errno, e.strerror)
+ log.error("Initial daemonize fork failed: %d, %s\n", e.errno,
e.strerror)
sys.exit(1)
os.chdir("/")
@@ -145,15 +145,17 @@
stdout = file(stdout, 'a+')
stderr = file(stderr, 'a+', 0)
if pidfile:
- pidfile = file(pidfile, 'w+')
- pidfile.write("%d\n" % os.getpid())
- pidfile.close()
+ file(pidfile, 'w+').write("%d\n" % os.getpid())
# Remap standard fds.
os.dup2(stdin.fileno(), sys.stdin.fileno())
os.dup2(stdout.fileno(), sys.stdout.fileno())
os.dup2(stderr.fileno(), sys.stderr.fileno())
+ # Replace any existing thread notifier pipe, otherwise we'll be listening
+ # to our parent's thread notifier.
+ _create_thread_notifier_pipe(new=False, purge=True)
+
return lock
-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
Freevo-cvslog mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/freevo-cvslog