17
17
import sys
18
18
import threading
19
19
import typing
20
+ import os
20
21
from enum import Enum
21
- from os import environ , linesep , register_at_fork
22
+ from os import environ , linesep
22
23
from typing import Optional
23
24
24
25
from opentelemetry .context import (
@@ -197,7 +198,9 @@ def __init__(
197
198
None
198
199
] * self .max_export_batch_size # type: typing.List[typing.Optional[Span]]
199
200
self .worker_thread .start ()
200
- register_at_fork (after_in_child = self ._at_fork_reinit )
201
+ # Only available in *nix since py37.
202
+ if hasattr (os , "register_at_fork" ):
203
+ os .register_at_fork (after_in_child = self ._at_fork_reinit )
201
204
202
205
def on_start (
203
206
self , span : Span , parent_context : typing .Optional [Context ] = None
@@ -222,12 +225,23 @@ def on_end(self, span: ReadableSpan) -> None:
222
225
self .condition .notify ()
223
226
224
227
def _at_fork_reinit (self ):
225
- self .condition ._at_fork_reinit ()
228
+ # worker_thread is local to a process, only the thread that issued fork continues
229
+ # to exist. A new worker thread must be started in child process.
226
230
self .worker_thread = threading .Thread (
227
231
name = "OtelBatchSpanProcessor" , target = self .worker , daemon = True
228
232
)
229
233
self .worker_thread .start ()
230
234
235
+ # could be in an inconsistent state after fork, reinitialise by calling `_at_fork_reinit`
236
+ # (creates a new lock internally https://github.com/python/cpython/blob/main/Python/thread_pthread.h#L727)
237
+ # if exists, otherwise create a new one.
238
+ if hasattr (self .condition , "_at_fork_reinit" ):
239
+ self .condition ._at_fork_reinit ()
240
+ else :
241
+ self .condition = threading .Condition (threading .Lock ())
242
+
243
+ self .queue .clear ()
244
+
231
245
def worker (self ):
232
246
timeout = self .schedule_delay_millis / 1e3
233
247
flush_request = None # type: typing.Optional[_FlushRequest]
0 commit comments