Skip to content

Make the workqueue threading backend once again fork safe. - #8195

Merged
sklam merged 2 commits into
numba:mainfrom
stuartarchibald:fix/7872_056
Jun 27, 2022
Merged

sklam merged 2 commits into
numba:mainfrom
stuartarchibald:fix/7872_056

Conversation

@stuartarchibald

Copy link
Copy Markdown
Contributor

As title, this fixes the workqueue threading backend such that it
is fork safe from any thread, not just the main thread.

Fixes #7872

As title, this fixes the workqueue threading backend such that it
is fork safe from _any_ thread, not just the main thread.

Fixes numba#7872
lgarrison
lgarrison previously approved these changes Jun 27, 2022

@lgarrison lgarrison left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Looks good to me! The logic seems pretty straightforward: if a parallel function is called and the thread pool is not yet initialized, start it. queues = NULL is used to signal that the thread pool is not active, via a reset_after_fork() handler.

The other piece seems to be that the global NUM_THREADS must be set by the caller before launch_threads() is entered (i.e. set by _launch_threads() in Python). But once it is set, it will be initialized in any subprocesses (because of NUM_THREADS = _INIT_NUM_THREADS; in the fork handler), so subprocesses don't need to call _launch_threads() again.

I guess this means that subprocesses inherit the same upper limit on threads as the parent process. But this is probably fine, since the upper limit is set explicitly by NUMBA_NUM_THREADS or inferred from the parent process affinity mask. Changing either of these in a child process would be a strange use-case, and so I think the current behavior makes sense.

I checked that this PR fixes the original issue abacusorg/abacusutils#47, and fixes the test cases derived from that issue.


# now run in a multiprocessing pool to get a fork from a
# non-main thread
with multiprocessing.Pool(10) as p:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

needs to force a fork context here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good point, OSX defaults to 'spawn'.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

b07d687 fixes, also adds a test skip for OS which don't have fork(2) and adds an assert to ensure the expected threading layer was used.

@stuartarchibald

Copy link
Copy Markdown
Contributor Author

Looks good to me! The logic seems pretty straightforward: if a parallel function is called and the thread pool is not yet initialized, start it. queues = NULL is used to signal that the thread pool is not active, via a reset_after_fork() handler.

The other piece seems to be that the global NUM_THREADS must be set by the caller before launch_threads() is entered (i.e. set by _launch_threads() in Python). But once it is set, it will be initialized in any subprocesses (because of NUM_THREADS = _INIT_NUM_THREADS; in the fork handler), so subprocesses don't need to call _launch_threads() again.

Thanks for the detailed review @lgarrison, much appreciated.

I guess this means that subprocesses inherit the same upper limit on threads as the parent process. But this is probably fine, since the upper limit is set explicitly by NUMBA_NUM_THREADS or inferred from the parent process affinity mask. Changing either of these in a child process would be a strange use-case, and so I think the current behavior makes sense.

I thought the same, an upper thread limit that changes would be unusual, and also quite tricky to implement. Such a behaviour could be emulated by creating a larger than needed upper limit and then using thread masks if desired.

I checked that this PR fixes the original issue abacusorg/abacusutils#47, and fixes the test cases derived from that issue.

Great, thanks for checking.

@stuartarchibald stuartarchibald added 4 - Waiting on reviewer Waiting for reviewer to respond to author and removed 3 - Ready for Review labels Jun 27, 2022

@sklam sklam left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

@sklam sklam added 5 - Ready to merge Review and testing done, is ready to merge and removed 4 - Waiting on reviewer Waiting for reviewer to respond to author labels Jun 27, 2022
@sklam
sklam merged commit bda89da into numba:main Jun 27, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

5 - Ready to merge Review and testing done, is ready to merge Effort - medium Medium size effort needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Interpreter hangs when running parallel function in subprocess with workqueue backend

3 participants