Dramatically reduce size of frozen/compiled code by pruning/masking unnecessary imports/code

This commit is contained in:
rdb 2016-02-12 22:20:27 +01:00
parent 591ce04ab1
commit 0d03207d1b
14 changed files with 767 additions and 1007 deletions

View File

@ -41,7 +41,9 @@ only when debugging (i.e. when it won't be checked-in) or where it helps
you resist using assert for error handling. you resist using assert for error handling.
""" """
wantVerifyPdb = 0 # Set to true to load pdb on failure. from panda3d.core import ConfigVariableBool
wantVerifyPdb = ConfigVariableBool('want-verify-pdb', False) # Set to true to load pdb on failure.
def verify(assertion): def verify(assertion):

View File

@ -12,7 +12,6 @@ from OnscreenImage import *
from direct.directtools.DirectUtil import ROUND_TO from direct.directtools.DirectUtil import ROUND_TO
from direct.showbase import DirectObject from direct.showbase import DirectObject
from direct.task import Task from direct.task import Task
from direct.showbase.PythonUtil import recordCreationStackStr
import types import types
guiObjectCollector = PStatCollector("Client::GuiObjects") guiObjectCollector = PStatCollector("Client::GuiObjects")
@ -651,13 +650,6 @@ def toggleGuiGridSnap():
def setGuiGridSpacing(spacing): def setGuiGridSpacing(spacing):
DirectGuiWidget.gridSpacing = spacing DirectGuiWidget.gridSpacing = spacing
# this should trigger off of __dev__, but it's not available at this point.
# __debug__ works because the production client is not __debug__ and the
# production AI doesn't create any GUI.
if get_config_showbase().GetBool('record-gui-creation-stack', __debug__):
# this will help track down the code that created DirectGui objects
# call obj.printCreationStackTrace() to figure out what code created it
DirectGuiBase = recordCreationStackStr(DirectGuiBase)
class DirectGuiWidget(DirectGuiBase, NodePath): class DirectGuiWidget(DirectGuiBase, NodePath):
# Toggle if you wish widget's to snap to grid when draggin # Toggle if you wish widget's to snap to grid when draggin

View File

@ -72,13 +72,6 @@ class ProjectileInterval(Interval):
self.projectileIntervalNum) self.projectileIntervalNum)
ProjectileInterval.projectileIntervalNum += 1 ProjectileInterval.projectileIntervalNum += 1
"""
# attempt to add info about the caller
file, line, func = PythonUtil.callerInfo()
if file is not None:
name += '-%s:%s:%s' % (file, line, func)
"""
args = (startPos, endPos, duration, startVel, endZ, args = (startPos, endPos, duration, startVel, endZ,
wayPoint, timeToWayPoint, gravityMult) wayPoint, timeToWayPoint, gravityMult)
self.implicitStartPos = 0 self.implicitStartPos = 0

View File

@ -1,4 +1,5 @@
from panda3d.direct import get_config_showbase __all__ = ["install"]
from direct.directnotify.DirectNotifyGlobal import directNotify from direct.directnotify.DirectNotifyGlobal import directNotify
from direct.showbase.PythonUtil import fastRepr from direct.showbase.PythonUtil import fastRepr
import sys import sys
@ -6,7 +7,6 @@ import types
import traceback import traceback
notify = directNotify.newCategory("ExceptionVarDump") notify = directNotify.newCategory("ExceptionVarDump")
config = get_config_showbase()
reentry = 0 reentry = 0
@ -187,7 +187,7 @@ def install(log, upload):
wantStackDumpLog = log wantStackDumpLog = log
wantStackDumpUpload = upload wantStackDumpUpload = upload
dumpOnExceptionInit = config.GetBool('variable-dump-on-exception-init', 0) dumpOnExceptionInit = ConfigVariableBool('variable-dump-on-exception-init', False)
if dumpOnExceptionInit: if dumpOnExceptionInit:
# this mode doesn't completely work because exception objects # this mode doesn't completely work because exception objects
# thrown by the interpreter don't get created until the # thrown by the interpreter don't get created until the

View File

@ -281,7 +281,7 @@ class GarbageReport(Job):
if self._args.findCycles: if self._args.findCycles:
s = ['===== GarbageReport: \'%s\' (%s %s) =====' % ( s = ['===== GarbageReport: \'%s\' (%s %s) =====' % (
self._args.name, self.numCycles, self._args.name, self.numCycles,
choice(self.numCycles == 1, 'cycle', 'cycles'))] ('cycle' if self.numCycles == 1 else 'cycles'))]
else: else:
s = ['===== GarbageReport: \'%s\' =====' % ( s = ['===== GarbageReport: \'%s\' =====' % (
self._args.name)] self._args.name)]
@ -499,7 +499,7 @@ class GarbageReport(Job):
rootId = index rootId = index
# check if the root object is one of the garbage instances (has __del__) # check if the root object is one of the garbage instances (has __del__)
objId = id(self.garbage[rootId]) objId = id(self.garbage[rootId])
numDelInstances = choice(objId in self.garbageInstanceIds, 1, 0) numDelInstances = int(objId in self.garbageInstanceIds)
stateStack.push(([rootId], rootId, numDelInstances, 0)) stateStack.push(([rootId], rootId, numDelInstances, 0))
while True: while True:
yield None yield None
@ -535,7 +535,7 @@ class GarbageReport(Job):
elif refId is not None: elif refId is not None:
# check if this object is one of the garbage instances (has __del__) # check if this object is one of the garbage instances (has __del__)
objId = id(self.garbage[refId]) objId = id(self.garbage[refId])
numDelInstances += choice(objId in self.garbageInstanceIds, 1, 0) numDelInstances += int(objId in self.garbageInstanceIds)
# this refId does not complete a cycle. Mark down # this refId does not complete a cycle. Mark down
# where we are in this list of referents, then # where we are in this list of referents, then
# start looking through the referents of the new refId # start looking through the referents of the new refId

View File

@ -5,7 +5,6 @@ __all__ = ['Diff', 'ObjectPool']
from direct.directnotify.DirectNotifyGlobal import directNotify from direct.directnotify.DirectNotifyGlobal import directNotify
from direct.showbase.PythonUtil import invertDictLossless, makeList, safeRepr from direct.showbase.PythonUtil import invertDictLossless, makeList, safeRepr
from direct.showbase.PythonUtil import getNumberedTypedString, getNumberedTypedSortedString from direct.showbase.PythonUtil import getNumberedTypedString, getNumberedTypedSortedString
from direct.showbase.PythonUtil import getNumberedTypedSortedStringWithReferrersGen
import gc import gc
class Diff: class Diff:
@ -97,7 +96,7 @@ class ObjectPool:
s += '\n%s\t%s' % (count, typ) s += '\n%s\t%s' % (count, typ)
return s return s
def printObjsByType(self, printReferrers=False): def printObjsByType(self):
print 'Object Pool: Objects By Type' print 'Object Pool: Objects By Type'
print '\n============================' print '\n============================'
counts = list(set(self._count2types.keys())) counts = list(set(self._count2types.keys()))
@ -109,11 +108,7 @@ class ObjectPool:
types = makeList(self._count2types[count]) types = makeList(self._count2types[count])
for typ in types: for typ in types:
print 'TYPE: %s, %s objects' % (repr(typ), len(self._type2objs[typ])) print 'TYPE: %s, %s objects' % (repr(typ), len(self._type2objs[typ]))
if printReferrers: print getNumberedTypedSortedString(self._type2objs[typ])
for line in getNumberedTypedSortedStringWithReferrersGen(self._type2objs[typ]):
print line
else:
print getNumberedTypedSortedString(self._type2objs[typ])
def containerLenStr(self): def containerLenStr(self):
s = 'Object Pool: Container Lengths' s = 'Object Pool: Container Lengths'

File diff suppressed because it is too large Load Diff

View File

@ -26,24 +26,22 @@ from BulletinBoardGlobal import bulletinBoard
from direct.task.TaskManagerGlobal import taskMgr from direct.task.TaskManagerGlobal import taskMgr
from JobManagerGlobal import jobMgr from JobManagerGlobal import jobMgr
from EventManagerGlobal import eventMgr from EventManagerGlobal import eventMgr
from PythonUtil import * #from PythonUtil import *
from direct.showbase import PythonUtil
#from direct.interval.IntervalManager import ivalMgr
from direct.interval import IntervalManager from direct.interval import IntervalManager
from direct.showbase.BufferViewer import BufferViewer from direct.showbase.BufferViewer import BufferViewer
from direct.task import Task from direct.task import Task
from direct.directutil import Verify
from direct.showbase import GarbageReport
import sys import sys
import Loader import Loader
import time import time
import atexit import atexit
import importlib
from direct.showbase import ExceptionVarDump from direct.showbase import ExceptionVarDump
import DirectObject import DirectObject
import SfxPlayer import SfxPlayer
if __debug__: if __debug__:
from direct.showbase import GarbageReport
from direct.directutil import DeltaProfiler from direct.directutil import DeltaProfiler
import OnScreenDebug import OnScreenDebug
import AppRunnerGlobal import AppRunnerGlobal
def legacyRun(): def legacyRun():
@ -73,7 +71,8 @@ class ShowBase(DirectObject.DirectObject):
if logStackDump or uploadStackDump: if logStackDump or uploadStackDump:
ExceptionVarDump.install(logStackDump, uploadStackDump) ExceptionVarDump.install(logStackDump, uploadStackDump)
self.__autoGarbageLogging = self.__dev__ and self.config.GetBool('auto-garbage-logging', False) if __debug__:
self.__autoGarbageLogging = self.__dev__ and self.config.GetBool('auto-garbage-logging', False)
## The directory containing the main Python file of this application. ## The directory containing the main Python file of this application.
self.mainDir = ExecutionEnvironment.getEnvironmentVariable("MAIN_DIR") self.mainDir = ExecutionEnvironment.getEnvironmentVariable("MAIN_DIR")
@ -88,9 +87,6 @@ class ShowBase(DirectObject.DirectObject):
#debug running multiplier #debug running multiplier
self.debugRunningMultiplier = 4 self.debugRunningMultiplier = 4
# Setup wantVerifyPdb as soon as reasonable:
Verify.wantVerifyPdb = self.config.GetBool('want-verify-pdb', 0)
# [gjeon] to disable sticky keys # [gjeon] to disable sticky keys
if self.config.GetBool('disable-sticky-keys', 0): if self.config.GetBool('disable-sticky-keys', 0):
storeAccessibilityShortcutKeys() storeAccessibilityShortcutKeys()
@ -373,8 +369,8 @@ class ShowBase(DirectObject.DirectObject):
builtins.wantUberdog = self.config.GetBool('want-uberdog', 1) builtins.wantUberdog = self.config.GetBool('want-uberdog', 1)
if __debug__: if __debug__:
builtins.deltaProfiler = DeltaProfiler.DeltaProfiler("ShowBase") builtins.deltaProfiler = DeltaProfiler.DeltaProfiler("ShowBase")
self.onScreenDebug = OnScreenDebug.OnScreenDebug() self.onScreenDebug = OnScreenDebug.OnScreenDebug()
builtins.onScreenDebug = self.onScreenDebug builtins.onScreenDebug = self.onScreenDebug
if self.wantRender2dp: if self.wantRender2dp:
builtins.render2dp = self.render2dp builtins.render2dp = self.render2dp
@ -388,10 +384,6 @@ class ShowBase(DirectObject.DirectObject):
self.createBaseAudioManagers() self.createBaseAudioManagers()
# set up recording of Functor creation stacks in __dev__
if self.__dev__ and self.config.GetBool('record-functor-creation-stacks', False):
PythonUtil.recordFunctorCreationStacks()
if self.__dev__ or self.config.GetBool('want-e3-hacks', False): if self.__dev__ or self.config.GetBool('want-e3-hacks', False):
if self.config.GetBool('track-gui-items', True): if self.config.GetBool('track-gui-items', True):
# dict of guiId to gui item, for tracking down leaks # dict of guiId to gui item, for tracking down leaks
@ -465,7 +457,8 @@ class ShowBase(DirectObject.DirectObject):
some Panda config settings. """ some Panda config settings. """
try: try:
import profile, pstats profile = importlib.import_module('profile')
pstats = importlib.import_module('pstats')
except ImportError: except ImportError:
return return
@ -1647,23 +1640,26 @@ class ShowBase(DirectObject.DirectObject):
def addAngularIntegrator(self): def addAngularIntegrator(self):
if not self.physicsMgrAngular: if not self.physicsMgrAngular:
from panda3d.physics import AngularEulerIntegrator physics = importlib.import_module('panda3d.physics')
self.physicsMgrAngular = 1 self.physicsMgrAngular = 1
integrator = AngularEulerIntegrator() integrator = physics.AngularEulerIntegrator()
self.physicsMgr.attachAngularIntegrator(integrator) self.physicsMgr.attachAngularIntegrator(integrator)
def enableParticles(self): def enableParticles(self):
if not self.particleMgrEnabled: if not self.particleMgrEnabled:
# Use importlib to prevent this import from being picked up
# by modulefinder when packaging an application.
if not self.particleMgr: if not self.particleMgr:
from direct.particles.ParticleManagerGlobal import particleMgr PMG = importlib.import_module('direct.particles.ParticleManagerGlobal')
self.particleMgr = particleMgr self.particleMgr = PMG.particleMgr
self.particleMgr.setFrameStepping(1) self.particleMgr.setFrameStepping(1)
if not self.physicsMgr: if not self.physicsMgr:
from PhysicsManagerGlobal import physicsMgr PMG = importlib.import_module('direct.showbase.PhysicsManagerGlobal')
from panda3d.physics import LinearEulerIntegrator physics = importlib.import_module('panda3d.physics')
self.physicsMgr = physicsMgr self.physicsMgr = PMG.physicsMgr
integrator = LinearEulerIntegrator() integrator = physics.LinearEulerIntegrator()
self.physicsMgr.attachLinearIntegrator(integrator) self.physicsMgr.attachLinearIntegrator(integrator)
self.particleMgrEnabled = 1 self.particleMgrEnabled = 1
@ -1886,9 +1882,10 @@ class ShowBase(DirectObject.DirectObject):
return Task.cont return Task.cont
def __igLoop(self, state): def __igLoop(self, state):
# We render the watch variables for the onScreenDebug as soon if __debug__:
# as we reasonably can before the renderFrame(). # We render the watch variables for the onScreenDebug as soon
self.onScreenDebug.render() # as we reasonably can before the renderFrame().
self.onScreenDebug.render()
if self.recorder: if self.recorder:
self.recorder.recordFrame() self.recorder.recordFrame()
@ -1900,9 +1897,10 @@ class ShowBase(DirectObject.DirectObject):
if self.multiClientSleep: if self.multiClientSleep:
time.sleep(0) time.sleep(0)
# We clear the text buffer for the onScreenDebug as soon if __debug__:
# as we reasonably can after the renderFrame(). # We clear the text buffer for the onScreenDebug as soon
self.onScreenDebug.clear() # as we reasonably can after the renderFrame().
self.onScreenDebug.clear()
if self.recorder: if self.recorder:
self.recorder.playFrame() self.recorder.playFrame()
@ -1925,9 +1923,10 @@ class ShowBase(DirectObject.DirectObject):
def __igLoopSync(self, state): def __igLoopSync(self, state):
# We render the watch variables for the onScreenDebug as soon if __debug__:
# as we reasonably can before the renderFrame(). # We render the watch variables for the onScreenDebug as soon
self.onScreenDebug.render() # as we reasonably can before the renderFrame().
self.onScreenDebug.render()
if self.recorder: if self.recorder:
self.recorder.recordFrame() self.recorder.recordFrame()
@ -1941,9 +1940,10 @@ class ShowBase(DirectObject.DirectObject):
if self.multiClientSleep: if self.multiClientSleep:
time.sleep(0) time.sleep(0)
# We clear the text buffer for the onScreenDebug as soon if __debug__:
# as we reasonably can after the renderFrame(). # We clear the text buffer for the onScreenDebug as soon
self.onScreenDebug.clear() # as we reasonably can after the renderFrame().
self.onScreenDebug.clear()
if self.recorder: if self.recorder:
self.recorder.playFrame() self.recorder.playFrame()
@ -2178,8 +2178,10 @@ class ShowBase(DirectObject.DirectObject):
self.texmem = None self.texmem = None
return return
from direct.showutil.TexMemWatcher import TexMemWatcher # Use importlib to prevent this import from being picked up
self.texmem = TexMemWatcher() # by modulefinder when packaging an application.
TMW = importlib.import_module('direct.showutil.TexMemWatcher')
self.texmem = TMW.TexMemWatcher()
def toggleShowVertices(self): def toggleShowVertices(self):
""" Toggles a mode that visualizes vertex density per screen """ Toggles a mode that visualizes vertex density per screen
@ -2675,16 +2677,18 @@ class ShowBase(DirectObject.DirectObject):
if not properties.getOpen(): if not properties.getOpen():
# If the user closes the main window, we should exit. # If the user closes the main window, we should exit.
self.notify.info("User closed main window.") self.notify.info("User closed main window.")
if self.__autoGarbageLogging: if __debug__:
GarbageReport.b_checkForGarbageLeaks() if self.__autoGarbageLogging:
GarbageReport.b_checkForGarbageLeaks()
self.userExit() self.userExit()
if properties.getForeground() and not self.mainWinForeground: if properties.getForeground() and not self.mainWinForeground:
self.mainWinForeground = 1 self.mainWinForeground = 1
elif not properties.getForeground() and self.mainWinForeground: elif not properties.getForeground() and self.mainWinForeground:
self.mainWinForeground = 0 self.mainWinForeground = 0
if self.__autoGarbageLogging: if __debug__:
GarbageReport.b_checkForGarbageLeaks() if self.__autoGarbageLogging:
GarbageReport.b_checkForGarbageLeaks()
if properties.getMinimized() and not self.mainWinMinimized: if properties.getMinimized() and not self.mainWinMinimized:
# If the main window is minimized, throw an event to # If the main window is minimized, throw an event to
@ -2814,7 +2818,10 @@ class ShowBase(DirectObject.DirectObject):
init_app_for_gui() init_app_for_gui()
import wx # Use importlib to prevent this import from being picked up
# by modulefinder when packaging an application.
wx = importlib.import_module('wx')
# Create a new base.wxApp. # Create a new base.wxApp.
self.wxApp = wx.PySimpleApp(redirect = False) self.wxApp = wx.PySimpleApp(redirect = False)
@ -2889,8 +2896,10 @@ class ShowBase(DirectObject.DirectObject):
# Don't do this twice. # Don't do this twice.
return return
from Tkinter import tkinter # Use importlib to prevent this import from being picked up
import Pmw # by modulefinder when packaging an application.
tkinter = importlib.import_module('Tkinter').tkinter
Pmw = importlib.import_module('Pmw')
# Create a new Tk root. # Create a new Tk root.
self.tkRoot = Pmw.initialise() self.tkRoot = Pmw.initialise()
@ -2953,8 +2962,10 @@ class ShowBase(DirectObject.DirectObject):
self.startWx(fWantWx) self.startWx(fWantWx)
self.wantDirect = fWantDirect self.wantDirect = fWantDirect
if self.wantDirect: if self.wantDirect:
from direct.directtools.DirectSession import DirectSession # Use importlib to prevent this import from being picked up
self.direct = DirectSession() # by modulefinder when packaging an application.
DirectSession = importlib.import_module('direct.directtools.DirectSession')
self.direct = DirectSession.DirectSession()
self.direct.enable() self.direct.enable()
builtins.direct = self.direct builtins.direct = self.direct
else: else:

View File

@ -3,7 +3,7 @@
__all__ = ['Transitions'] __all__ = ['Transitions']
from panda3d.core import * from panda3d.core import *
from direct.gui.DirectGui import * from direct.gui.DirectGui import DirectFrame
from direct.gui import DirectGuiGlobals as DGG from direct.gui import DirectGuiGlobals as DGG
from direct.interval.LerpInterval import LerpColorScaleInterval, LerpColorInterval, LerpScaleInterval, LerpPosInterval from direct.interval.LerpInterval import LerpColorScaleInterval, LerpColorInterval, LerpScaleInterval, LerpPosInterval
from direct.interval.MetaInterval import Sequence, Parallel from direct.interval.MetaInterval import Sequence, Parallel

View File

@ -30,8 +30,8 @@ isDebugBuild = (python.lower().endswith('_d'))
# These are modules that Python always tries to import up-front. They # These are modules that Python always tries to import up-front. They
# must be frozen in any main.exe. # must be frozen in any main.exe.
startupModules = [ startupModules = [
'site', 'sitecustomize', 'os', 'encodings.cp1252', 'os', 'encodings.cp1252',
'encodings.latin_1', 'encodings.utf_8', 'io', 'org', 'encodings.latin_1', 'encodings.utf_8', 'io',
] ]
# These are missing modules that we've reported already this session. # These are missing modules that we've reported already this session.

View File

@ -70,6 +70,13 @@ class LockType:
def __exit__(self, t, v, tb): def __exit__(self, t, v, tb):
self.release() self.release()
# Helper to generate new thread names
_counter = 0
def _newname(template="Thread-%d"):
global _counter
_counter = _counter + 1
return template % _counter
_threads = {} _threads = {}
_nextThreadId = 0 _nextThreadId = 0
_threadsLock = core.Mutex('thread._threadsLock') _threadsLock = core.Mutex('thread._threadsLock')

View File

@ -42,6 +42,7 @@ __all__ = [
] ]
local = _thread._local local = _thread._local
_newname = _thread._newname
class ThreadBase: class ThreadBase:
""" A base class for both Thread and ExternalThread in this """ A base class for both Thread and ExternalThread in this
@ -98,8 +99,7 @@ class Thread(ThreadBase):
self.__kwargs = kwargs self.__kwargs = kwargs
if not name: if not name:
import threading2 name = _newname()
name = threading2._newname()
current = current_thread() current = current_thread()
self.__dict__['daemon'] = current.daemon self.__dict__['daemon'] = current.daemon
@ -404,106 +404,107 @@ def setprofile(func):
def stack_size(size = None): def stack_size(size = None):
raise ThreadError raise ThreadError
def _test(): if __debug__:
def _test():
from collections import deque
from collections import deque _sleep = core.Thread.sleep
_sleep = core.Thread.sleep
_VERBOSE = False _VERBOSE = False
class _Verbose(object): class _Verbose(object):
def __init__(self, verbose=None): def __init__(self, verbose=None):
if verbose is None: if verbose is None:
verbose = _VERBOSE verbose = _VERBOSE
self.__verbose = verbose self.__verbose = verbose
def _note(self, format, *args): def _note(self, format, *args):
if self.__verbose: if self.__verbose:
format = format % args format = format % args
format = "%s: %s\n" % ( format = "%s: %s\n" % (
currentThread().getName(), format) currentThread().getName(), format)
_sys.stderr.write(format) _sys.stderr.write(format)
class BoundedQueue(_Verbose): class BoundedQueue(_Verbose):
def __init__(self, limit): def __init__(self, limit):
_Verbose.__init__(self) _Verbose.__init__(self)
self.mon = Lock(name = "BoundedQueue.mon") self.mon = Lock(name = "BoundedQueue.mon")
self.rc = Condition(self.mon) self.rc = Condition(self.mon)
self.wc = Condition(self.mon) self.wc = Condition(self.mon)
self.limit = limit self.limit = limit
self.queue = deque() self.queue = deque()
def put(self, item): def put(self, item):
self.mon.acquire() self.mon.acquire()
while len(self.queue) >= self.limit: while len(self.queue) >= self.limit:
self._note("put(%s): queue full", item) self._note("put(%s): queue full", item)
self.wc.wait() self.wc.wait()
self.queue.append(item) self.queue.append(item)
self._note("put(%s): appended, length now %d", self._note("put(%s): appended, length now %d",
item, len(self.queue)) item, len(self.queue))
self.rc.notify() self.rc.notify()
self.mon.release() self.mon.release()
def get(self): def get(self):
self.mon.acquire() self.mon.acquire()
while not self.queue: while not self.queue:
self._note("get(): queue empty") self._note("get(): queue empty")
self.rc.wait() self.rc.wait()
item = self.queue.popleft() item = self.queue.popleft()
self._note("get(): got %s, %d left", item, len(self.queue)) self._note("get(): got %s, %d left", item, len(self.queue))
self.wc.notify() self.wc.notify()
self.mon.release() self.mon.release()
return item return item
class ProducerThread(Thread): class ProducerThread(Thread):
def __init__(self, queue, quota): def __init__(self, queue, quota):
Thread.__init__(self, name="Producer") Thread.__init__(self, name="Producer")
self.queue = queue self.queue = queue
self.quota = quota self.quota = quota
def run(self): def run(self):
from random import random from random import random
counter = 0 counter = 0
while counter < self.quota: while counter < self.quota:
counter = counter + 1 counter = counter + 1
self.queue.put("%s.%d" % (self.getName(), counter)) self.queue.put("%s.%d" % (self.getName(), counter))
_sleep(random() * 0.00001) _sleep(random() * 0.00001)
class ConsumerThread(Thread): class ConsumerThread(Thread):
def __init__(self, queue, count): def __init__(self, queue, count):
Thread.__init__(self, name="Consumer") Thread.__init__(self, name="Consumer")
self.queue = queue self.queue = queue
self.count = count self.count = count
def run(self): def run(self):
while self.count > 0: while self.count > 0:
item = self.queue.get() item = self.queue.get()
print item print item
self.count = self.count - 1 self.count = self.count - 1
NP = 3 NP = 3
QL = 4 QL = 4
NI = 5 NI = 5
Q = BoundedQueue(QL) Q = BoundedQueue(QL)
P = [] P = []
for i in range(NP): for i in range(NP):
t = ProducerThread(Q, NI) t = ProducerThread(Q, NI)
t.setName("Producer-%d" % (i+1)) t.setName("Producer-%d" % (i+1))
P.append(t) P.append(t)
C = ConsumerThread(Q, NI*NP) C = ConsumerThread(Q, NI*NP)
for t in P: for t in P:
t.start() t.start()
_sleep(0.000001) _sleep(0.000001)
C.start() C.start()
for t in P: for t in P:
t.join() t.join()
C.join() C.join()
if __name__ == '__main__': if __name__ == '__main__':
_test() _test()

View File

@ -16,13 +16,12 @@ implementation. """
import sys as _sys import sys as _sys
from direct.stdpy import thread from direct.stdpy import thread
from direct.stdpy.thread import stack_size, _local as local from direct.stdpy.thread import stack_size, _newname, _local as local
from panda3d import core from panda3d import core
_sleep = core.Thread.sleep _sleep = core.Thread.sleep
from time import time as _time from time import time as _time
from traceback import format_exc as _format_exc from traceback import format_exc as _format_exc
from collections import deque
# Rename some stuff so "from threading import *" is safe # Rename some stuff so "from threading import *" is safe
__all__ = ['activeCount', 'Condition', 'currentThread', 'enumerate', 'Event', __all__ = ['activeCount', 'Condition', 'currentThread', 'enumerate', 'Event',
@ -377,13 +376,6 @@ class _Event(_Verbose):
finally: finally:
self.__cond.release() self.__cond.release()
# Helper to generate new thread names
_counter = 0
def _newname(template="Thread-%d"):
global _counter
_counter = _counter + 1
return template % _counter
# Active thread administration # Active thread administration
_active_limbo_lock = _allocate_lock() _active_limbo_lock = _allocate_lock()
_active = {} # maps thread id to Thread object _active = {} # maps thread id to Thread object
@ -741,88 +733,89 @@ _shutdown = _MainThread()._exitfunc
# Self-test code # Self-test code
if __debug__:
def _test():
from collections import deque
def _test(): class BoundedQueue(_Verbose):
class BoundedQueue(_Verbose): def __init__(self, limit):
_Verbose.__init__(self)
self.mon = RLock()
self.rc = Condition(self.mon)
self.wc = Condition(self.mon)
self.limit = limit
self.queue = deque()
def __init__(self, limit): def put(self, item):
_Verbose.__init__(self) self.mon.acquire()
self.mon = RLock() while len(self.queue) >= self.limit:
self.rc = Condition(self.mon) self._note("put(%s): queue full", item)
self.wc = Condition(self.mon) self.wc.wait()
self.limit = limit self.queue.append(item)
self.queue = deque() self._note("put(%s): appended, length now %d",
item, len(self.queue))
self.rc.notify()
self.mon.release()
def put(self, item): def get(self):
self.mon.acquire() self.mon.acquire()
while len(self.queue) >= self.limit: while not self.queue:
self._note("put(%s): queue full", item) self._note("get(): queue empty")
self.wc.wait() self.rc.wait()
self.queue.append(item) item = self.queue.popleft()
self._note("put(%s): appended, length now %d", self._note("get(): got %s, %d left", item, len(self.queue))
item, len(self.queue)) self.wc.notify()
self.rc.notify() self.mon.release()
self.mon.release() return item
def get(self): class ProducerThread(Thread):
self.mon.acquire()
while not self.queue:
self._note("get(): queue empty")
self.rc.wait()
item = self.queue.popleft()
self._note("get(): got %s, %d left", item, len(self.queue))
self.wc.notify()
self.mon.release()
return item
class ProducerThread(Thread): def __init__(self, queue, quota):
Thread.__init__(self, name="Producer")
self.queue = queue
self.quota = quota
def __init__(self, queue, quota): def run(self):
Thread.__init__(self, name="Producer") from random import random
self.queue = queue counter = 0
self.quota = quota while counter < self.quota:
counter = counter + 1
def run(self): self.queue.put("%s.%d" % (self.getName(), counter))
from random import random _sleep(random() * 0.00001)
counter = 0
while counter < self.quota:
counter = counter + 1
self.queue.put("%s.%d" % (self.getName(), counter))
_sleep(random() * 0.00001)
class ConsumerThread(Thread): class ConsumerThread(Thread):
def __init__(self, queue, count): def __init__(self, queue, count):
Thread.__init__(self, name="Consumer") Thread.__init__(self, name="Consumer")
self.queue = queue self.queue = queue
self.count = count self.count = count
def run(self): def run(self):
while self.count > 0: while self.count > 0:
item = self.queue.get() item = self.queue.get()
print item print item
self.count = self.count - 1 self.count = self.count - 1
NP = 3 NP = 3
QL = 4 QL = 4
NI = 5 NI = 5
Q = BoundedQueue(QL) Q = BoundedQueue(QL)
P = [] P = []
for i in range(NP): for i in range(NP):
t = ProducerThread(Q, NI) t = ProducerThread(Q, NI)
t.setName("Producer-%d" % (i+1)) t.setName("Producer-%d" % (i+1))
P.append(t) P.append(t)
C = ConsumerThread(Q, NI*NP) C = ConsumerThread(Q, NI*NP)
for t in P: for t in P:
t.start() t.start()
_sleep(0.000001) _sleep(0.000001)
C.start() C.start()
for t in P: for t in P:
t.join() t.join()
C.join() C.join()
if __name__ == '__main__': if __name__ == '__main__':
_test() _test()

View File

@ -12,6 +12,7 @@ from direct.showbase.PythonUtil import *
from direct.showbase.MessengerGlobal import messenger from direct.showbase.MessengerGlobal import messenger
import types import types
import random import random
import importlib
try: try:
import signal import signal
@ -591,7 +592,6 @@ class TaskManager:
def popupControls(self): def popupControls(self):
# Don't use a regular import, to prevent ModuleFinder from picking # Don't use a regular import, to prevent ModuleFinder from picking
# it up as a dependency when building a .p3d package. # it up as a dependency when building a .p3d package.
import importlib
TaskManagerPanel = importlib.import_module('direct.tkpanels.TaskManagerPanel') TaskManagerPanel = importlib.import_module('direct.tkpanels.TaskManagerPanel')
return TaskManagerPanel.TaskManagerPanel(self) return TaskManagerPanel.TaskManagerPanel(self)
@ -602,8 +602,8 @@ class TaskManager:
# Defer this import until we need it: some Python # Defer this import until we need it: some Python
# distributions don't provide the profile and pstats modules. # distributions don't provide the profile and pstats modules.
from direct.showbase.ProfileSession import ProfileSession PS = importlib.import_module('direct.showbase.ProfileSession')
return ProfileSession(name) return PS.ProfileSession(name)
def profileFrames(self, num=None, session=None, callback=None): def profileFrames(self, num=None, session=None, callback=None):
if num is None: if num is None:
@ -629,8 +629,8 @@ class TaskManager:
self._profileFrames.set(profileFrames) self._profileFrames.set(profileFrames)
if (not self._frameProfiler) and profileFrames: if (not self._frameProfiler) and profileFrames:
# import here due to import dependencies # import here due to import dependencies
from direct.task.FrameProfiler import FrameProfiler FP = importlib.import_module('direct.task.FrameProfiler')
self._frameProfiler = FrameProfiler() self._frameProfiler = FP.FrameProfiler()
def getProfileTasks(self): def getProfileTasks(self):
return self._profileTasks.get() return self._profileTasks.get()
@ -642,8 +642,8 @@ class TaskManager:
self._profileTasks.set(profileTasks) self._profileTasks.set(profileTasks)
if (not self._taskProfiler) and profileTasks: if (not self._taskProfiler) and profileTasks:
# import here due to import dependencies # import here due to import dependencies
from direct.task.TaskProfiler import TaskProfiler TP = importlib.import_module('direct.task.TaskProfiler')
self._taskProfiler = TaskProfiler() self._taskProfiler = TP.TaskProfiler()
def logTaskProfiles(self, name=None): def logTaskProfiles(self, name=None):
if self._taskProfiler: if self._taskProfiler:
@ -689,9 +689,9 @@ class TaskManager:
# Defer this import until we need it: some Python # Defer this import until we need it: some Python
# distributions don't provide the profile and pstats modules. # distributions don't provide the profile and pstats modules.
from direct.showbase.ProfileSession import ProfileSession PS = importlib.import_module('direct.showbase.ProfileSession')
profileSession = ProfileSession('profiled-task-%s' % task.getName(), profileSession = PS.ProfileSession('profiled-task-%s' % task.getName(),
Functor(profileInfo.taskFunc, *profileInfo.taskArgs)) Functor(profileInfo.taskFunc, *profileInfo.taskArgs))
ret = profileSession.run() ret = profileSession.run()
# set these values *after* profiling in case we're profiling the TaskProfiler # set these values *after* profiling in case we're profiling the TaskProfiler