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

Add a way to force-initialize a System.Lazy #27509

Closed
GSPP opened this issue Sep 30, 2018 · 13 comments
Closed

Add a way to force-initialize a System.Lazy #27509

GSPP opened this issue Sep 30, 2018 · 13 comments
Labels
api-needs-work API needs work before it is approved, it is NOT ready for implementation area-System.Runtime design-discussion Ongoing discussion about design without consensus
Milestone

Comments

@GSPP
Copy link

GSPP commented Sep 30, 2018

Lazy should have a way to make it compute the value on demand. This could be as simple as this:

    void ForceValue()
    {
      var dummy = Value;
    }

This is useful if Lazy is used as a cache and you want to pre-initialize that cache. That pre-initialization could be done in the background by Task.Run(() => myLazy.ForceValue()). I'm sure a ForceValue is handy in other situations as well. It does not clutter the class much and is trivial to implement.

@jnm2
Copy link
Contributor

jnm2 commented Sep 30, 2018

I do _ = lazy.Value;. Is there anything wrong with just discarding the return value?

(Also, changing the syntax from _ = lazy.Value; to something you prefer like lazy.CreateValue(); is a great use case for an extension method.)

@Clockwork-Muse
Copy link
Contributor

...I have to ask, if you're force-initializing a Lazy, is it even, really, lazy anymore?

Once you start doing things like Task.Run(() => myLazy.ForceValue()), why not just do the computation in the task and await it wherever? The task is going to cache the result, and not recompute it.

Note that for the pre-initialization to be worth it, you have to either force the Lazy to be threadsafe (and thus cause the Value call to block/wait), or be pretty sure that that initialization is going to be finished before the first real call (or it throws out the result, meaning the pre-initialization was wasted).

@MarcoRossignoli
Copy link
Member

MarcoRossignoli commented Oct 1, 2018

...I have to ask, if you're force-initializing a Lazy, is it even, really, lazy anymore?

Also to me it's strange use lazy as "cache" system...i think that Lazy is useful to avoid "unuseful" allocation, preload seems opposite than lazyness. But maybe with complete "scenario" i could understand well the idea.

@GSPP
Copy link
Author

GSPP commented Oct 1, 2018

I made this feature request because I recalled that I needed it. Indeed, I have my own Lazy implementation because System.Lazy stores exceptions which makes it extremely dangerous for long-running apps.

One scenario might be a lazy initialized cache but you want the cache to be pre-initialized in the background but only after a few seconds have passed in order to not slow down the start of the application. Here, we cannot just use a Task.Run because that would start running immediately.

I have now checked where I used this feature... It was in just two fairly esoteric scenarios that are not worth posting here.

So I understand the interjection that this might not be very useful. Feel free to close this if the team decides against having this feature!

@Clockwork-Muse
Copy link
Contributor

@GSPP -
The documentation for Lazy explicitly mentions that:

If there is a potential for a recoverable failure, we recommend that you build the retry logic into the initialization routine (in this case, the factory method), just as you would if you weren’t using lazy initialization.

...the exception caching is considered a feature.

As for worries about Task.Run starting immediately... trivially that can be changed to a Task.Delay(timespan).ContinueWith(...), but perhaps more interestingly could be changed to taskCompletionSource.Task.ContinueWith(...), which makes the launching controllable.

@GSPP
Copy link
Author

GSPP commented Oct 1, 2018

Retries cannot really be placed inside because retrying might take extremely long (or never complete). All threads consuming the lazy would be held up indefinitely. In a web app that quickly consumes the entire thread pool. With an async lazy the same issue exists. Unbounded resources will be consumed and incoming requests will be delayed indefinitely.

Rather, failures must propagate out of the lazy. Yet the lazy must be restartable.

Regarding the delay init scenario, Task.Delay(timespan).ContinueWith(...) does not work because initialization must start immediately when the lazy is needed or based on a delay.

@Clockwork-Muse
Copy link
Contributor

Rather, failures must propagate out of the lazy. Yet the lazy must be restartable.

This sounds like a separate api-suggesstion, perhaps propose that too?

@GSPP
Copy link
Author

GSPP commented Oct 1, 2018

You mean a way to reset the lazy? I personally really needed that for caching scenarios and it was an important reason to create my own lazy. Implementing that could be difficult and force worse performance. Now, the lazy transitions only forward in time. With resetting it can transition back into old states (ABA problem). That makes low-synchronization code problematic to achieve.

@tsvx
Copy link

tsvx commented Oct 26, 2018

I made this feature request because I recalled that I needed it. Indeed, I have my own Lazy implementation because System.Lazy stores exceptions which makes it extremely dangerous for long-running apps.

@GSPP, could you give a link to your implementation, please?

@Clockwork-Muse
Copy link
Contributor

@tsvx - I've become less convinced that this is a necessary new feature, especially since the reason you're quoting is a red herring.

Essentially, Lazy's position is that the resource should be initialized once (let's ignore threading issues, here), and that either the initialization succeeded, or it failed, just like normal object instance initialization. The only difference is that the initialization was delayed until actually needed. It's not really meant to be a caching system (there are other types/libraries for that). It's also not really intended to be an off-thread initialization system (use async for that).

@GrabYourPitchforks
Copy link
Member

It has been a while and this issue hasn't had any recent activity. Is there any reason the suggestion at https://github.com/dotnet/corefx/issues/32551#issuecomment-425735834 (use a discard) isn't satisfactory?

@GSPP
Copy link
Author

GSPP commented Jan 31, 2020

I have laid down my case for this feature, and the discussion was pretty exhaustive. If the team is not convinced at this point, I think it's time to let this go 😄

@msftgits msftgits transferred this issue from dotnet/corefx Jan 31, 2020
@msftgits msftgits added this to the 5.0 milestone Jan 31, 2020
@maryamariyan maryamariyan added the untriaged New issue has not been triaged by the area owner label Feb 23, 2020
@joperezr joperezr added api-needs-work API needs work before it is approved, it is NOT ready for implementation and removed untriaged New issue has not been triaged by the area owner labels Jul 6, 2020
@joperezr joperezr modified the milestones: 5.0.0, Future Jul 6, 2020
@terrajobst terrajobst removed the api-suggestion Early API idea and discussion, it is NOT ready for implementation label Jun 25, 2021
@stephentoub
Copy link
Member

stephentoub commented Mar 11, 2022

If the team is not convinced at this point, I think it's time to let this go

Thanks for the suggestion and discussion. I'm going to close it.

@ghost ghost locked as resolved and limited conversation to collaborators Apr 10, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api-needs-work API needs work before it is approved, it is NOT ready for implementation area-System.Runtime design-discussion Ongoing discussion about design without consensus
Projects
None yet
Development

No branches or pull requests