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

import numpy asynchronously causes EXC_BAD_ACCESS #85

Open
LeoneBacciu opened this issue Apr 17, 2024 · 10 comments
Open

import numpy asynchronously causes EXC_BAD_ACCESS #85

LeoneBacciu opened this issue Apr 17, 2024 · 10 comments
Assignees
Labels
bug Something isn't working

Comments

@LeoneBacciu
Copy link

Hello,
the package is amazing, but I encountered an error that makes it impossible to continue developing.
I am working on macOS with an Apple Silicon processor (M2).
The line import numpy as np always makes the code (and the whole flutter app) crashes with EXC_BAD_ACCESS, specifically, I tracked down the error to the line let result = PyRun_SimpleFileEx(file, appPath, 1) in SeriousPythonPlugin.swift.
The interesting thing is that if I use sync: true, the python code gets executed correctly without issues, but the flutter app hangs, so it's not a viable solution.
I've tried using Isolates, but either the method channel cannot be invoked, or the flutter UI hangs (if I use BackgroundIsolateBinaryMessenger.ensureInitialized).
Let me know if I can provide more info.

@FeodorFitsner
Copy link
Contributor

Are you able to provide the code to reproduce?

@LeoneBacciu
Copy link
Author

LeoneBacciu commented Apr 17, 2024

Sure, are the files for the simplest example:

main.py:

import numpy as np

if __name__ == '__main__':
    print("Success")

requirements.txt:

numpy

The flutter code is a default boilerplate app, with the following update:

void main() {
  SeriousPython.run("app/app.zip", sync: false);

  runApp(const MyApp());
}

The app.zip is generated via dart run serious_python:main package.
As said before, if sync: true the code works, otherwise everything crashes.
I am building for macOS.

@FeodorFitsner
Copy link
Contributor

I haven't verified yet, but just a quick though...Flutter on Apple ARM should generate a fat binary with both x86_64 and arm64 architectures (check that with file utility) and, in theory, you should be using arm64 version of Python as well (not x64 running under Rosetta). I mean it could be arch incompatibilities of Flutter app and numpy libs installed during the packaging. Could it be the case?

@LeoneBacciu
Copy link
Author

LeoneBacciu commented Apr 17, 2024

I don't think so, as it works perfectly (I can call numpy functions from python) with sync: true.
It seems to be a problem with multithreading in the Swift code. It could be a GIL related problem.

@FeodorFitsner
Copy link
Contributor

Will be looking into that - it should be a solution to that.

@FeodorFitsner FeodorFitsner added the bug Something isn't working label Apr 17, 2024
@jackie099
Copy link

Following, having same issue with my m1 max MacBook pro.

@FeodorFitsner
Copy link
Contributor

Here's the source of this issue: python/cpython#96650

Setting OPENBLAS_NUM_THREADS environment variable in Python program does the trick:

import os

os.environ["OPENBLAS_NUM_THREADS"] = "1"

import numpy as np

if __name__ == "__main__":
    print("Random:", np.random.rand())

Another workaround that proves there is something with threads - importing numpy inside Python's thread works:

from threading import Thread

def test_numpy():
    import numpy as np
    print(np.random.rand())

if __name__ == "__main__":
    thread = Thread(target=test_numpy)
    thread.start()
    thread.join()
    print("Finished")

FeodorFitsner added a commit to flet-dev/flet-build-template that referenced this issue Apr 18, 2024
FeodorFitsner added a commit to flet-dev/flet-build-template that referenced this issue Apr 18, 2024
@FeodorFitsner
Copy link
Contributor

Upon further investigation setting explicit stack size for a new thread in Swift fixes numpy issue as well.

Default stack size for a new thread on macOS is 512 KB (524,288 bytes). Setting it to 1 MB does not cause the crash:

let t = Thread(target: self, selector: #selector(runPythonFile), object: appPath)
t.stackSize = 1 * 1024 * 1024 // 1 MB
t.start()

It could be a better solution for serious_python/Flet going forward rather than setting OPENBLAS_NUM_THREADS variable.

@LeoneBacciu
Copy link
Author

LeoneBacciu commented Apr 18, 2024

Amazing debugging! What do you think an optimal stack size should be? To avoid future issues I would propose to set it to 8MB, which is the stack size of the main thread on macOS. I think this is reasonable, as the function of that thread is basically that of a main thread.

Edit: I checked and it works for me too!

@FeodorFitsner
Copy link
Contributor

iOS has 1 MB main stack and 512 KB for secondary (source).

Will do a parameter! 😏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
Status: 🆕 New
Development

No branches or pull requests

3 participants