DebugClients/Python/DebugClientThreads.py

changeset 0
de9c2efb9d02
child 13
1af94a91f439
equal deleted inserted replaced
-1:000000000000 0:de9c2efb9d02
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2003 - 2009 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the multithreaded version of the debug client.
8 """
9
10 import thread
11
12 from AsyncIO import *
13 from DebugThread import *
14 from DebugBase import *
15 import DebugClientBase
16
17
18 def _debugclient_start_new_thread(target, args, kwargs={}):
19 """
20 Module function used to allow for debugging of multiple threads.
21
22 The way it works is that below, we reset thread._start_new_thread to
23 this function object. Thus, providing a hook for us to see when
24 threads are started. From here we forward the request onto the
25 DebugClient which will create a DebugThread object to allow tracing
26 of the thread then start up the thread. These actions are always
27 performed in order to allow dropping into debug mode.
28
29 See DebugClientThreads.attachThread and DebugThread.DebugThread in
30 DebugThread.py
31
32 @param target the start function of the target thread (i.e. the user code)
33 @param args arguments to pass to target
34 @param kwargs keyword arguments to pass to target
35 @return The identifier of the created thread
36 """
37 if DebugClientBase.DebugClientInstance is not None:
38 return DebugClientBase.DebugClientInstance.attachThread(target, args, kwargs)
39 else:
40 return _original_start_thread(target, args, kwargs)
41
42 # make thread hooks available to system
43 _original_start_thread = thread.start_new_thread
44 thread.start_new_thread = _debugclient_start_new_thread
45
46 # NOTE: import threading here AFTER above hook, as threading cache's
47 # thread._start_new_thread.
48 from threading import RLock
49
50 class DebugClientThreads(DebugClientBase.DebugClientBase, AsyncIO):
51 """
52 Class implementing the client side of the debugger.
53
54 This variant of the debugger implements a threaded debugger client
55 by subclassing all relevant base classes.
56 """
57 def __init__(self):
58 """
59 Constructor
60 """
61 AsyncIO.__init__(self)
62
63 DebugClientBase.DebugClientBase.__init__(self)
64
65 # protection lock for synchronization
66 self.clientLock = RLock()
67
68 # the "current" thread, basically the thread we are at a breakpoint for.
69 self.currentThread = None
70
71 # special objects representing the main scripts thread and frame
72 self.mainThread = None
73 self.mainFrame = None
74
75 self.variant = 'Threaded'
76
77 def attachThread(self, target = None, args = None, kwargs = None, mainThread = 0):
78 """
79 Public method to setup a thread for DebugClient to debug.
80
81 If mainThread is non-zero, then we are attaching to the already
82 started mainthread of the app and the rest of the args are ignored.
83
84 @param target the start function of the target thread (i.e. the user code)
85 @param args arguments to pass to target
86 @param kwargs keyword arguments to pass to target
87 @param mainThread non-zero, if we are attaching to the already
88 started mainthread of the app
89 @return The identifier of the created thread
90 """
91 try:
92 self.lockClient()
93 newThread = DebugThread(self, target, args, kwargs, mainThread)
94 ident = -1
95 if mainThread:
96 ident = thread.get_ident()
97 self.mainThread = newThread
98 if self.debugging:
99 sys.setprofile(newThread.profile)
100 else:
101 ident = _original_start_thread(newThread.bootstrap, ())
102 newThread.set_ident(ident)
103 self.threads[newThread.get_ident()] = newThread
104 finally:
105 self.unlockClient()
106 return ident
107
108 def threadTerminated(self, dbgThread):
109 """
110 Public method called when a DebugThread has exited.
111
112 @param dbgThread the DebugThread that has exited
113 """
114 try:
115 self.lockClient()
116 try:
117 del self.threads[dbgThread.get_ident()]
118 except KeyError:
119 pass
120 finally:
121 self.unlockClient()
122
123 def lockClient(self, blocking = 1):
124 """
125 Public method to acquire the lock for this client.
126
127 @param blocking flag to indicating a blocking lock
128 @return flag indicating successful locking
129 """
130 if blocking:
131 self.clientLock.acquire()
132 else:
133 return self.clientLock.acquire(blocking)
134
135 def unlockClient(self):
136 """
137 Public method to release the lock for this client.
138 """
139 try:
140 self.clientLock.release()
141 except AssertionError:
142 pass
143
144 def setCurrentThread(self, id):
145 """
146 Private method to set the current thread.
147
148 @param id the id the current thread should be set to.
149 """
150 try:
151 self.lockClient()
152 if id is None:
153 self.currentThread = None
154 else:
155 self.currentThread = self.threads[id]
156 finally:
157 self.unlockClient()
158
159 def eventLoop(self, disablePolling = False):
160 """
161 Public method implementing our event loop.
162
163 @param disablePolling flag indicating to enter an event loop with
164 polling disabled (boolean)
165 """
166 # make sure we set the current thread appropriately
167 threadid = thread.get_ident()
168 self.setCurrentThread(threadid)
169
170 DebugClientBase.DebugClientBase.eventLoop(self, disablePolling)
171
172 self.setCurrentThread(None)
173
174 def set_quit(self):
175 """
176 Private method to do a 'set quit' on all threads.
177 """
178 try:
179 locked = self.lockClient(0)
180 try:
181 for key in self.threads.keys():
182 self.threads[key].set_quit()
183 except:
184 pass
185 finally:
186 if locked:
187 self.unlockClient()
188
189 # We are normally called by the debugger to execute directly.
190
191 if __name__ == '__main__':
192 debugClient = DebugClientThreads()
193 debugClient.main()

eric ide

mercurial