Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle failure during thread creation #2471

Merged
2 changes: 1 addition & 1 deletion sentry_sdk/metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ def _ensure_thread(self):
try:
self._flusher.start()
except RuntimeError:
# Unfortunately at this point the interpreter is in a start that no
# Unfortunately at this point the interpreter is in a state that no
# longer allows us to spawn a thread and we have to bail.
self._running = False
return False
Expand Down
9 changes: 8 additions & 1 deletion sentry_sdk/monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,14 @@

thread = Thread(name=self.name, target=_thread)
thread.daemon = True
thread.start()
try:
thread.start()
except RuntimeError:

Check warning on line 58 in sentry_sdk/monitor.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/monitor.py#L56-L58

Added lines #L56 - L58 were not covered by tests
# Unfortunately at this point the interpreter is in a state that no
# longer allows us to spawn a thread and we have to bail.
self._running = False
return None

Check warning on line 62 in sentry_sdk/monitor.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/monitor.py#L61-L62

Added lines #L61 - L62 were not covered by tests
sentrivana marked this conversation as resolved.
Show resolved Hide resolved

self._thread = thread
self._thread_for_pid = os.getpid()

Expand Down
8 changes: 7 additions & 1 deletion sentry_sdk/profiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -918,7 +918,13 @@
# can keep the application running after other threads
# have exited
self.thread = threading.Thread(name=self.name, target=self.run, daemon=True)
self.thread.start()
try:
self.thread.start()
except RuntimeError:

Check warning on line 923 in sentry_sdk/profiler.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/profiler.py#L923

Added line #L923 was not covered by tests
# Unfortunately at this point the interpreter is in a state that no
# longer allows us to spawn a thread and we have to bail.
self.running = False
return

Check warning on line 927 in sentry_sdk/profiler.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/profiler.py#L926-L927

Added lines #L926 - L927 were not covered by tests
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find it slightly confusing here that the method is called ensure_running, but it is possible for the method to terminate without the thread running. We should have some way to indicate failure, perhaps by raising a custom exception when ensure_running cannot start the scheduler or by returning a value to indicate success/failure. We might instead or additionally consider renaming the function to indicate that we may fail to ensure that the scheduler is running and/or add a documentation comment.

If we make this change to indicate when the ensure_running fails, we should also make sure to handle failure appropriately in the calling code.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@szokeasaurusrex Added docstrings to the three ensure_running functions.

I considered making the function return a bool, but in all three cases it already communicates success/failure by setting the self.(_)running variable, so it feels a bit redundant to make it return essentially the same information, especially since the calling places usually can't do much with the return value -- all code that depends on ensure_running being run checks the value of self.(_)running instead anyway. Which feels like an ok pattern to me.

Regarding the other points:

  • added some tests
  • added the same logic to the gevent scheduler (I checked and it would also attempt to spawn a thread)


def run(self):
# type: () -> None
Expand Down
10 changes: 9 additions & 1 deletion sentry_sdk/sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,17 @@

thread = Thread(target=_thread)
thread.daemon = True
thread.start()
try:
thread.start()
except RuntimeError:

Check warning on line 125 in sentry_sdk/sessions.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/sessions.py#L125

Added line #L125 was not covered by tests
# Unfortunately at this point the interpreter is in a state that no
# longer allows us to spawn a thread and we have to bail.
self._running = False
return None

Check warning on line 129 in sentry_sdk/sessions.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/sessions.py#L128-L129

Added lines #L128 - L129 were not covered by tests
sentrivana marked this conversation as resolved.
Show resolved Hide resolved

self._thread = thread
self._thread_for_pid = os.getpid()

return None

def add_aggregate_session(
Expand Down