Author: duncan
Date: Sun Feb 18 14:24:56 2007
New Revision: 9242

Modified:
   branches/rel-1/freevo/src/childapp.py
   branches/rel-1/freevo/src/helpers/recordserver.py
   branches/rel-1/freevo/src/osd.py
   branches/rel-1/freevo/src/plugins/rom_drives.py
   branches/rel-1/freevo/src/rc.py
   branches/rel-1/freevo/src/tv/channels.py
   branches/rel-1/freevo/src/util/Rendezvous.py
   branches/rel-1/freevo/src/util/popen3.py

Log:
Added a try finally block around all lock acquire lock release blocks.
This is a safety measure to ensure that there a no deadlocks caused by a 
missing release


Modified: branches/rel-1/freevo/src/childapp.py
==============================================================================
--- branches/rel-1/freevo/src/childapp.py       (original)
+++ branches/rel-1/freevo/src/childapp.py       Sun Feb 18 14:24:56 2007
@@ -181,75 +181,76 @@
             return
 
         self.lock.acquire()
-        # maybe child is dead and only waiting?
-        if self.wait():
-            _debug_('done the easy way', 2)
-            self.child = None
-            if not self.infile.closed:
-                self.infile.close()
-            self.lock.release()
-            return
+        try:
+            # maybe child is dead and only waiting?
+            if self.wait():
+                _debug_('done the easy way', 2)
+                self.child = None
+                if not self.infile.closed:
+                    self.infile.close()
+                return
 
-        if signal:
-            _debug_('childapp: killing pid %s signal %s' % (self.child.pid, 
signal))
-            try:
-                os.kill(self.child.pid, signal)
-            except OSError:
-                pass
+            if signal:
+                _debug_('childapp: killing pid %s signal %s' % 
(self.child.pid, signal))
+                try:
+                    os.kill(self.child.pid, signal)
+                except OSError:
+                    pass
 
-        _debug_('childapp: Before wait(%s)' % self.child.pid)
-        for i in range(60):
-            if self.wait():
-                break
-            time.sleep(0.1)
-        else:
-            print 'force killing with signal 9'
-            try:
-                os.kill(self.child.pid, 9)
-            except OSError:
-                pass
-            for i in range(20):
+            _debug_('childapp: Before wait(%s)' % self.child.pid)
+            for i in range(60):
                 if self.wait():
                     break
                 time.sleep(0.1)
-        _debug_('childapp: After wait()')
+            else:
+                print 'force killing with signal 9'
+                try:
+                    os.kill(self.child.pid, 9)
+                except OSError:
+                    pass
+                for i in range(20):
+                    if self.wait():
+                        break
+                    time.sleep(0.1)
+            _debug_('childapp: After wait()')
 
 
-        # now check if the app is really dead. If it is, outfile
-        # should be closed by the reading thread
-        for i in range(5):
-            if self.outfile.closed:
-                break
-            time.sleep(0.1)
-        else:
-            # Problem: the program had more than one thread, each thread has a
-            # pid. We killed only a part of the program. The filehandles are
-            # still open, the program still lives. If we try to close the 
infile
-            # now, Freevo will be dead.
-            # Solution: there is no good one, let's try killall on the binary. 
It's
-            # ugly but it's the _only_ way to stop this nasty app
-            print 'Oops, command refuses to die, try bad hack....'
-            util.killall(self.binary, sig=15)
-            for i in range(20):
+            # now check if the app is really dead. If it is, outfile
+            # should be closed by the reading thread
+            for i in range(5):
                 if self.outfile.closed:
                     break
                 time.sleep(0.1)
             else:
-                # still not dead. Puh, something is realy broekn here.
-                # Try killall -9 as last chance
-                print 'Try harder to kill the app....'
-                util.killall(self.binary, sig=9)
+                # Problem: the program had more than one thread, each thread 
has a
+                # pid. We killed only a part of the program. The filehandles 
are
+                # still open, the program still lives. If we try to close the 
infile
+                # now, Freevo will be dead.
+                # Solution: there is no good one, let's try killall on the 
binary. It's
+                # ugly but it's the _only_ way to stop this nasty app
+                print 'Oops, command refuses to die, try bad hack....'
+                util.killall(self.binary, sig=15)
                 for i in range(20):
                     if self.outfile.closed:
                         break
                     time.sleep(0.1)
                 else:
-                    # Oops...
-                    print 'PANIC'
-            if not self.infile.closed:
-                self.infile.close()
-        self.child = None
-        self.lock.release()
+                    # still not dead. Puh, something is realy broekn here.
+                    # Try killall -9 as last chance
+                    print 'Try harder to kill the app....'
+                    util.killall(self.binary, sig=9)
+                    for i in range(20):
+                        if self.outfile.closed:
+                            break
+                        time.sleep(0.1)
+                    else:
+                        # Oops...
+                        print 'PANIC'
+                if not self.infile.closed:
+                    self.infile.close()
+            self.child = None
+        finally:
+            self.lock.release()
 
 
 

Modified: branches/rel-1/freevo/src/helpers/recordserver.py
==============================================================================
--- branches/rel-1/freevo/src/helpers/recordserver.py   (original)
+++ branches/rel-1/freevo/src/helpers/recordserver.py   Sun Feb 18 14:24:56 2007
@@ -1203,8 +1203,8 @@
 
     def xmlrpc_isPlayerRunning(self):
         (status, message) = (FALSE, 'RecordServer::isPlayerRunning: cannot 
acquire lock')
+        self.lock.acquire()
         try:
-            self.lock.acquire()
             status = self.isPlayerRunning()
             message = status and 'player is running' or 'player is not running'
         finally:
@@ -1213,6 +1213,7 @@
 
     def xmlrpc_isRecording(self):
         (status, message) = (FALSE, 'RecordServer::isRecording: cannot acquire 
lock')
+        self.lock.acquire()
         try:
             status = self.isRecording()
             message = status and 'is recording' or 'is not recording'
@@ -1222,8 +1223,8 @@
 
     def xmlrpc_findNextProgram(self):
         (status, message) = (FALSE, 'RecordServer::findNextProgram: cannot 
acquire lock')
+        self.lock.acquire()
         try:
-            self.lock.acquire()
             response = self.findNextProgram()
             status = response != None
             return (status, jellyToXML(response))
@@ -1233,8 +1234,8 @@
 
     def xmlrpc_getScheduledRecordings(self):
         (status, message) = (FALSE, 'RecordServer::getScheduledRecordings: 
cannot acquire lock')
+        self.lock.acquire()
         try:
-            self.lock.acquire()
             return (TRUE, jellyToXML(self.getScheduledRecordings()))
         finally:
             self.lock.release()
@@ -1243,8 +1244,8 @@
 
     def xmlrpc_saveScheduledRecordings(self, scheduledRecordings=None):
         (status, message) = (FALSE, 'RecordServer::saveScheduledRecordings: 
cannot acquire lock')
+        self.lock.acquire()
         try:
-            self.lock.acquire()
             status = self.saveScheduledRecordings(scheduledRecordings)
             message = status and 'saveScheduledRecordings::success' or 
'saveScheduledRecordings::failure'
         finally:
@@ -1257,8 +1258,8 @@
             return (FALSE, 'RecordServer::scheduleRecording:  no prog')
 
         (status, message) = (FALSE, 'RecordServer::scheduleRecording: cannot 
acquire lock')
+        self.lock.acquire()
         try:
-            self.lock.acquire()
             prog = unjellyFromXML(prog)
             (status, response) = self.scheduleRecording(prog)
             message = 'RecordServer::scheduleRecording: %s' % response
@@ -1272,8 +1273,8 @@
             return (FALSE, 'RecordServer::removeScheduledRecording:  no prog')
 
         (status, message) = (FALSE, 'RecordServer::removeScheduledRecording: 
cannot acquire lock')
+        self.lock.acquire()
         try:
-            self.lock.acquire()
             prog = unjellyFromXML(prog)
             (status, response) = self.removeScheduledRecording(prog)
             message = 'RecordServer::removeScheduledRecording: %s' % response
@@ -1287,8 +1288,8 @@
             return (FALSE, 'removeScheduledRecording::failure:  no prog')
 
         (status, message) = (FALSE, 'RecordServer::removeScheduledRecording: 
cannot acquire lock')
+        self.lock.acquire()
         try:
-            self.lock.acquire()
             prog = unjellyFromXML(prog)
             if schedule:
                 schedule = unjellyFromXML(schedule)
@@ -1301,8 +1302,8 @@
 
     def xmlrpc_findProg(self, chan, start):
         (status, message) = (FALSE, 'RecordServer::findProg: cannot acquire 
lock')
+        self.lock.acquire()
         try:
-            self.lock.acquire()
             (status, response) = self.findProg(chan, start)
             message = status and jellyToXML(response) or 
('RecordServer::findProg: %s' % response)
         finally:
@@ -1312,8 +1313,8 @@
 
     def xmlrpc_findMatches(self, find, movies_only):
         (status, message) = (FALSE, 'RecordServer::findMatches: cannot acquire 
lock')
+        self.lock.acquire()
         try:
-            self.lock.acquire()
             (status, response) = self.findMatches(find, movies_only)
             message = status and jellyToXML(response) or 
('RecordServer::findMatches: %s' % response)
         finally:
@@ -1323,8 +1324,8 @@
 
     def xmlrpc_echotest(self, blah):
         (status, message) = (FALSE, 'RecordServer::echotest: cannot acquire 
lock')
+        self.lock.acquire()
         try:
-            self.lock.acquire()
             (status, message) = (TRUE, 'RecordServer::echotest: %s' % blah)
         finally:
             self.lock.release()
@@ -1333,8 +1334,8 @@
 
     def xmlrpc_addFavorite(self, name, prog, exactchan=FALSE, exactdow=FALSE, 
exacttod=FALSE):
         (status, message) = (FALSE, 'RecordServer::addFavorite: cannot acquire 
lock')
+        self.lock.acquire()
         try:
-            self.lock.acquire()
             prog = unjellyFromXML(prog)
             (status, response) = self.addFavorite(name, prog, exactchan, 
exactdow, exacttod)
             message = 'RecordServer::addFavorite: %s' % response
@@ -1345,8 +1346,8 @@
 
     def xmlrpc_addEditedFavorite(self, name, title, chan, dow, mod, priority, 
allowDuplicates, onlyNew):
         (status, message) = (FALSE, 'RecordServer::addEditedFavorite: cannot 
acquire lock')
+        self.lock.acquire()
         try:
-            self.lock.acquire()
             (status, response) = self.addEditedFavorite(unjellyFromXML(name), \
             unjellyFromXML(title), chan, dow, mod, priority, allowDuplicates, 
onlyNew)
             message = 'RecordServer::addEditedFavorite: %s' % response
@@ -1357,8 +1358,8 @@
 
     def xmlrpc_removeFavorite(self, name=None):
         (status, message) = (FALSE, 'RecordServer::removeFavorite: cannot 
acquire lock')
+        self.lock.acquire()
         try:
-            self.lock.acquire()
             (status, response) = self.removeFavorite(name)
             message = 'RecordServer::removeFavorite: %s' % response
         finally:
@@ -1368,8 +1369,8 @@
 
     def xmlrpc_clearFavorites(self):
         (status, message) = (FALSE, 'RecordServer::clearFavorites: cannot 
acquire lock')
+        self.lock.acquire()
         try:
-            self.lock.acquire()
             (status, response) = self.clearFavorites()
             message = 'RecordServer::clearFavorites: %s' % response
         finally:
@@ -1379,8 +1380,8 @@
 
     def xmlrpc_getFavorites(self):
         (status, message) = (FALSE, 'RecordServer::getFavorites: cannot 
acquire lock')
+        self.lock.acquire()
         try:
-            self.lock.acquire()
             (status, message) = (TRUE, 
jellyToXML(self.getScheduledRecordings().getFavorites()))
         finally:
             self.lock.release()
@@ -1389,8 +1390,8 @@
 
     def xmlrpc_getFavorite(self, name):
         (status, message) = (FALSE, 'RecordServer::getFavorite: cannot acquire 
lock')
+        self.lock.acquire()
         try:
-            self.lock.acquire()
             (status, response) = self.getFavorite(name)
             message = status and jellyToXML(response) or 
'RecordServer::getFavorite: %s' % response
         finally:
@@ -1400,8 +1401,8 @@
 
     def xmlrpc_adjustPriority(self, favname, mod=0):
         (status, message) = (FALSE, 'RecordServer::adjustPriority: cannot 
acquire lock')
+        self.lock.acquire()
         try:
-            self.lock.acquire()
             (status, response) = self.adjustPriority(favname, mod)
             message = 'RecordServer::adjustPriority: %s' % response
         finally:
@@ -1411,8 +1412,8 @@
 
     def xmlrpc_isProgAFavorite(self, prog, favs=None):
         (status, message) = (FALSE, 'RecordServer::adjustPriority: cannot 
acquire lock')
+        self.lock.acquire()
         try:
-            self.lock.acquire()
             prog = unjellyFromXML(prog)
             if favs:
                 favs = unjellyFromXML(favs)
@@ -1425,8 +1426,8 @@
 
     def xmlrpc_removeFavoriteFromSchedule(self, fav):
         (status, message) = (FALSE, 'RecordServer::removeFavoriteFromSchedule: 
cannot acquire lock')
+        self.lock.acquire()
         try:
-            self.lock.acquire()
             (status, response) = self.removeFavoriteFromSchedule(fav)
             message = 'RecordServer::removeFavoriteFromSchedule: %s' % response
         finally:
@@ -1436,8 +1437,8 @@
 
     def xmlrpc_addFavoriteToSchedule(self, fav):
         (status, message) = (FALSE, 'RecordServer::addFavoriteToSchedule: 
cannot acquire lock')
+        self.lock.acquire()
         try:
-            self.lock.acquire()
             (status, response) = self.addFavoriteToSchedule(fav)
             message = 'RecordServer::addFavoriteToSchedule: %s' % response
         finally:
@@ -1447,8 +1448,8 @@
 
     def xmlrpc_updateFavoritesSchedule(self):
         (status, message) = (FALSE, 'updateFavoritesSchedule: cannot acquire 
lock')
+        self.lock.acquire()
         try:
-            self.lock.acquire()
             (status, response) = self.updateFavoritesSchedule()
             message = 'RecordServer::updateFavoritesSchedule: %s' % response
         finally:

Modified: branches/rel-1/freevo/src/osd.py
==============================================================================
--- branches/rel-1/freevo/src/osd.py    (original)
+++ branches/rel-1/freevo/src/osd.py    Sun Feb 18 14:24:56 2007
@@ -248,10 +248,12 @@
         
     def wait(self, timer):
         self.lock.acquire()
-        self.active = True
-        self.timer  = timer
-        self.mode_flag.set()
-        self.lock.release()
+        try:
+            self.active = True
+            self.timer  = timer
+            self.mode_flag.set()
+        finally:
+            self.lock.release()
         
     def stop(self):
         self.lock.acquire()
@@ -268,26 +270,28 @@
             if self.active:
                 import skin
                 self.lock.acquire()
-                osd = get_singleton()
-                icon = skin.get_icon('misc/osd_busy')
-                if icon:
-                    image  = osd.loadbitmap(icon)
-                    width  = image.get_width()
-                    height = image.get_height()
-                    x = osd.width  - config.OSD_OVERSCAN_X - 20 - width
-                    y = osd.height - config.OSD_OVERSCAN_Y - 20 - height
-
-                    self.rect = (x,y,width,height)
-                    # backup the screen
-                    screen = pygame.Surface((width,height))
-                    screen.blit(osd.screen, (0,0), self.rect)
-                    # draw the icon
-                    osd.drawbitmap(image, x, y)
-                    osd.update(rect=self.rect, stop_busyicon=False)
-
-                    # restore the screen
-                    osd.screen.blit(screen, (x,y))
-                self.lock.release()
+                try:
+                    osd = get_singleton()
+                    icon = skin.get_icon('misc/osd_busy')
+                    if icon:
+                        image  = osd.loadbitmap(icon)
+                        width  = image.get_width()
+                        height = image.get_height()
+                        x = osd.width  - config.OSD_OVERSCAN_X - 20 - width
+                        y = osd.height - config.OSD_OVERSCAN_Y - 20 - height
+
+                        self.rect = (x,y,width,height)
+                        # backup the screen
+                        screen = pygame.Surface((width,height))
+                        screen.blit(osd.screen, (0,0), self.rect)
+                        # draw the icon
+                        osd.drawbitmap(image, x, y)
+                        osd.update(rect=self.rect, stop_busyicon=False)
+
+                        # restore the screen
+                        osd.screen.blit(screen, (x,y))
+                finally:
+                    self.lock.release()
                 
             while self.active:
                 time.sleep(0.01)

Modified: branches/rel-1/freevo/src/plugins/rom_drives.py
==============================================================================
--- branches/rel-1/freevo/src/plugins/rom_drives.py     (original)
+++ branches/rel-1/freevo/src/plugins/rom_drives.py     Sun Feb 18 14:24:56 2007
@@ -691,18 +691,20 @@
             return
         
         self.lock.acquire()
-        for media in config.REMOVABLE_MEDIA:
-            last_status = media.drive_status
-            self.identify(media)
-
-            if last_status != media.drive_status:
-                _debug_('MEDIA: Status=%s' % media.drive_status,2)
-                _debug_('Posting IDENTIFY_MEDIA event',2)
-                if last_status == None:
-                    rc.post_event(plugin.event('IDENTIFY_MEDIA', arg=(media, 
True)))
-                else:
-                    rc.post_event(plugin.event('IDENTIFY_MEDIA', arg=(media, 
False)))
-        self.lock.release()
+        try:
+            for media in config.REMOVABLE_MEDIA:
+                last_status = media.drive_status
+                self.identify(media)
+
+                if last_status != media.drive_status:
+                    _debug_('MEDIA: Status=%s' % media.drive_status,2)
+                    _debug_('Posting IDENTIFY_MEDIA event',2)
+                    if last_status == None:
+                        rc.post_event(plugin.event('IDENTIFY_MEDIA', 
arg=(media, True)))
+                    else:
+                        rc.post_event(plugin.event('IDENTIFY_MEDIA', 
arg=(media, False)))
+        finally:
+            self.lock.release()
 
                 
     def __init__(self):

Modified: branches/rel-1/freevo/src/rc.py
==============================================================================
--- branches/rel-1/freevo/src/rc.py     (original)
+++ branches/rel-1/freevo/src/rc.py     Sun Feb 18 14:24:56 2007
@@ -511,11 +511,13 @@
         add event to the queue
         """
         self.lock.acquire()
-        if not isinstance(e, Event):
-            self.queue += [ Event(e, context=self.context) ]
-        else:
-            self.queue += [ e ]
-        self.lock.release()
+        try:
+            if not isinstance(e, Event):
+                self.queue += [ Event(e, context=self.context) ]
+            else:
+                self.queue += [ e ]
+        finally:
+            self.lock.release()
 
         if self.event_callback:
             self.event_callback()
@@ -549,14 +551,16 @@
         timer:  timer * 0.01 seconds when to call the function
         """
         self.lock.acquire()
-        if timer == SHUTDOWN:
-            _debug_('register shutdown callback: %s' % function, 2)
-            self.shutdown_callbacks.append([ function, arg ])
-        else:
-            if repeat:
-                _debug_('register callback: %s' % function, 2)
-            self.callbacks.append([ function, repeat, timer, 0, arg ])
-        self.lock.release()
+        try:
+            if timer == SHUTDOWN:
+                _debug_('register shutdown callback: %s' % function, 2)
+                self.shutdown_callbacks.append([ function, arg ])
+            else:
+                if repeat:
+                    _debug_('register callback: %s' % function, 2)
+                self.callbacks.append([ function, repeat, timer, 0, arg ])
+        finally:
+            self.lock.release()
 
         
     def unregister(self, function):
@@ -564,15 +568,17 @@
         unregister an object from the main loop
         """
         self.lock.acquire()
-        for c in copy.copy(self.callbacks):
-            if c[0] == function:
-                _debug_('unregister callback: %s' % function, 2)
-                self.callbacks.remove(c)
-        for c in copy.copy(self.shutdown_callbacks):
-            if c[0] == function:
-                _debug_('unregister shutdown callback: %s' % function, 2)
-                self.shutdown_callbacks.remove(c)
-        self.lock.release()
+        try:
+            for c in copy.copy(self.callbacks):
+                if c[0] == function:
+                    _debug_('unregister callback: %s' % function, 2)
+                    self.callbacks.remove(c)
+            for c in copy.copy(self.shutdown_callbacks):
+                if c[0] == function:
+                    _debug_('unregister shutdown callback: %s' % function, 2)
+                    self.shutdown_callbacks.remove(c)
+        finally:
+            self.lock.release()
 
         
     def suspend(self):
@@ -607,9 +613,11 @@
                 if not c[1]:
                     # remove if it is no repeat callback:
                     self.lock.acquire()
-                    if c in self.callbacks: 
-                        self.callbacks.remove(c)
-                    self.lock.release()
+                    try:
+                        if c in self.callbacks: 
+                            self.callbacks.remove(c)
+                    finally:
+                        self.lock.release()
                 else:
                     # reset counter for next run
                     c[3] = 0

Modified: branches/rel-1/freevo/src/tv/channels.py
==============================================================================
--- branches/rel-1/freevo/src/tv/channels.py    (original)
+++ branches/rel-1/freevo/src/tv/channels.py    Sun Feb 18 14:24:56 2007
@@ -80,8 +80,8 @@
         """
         Gets the VideoGroup object used by this Freevo channel.
         """
+        self.lock.acquire()
         try:
-            self.lock.acquire()
             group = 0
 
             for i in range(len(config.TV_CHANNELS)):

Modified: branches/rel-1/freevo/src/util/Rendezvous.py
==============================================================================
--- branches/rel-1/freevo/src/util/Rendezvous.py        (original)
+++ branches/rel-1/freevo/src/util/Rendezvous.py        Sun Feb 18 14:24:56 2007
@@ -796,8 +796,10 @@
                 # or addition of a socket
                 #
                 self.condition.acquire()
-                self.condition.wait(self.timeout)
-                self.condition.release()
+                try:
+                    self.condition.wait(self.timeout)
+                finally:
+                    self.condition.release()
             else:
                 rr, wr, er = select.select(rs, [], [], self.timeout)
                 for socket in rr:
@@ -809,26 +811,34 @@
     def getReaders(self):
         result = []
         self.condition.acquire()
-        result = self.readers.keys()
-        self.condition.release()
+        try:
+            result = self.readers.keys()
+        finally:
+            self.condition.release()
         return result
     
     def addReader(self, reader, socket):
         self.condition.acquire()
-        self.readers[socket] = reader
-        self.condition.notify()
-        self.condition.release()
+        try:
+            self.readers[socket] = reader
+            self.condition.notify()
+        finally:
+            self.condition.release()
 
     def delReader(self, socket):
         self.condition.acquire()
-        del self.readers[socket]
-        self.condition.notify()
-        self.condition.release()
+        try:
+            del self.readers[socket]
+            self.condition.notify()
+        finally:
+            self.condition.release()
 
     def notify(self):
         self.condition.acquire()
-        self.condition.notify()
-        self.condition.release()
+        try:
+            self.condition.notify()
+        finally:
+            self.condition.release()
 
 class Listener(object):
     """A Listener is used by this module to listen on the multicast
@@ -1205,14 +1215,18 @@
         """Calling thread waits for a given number of milliseconds or
         until notified."""
         self.condition.acquire()
-        self.condition.wait(timeout/1000)
-        self.condition.release()
+        try:
+            self.condition.wait(timeout/1000)
+        finally:
+            self.condition.release()
 
     def notifyAll(self):
         """Notifies all waiting threads"""
         self.condition.acquire()
-        self.condition.notifyAll()
-        self.condition.release()
+        try:
+            self.condition.notifyAll()
+        finally:
+            self.condition.release()
 
     def getServiceInfo(self, type, name, timeout=3000):
         """Returns network's service information for a particular

Modified: branches/rel-1/freevo/src/util/popen3.py
==============================================================================
--- branches/rel-1/freevo/src/util/popen3.py    (original)
+++ branches/rel-1/freevo/src/util/popen3.py    Sun Feb 18 14:24:56 2007
@@ -112,8 +112,10 @@
         
         if pid:
             wait_lock.acquire()
-            dead_childs.append(pid)
-            wait_lock.release()
+            try:
+                dead_childs.append(pid)
+            finally:
+                wait_lock.release()
         return
     
     if config.IS_RECORDSERVER:
@@ -130,12 +132,14 @@
         
     _debug_('poll', 2)
     wait_lock.acquire()
-    if pid in dead_childs:
-        dead_childs.remove(pid)
+    try:
+        if pid in dead_childs:
+            dead_childs.remove(pid)
+            wait_lock.release()
+            return 1
+        return 0
+    finally:
         wait_lock.release()
-        return 1
-    wait_lock.release()
-    return 0
     
 
 def stdout(app):

-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys-and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
Freevo-cvslog mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/freevo-cvslog

Reply via email to