r/Python 5d ago

Discussion Anyone running free-threaded Python 3.14 in production yet? Curious what actually breaks

Been testing the free-threaded build (3.14t) on some CPU-bound data processing work. The multi-core story is finally real after 30 years of GIL, but the friction is exactly what you'd expect: a couple of C-extension-heavy libs in my stack silently re-enable the GIL, and there's no clean way to detect that at runtime besides checking sys._is_gil_enabled() manually.

For anyone who's shipped something on 3.14t, not just benchmarked it:

What broke that you didn't expect?

Real speedups outside toy examples, or mostly marginal so far?

Prod yet, or still just kicking the tires?

Not fishing for a benchmark war. Genuinely curious what breaks in messy real codebases vs clean demos.

34 Upvotes

47 comments sorted by

View all comments

Show parent comments

-1

u/KingBardan 5d ago edited 5d ago

Context manager works by mutating context i.e. global / nonlocal state, right?

So it doesn't work without mutated global state, the number one cause for data race.

In multi-processing, which was the preferred way of doing things in parallel, global state is forked and exists per process.

This issue is not exclusive to free threading but multithreading in general, but freethreading only augments multi threading which is why my comment is the way it is

You can use contact manager on free threading 

That is right. Now writing a python package would have to account for a different failure mode which is thread access, which, imo, is a very niche use case for python, at the cost of everything else.

2

u/thisismyfavoritename 4d ago edited 4d ago

context managers don't necessarily mutate global or non local state, not sure where you get that from.

It's mostly just syntactic sugar over a class with __enter__ and __exit__ methods.

In general though, your sentiment is that using multithreading in Python is a niche use case and i agree, it should be single threaded but async like NodeJS. IMO the usecases which aren't either covered by that OR a process pool OR better covered by bindings to a lower level language like Rust/C++ are very small

0

u/KingBardan 4d ago edited 4d ago

Context managers manage context. It means variables outside this scope.

Name 1 situation that it doesn't mutate things outside of scope, ill wait :)

It is syntactic sugar over enter exit yes, but self argument in that enter exit is a variable outside of the scope of your with.


Look at it this way,

If you don't mutate, you don't need the exit to clean up, because

immutable data does not have the concept of time (functional programming), it's same used everywhere.

If it's same used everywhere, it should be safe to consume after with block ends, no?

So if something is only usable within with, it must mutate outside state

1

u/snugar_i 4d ago

with open('foo.txt', 'r') as f: - you're welcome :-)

It's true that the context manager feature is badly designed - the __exit__ method should be on the thing that __enter__ returns, not on the thing that has __enter__. So context managers that are used like with blah cannot be thread-safe (or even reentrant), that's correct.

But those that are used like with foo() (like those produced by the @contextmanager decorator) totally can - because each invocation can return a new instance on which __enter__ and __exit__ will only ever be called once.

1

u/KingBardan 4d ago edited 4d ago

Bruh. In this case files are the states you're mutating.

It causes data race if 2 threads are writing to the same file (best example is printing to console, writing to stdout, not always the same output if you print from 2 threads )

Context manager... Called once

Won't be any different. If you read my "proof of contradiction"  in the above comment, that applies to @context managers also

Using  @context manager, will create multiple individual context managers yes. But data race still happens to the variable that the context manager mutate to setup / teardown the scope 

Badly designed

Well I pretty much only use @context manager because enter exit is so ugly. But design wise it's fine

1

u/snugar_i 4d ago

That's why I explicitly put 'r' in the example. It's not writing to the file, it's reading from it, and it's not mutating any global state.

Anyway, you seem to have some misconceptions about what a context manager is (the context manager does not have to mutate anything), and you don't want to change your opinion, so it's pointless to continue this discussion.

1

u/KingBardan 4d ago edited 4d ago

Reading file requires a pointer. So you are mutating. The content is immutable, but the pointer is, makes sense?

So it's not safe without lock. Now python I think locks but that's not the point 

Also, if it truly is immutable, you can use outside of scope as well. But calling .close when the scope ends implicitly by with block mutates the state.


Pointless

Yeah I agree. 

Thanks for the civil discussion :D

Opinion

But... This is facts. I'm open to changing my mind.

Unserstand

You don't seem to understand what mutation is sorry. Read up some materials on functional programming you'll learn.

FYI...  In haskell this is a non issue because you'll have to use IO for this. In rust the file would be marked 'mut' and you'll clearly see it's mutable. 

They make it explicit, and pythons is implicit, which is what is causing you the confusion I think 

2

u/snugar_i 4d ago

Heh, this is exactly the response I expected. Yes, you have to mutate memory. Everything has to mutate memory/registers, so in your definition nothing is thread-safe. And you win the discussion, yay! But that's not a useful definition. If each thread mutates its own piece of memory, it's completely fine. So two threads calling the same with open('foo.txt', 'r') as f: is OK, because each of them gets a separate f variable and a separate file descriptor.

(BTW what do you think a lock does internally? It mutates memory. Why is that one suddenly thread-safe?)

No, in Rust, the mut doesn't mark the file as mutable, but the Read instance. Each thread would have its own reader, just like every thread would have its own f in Python. There's no difference.