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

Fix trasient typo; make general syntax and punctuation edits to readme #235

Merged
merged 4 commits into from
Jun 1, 2023

Conversation

ColinDKelley
Copy link
Contributor

@ColinDKelley ColinDKelley commented May 31, 2023

  • Fixed 'trasient` typo
  • Made a bunch of small syntax and punctuation edits

Types of Changes

  • Documentation

Contribution

@olleolleolle
Copy link
Contributor

@ColinDKelley Thanks for this big edit! Very nice formatting, and grammar improvements improve the clarity of the text.

guides/asynchronous-tasks/readme.md Show resolved Hide resolved
guides/asynchronous-tasks/readme.md Show resolved Hide resolved
@@ -124,7 +124,7 @@ end

## Waiting for Tasks

Waiting for a single task is trivial, simply invoke {ruby Async::Task#wait}. To wait for multiple tasks, you may want to use a {ruby Async::Barrier}. You can use {ruby Async::Barrier#async} to create multiple child tasks, and wait for them all to complete using {ruby Async::Barrier#wait}.
Waiting for a single task is trivial: simply invoke {ruby Async::Task#wait}. To wait for multiple tasks, you may either {ruby Async::Task#wait} on each in turn, or you may want to use a {ruby Async::Barrier}. You can use {ruby Async::Barrier#async} to create multiple child tasks, and wait for them all to complete using {ruby Async::Barrier#wait}.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could still use some more guidance here about which is preferred and why.

We noticed that Async::Barrier#wait was less convenient because it didn't allow us access to the final values the way Async::Task#wait does. But we did find that Async::Barrier#wait performed a bit better in our use case. (We still need to boil this down to a stand-alone example.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can still call wait on individual tasks if required after calling Barrier#wait.

Depending on your use case, you should only follow that model if you are doing map-reduce. Explicit fan-out only to do a linear map of the results is a bit pointless - better to move all the mapping logic to the tasks themselves for the best concurrency.

I think we can elaborate more in the barrier.md documentation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, no changes in the follow-up here.

guides/asynchronous-tasks/readme.md Show resolved Hide resolved
@@ -222,7 +222,7 @@ Async do
end
```

If you're letting individual tasks held by a barrier throw unhandled exceptions, be sure to call ({ruby Async::Barrier#stop}):
If you're letting individual tasks held by a barrier raise unhandled exceptions, be sure to call ({ruby Async::Barrier#stop}) to stop the remaining tasks:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't follow the "letting" part at first. I think I do now, so I can take a shot at clarification.

I think we can assume it's normal to let unhandled exceptions out. Especially SystemErrors like SyntaxError that won't be rescued by a plain rescue => ex.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added some clarification in the follow-up.

guides/asynchronous-tasks/readme.md Show resolved Hide resolved
@@ -364,21 +366,23 @@ require 'async'
require 'thread/local' # thread-local gem.

class TimeCache
extend Thread::Local
extend Thread::Local # defines `instance` class method that lazy-creates a separate instance per thread
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found the use of this gem was distracting from the example. Maybe we can think up a different example that doesn't have the thread-local complexity?

Or, alternatively, if thread safety is critical topic to address, maybe a separate section just for that point?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not possible to share tasks globally, so we need a per-thread cache (which is actually the best from a contention point-of-view). Maybe there is a better way we can demonstrate it.


def initialize
@current_time = nil
end

def current_time
def current_time_string
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to read this a couple times to get the point that that we were caching the string and hence only needed 1-second granularity.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can add that as a comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in the lead-in paragraph in the follow-up.

guides/asynchronous-tasks/readme.md Show resolved Hide resolved
@refresh ||= Async(transient: true) do
while true
loop do
@current_time = Time.now.to_s
sleep(1)
end
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re: the comment just below, I don't follow how an exception will get raised. I thought that would require us to call .wait on the task. Given that we're not doing that, doesn't Async(transient: true) do ... end return immediately without any chance of raising an exception when the reactor terminates all tasks?

And BTW, do you mean "terminates all transient tasks" there?

But also, I'm missing the significance of storing nil into the @refresh variable. The rest of the class doesn't use that. So the only effect would be if refresh! were called again, right? Was that the point?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The refresh! task should exist while the reactor exists, if the user calls a top level Async{TimeCache.current_time_string} after exiting the top level block, the task created in refresh! will exit. We want to ensure that the next time refresh! is called, the task is recreated. Hence, on exiting the task, we ensure the instance variable is cleared.

ColinDKelley and others added 2 commits May 31, 2023 00:09
Co-authored-by: Olle Jonsson <olle.jonsson@gmail.com>
Co-authored-by: Olle Jonsson <olle.jonsson@gmail.com>
@ColinDKelley
Copy link
Contributor Author

Thanks for this big edit! Very nice formatting, and grammar improvements improve the clarity of the text.

@olleolleolle You're welcome! My team has just been converting a whole service over from Synchrony to Async so we've been studying these docs very closely in the last week. :)


There are various situations where you may want to stop a task ({ruby Async::Task#stop}). The most common case is shutting down a server, but other important situations exist, e.g. you may fan out multiple (10s, 100s) of requests, wait for a subset to complete (e.g. the first 5 or all those that complete within a given deadline), and then stop (terminate/cancel) the remaining operations.
There are various situations where you may want to stop a task ({ruby Async::Task#stop}) before it completes. The most common case is shutting down a server, but other important situations exist, e.g. you may fan out multiple (10s, 100s) of requests, wait for a subset to complete (e.g. the first 5 or all those that complete within a given deadline), and then stop (terminate/cancel) the remaining operations.

Using the above program as an example, we can
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also stopped half way through a sentence lol.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I took a shot at finishing the sentence in the follow-up. :)

end
```

Upon existing the top level async block, the {ruby @refresh} task will be set to `nil`. Bear in mind, you should not share these resources across threads, doing so would need further locking/coordination.
Upon existing the top level async block, the {ruby @refresh} task will be set to `nil`. Bear in mind, you should not share these resources across threads; doing so would need some form of mutual exclusion.
Copy link
Member

@ioquatix ioquatix Jun 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Upon existing the top level async block, the {ruby @refresh} task will be set to `nil`. Bear in mind, you should not share these resources across threads; doing so would need some form of mutual exclusion.
Upon exiting the top level async block, the {ruby @refresh} task will be set to `nil`. Bear in mind, you should not share these resources across threads; doing so would need some form of mutual exclusion.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in follow-up.

Copy link
Member

@ioquatix ioquatix left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is such a great improvement, I'll merge, it, and if you feel inclined to do a 2nd round of improvements, you have feedback.

@ioquatix ioquatix merged commit 5423657 into socketry:main Jun 1, 2023
12 of 15 checks passed
@ColinDKelley
Copy link
Contributor Author

ColinDKelley commented Jun 1, 2023

if you feel inclined to do a 2nd round of improvements, you have feedback

Absolutely! I'll work on another PR tomorrow.

@ColinDKelley ColinDKelley deleted the fix-trasient-typo branch June 2, 2023 01:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants