DebugClients/Python2/DebugClientThreads.py

branch
debugger speed
changeset 5174
8c48f5e0cd92
parent 5161
f7b6ded9cc37
equal deleted inserted replaced
5170:fb9168c2e069 5174:8c48f5e0cd92
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2003 - 2016 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 import sys
12
13 from DebugThread import DebugThread
14 import DebugClientBase
15
16
17 def _debugclient_start_new_thread(target, args, kwargs={}):
18 """
19 Module function used to allow for debugging of multiple threads.
20
21 The way it works is that below, we reset thread._start_new_thread to
22 this function object. Thus, providing a hook for us to see when
23 threads are started. From here we forward the request onto the
24 DebugClient which will create a DebugThread object to allow tracing
25 of the thread then start up the thread. These actions are always
26 performed in order to allow dropping into debug mode.
27
28 See DebugClientThreads.attachThread and DebugThread.DebugThread in
29 DebugThread.py
30
31 @param target the start function of the target thread (i.e. the user code)
32 @param args arguments to pass to target
33 @param kwargs keyword arguments to pass to target
34 @return The identifier of the created thread
35 """
36 if DebugClientBase.DebugClientInstance is not None:
37 return DebugClientBase.DebugClientInstance.attachThread(
38 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
51 class DebugClientThreads(DebugClientBase.DebugClientBase):
52 """
53 Class implementing the client side of the debugger.
54
55 This variant of the debugger implements a threaded debugger client
56 by subclassing all relevant base classes.
57 """
58 def __init__(self):
59 """
60 Constructor
61 """
62 DebugClientBase.DebugClientBase.__init__(self)
63
64 # protection lock for synchronization
65 self.clientLock = RLock()
66
67 # the "current" thread, basically the thread we are at a breakpoint
68 # 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,
78 mainThread=False):
79 """
80 Public method to setup a thread for DebugClient to debug.
81
82 If mainThread is True, then we are attaching to the already
83 started mainthread of the app and the rest of the args are ignored.
84
85 @param target the start function of the target thread (i.e. the
86 user code)
87 @param args arguments to pass to target
88 @param kwargs keyword arguments to pass to target
89 @param mainThread True, if we are attaching to the already
90 started mainthread of the app
91 @return identifier of the created thread
92 """
93 try:
94 self.lockClient()
95 newThread = DebugThread(self, target, args, kwargs, mainThread)
96 ident = -1
97 if mainThread:
98 ident = thread.get_ident()
99 self.mainThread = newThread
100 if self.debugging:
101 sys.setprofile(newThread.profile)
102 else:
103 ident = _original_start_thread(newThread.bootstrap, ())
104 if self.mainThread is not None:
105 self.tracePython = self.mainThread.tracePython
106 newThread.set_ident(ident)
107 self.threads[newThread.get_ident()] = newThread
108 finally:
109 self.unlockClient()
110 return ident
111
112 def threadTerminated(self, dbgThread):
113 """
114 Public method called when a DebugThread has exited.
115
116 @param dbgThread the DebugThread that has exited
117 """
118 try:
119 self.lockClient()
120 try:
121 del self.threads[dbgThread.get_ident()]
122 except KeyError:
123 pass
124 finally:
125 self.unlockClient()
126
127 def lockClient(self, blocking=True):
128 """
129 Public method to acquire the lock for this client.
130
131 @param blocking flag to indicating a blocking lock
132 @type bool
133 @return flag indicating successful locking
134 @rtype bool
135 """
136 if blocking:
137 self.clientLock.acquire()
138 else:
139 return self.clientLock.acquire(blocking)
140
141 def unlockClient(self):
142 """
143 Public method to release the lock for this client.
144 """
145 try:
146 self.clientLock.release()
147 except AssertionError:
148 pass
149
150 def setCurrentThread(self, id):
151 """
152 Public method to set the current thread.
153
154 @param id the id the current thread should be set to.
155 """
156 try:
157 self.lockClient()
158 if id is None:
159 self.currentThread = None
160 else:
161 self.currentThread = self.threads[id]
162 finally:
163 self.unlockClient()
164
165 def eventLoop(self, disablePolling=False):
166 """
167 Public method implementing our event loop.
168
169 @param disablePolling flag indicating to enter an event loop with
170 polling disabled (boolean)
171 """
172 # make sure we set the current thread appropriately
173 threadid = thread.get_ident()
174 self.setCurrentThread(threadid)
175
176 DebugClientBase.DebugClientBase.eventLoop(self, disablePolling)
177
178 self.setCurrentThread(None)
179
180 def set_quit(self):
181 """
182 Public method to do a 'set quit' on all threads.
183 """
184 try:
185 locked = self.lockClient(False)
186 try:
187 for key in self.threads.keys():
188 self.threads[key].set_quit()
189 except Exception:
190 pass
191 finally:
192 if locked:
193 self.unlockClient()
194
195 # We are normally called by the debugger to execute directly.
196
197 if __name__ == '__main__':
198 debugClient = DebugClientThreads()
199 debugClient.main()
200
201 #
202 # eflag: FileType = Python2
203 # eflag: noqa = M601, M702, E402

eric ide

mercurial