-
Notifications
You must be signed in to change notification settings - Fork 417
Promise extends IVar #270
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
Promise extends IVar #270
Conversation
Thanks @jdantonio, looks much better then my PR. Testing. |
def set(value = IVar::NO_VALUE) | ||
raise PromiseExecutionError.new('supported only on root promise') unless root? | ||
super | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might make sense to also call execute
here.
Sorry if this doesn't make sense, because I might not have the full context: I run into a problem with manually setting promises where if you attach child promises class FulfillablePromise < Concurrent::Promise
def initialize(*)
super
@state = :pending
end
def fulfill(value)
on_fulfill(value)
end
def reject(error)
on_reject(error)
end
end |
@obrok That is very good information. Thank you. I think the context of this PR may be a little different than your example. If I correctly understand your example, it is possible to manually set the value of a child Promise. In this use case we only want to manually set the root promise and never manually set the children. Basically:
There may be bugs in this implementation because it is just a spike, but the intent is that we would never manually set any of the children. This is why Does that make sense, or do I misunderstand your example? |
It makes sense but I still think there might be a problem. Consider this function (which I imagine is the use case): def do_something_later
promise = Promise.new
Thread.new { promise.set(:result) }
promise
end
do_something_later.then { |result| p result }.execute
# I think this will output nil most of the time or possibly it's a race The client of |
@@ -243,6 +240,26 @@ def execute | |||
self | |||
end | |||
|
|||
def set(value = IVar::NO_VALUE, &block) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@pitr-ch This version of set
follows your suggestion and calls execute
. The function itself could use a little cleanup but the tests still pass. I think the behavior here is probably more accurate. I haven't changed fail
yet but I anticipate it will be very similar except for something like this:
@promise_body = Proc.new { |result| raise reason }
Thoughts?
@obrok That makes sense. Once we get the behavior right for manually setting the root promise we can dig more deeply into your example. |
Based on the tests it seems there is a race hidden. What is the purpose of |
@chrisseaton Can you answer @pitr-ch's question, please? |
The point of |
This update makes
|
@jdantonio I would suggest merging with |
* Dereferenceable supports injection of a mutex * Obligation supports injection of a mutex * IVar extends Synchronization::Object * IVar passes `self` to Obligation as injected mutex * IVar, Promise, and Future no longer call `mutex#lock`
The test that failed in the last run is one that is known to fail intermittently. See #271 |
true | ||
else | ||
false | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since delay is migrated to Delay < Synchronization::Object
it probably should be using its method synchronize
, is there a reason for using mutex
which I am missing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is using the #synchronize
method from Synchronization::Object
, it's just doing so through a level of indirection. See line 64. Delay
includes Obligation
which also includes Dereferenceable
, the latter of which creates its own mutex within the #init_mutex
function. I've added an argument to both #init_obligation
and #init_mutex
that allows the mutex to be injected. Delay
passes self
into those initialization functions, thus allowing the delay object to be its own lock. The #mutex
method returns self
. The mutex.synchronize
call on line 150 is actually self.synchronize
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah right, I've missed that. It seems unnecessarily complicated, but I am not sure if it makes sense to invest time in refactoring it - I still hope that the new futures will replace this class soon.
@jdantonio I've looked through the code and added few comments. I think this is not dependent on #274. This brings some of the features of the new futures to the original implementation. (I really have to sit down soon and write a documentation for that.) |
Promise extends IVar
Promise
extendsIVar
the wayFuture
does. This allows a Promise to be manually (and safely) be set from any thread. It also begins the process of consolidating Promise, Future, and IVar.See #257, #139, and #269
IVar is a Synchronization::Object
IVar
is now aSynchronization::Object
.Dereferenceable
supports injection of a mutexObligation
supports injection of a mutexIVar
extendsSynchronization::Object
IVar
passesself
toObligation
as injected mutexIVar
,Promise
, andFuture
no longer callmutex#lock
See #273 and #275
Moved mutex unlocking into ensure clauses.
See #244