Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
  • Feb 16 20:43
    ioquatix edited #103
  • Feb 16 20:43
    ioquatix edited #103
  • Feb 16 20:43
    ioquatix opened #103
  • Feb 16 20:42

    ioquatix on io-wait_readable-nil

    Return nil instead of throwing … (compare)

  • Feb 16 20:28
    orlando-labs commented #102
  • Feb 16 20:22
    ioquatix commented #102
  • Feb 16 20:22
    ioquatix commented #102
  • Feb 16 12:01
    orlando-labs edited #102
  • Feb 16 11:59
    orlando-labs opened #102
  • Feb 14 07:33

    ioquatix on master

    Modernize gem. (compare)

  • Feb 14 07:18

    github-actions[bot] on gh-pages

    Initial gh-pages commit Deploying to gh-pages from @ so… (compare)

  • Feb 14 07:16

    ioquatix on master

    Modernize gem. (compare)

  • Feb 14 07:15

    ioquatix on linked-list

    (compare)

  • Feb 14 07:15

    ioquatix on cancelled-from

    (compare)

  • Feb 14 07:15

    ioquatix on stop-fix

    (compare)

  • Feb 14 07:15

    ioquatix on olleolleolle-add-27

    (compare)

  • Feb 14 07:15

    ioquatix on transient-tasks

    (compare)

  • Feb 14 07:15

    ioquatix on nested-logger

    (compare)

  • Feb 14 07:15

    ioquatix on ruby-3-testing

    (compare)

  • Feb 14 07:15
    ioquatix closed #83
nicholas a. evans
@nevans
Would you use a kwarg e.g. fiber.raise(ex [, msg [, array]], transfer: true) to work on transferring fibers, or just always allow it and let Fiber#raise figure out whether to call fiber_switch with args for yielding.resume vs transferring.transfer?
I do think the downward propagating semantics for resuming fibers that I added to Fiber#cancel should not be added to Fiber#raise, but deserve their own method. And I also think that "break/return" and "don't raise in return fiber" semantics make more sense as the defaults vs "raise" semantics. But I added a table of potential cases (with suggested kwargs and suggested use-cases) to my comment here: https://bugs.ruby-lang.org/issues/17325#note-12
nicholas a. evans
@nevans
I'm not working as much this week at my dayjob, because I'm "attending" RubyConf, so I'll definitely have time for a first pass of a "raise on transferring fibers" PR. I'll use a transfer: true kwarg in my first pass. But it's not hard to make it automatic if people think that's a better idea.
Samuel Williams
@ioquatix
It should be automatic
don't bother with a kwarg
Samuel Williams
@ioquatix
by the way thanks for your effort.
nicholas a. evans
@nevans
ahh... well... I just finished implementing it with the kwarg. I guess I'll take that out later tonight or tomorrow. :) https://bugs.ruby-lang.org/issues/17331
IMO, transferring vs yielding makes a difference in where control is passed back to when the fiber exits. Since this has a high likelihood of causing an immediate fiber switch (due to termination) back to--where??? return fiber??? last fiber in the root fiber resuming stack???--it feels like it makes at least as much sense to use an explicit arg here as it does to use different methods for resume and transfer. But I don't really mind making it implicit.
nicholas a. evans
@nevans
I had different reasons for wanting Fiber#cancel to be implicit: I wanted fiber.cancel to always be simple and safe just like io.close. But I've actually been very annoyed by the potential exploding complexity from not knowing whether a cancellation uses resume vs transfer and when/how the caller should expect control to return to it. I think it's reasonable for the caller to know what kind of fiber it's dealing with. I could be convinced 1) both should be automatic, 2) both should be explicit, or 3) raise should be explicit and cancel should be automatic (my current PRs).
Samuel Williams
@ioquatix
The use case for Fiber.raise is to go back to a fiber that has called Fiber.yield or Fiber#transfer. I think the code would know which one, but we don't want to introduce Fiber.raise_by_resume and Fiber.raise_by_transfer because it just seems cumbersome and we already know it internally.
So, both should be implicit.
Once it goes back to the fiber, how the fiber exits is up to the fiber.
nicholas a. evans
@nevans
Yeah, I think I've come around to that. The code should know already which one to use and there's not much benefit in forcing the code to prove it knows. OTOH, I'm still very against violating https://bugs.ruby-lang.org/issues/17221 (or at least what I perceive as a violation) wrt resuming fibers. If you don't know which fiber you're raising on, you probably shouldn't be raising on it, IMO.
nicholas a. evans
@nevans
Even though I did suggest doing something like it myself here. I was just enumerating all plausible permutations; I've never had need for it myself and I hadn't really thought through the implications yet.
Samuel Williams
@ioquatix
The main reason for Fiber#raise is to more easily implement Async::Task#stop.
Samuel Williams
@ioquatix
You should take a look at the logic of Async::Task#stop. It's probably one of the most complex parts of Async despite it's usage being incredibly simple.
It seems like you know this already, but it has to handle stopping fibers which are already resumed...
nicholas a. evans
@nevans
Yeah, I've written many bad versions of this exact code over the last decade. ;) I just looked it up and the article that taught me how to use fibers was 10 years ago! wow. https://www.igvita.com/2010/03/22/untangling-evented-code-with-ruby-fibers/
I'm hoping Fiber#cancel can combine the common core of the best (ie my favorite) of what I saw in em-synchrony, async, kotlin, golang, etc and my own fiber-hacking. I've been trying to think of the most "ruby-ish" way to meld all of that, and it seemed to me that adhering as much as possible to the metaphor of "break/return/throw for fibers" ought to be a good way to follow the PoLS and keeping it simple while still being useful to a variety of different use cases.
nicholas a. evans
@nevans
I expect most users should still use async (or some other task management library) and task.stop rather than interact with Fibers directly. But working directly should be easy and safe too, IMO. I use both raise and cancel on my tasks--I think there are uses cases for both. :)
nicholas a. evans
@nevans
@ioquatix I only realized last week that 3.0's root fiber is always blocking. If I'm not mistaken, that means we can't auto-vivify a scheduler with its own fiber and auto-transfer into it from root (not unless we continue to use non-blocking wrappers.) IIRC, lightio does this (only if you use its wrappers or monkey patches, of course). To really simulate green threads or goroutines, a scheduler should be able to opt-in to non-blocking root, right? Maybe in 3.1?
nicholas a. evans
@nevans
also, for my Fiber#cancel PR, I was originally punting on the "how does control return to current fiber when canceling resuming or transferring fibers." because I wanted to keep it simple and that was out of scope: that's up to the scheduler, right? But maybe a better PR would add an optional hook to the scheduler that's called when resuming or transferring fibers are canceled (or when transferring fibers are raised!)?
also, your working through the remaining blocking points like Process.wait and Addrinfo.getaddrinfo is super awesome and way more important than taking a look at my code or questions. :)
lunarfyre7
@lunarfyre7
This is a silly Q, but how does async compare to ruby-concurrency?
lunarfyre7
@lunarfyre7
nvm, they seem very different
Samuel Williams
@ioquatix
concurrent-ruby is a toolbox of primitives.... that I'm not sure actually solve any real problem and introduce a bunch of problems into your code.
I've used it in the past... it seems well put together.
lunarfyre7
@lunarfyre7
Yeah completely different objectives it seems
Tim Hatch
@tim_hatch_twitter
Hi. Let me know if this the wrong place to ask, but could anyone whether I might be able to use the socketry/timers gem to set up a nested interval timer. I have a particular problem which requires a countdown from 5:00 to 0.00 followed by a second countdown from 0:15 to 0:00, whereupon the countdowns repeat.
Samuel Williams
@ioquatix
That should be easy to implement even without any extra
gems
Tim Hatch
@tim_hatch_twitter
Appreciate that. Wanted to explore how best do implement this without timer blocking other input. It gave me the opportunity to look at the Async and Timers gems, but examples are thin on the ground so it's somewhat uphill.
Samuel Williams
@ioquatix
I see do you mean like terminal input?
Tim Hatch
@tim_hatch_twitter

Yes, that would be one case. Mainly I'm trying to get my head around the Async way of doing things by taking simple examples and trying them out.
I thought the timers gem might be something to start with before moving on to figuring out how to use Async for general concurrency work.
I couldn't find much in the way of example usage for these gems.
I fashioned something which seems to work by creating two timers "main" and "rest" and running them like so:

main.resume
rest.pause
10.times { timers.wait }
main.pause
rest.resume 
5.times { timers.wait }

which creates the effect I'm after (the 10/5 values here are arbitrary) but I wonder if there's a more elegant solution which I'm missing.

Samuel Williams
@ioquatix
It seems like you might be overthinking this:

require 'async'

def countdown(trigger_time)
    while trigger_time > Time.now
        remaining = trigger_time - Time.now
        puts "Remaining #{remaining}"
        Async::Task.current.sleep(remaining < 1.0 ? remaining : 1.0)
    end

    puts "Triggered"
end

Async do |task|
        countdown(Time.now+60*5)
        countdown(Time.now+15)
end
Tim Hatch
@tim_hatch_twitter
I thought I might be overthinking. I think I can see how what you've done could make sense - I'll read the source to make sure I understand the mechanics. Thanks for the pointer.
nicholas a. evans
@nevans
I just saw https://bugs.ruby-lang.org/issues/18020 (the IO::Buffer proposal) but ... my need for nice buffer implementations (e.g. like go's bufio) was the very first time I started really digging into ruby's C source code. There are at least three different buffered IO implementations in ruby's stdlib itself: OpenSSL::Buffering, Net::Protocol::BufferedIO, and of course the implementation in IO. Looking around at gems, of course async had its own, but so do at least another dozen easily googlable gems.
nicholas a. evans
@nevans
haven't finished reading through your proposal there. Your IO::Buffer proposal is more low-level than what I'm talking about. Yours is a byte array that's been optimzed for IO (zero-copy slices, locking, etc). I'd like both. And event looks very interesting, too. :)
Samuel Williams
@ioquatix
The goal of IO::Buffer is to provide a low level interface for IO. On top of that you can make a general buffering for String IO.
Yes, It's a bit rediculous how many places implement their own buffering (probably with different bugs too).
Chuck Remes
@chuckremes
Starting to get back into some open source dev work in ruby
Other than the couple of projects listed on the async projects page, can anyone list some good examples of mature working projects built with these gems?
Also, interested to know if anyone has cobbled together an async-pubsub proof of concept or gem… didn’t see one when searching but my searches oftentimes miss stuff
Lastly, Gitter seems pretty dead for projects these days… any other place like slack or discord where y’all hang out?
Samuel Williams
@ioquatix
@chuckremes we have an active slack for discussions
There are some people with commercial projects on the slack you can ask that question
For pubsub, it's straight forward to build it with redis for example. I have another gem which implements something very similar to drb too. It kind of depends on your use case.
Chuck Remes
@chuckremes
Cool, thanks for the quick reply @ioquatix
Where’s the slack room?
Samuel Williams
@ioquatix
@chuckremes I sent you an invite to your provided email address git@chuckremes