@@ -38,9 +38,9 @@ def current_task(asyncio):
3838 if not asyncio :
3939 return
4040
41- current_task = getattr (asyncio , ' current_task' , None )
41+ current_task = getattr (asyncio , " current_task" , None )
4242 if current_task is None :
43- current_task = getattr (asyncio .Task , ' current_task' , None )
43+ current_task = getattr (asyncio .Task , " current_task" , None )
4444
4545 try :
4646 return current_task ()
@@ -52,9 +52,9 @@ def all_tasks(asyncio):
5252 if not asyncio :
5353 return
5454
55- all_tasks = getattr (asyncio , ' all_tasks' , None )
55+ all_tasks = getattr (asyncio , " all_tasks" , None )
5656 if all_tasks is None :
57- all_tasks = getattr (asyncio .Task , ' all_tasks' , None )
57+ all_tasks = getattr (asyncio .Task , " all_tasks" , None )
5858
5959 try :
6060 return all_tasks ()
@@ -63,15 +63,14 @@ def all_tasks(asyncio):
6363
6464
6565def get_event_loop (task ):
66- get_loop = getattr (task , ' get_loop' , None )
66+ get_loop = getattr (task , " get_loop" , None )
6767 if get_loop :
6868 return get_loop ()
6969 else :
70- return getattr (task , ' _loop' , None )
70+ return getattr (task , " _loop" , None )
7171
7272
7373class cached_module (object ):
74-
7574 def __init__ (self , module_path , name = None ):
7675 self .module_path = module_path
7776 self .name = name or module_path
@@ -172,9 +171,9 @@ def active_threads(self):
172171 transaction = trace and trace .transaction
173172 if transaction is not None :
174173 if transaction .background_task :
175- yield transaction , thread_id , ' BACKGROUND' , frame
174+ yield transaction , thread_id , " BACKGROUND" , frame
176175 else :
177- yield transaction , thread_id , ' REQUEST' , frame
176+ yield transaction , thread_id , " REQUEST" , frame
178177 else :
179178 # Note that there may not always be a thread object.
180179 # This is because thread could have been created direct
@@ -183,10 +182,10 @@ def active_threads(self):
183182 # obtain a name for as being 'OTHER'.
184183
185184 thread = threading ._active .get (thread_id )
186- if thread is not None and thread .getName ().startswith (' NR-' ):
187- yield None , thread_id , ' AGENT' , frame
185+ if thread is not None and thread .getName ().startswith (" NR-" ):
186+ yield None , thread_id , " AGENT" , frame
188187 else :
189- yield None , thread_id , ' OTHER' , frame
188+ yield None , thread_id , " OTHER" , frame
190189
191190 # Now yield up those corresponding to greenlets. Right now only
192191 # doing this for greenlets in which any active transactions are
@@ -202,11 +201,9 @@ def active_threads(self):
202201 gr = transaction ._greenlet ()
203202 if gr and gr .gr_frame is not None :
204203 if transaction .background_task :
205- yield (transaction , thread_id ,
206- 'BACKGROUND' , gr .gr_frame )
204+ yield (transaction , thread_id , "BACKGROUND" , gr .gr_frame )
207205 else :
208- yield (transaction , thread_id ,
209- 'REQUEST' , gr .gr_frame )
206+ yield (transaction , thread_id , "REQUEST" , gr .gr_frame )
210207
211208 def prepare_for_root (self ):
212209 """Updates the cache state so that a new root can be created if the
@@ -217,7 +214,7 @@ def prepare_for_root(self):
217214 if not trace :
218215 return None
219216
220- if not hasattr (trace , ' _task' ):
217+ if not hasattr (trace , " _task" ):
221218 return trace
222219
223220 task = current_task (self .asyncio )
@@ -243,15 +240,16 @@ def save_trace(self, trace):
243240
244241 if thread_id in self ._cache :
245242 cache_root = self ._cache [thread_id ].root
246- if (cache_root and cache_root is not trace .root and
247- not cache_root .exited ):
243+ if cache_root and cache_root is not trace .root and not cache_root .exited :
248244 # Cached trace exists and has a valid root still
249- _logger .error ('Runtime instrumentation error. Attempt to '
250- 'save a trace from an inactive transaction. '
251- 'Report this issue to New Relic support.\n %s' ,
252- '' .join (traceback .format_stack ()[:- 1 ]))
245+ _logger .error (
246+ "Runtime instrumentation error. Attempt to "
247+ "save a trace from an inactive transaction. "
248+ "Report this issue to New Relic support.\n %s" ,
249+ "" .join (traceback .format_stack ()[:- 1 ]),
250+ )
253251
254- raise TraceCacheActiveTraceError (' transaction already active' )
252+ raise TraceCacheActiveTraceError (" transaction already active" )
255253
256254 self ._cache [thread_id ] = trace
257255
@@ -266,21 +264,39 @@ def save_trace(self, trace):
266264
267265 trace ._greenlet = None
268266
269- if hasattr (sys , ' _current_frames' ):
267+ if hasattr (sys , " _current_frames" ):
270268 if thread_id not in sys ._current_frames ():
271269 if self .greenlet :
272270 trace ._greenlet = weakref .ref (self .greenlet .getcurrent ())
273271
274- if self .asyncio and not hasattr (trace , ' _task' ):
272+ if self .asyncio and not hasattr (trace , " _task" ):
275273 task = current_task (self .asyncio )
276274 trace ._task = task
277275
276+ def thread_start (self , trace ):
277+ current_thread_id = self .current_thread_id ()
278+ if current_thread_id not in self ._cache :
279+ self ._cache [current_thread_id ] = trace
280+ else :
281+ _logger .error (
282+ "Runtime instrumentation error. An active "
283+ "trace already exists in the cache on thread_id %s. Report "
284+ "this issue to New Relic support.\n " , current_thread_id
285+ )
286+ return None
287+
288+ return current_thread_id
289+
290+ def thread_stop (self , thread_id ):
291+ if thread_id :
292+ self ._cache .pop (thread_id , None )
293+
278294 def pop_current (self , trace ):
279295 """Restore the trace's parent under the thread ID of the current
280296 executing thread."""
281297
282- if hasattr (trace , ' _task' ):
283- delattr (trace , ' _task' )
298+ if hasattr (trace , " _task" ):
299+ delattr (trace , " _task" )
284300
285301 thread_id = trace .thread_id
286302 parent = trace .parent
@@ -294,7 +310,7 @@ def complete_root(self, root):
294310
295311 """
296312
297- if hasattr (root , ' _task' ):
313+ if hasattr (root , " _task" ):
298314 if root .has_outstanding_children ():
299315 task_ids = (id (task ) for task in all_tasks (self .asyncio ))
300316
@@ -319,17 +335,19 @@ def complete_root(self, root):
319335 if thread_id not in self ._cache :
320336 thread_id = self .current_thread_id ()
321337 if thread_id not in self ._cache :
322- raise TraceCacheNoActiveTraceError (' no active trace' )
338+ raise TraceCacheNoActiveTraceError (" no active trace" )
323339
324340 current = self ._cache .get (thread_id )
325341
326342 if root is not current :
327- _logger .error ('Runtime instrumentation error. Attempt to '
328- 'drop the root when it is not the current '
329- 'trace. Report this issue to New Relic support.\n %s' ,
330- '' .join (traceback .format_stack ()[:- 1 ]))
343+ _logger .error (
344+ "Runtime instrumentation error. Attempt to "
345+ "drop the root when it is not the current "
346+ "trace. Report this issue to New Relic support.\n %s" ,
347+ "" .join (traceback .format_stack ()[:- 1 ]),
348+ )
331349
332- raise RuntimeError (' not the current trace' )
350+ raise RuntimeError (" not the current trace" )
333351
334352 del self ._cache [thread_id ]
335353 root ._greenlet = None
@@ -354,26 +372,28 @@ def record_event_loop_wait(self, start_time, end_time):
354372 roots = set ()
355373 seen = set ()
356374
357- task = getattr (transaction .root_span , ' _task' , None )
375+ task = getattr (transaction .root_span , " _task" , None )
358376 loop = get_event_loop (task )
359377
360378 for trace in self ._cache .values ():
361379 if trace in seen :
362380 continue
363381
364382 # If the trace is on a different transaction and it's asyncio
365- if (trace .transaction is not transaction and
366- getattr (trace , '_task' , None ) is not None and
367- get_event_loop (trace ._task ) is loop and
368- trace ._is_leaf ()):
383+ if (
384+ trace .transaction is not transaction
385+ and getattr (trace , "_task" , None ) is not None
386+ and get_event_loop (trace ._task ) is loop
387+ and trace ._is_leaf ()
388+ ):
369389 trace .exclusive -= duration
370390 roots .add (trace .root )
371391 seen .add (trace )
372392
373393 seen = None
374394
375395 for root in roots :
376- guid = ' %016x' % random .getrandbits (64 )
396+ guid = " %016x" % random .getrandbits (64 )
377397 node = LoopNode (
378398 fetch_name = fetch_name ,
379399 start_time = start_time ,
0 commit comments