When the Ruby started early, only start one thread thread, we can get it from Thread.main.
We can share data in this way:
Thread.main["foo"] = "bar"
threads = 2.times.map do |i|
sleep 0.5
Thread.new(i) do
`notify-send \"#{Thread.main["foo"]} #{i}\"`
end
end
threads.each(&:join)
We can see “bar1” and “bar2” output in the desktop notification.
There’s no fiber-local storage in Crystal because it would be too limiting. One way around it I’ve seen is to monkeypatch things onto Fiber. Also, since there isn’t a Fiber.main, you can just use a constant:
Using the future shard to get the equivalent of Ruby’s Thread#join:
require "future"
MAIN_FIBER = Fiber.current
fibers = Array.new(2) do |i|
sleep 500.milliseconds
`notify-send \"#{Thread.main["foo"]} #{i}\"`
end
fibers.each(&.wait)
Ha! You’re right, that’s what I get for writing on machine that doesn’t have Crystal installed, while distracted.
Try this one, making sure you have the future.cr shard linked above in your shard.yml:
require "future"
MAIN_FIBER = Fiber.current
MAIN_FIBER.foo = "bar"
fibers = Array.new(2) do |i|
future do
sleep 2.seconds
`notify-send \"#{MAIN_FIBER.foo}#{i}\"`
end
end
pp fibers.map(&.get)
class Fiber
property foo : String?
end
I would strongly recommend against reopening Fiber to add an instance variable unless you have very specific reason to do so.
What’s the actual use case, though? What do you need that fiber local variable for?
In the example you’re just passing in a value which can be done in a couple of different ways much easier and less intrusively.
Just because you can do this in Ruby doesn’t mean you should do a similar thing in Crystal. Heck, it doesn’t even mean it was a good call to use it in Ruby in the first place.
Transforming Ruby code to Crystal with a focus on equivalence to the Ruby implementation will inevitably result in bad software design and potentially buggy behaviour. Ruby and Crystal are similar in many ways, but there are also a lot of significant differences (for example in concurrency mechanisms).
IMO it’s a wast of time if you’re just trying to find equivalents for Ruby and you’ll get a sub-par result.
Try to solve the problem in Crystal instead. Use the tools you have in Crystal. The Ruby implementation can serve as inspiration, of course.
Make it about the problem you want to solve, not about translating one solution for the problem found in Ruby.
Here, for example - I am trying to write small “universal domain object file data store” with simple transactions (consistent domain model changes) and that’s why I definitely need something like “transaction context” and that’s why, for example, I definitely need context from places in the code (entity getters and setters for example, lazy loading…) where I have no other way to get the context than from the “thread/fiber local variable” (every transaction will be in its own fiber/thread). So I can end up with transparent domain model changes like this (thats a dream):
OOStore.transaction do |t|
o = t.get_some_object_from_the_file_store
o.addresses.first.city # another "object relations" lazy loaded
o.name = "new name"
# no need to call some nasty methods like dirty or save
end
# bang, done, changes written to the file in a transaction...
Is there any other solution or approach without the use of “thread local” var with something like “transaction context”?
I have also question about locking, I need 1) simple synchronization over some value/hash/cache 2) and something like one writer + many non-blocking readers + one exclusive lock for all - when I was looking around for something like that, I found a class Crystal::SpinLock (nodoc) for example, what’s it good for? Is that some kind of “very fast” simple sync lock?
Thanks for explain, you probably right, maybe some code in original ruby not necessary when use Crystal, or need to do totally different way, even my final Crystal code might be cut in half.
Anyway, i will ask some alternative API for Crystal, e.g. IO.eof? equivalent in Crystal, this should be a generic system API call, probably Crystal not implemented, or hidden somewhere i can’t find it.
You can try to read from it. If you get no bytes then it’s at the end.
I’m thinking adding eof? to Crystal might be a good idea. It’s just thst: read and see if there’s no more data. For a socket that would block forever unless the other end closes it. But it’s fine, as long as we document it.
I don’t get in what way your use case demands for thread/fiber local storage. I see a transaction block and some stuff happening in that transaction based on an object model. As far as I can tell, any information about a transaction context can be encapsulated in the transaction object an it would be accessible from all locations where it would need to be.
The loaded object o has no idea where any transaction is (it’s domain objec / entity) and it is inappropriate to pass any transaction context or some sort of DB instances manually somewhere to the domain objects (entities) - so, for example o getters addresses, city (generated by my own property macros with lazy loading logic) can get active running transaction context from the thread/fiber local var and do some lazy loading (from inside the entity). It’s pretty common to store some active contexts like transaction context in thread local var IMHO (ORM developers?).