Threading.py

I keep getting this error once in a while, but I have no clue how I am getting it. The error never crashes the server, but seems more like a warnnings of how a lock isn’t be released when I am not using locks with my threads. Is this like a deadlock kinda of issue?

> Unhandled exception in thread started by <bound method Future.__bootstrap of <Future(GUI_Task, ini
tial daemon)>>
Traceback (most recent call last):
  File "C:\Panda3D-1.7.2\python\lib\threading.py", line 504, in __bootstrap
    self.__bootstrap_inner()
  File "C:\Panda3D-1.7.2\python\lib\threading.py", line 580, in __bootstrap_inner
    self.__stop()
  File "C:\Panda3D-1.7.2\python\lib\threading.py", line 592, in __stop
    self.__block.release()
thread.error: release unlocked lock

Not sure if it’s a big issue, but I also notice some code change from another threading.py and the threading.py panda supplys:

class _RLock(_Verbose): line 99:
Panda 3d:

        owner = self.__owner
        try:
            owner = _active[owner].name
        except KeyError:
            pass
        return "<%s owner=%r count=%d>" % (
                self.__class__.__name__, owner, self.__count)

Code from the other threading.py:

    def __repr__(self):
        owner = self.__owner
        return "<%s(%s, %d)>" % (
                self.__class__.__name__,
                owner and owner.name,
                self.__count)

I might have that backwards… but worth noting I think. Seeing as it looks like a 1 to 1 copy.

When you say “the threading.py panda supplies”, are you referring to stdpy.threading? But are you actually using that threading.py? In order to use it, you have to import stdpy.threading, not just import threading.

In Panda 1.7.2, you should never import the system threading module–this module is fundamentally incompatible with Panda’s internal threads. (You can safely use it in Panda 1.8.0 and later.) In Panda 1.7.2, you have to use stdpy.threading instead, or you will get mysterious errors like this or much worse.

David

Sorry I was half asleep when I was writing that… and then shortly after that the server went down. You are right… not using the stdpy.threading and using the import threading way… “C:\Panda3D-1.7.2\python\lib\threading.py” as seen by the error.

Actully… I guess I should say I am not using panda 3d yet on the server. It’s a full on python project (meaning I haven’t imported anything that doesn’t come with python naturally).

With that… I am guessing I may be out of luck because of the given error? Looking up the error doesn’t come up with anything other than my post xD

Or will switching to 1.8 and import panda help with the issue?

If you’re just using vanilla Python, switching to Panda 1.8 won’t make a difference.

Are you sure you’re not releasing an unlocked lock in your own code?

David

Yea, I’m sure. Um, maybe post the thread code I am using will help?

###############################################################################
class Future(threading.Thread):
    #------------------------------------------------------------------------------
    def __init__(self, func, param, name):
        threading.Thread.__init__(self)
        self.daemon = True
        self.Error = ''
        self.Finish = 0
        self.Func = func
        self.Param = param
        self.StartTime = 0
        self.FinishTime = 0
        self.threadname = name


    #------------------------------------------------------------------------------
    # Runs the program.
    def run(self):
        self.StartTime = globalClock()
        try:
            self.Var = self.Func(*self.Param)

        # On systemexit, we don't need to show it, so we just say there was a call to exit.
        except SystemExit:
            self.Error = 1

        # If it's not a systemexit, then we show the error and say there was an error.
        except:
            from traceback import print_exc
            print self.threadname, print_exc()
            self.Error = 1

        self.FinishTime = globalClock()
        self.Finish = 1

I also have a main while loop that does this:

        while self.ListOfTask:
            Done = 1
            Time = globalClock()
            for Task in sorted(self.ListOfTask.values(), key=itemgetter('priority'), reverse=not self.End):
                # Sees if a task can be ran yet.
                if Task["nextRun"] <= Time and Task["Frame"] is not self.Frames:
                    self.delayTime = Task["delayTime"]
                    self.priority = Task["priority"]
                    self.taskName = Task["Name"]
                    self.taskID = Task["ID"]
                    self.uponDeath = Task["uponDeath"]
                    self.taskChain = Task["taskChain"]
                    self.extraArgs = Task["extraArgs"]
                    self.extrArgsuponDeath = Task["extrArgsuponDeath"]
                    self.function = Task["function"]
                    self.CurrentTask = Task

                    if not Task["thread"].Finish:
                        if not Task["thread"].isAlive():
                            Task["thread"].__init__(self.function, self.extraArgs, self.taskName)
                            if self.setThreads:
                                Task["thread"].start()
                            else:
                                Task["thread"].run()
                        Task["thread"].join(0)

                    if Task["thread"].Finish and self.taskID in self.ListOfTask:
                        # On error, we need to exit like we would noramlly would on an error by the main thread.
                        if Task["thread"].Error:
                            raise SystemExit

                        elif not self.End:
                            self.VebosData[self.taskID][1] += 1
                            self.VebosData[self.taskID][2] += Task["thread"].FinishTime - Task["thread"].StartTime

                            Task["Name"] = self.taskName
                            Task["ID"] = self.taskID
                            Task["function"] = self.function
                            Task["delayTime"] = self.delayTime
                            Task["priority"] = self.priority
                            Task["uponDeath"] = self.uponDeath
                            Task["taskChain"] = self.taskChain
                            Task["extraArgs"] = self.extraArgs
                            Task["extrArgsuponDeath"] = self.extrArgsuponDeath
                            Task["thread"].Finish = 0
                            Task["Frame"] = self.Frames

                            # Do on next frame.
                            if Task["thread"].Var is 1:
                                Task["nextRun"] = Task["thread"].FinishTime
                                continue

                            # Repeat with same delay.
                            elif Task["thread"].Var is 2:
                                Task["nextRun"] = Task["thread"].FinishTime + self.delayTime
                                continue

                        # Runs any at-death task before removing.
                        if self.uponDeath:
                            self.add(self.uponDeath, self.taskName, extraArgs=self.extrArgsuponDeath)
                        del self.VebosData[self.taskID]
                        del self.ListOfTask[self.taskID]

                    else:
                        Done = 0

            self.CurrentTask = None

            if Done:
                sleep(self.GlobalDelayTime)
                self.Frames += 1

        if taskMgr.End is 2:
            raise SystemExit

In short… it does a while loop and calls .join(0) on each thread.

^^; It’s a task manager, sort of like how panda has one, but base around threads instead. I do plan on adding panda later… so a lot of the name spaceing is the same already to switch between the two. Not a big deal if it doesn’t work out. I just thought it was strange that I kept getting this error with no crashes.

Well, maybe you’ve got multiple threads that are calling join() on the same thread at the same time, or who knows what?

I can’t tell you what’s wrong exactly, but I suspect you do have a race condition somewhere in your code, because (a) the report that it happens “every once in a while” is exactly the sort of thing that happens when you have a race condition, and (b) your Google search didn’t turn up other people reporting a similar problem, so it’s probably not an inherent bug in Python or the system threading API.

David

Alright. Wasn’t sure what it was. Not a big issue then (for say)… There should only be one thread controlling the .join calls (far as I know… the main thread). Would putting locks around each join call help fix it?

I don’t know; probably not, if there’s just one thread calling join().

Alright. Thanks again for at least taking a look for me. Have a good day / weekend :slight_smile: