-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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
rust: switch to stable compilers #752
Conversation
e96d8bc
to
850ae85
Compare
I have no fundamental objections to switching to stable, but currently 'make check' is failing - either another way of excluding that file needs to be found, or it needs to be automatically formatted when it's created |
The simplest solution might be to support compiling with stable but use |
Alright, |
Thanks for doing that. Have you used this in testing for any period of time yet? I seem to recall issues using a newer pyo3 - it was previously rolled back in 17edbd1 |
An easy way to trigger it is to use Tools>Check Database. |
Yup, I just managed to trigger a |
I fear there's no easy fix for this. We can't chuck a mutex at it because we need to be able to run multiple calls in parallel. Changing the code to run things like syncing on a background thread is probably the long term fix, but it will be a some time before I have a chance to look into that. |
prost-build doesn't generate rustfmt-safe code, so we had to add it to the ignore list for rustfmt on "make check". However, the ignore list isn't supported by stable rustfmt, so we have to work around this some other way -- in this case, just do "rustfmt" on the generated file in "build.rs" (this way, formatting errors in checked-in source code are still caught but generated code doesn't cause spurrious errors). Signed-off-by: Aleksa Sarai <[email protected]>
tl;dr I think I found the easy fix for this. I started working on it yesterday and I have a version which doesn't trigger The main change which makes this entire procedure much simpler is to simply switch Making this minor change reveals that there only two things that aren't
With these changes, I can now build Anki and it no longer has any (As an aside, this is probably also a general memory safety issue because the need for locking behind a |
4ea905c
to
9fda265
Compare
This comment has been minimized.
This comment has been minimized.
Appreciate you looking into it - that didn't turn out to be as bad as I was expecting. Rather than having the runtime created as needed, I'd been planning to pass a handle in at backend creation time, so that a single runtime can be shared by multiple collections & configured by the callers. It's not urgent though, so happy to run with once_cell for now if you don't feel like changing it. A vec or hashmap of abort handles would be better, but until then, I'd prefer the code doesn't panic - just logging to the console should suffice. |
In addition to the following, I'll remove
I'll take a look but if it ends up being a bit odd to implement, I can do that in a follow-up PR.
Yup, I'll just make it log for now -- the slightly tricky thing is that Though it should be noted that One other thing I wanted to mention which I noticed while looking a bit more at the abort code is that there appears to be three different ways of aborting an operation:
I won't touch these in this PR (since it's not really relevant), but I do wonder whether this could be simplified a little bit -- is there a case where a user or plugin would want to abort media synchronisation but not any other kind of synchronisation that is happening at the same time? In addition (maybe I'm wrong here) but shouldn't the whole Future abort system avoid the need for Thanks. |
I'll leave the backend configuration for a separate PR since we'd need to update the protobuf schema in order to add the necessary information so that Python can configure the runtime -- and if you wanted to share the same runtime among |
The previous implementation had some slightly questionable memory safety properties (older versions of PyO3 didn't uphold the Rust aliasing rules and would thus create multiple &mut references to #[pyclass] objects). This explains why Backend has internal Mutex<T>s even though all of its methods took &mut self. The solution is to simply make all methods take &self, which luckily doesn't pose too make issues -- most of the code inside Backend already has sufficient locking. The only two things which needed to be explicitly handled where: 1. "self.runtime" which was fairly easy to handle. All usages of the Runtime only require an immutable reference to create a new Handle, so we could switch to OnceCell which provides lazy-initialisation semantics without needing a more heavy-handed Mutex<tokio::runtime::Handle>. 2. "self.sync_abort" was simply wrapped in a Mutex<>, though some of the odd semantics of sync_abort (not being able to handle multiple processes synchronising at the same time) become pretty obvious with this change (for now we just log a warning in that case). In addition, switch to an RAII-style guard to make sure we don't forget to clear the abort_handle. As a result, we now no longer break Rust's aliasing rules and we can build with newer versions of PyO3 which have runtime checks for these things (and build on stable Rust). Signed-off-by: Aleksa Sarai <[email protected]>
We can't link against libzip2.so.1 when building manylinux wheels, but the only dependency which required this was "zip" and we don't appear to make use of bzip2 in Anki. This fixes the following "make build" error on Linux: x maturin failed Caused by: Failed to ensure manylinux compliance Caused by: Your library is not manylinux compliant because it links the following forbidden libraries: ["libbz2.so.1"] Signed-off-by: Aleksa Sarai <[email protected]>
The only dependency which required nightly compilers was pyo3, which supports stable Rust (1.39.0 and later) since version 0.11.0. Supporting stable Rust makes it easier to package Anki for distributions. No other code changes were required. Signed-off-by: Aleksa Sarai <[email protected]>
Thank you for your work on this! The two separate methods of cancelling is ugly, but address different needs - the progress callback can be used to abort long-running operations on the current thread like "check media" and the media folder scan at the start of a media sync, and the abort handle is used to abort network operations that may hang for long periods of time without calling the progress handler. When the media checking code was written we hadn't yet bought into any of the async ecosystem, and while the code could theoretically be moved to a blocking future pool, AFAIK tokio can not actually terminate code running in the blocking pool - so an abort handle would return control, but the code would continue running until it completed in the background, with its computed results being discarded. By hashmap, I was thinking of the key identifying which kind of operation it is, eg "media_sync" or an integer constant for each different operation. |
Just in case you aren't aware, currently an anki build still needs nightly rust to build the In any case, thank you for your efforts! This PR is already a big step. |
This comment has been minimized.
This comment has been minimized.
Oh, I misunderstood -- |
When I tested it in the past, orjson was significantly faster than the stock json library for bulk DB operations. You could patch the compatibility wrapper for the latter back into Anki if you want to avoid the pre-built orjson binary on PyPI, but your users would be getting a slower version of Anki. Doesn't look like orjson's in a hurry to move away from nightly unfortunately: ijl/orjson#108 |
Thanks for the suggestion. Pre-built binaries can be problematic, not just from a purity standpoint but also from a pragmatic one (supporting different architectures for example). Slowness is probably better than being stuck on an old version. Can the standard library version be used as a drop-in replacement for Anki's purposes? |
Yes: b0aedd6 |
Great, thank you! |
I've added the wrapper back to the latest git: f9e939a |
Thanks again :) |
The only dependency which required nightly compilers was pyo3, which
supports stable Rust (1.39.0 and later) since version 0.11.0. Supporting
stable Rust makes it easier to package Anki for distributions.
In addition, we can't link against libzip2.so.1 when building manylinux
wheels, but the only dependency which required this was "zip" and we
don't appear to make use of bzip2 in Anki.
One additional problem is that Anki was inadvertently violating Rust's aliasing
rules (it was implicitly passing out multiple
&mut Backend
references) andthe pyo3 update revealed this behaviour. The solution is to make all of the
methods take
&self
-- removing allPyMutBorrowError
s -- and then clean upanything that failed to compile. Since
Backend
already had plenty of internallocking (further pointing to aliasing violations) this wasn't too complicated
to do.
Signed-off-by: Aleksa Sarai [email protected]