-
Notifications
You must be signed in to change notification settings - Fork 29
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
Progress reporting functionality #34
base: master
Are you sure you want to change the base?
Conversation
…tation of handler block to support promise progress
Hi Petro Thank you very much for the useful addition. I've couple of questions, though:
And lastly: This is actually a quite opinionated and debatable question. IMHO, this would be a more appropriate property for a task - rather than a promise. A promise isn't a task, though - but there's also no abstraction for a task in the Promise library - so this is a dilemma ;) Thanks again, I'm eager to here your comments and opinions :) |
In several unit tests, the local variable progress has been accessed from different continuations and the main thread. Since there was no synchronization, this is a data race. This issue has been fixed now, through explicitly specifying the execution context.
XCTestExpectation *expectation = [self expectationWithDescription:@"Progress should be equal to sum of progresses"]; | ||
NSArray *progressValues = @[@0.0, @0.25, @0.5, @0.75, @1.0]; | ||
RXPromise *promise = [[RXPromise alloc] init]; | ||
__block float progress = 0; |
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.
Here, progress
will be accessed from different threads. We need to ensure the access is thread-safe, for example ensuring the execution context is set to the main thread, or use a dedicated serial dispatch queue. Note, the unit test itself will execute on the main thread, too.
This data race exists basically in all progress test.
It was interesting to experience the effect when running the tests: they intermittently failed one my iMac.
Note: I've made a new topic branch which fixes these issues in all tests. You may now apply these changes, or I merge the new fixed branch into master.
Hi Andreas, thanks for the comprehensive feedback! I'll evaluate it over the weekend and will come up with my thoughts / code |
Fix data races in unit tests
Hi Andreas! The following are my thoughts on your feedback:
Answer: I thought about concurrent std::list mutations, however, your point is completely correct, access is synchronized via
Answer: Your guess is correct, i thought exactly about the size of the resulting promise. However, two std::list sizes won't be that much, especially, if progress functionality will be extracted into subclass
Answer: I see it's with following prospectives:
Personally I see no strong evidence in extracting progress functionality into subclass.
Answer: I'd like to emphasize on that the desired functionality is exactly the progress value. Why not
Answer: I had concern about whether adding progress functionality to RXPromise is appropriate before starting this code. I understand what kind of abstraction promise is, and i clearly understand that it isn't the task. However, looking at examples, i see that promises are mostly used in a bit simplified ways and scenarios:
Keeping in mind above usages, i think we can have progress implemented on promise at all, since this will be very handy in terms of providing application layers interconnectivity functionality. Regarding the SummaryLooking forward to see your feedback and fixing mentioned above issues meanwhile |
@couchdeveloper feel free to review fixes / leave comments, appended pull-request with them. Also, feel free to delete soxjke-master branch, i've merged the changes. |
I very much appreciate your comments and reasoning. So, the OSSpinLock can be omitted and the C++ containers became values (this makes the code a bit more concise). I do have a few improvements, though ;) First, I need to explain a rather subtle and undocumented feature. In order to implement cancellation, I required each promise to remember their "returned promise" - which will be created in method It seems to me, this is exactly what you have accomplished with the member variable In my implementation, though, this container became a shared container of type I used this shared container approach in order to make the memory layout of a promise as small as possible, scarifying some other properties. If I'm not wrong, This is how it works: Enqueueing a "returned promise":
Dequeuing a child promise:
Iterating over the children:
Note: Accesses MUST be executed on the shared sync queue! So, if the IFF
However, there's a caveat: Currently, enqueueing the returned promise happens only when the handler has been executed. This is too late for the progress in order to be working correctly with the children. That is, the code above must be moved to a place where it is correct for both. Other comments will be included in the diffs: |
_progressValue = progress; | ||
if (_progressHandlers) { | ||
for (const std::pair<id, promise_progressHandler_t> handlerPair : *_progressHandlers) { | ||
if (_progressHandlers.size()) { |
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.
The enclosing if-statement if (_progressHandlers.size())
(not the block!) may be omitted. Just iterating over an empty list should work, too.
Hi Andreas. The comments above make much sense. I'd verify |
Ok, thank you too. ;) But we are not yet finished ;) Now, I have a question, how forwarding the progress value to the children should work.
Here, we have two tasks, Suppose, task1 takes 100 WorkUnits and task2 takes 50 WorkUnits, where the "WorkUnits" values set the supposed duration of the tasks in relation to each other. If the mechanic is simply passing the progress through to the child promise What I really want is, if I would observe the progress of the result promise, I would expect something like below:
If I think about it, in order to realise this form of progress, a promise would need to know its supposed value of "WorkUnit" from its "resolver". Since a promise will be created with |
This implements progress reporting