DebugClients/Python/DebugClientThreads.py

changeset 5141
bc64243b7672
parent 5126
d28b92dabc2b
parent 5140
01484c0afbc6
child 5144
1ab536d25072
equal deleted inserted replaced
5126:d28b92dabc2b 5141:bc64243b7672
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 AsyncIO import AsyncIO
14 from DebugThread import DebugThread
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(
39 target, args, kwargs)
40 else:
41 return _original_start_thread(target, args, kwargs)
42
43 # make thread hooks available to system
44 _original_start_thread = thread.start_new_thread
45 thread.start_new_thread = _debugclient_start_new_thread
46
47 # Note: import threading here AFTER above hook, as threading cache's
48 # thread._start_new_thread.
49 from threading import RLock
50
51
52 class DebugClientThreads(DebugClientBase.DebugClientBase, AsyncIO):
53 """
54 Class implementing the client side of the debugger.
55
56 This variant of the debugger implements a threaded debugger client
57 by subclassing all relevant base classes.
58 """
59 def __init__(self):
60 """
61 Constructor
62 """
63 AsyncIO.__init__(self)
64
65 DebugClientBase.DebugClientBase.__init__(self)
66
67 # protection lock for synchronization
68 self.clientLock = RLock()
69
70 # the "current" thread, basically the thread we are at a breakpoint
71 # for.
72 self.currentThread = None
73
74 # special objects representing the main scripts thread and frame
75 self.mainThread = None
76 self.mainFrame = None
77
78 self.variant = 'Threaded'
79
80 def attachThread(self, target=None, args=None, kwargs=None, mainThread=0):
81 """
82 Public method to setup a thread for DebugClient to debug.
83
84 If mainThread is non-zero, then we are attaching to the already
85 started mainthread of the app and the rest of the args are ignored.
86
87 @param target the start function of the target thread (i.e. the
88 user code)
89 @param args arguments to pass to target
90 @param kwargs keyword arguments to pass to target
91 @param mainThread non-zero, if we are attaching to the already
92 started mainthread of the app
93 @return The identifier of the created thread
94 """
95 try:
96 self.lockClient()
97 newThread = DebugThread(self, target, args, kwargs, mainThread)
98 ident = -1
99 if mainThread:
100 ident = thread.get_ident()
101 self.mainThread = newThread
102 if self.debugging:
103 sys.setprofile(newThread.profile)
104 else:
105 ident = _original_start_thread(newThread.bootstrap, ())
106 if self.mainThread is not None:
107 self.tracePython = self.mainThread.tracePython
108 newThread.set_ident(ident)
109 self.threads[newThread.get_ident()] = newThread
110 finally:
111 self.unlockClient()
112 return ident
113
114 def threadTerminated(self, dbgThread):
115 """
116 Public method called when a DebugThread has exited.
117
118 @param dbgThread the DebugThread that has exited
119 """
120 try:
121 self.lockClient()
122 try:
123 del self.threads[dbgThread.get_ident()]
124 except KeyError:
125 pass
126 finally:
127 self.unlockClient()
128
129 def lockClient(self, blocking=1):
130 """
131 Public method to acquire the lock for this client.
132
133 @param blocking flag to indicating a blocking lock
134 @return flag indicating successful locking
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(0)
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