threading.Thread and DirectObject...

Hi! I am working @ a threading thingie right now and without Panda3D everything works as it should. Now that I want to create an thread that should take over the communication with the User (and thus requires Panda3D) I am stuck. After just copying and pasting some of the Panda3D-prog that worked, I don’t get any visible output at all.

Here is the header of Thread:

import direct.directbase.DirectStart
from direct.actor import Actor
from direct.showbase import DirectObject
from pandac.PandaModules import *
from direct.interval.IntervalGlobal import *
from direct.gui.DirectGui import *
from direct.task import Task
#from pandac import *
import cPickle

import threading, Queue, time, types

class Container:
        def __init__(self):
                self.items={}

        def __getitem__(self, name='text'):
                return self.items[name]

        def __setitem__(self, name='text', content='text'):
                self.items[name]=content

        def addNewTextNode(self, name='text'):
                self.items[name]=TextNode(name)

        def addNewString(self, name='text'):
                self.items[name]=str(name)

        def addNewModel(self, name='text', model='model'):
                self.items[name]=loader.loadModelCopy(model)


class ClientThread(threading.Thread, DirectObject):
        def __init__(self, jobqueue, pipeline, name, individual={}):
                threading.Thread.__init__(self)
                DirectObject.__init__(self)
                self.name=name
                print "Thread",self.name,"is coming up..."
                self.jobqueue=jobqueue
                self.pipeline=pipeline
                self.individual=individual
                self.userName=None
                self.password=None
                self.secret=None
                self.reference={}
                self.referenceNr=0
                self.supervisor=Container()
                self.accept("escape",self.quit)
                self.accept("end", self.screenie)
                ...

The next important thing for a thread is the run()-part:

        def run(self):
                     print "starting loop!"
                     run()

But all I get is an empty Panda3D-screen with an error message like:

starting loop!
:express(warning): Adjusting global clock's real time by -3.36449 seconds.
Exception in thread Thread-9:Traceback (most recent call last):
  File "/usr/lib/python2.3/threading.py", line 442, in __bootstrap
    self.run()
  File "/home/bigfoot29/panda-workdir/threads/ClientThread.py", line 367, in run
    run()
  File "debtmp/usr/share/panda3d/direct/src/showbase/ShowBase.py", line 1603, in run
  File "debtmp/usr/share/panda3d/direct/src/task/Task.py", line 781, in run
  File "debtmp/usr/share/panda3d/direct/src/task/Task.py", line 720, in step
ValueError: signal only works in main thread

What I am wondering is: All processes I am calling are working. But he doesn’t like the loop as it seems… But I can’t run that one out of the main loop because a thread doesn’t have a main loop :frowning:

Do I need to use the version without the “run()” command? Is there anything else I do wrong?

Regards, Bigfoot29

Edit: Trying it with while True: Task.step() instead of run() tells me:

starting loop!
Exception in thread Thread-9:Traceback (most recent call last):
  File "/usr/lib/python2.3/threading.py", line 442, in __bootstrap
    self.run()
  File "/home/bigfoot29/panda-workdir/threads/ClientThread.py", line 368, in run
    Task.step()
AttributeError: 'module' object has no attribute 'step'

Seems as if I have some sort of import problems? :frowning:

Edit2: My Fault: It has to be taskMgr.step() of course! However, then I get the same error like mentioned above :slight_smile:

Hmm, try hacking Task.py and comment out the references to signal.signal() (there are two of them) in the Task.step() method.

David

I am there now, but what to comment out? wonders
(me being noobish there… sorry…)

Regards, Bigfoot29

Edited some mistakes out :slight_smile:

Change the lines:

        self.fKeyboardInterrupt = 0
        self.interruptCount = 0
        signal.signal(signal.SIGINT, self.keyboardInterruptHandler)

To:

        self.fKeyboardInterrupt = 0
        self.interruptCount = 0
        #signal.signal(signal.SIGINT, self.keyboardInterruptHandler)

David

This is creating a funny “Segmentation Fault”, so I guess thats not really an option… following Hamled, Panda3D isn’t built for multithreaded use. :confused:

ATM he is also trying to figure out a solution, but I guess a real solution will be hard…

However, thanks till now :slight_smile:

(If you have further help, just post right away! :slight_smile: )

Regards, Bigfoot29

Hmm, yes, although Panda can be run in a thread, you then have to be careful to call into Panda only from within that thread.

However, Panda can be compiled to be thread-safe. To do this, you’ll have to download the latest version of Panda from CVS, and edit makepanda.py to define HAVE_THREADS (change the line that references HAVE_THREADS to look more like, say, HAVE_AUDIO). Once you have done this, it should theoretically be safer to run Panda in a thread. However, this is still experimental code, so it may not be 100% safe.

David

Thats exactly the fact (its being called and processed within one thread and remains there), but it gives me the signal problem. So at least the “normal” Panda3D seemed not to be able to be run in a thread (for python) as long as you need any of the events (self.accept, graphics, …) because that ones need taskMgr.step()

However, going to try CVS version as you suggested… :slight_smile:

Regards, Bigfoot29

Are you reffering to a network client? In that case, you could just put the socket handling part in a new thread. Perhaps its also usefull if I’m mistaken about my assumption.

Example:


class Client(DirectObject):

    def __init__(self,name,addr):
        self.name = name
        self.password = 'nopass'
        self.guid = None
        self.addr = addr
        self.s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        self.world = None

        self.accept('escape',self.quit)
        
        self.handlers = {
            SMSG_AUTH_CHALLENGE : self.msgAuthChallenge,
            SMSG_AUTH_RESPONSE  : self.msgAuthResponse,
        }

    try:
        self.s.connect(self.addr)
        self.connected = True
    except:
        print "Could not connect to: ", self.addr
        self.connected = False

    thread.start_new_thread(self.connectionLoop, ())

    def connectionLoop(self):
        while self.connected:
            data = self.s.recv(4)
            if len(data) > 0:
                (length,) = struct.unpack('>H',data[0:2])
                (opcode,) = struct.unpack('<H',data[2:4])
                data = DataBuffer(self.s.recv(length-2))
                
                print "Length: ", length-2
                print "Opcode: ", opcode
                print "Data: "
                data.hexdump()
                
                if opcode in self.handlers.keys():
                    self.handlers[opcode](data)
                else:
                    print "Unknown opcode: ", opcode
            time.sleep(0.1)
        # disconnected-> cleanup, saves & exit thread
        thread.exit_thread()