Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Twisted logic error

My twisted program works but now I have a problem with one of my reactors not passing priority to the others. I want the controlListener reactor to do one iteration and then pass priority to the printstuffs reactor.

 #Random class as proof of concept
 class printStuffs(object):  
         print "counting "
         printerCount = 0
         def count(self):
             self.printerCount = self.printerCount + 1
             print ("the counter is at " + str(self.printerCount))

  ##########################################################################
  ## The control listneer class is designed to kill given reactor threads ##
  ## on demand from something once it recieves a signal it is supposed    ##
  ## to do one ieteration then release                                    ##
  ##########################################################################

  class controlListener(object):
          counter = 20
          def count(self):
               if self.counter == 0:
                  print "Killing Process"
                  reactor.stop()
              else:
                  print self.counter, '...'
                  self.counter -= 1
                  reactor.callLater(1, self.count)

 from twisted.internet import reactor

 print "Printing random stuff"
 reactor.callWhenRunning(printStuffs().count)

 print "Intializing kill listner"
 reactor.callWhenRunning(controlListener().count)
 reactor.run()

 print "Process killed"

here is the output

  Printing random stuff
  Intializing kill listner
  the counter is at 1
  20 ...
  19 ...
  18 ...
  17 ...
  16 ...
  15 ...
  14 ...
  13 ...
  12 ...
  11 ...
  10 ...
  9 ...
  8 ...
  7 ...
  6 ...
  5 ...
  4 ...
  3 ...
  2 ...
  1 ...
  Killing Process
  Process killed

I want it to do something like

 the counter is at 1 
 20 ...
 the counter is at 2 
 the counter is at 3
 19 ...

etc.

Any ideas?

like image 368
Kyle Sponable Avatar asked Apr 17 '16 22:04

Kyle Sponable


1 Answers

You simply forgot to make printStuffs.count() reschedule itself using reactor.callLater(), like controlListener.count() does.

class printStuffs(object):  
    printerCount = 0
    def count(self):
        self.printerCount = self.printerCount + 1
        print ("the counter is at " + str(self.printerCount))
        reactor.callLater(1, self.count)

Also, putting a print statement (print "counting") directly in the class definition rather than in a function causes it to be run right when the python interpreter reads the class definition. This is misleading because the message says "counting" but nothing really happens at that time (yet).


This may be one of those bugs that if one doesn't see it, one doesn't see it.
That's why for some important functions or threads, I add trace logging statements to my code that tell me when a function is called, or when a thread starts and when it ends. This is especially useful for functions that might abort due to an error and threads that you expect to run most of the time.

This is how you could adapt this pattern to your example:

class printStuffs(object):
    printerCount = 0
    def count(self):
        try:
            ##print "Entering printStuffs.count()."
            self.printerCount = self.printerCount + 1
            print ("The counter is at " + str(self.printerCount))
            # Run again later.
            reactor.callLater(1, self.count)
        except:
            # We won't run again later.
            print "Error in printStuffs.count(), won't run again:", sys.exc_info()[0]
            # Don't swallow the exception.
            raise
        finally:
            ##print "Leaving printStuffs.count()."

Of course, this would be overkill for your example, but your real code is probably more complex.

When your programs get larger and more complex, using logging in this way helps you verify that the basic processes in your program are working as intended.

like image 62
Norman Avatar answered Oct 21 '22 00:10

Norman