DebugClients/Python2/DebugClientThreads.py

branch
jsonrpc
changeset 5133
b7fe69c6cb1c
parent 5132
a094eee9f862
child 5161
f7b6ded9cc37
equal deleted inserted replaced
5132:a094eee9f862 5133:b7fe69c6cb1c
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, 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
85 user code)
86 @param args arguments to pass to target
87 @param kwargs keyword arguments to pass to target
88 @param mainThread non-zero, if we are attaching to the already
89 started mainthread of the app
90 @return The identifier of the created thread
91 """
92 try:
93 self.lockClient()
94 newThread = DebugThread(self, target, args, kwargs, mainThread)
95 ident = -1
96 if mainThread:
97 ident = thread.get_ident()
98 self.mainThread = newThread
99 if self.debugging:
100 sys.setprofile(newThread.profile)
101 else:
102 ident = _original_start_thread(newThread.bootstrap, ())
103 if self.mainThread is not None:
104 self.tracePython = self.mainThread.tracePython
105 newThread.set_ident(ident)
106 self.threads[newThread.get_ident()] = newThread
107 finally:
108 self.unlockClient()
109 return ident
110
111 def threadTerminated(self, dbgThread):
112 """
113 Public method called when a DebugThread has exited.
114
115 @param dbgThread the DebugThread that has exited
116 """
117 try:
118 self.lockClient()
119 try:
120 del self.threads[dbgThread.get_ident()]
121 except KeyError:
122 pass
123 finally:
124 self.unlockClient()
125
126 def lockClient(self, blocking=1):
127 """
128 Public method to acquire the lock for this client.
129
130 @param blocking flag to indicating a blocking lock
131 @return flag indicating successful locking
132 """
133 if blocking:
134 self.clientLock.acquire()
135 else:
136 return self.clientLock.acquire(blocking)
137
138 def unlockClient(self):
139 """
140 Public method to release the lock for this client.
141 """
142 try:
143 self.clientLock.release()
144 except AssertionError:
145 pass
146
147 def setCurrentThread(self, id):
148 """
149 Public method to set the current thread.
150
151 @param id the id the current thread should be set to.
152 """
153 try:
154 self.lockClient()
155 if id is None:
156 self.currentThread = None
157 else:
158 self.currentThread = self.threads[id]
159 finally:
160 self.unlockClient()
161
162 def eventLoop(self, disablePolling=False):
163 """
164 Public method implementing our event loop.
165
166 @param disablePolling flag indicating to enter an event loop with
167 polling disabled (boolean)
168 """
169 # make sure we set the current thread appropriately
170 threadid = thread.get_ident()
171 self.setCurrentThread(threadid)
172
173 DebugClientBase.DebugClientBase.eventLoop(self, disablePolling)
174
175 self.setCurrentThread(None)
176
177 def set_quit(self):
178 """
179 Public method to do a 'set quit' on all threads.
180 """
181 try:
182 locked = self.lockClient(0)
183 try:
184 for key in self.threads.keys():
185 self.threads[key].set_quit()
186 except Exception:
187 pass
188 finally:
189 if locked:
190 self.unlockClient()
191
192 # We are normally called by the debugger to execute directly.
193
194 if __name__ == '__main__':
195 debugClient = DebugClientThreads()
196 debugClient.main()
197
198 #
199 # eflag: FileType = Python2
200 # eflag: noqa = M601, M702, E402

eric ide

mercurial