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

Reply via email to