As part of the Client API needed for IPS GUI we need to have the ability to cancel long running tasks, including long running network tasks. This needs to handle the scenario where the network goes down during a download:
http://defect.opensolaris.org/bz/show_bug.cgi?id=2201

The current behavior is that the client will hang and needs to be killed.

Danek has proposed a way to allow us to cancel such long running tasks by injecting a "STOP" string in to the pipe that is reading the download data, when the user hits cancel. Michal has created a sample PyGTK app (modified Danek's sample) to do this and it works well. We have put in place the objects we'd plan to use at a conceptual level in the API to support this. We use a shared Cancel object to signal from the GUI that the task is to be stopped and in the case of a Long Running Network task a separate Watcher Thread to inject the "STOP" string into the pipe to get the Long Running Network thread to cancel, mid download.

The question we have is how would one override the tar read method to allow us to do this within IPS?

It also looks like this would the appropriate place to call Progress Tracker with file chunk size progress info.

JR & Michal


#!/usr/bin/python

from threading import Thread
import time
import select
import os
import sys

try:
   import pygtk
   pygtk.require("2.0")
except:
   pass
try:
   import gobject
   gobject.threads_init()
   import gtk
except:
   sys.exit(1)


class PyGtkSpecific:
        '''Example pygtk small application, which will allow to stop two types
        of long running tasks:
        1. Network based long running task
        2. Long running task without network activity'''
        def __init__(self):
                window = gtk.Window(gtk.WINDOW_TOPLEVEL)
                box = gtk.HBox(False, 0)
                window.add(box)
                self.text_box = gtk.Entry(50)
                self.text_box.set_editable(False)                
                self.text_box1 = gtk.Entry(30)
                self.text_box1.set_editable(False)                
                button1 = gtk.Button("Stop long no network")
                button2 = gtk.Button("Stop network task")
                box.pack_start(self.text_box, True, True, 0)
                box.pack_start(button1, True, True, 0)
                box.pack_start(button2, True, True, 0)
                box.pack_start(self.text_box1, True, True, 0)
                button1.show()
                button2.show()
                self.text_box.show()
                self.text_box1.show()

                #Two cancel objects, works exactly the same but underlying functions
                #are using them differently
                self.cancel_no_network=SharedCancelObject()
                self.cancel_network=SharedCancelObject()

                #Those are two long running tasks, first one is without network
                #stuff and the second is showing example usage when we are using
                #something from the urrlib2 etc... something which may block us.
                Thread(target=self.no_network_task, name="No network", args=[]).start()
                Thread(target=self.network_task, name="Network", args=[]).start()

                button1.connect_object("clicked", self.do_cancel, self.cancel_no_network)
                button2.connect_object("clicked", self.do_cancel, self.cancel_network)
                window.connect("destroy", self.do_quit)

                box.show()
                window.show()

        def no_network_task(self):
                long_running = LongRunningTask(self, self.cancel_no_network)
                try:
                        long_running.do_something_long()
                except CancelException, e:
                        gobject.idle_add(self.do_print, self.text_box1,("%s") % e)

        def network_task(self):
                long_running_net = LongRunningNetworkTask(self, self.cancel_network)
                try:
                        long_running_net.download_files()
                except CancelException, e:
                        gobject.idle_add(self.do_print, self.text_box1, ("%s") % e)

        def do_cancel(self, shared_cancel_object):
                shared_cancel_object.do_cancel()

        def do_quit(self, *args):
                if not self.cancel_no_network.is_canceled():
                        print "The long task is still running... canceling"
                        self.cancel_no_network.do_cancel()
                if not self.cancel_network.is_canceled():
                        print "The network task is still running... canceling"
			# Comment out the following self.cancel... line to simulate the hang in bug 2201
                        # and uncomment pass
                        self.cancel_network.do_cancel()
                        # pass
                print "Quit main application"
                gtk.main_quit()

        def do_print(self, text_box, message=None):
                 text_box.delete_text(0, -1)
                 text_box.insert_text(message, 0)

        def print_progress(self, message):
                gobject.idle_add(self.do_print, self.text_box, message)

class SharedCancelObject:
        '''This class gives object/objects which may be shared across different
        threads to allow cancel functionallity'''
        def __init__(self):
                self.cancelled = False
        def is_canceled(self):
                return self.cancelled
        def do_cancel(self):
                self.cancelled = True

class LongRunningTask:
        def __init__(self, progtrack, shared_cancel_object=None):
                self.progtrack = progtrack
                self.shared_cancel_object = shared_cancel_object

        def do_something_long(self):
                i = 0
                while True:
                        #condition and will check if the cancel object exists if not
                        #then it will NOT check if the is_canceled is True and will
                        #never hit the CancelException
                        i += 1
                        if self.shared_cancel_object and self.shared_cancel_object.is_canceled():
                                raise CancelException, "Long task canceled"
                        self.progtrack.print_progress("%d LONG TASK" % i)
                        time.sleep(2)

class LongRunningNetworkTask:
        def __init__(self, progtrack, shared_cancel_object=None):
                self.progtrack = progtrack
                self.pipe_r, self.pipe_w = os.pipe()
                self.shared_cancel_object = shared_cancel_object
                if self.shared_cancel_object:
                        Thread(target=self.do_watch, name="Watcher", args=[]).start()

        def download_files(self):
                i = 0
                time.sleep(1)
                while True:
                        i += 1
                        inl, outl, errl = select.select([sys.stdin, self.pipe_r], [], [],2)
                        if self.pipe_r in inl:
                                pipe_s = os.read(self.pipe_r, 100)
                                if pipe_s == "some uniqe stop uuid!":
                                        raise CancelException, "Network task canceled"
                        self.progtrack.print_progress("%d NET TASK " % i)

        def do_watch(self):
                while True:
                        if self.shared_cancel_object and self.shared_cancel_object.is_canceled():
                                os.write(self.pipe_w, "some uniqe stop uuid!")
                                return
                        time.sleep(0.5)


class CancelException(Exception):
        def __init__(self, args=None):
                Exception.__init__(self)
                self.args = args

if __name__ == '__main__':
        PyGtkSpecific()
        gtk.main()
_______________________________________________
pkg-discuss mailing list
[email protected]
http://mail.opensolaris.org/mailman/listinfo/pkg-discuss

Reply via email to