Skip to content

address darwin system parallel threading issues, without numpydoc fixes#708

Closed
s01st wants to merge 2 commits intoAnswerDotAI:mainfrom
s01st:parallel-pr-clean
Closed

address darwin system parallel threading issues, without numpydoc fixes#708
s01st wants to merge 2 commits intoAnswerDotAI:mainfrom
s01st:parallel-pr-clean

Conversation

@s01st
Copy link
Copy Markdown
Contributor

@s01st s01st commented Nov 24, 2025

No description provided.

@s01st
Copy link
Copy Markdown
Contributor Author

s01st commented Nov 25, 2025

@jph00 Thoughts?

@jph00
Copy link
Copy Markdown
Contributor

jph00 commented Nov 26, 2025

Thanks for this. Can you run nbdev_export please, otherwise CI will fail when I try to merge.

I don't actually know what's happening in this PR. Can you tell me about the problem and solution?

@s01st
Copy link
Copy Markdown
Contributor Author

s01st commented Nov 28, 2025

@jph00 can do, apologies, that I forgot to do so

@s01st
Copy link
Copy Markdown
Contributor Author

s01st commented Dec 1, 2025

@jph00 I think this is an issue with nbs/02b_nets.ipynb

Specifically the cell:

test_eq(urljson('https://httpbin.org/get')['headers']['User-Agent'], url_default_headers['User-Agent'])

Re-running this multiple times on both main and on this branch I've found non-deterministic behavior.

@s01st
Copy link
Copy Markdown
Contributor Author

s01st commented Dec 6, 2025

@jph00 thoughts? Or how else should I address this?

@jph00 jph00 closed this Dec 7, 2025
@jph00 jph00 reopened this Dec 7, 2025
Copy link
Copy Markdown
Contributor

@jph00 jph00 left a comment

Choose a reason for hiding this comment

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

I'm not sure about these changes, but certainly interested in discussing them. Would love to hear your more detailed thoughts.

Comment thread fastcore/parallel.py
@wraps(f)
def _f(*args, **kwargs):
res = (Thread,Process)[process](target=g, args=args, kwargs=kwargs)
if process:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't think we want this, do we? I'd expect process=True to give us a normal Process. Why would we want something different on Mac?

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.

Disclaimer my understanding of this is not the best, but I know a bit about concurrency // distributed systems.

My understanding is that what Process does depends on default start method of the platform (which I suppose, could change in an update, but not likely).

Anyway, I believe for true linux systems Process forks a copy while macOS (apple silicon, maybe the old intel chips too) spawns a new interpreter.

So supposing threaded(process=True) linux ends up with the forked process and macOS does spawn which carries the implications of a fresh interpreter, reloading the module, and requires picklablity (which is its own headache in its own way)

There is a push to using spawn, but at the moment picklability + nested functions will likely lead to errors (which I've encountered)

So the below get_context('fork').Process is the special-case to make process=True behave the same way on macOS as it does on Linux — not to make it special, but to avoid macOS being the odd one out.

Provided that I understand.

Comment thread fastcore/parallel.py
if threadpool: pool = ThreadPoolExecutor
else:
if not method and sys.platform == 'darwin': method='fork'
if not method and sys.platform == 'darwin':
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES is a good workaround in practice, in our experience. How about we check for that first?

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.

That certainly helps. Do we want to force that as fix? or UserWarning?

Comment thread fastcore/parallel.py
def run_procs(f, f_done, args):
"Call `f` for each item in `args` in parallel, yielding `f_done`"
processes = L(args).map(Process, args=arg0, target=f)
Proc = get_context('fork').Process if sys.platform == 'darwin' else Process
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

fork feels a bit unexpected to me here too.

@jph00
Copy link
Copy Markdown
Contributor

jph00 commented Dec 7, 2025

@jph00 thoughts? Or how else should I address this?

I re-ran the tests - sorry about that!

@s01st
Copy link
Copy Markdown
Contributor Author

s01st commented Jan 20, 2026

@jph00 thoughts? What needs be done?

@jph00
Copy link
Copy Markdown
Contributor

jph00 commented Jan 27, 2026

@jph00 thoughts? What needs be done?

Need to resolve the questions I asked above. I'll take a look at my end and see if I can find a clean fix.

@s01st
Copy link
Copy Markdown
Contributor Author

s01st commented Jan 27, 2026

I think I put my comments under review. They are marked as pending. Do they not show up?

@jph00
Copy link
Copy Markdown
Contributor

jph00 commented Jan 27, 2026

I think I put my comments under review. They are marked as pending. Do they not show up?

No they don't - I think there must be another button you have to hit to move them from pending to sent...

@s01st
Copy link
Copy Markdown
Contributor Author

s01st commented Jan 28, 2026

Comments

Regarding Process

Disclaimer my understanding of this is not the best, but I know a bit about concurrency // distributed systems.

My understanding is that what Process does depends on default start method of the platform (which I suppose, could change in an update, but not likely).

Anyway, I believe for true linux systems Process forks a copy while macOS (apple silicon, maybe the old intel chips too) spawns a new interpreter.

So supposing threaded(process=True) linux ends up with the forked process and macOS does spawn which carries the implications of a fresh interpreter, reloading the module, and requires picklablity (which is its own headache in its own way)

There is a push to using spawn, but at the moment picklability + nested functions will likely lead to errors (which I've encountered)

So the below get_context('fork').Process is the special-case to make process=True behave the same way on macOS as it does on Linux — not to make it special, but to avoid macOS being the odd one out.

Provided that I understand.

Regarding OBJC_DISABLE_INITIALIZE_FORK_SAFETY

That certainly helps. Do we want to force that as fix? or UserWarning?

Screenshots

Screenshot 2026-01-28 at 9 27 19 AM Screenshot 2026-01-28 at 9 27 26 AM Screenshot 2026-01-28 at 9 27 33 AM

@jph00
Copy link
Copy Markdown
Contributor

jph00 commented Jan 29, 2026

Funnily enough I actually hit this today when running a test on my mac, so just popped in a fix while I was there - I haven't checked, but I suspect it's probably basically the same as you had here. Take a look - if you think it's missing anything, lemme know.

@jph00 jph00 closed this Jan 29, 2026
@s01st
Copy link
Copy Markdown
Contributor Author

s01st commented Jan 29, 2026

@jph00 where is the fix?

@jph00
Copy link
Copy Markdown
Contributor

jph00 commented Jan 29, 2026

1419852

s01st referenced this pull request Jan 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants