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

react: Update only dependents of the just-set cell #131

Merged
merged 2 commits into from
Apr 7, 2017
Merged

react: Update only dependents of the just-set cell #131

merged 2 commits into from
Apr 7, 2017

Conversation

petertseng
Copy link
Member

The initial version of react (see #126) let the reactor store all cells
in a linked list, then blindly updated all cells whenever any cell was
set. This was correct but wasteful.

This commit rearchitects the example solution so that:

  • reactors only keep track of the input cells
  • all other cells keep track of their dependent compute cells
  • only dependents get updated when a cell is set

Space cost of:

  • one extra struct child allocated per compute2 cell created.
  • one extra integer field per cell.

Copy link
Member Author

@petertseng petertseng left a comment

Choose a reason for hiding this comment

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

Reviewers: Obviously, it wouldn't be necessary to accept this PR, since the example solution already passes the tests. The only reason this is here is that in #126 (comment) we wondered whether it was possible to only update cells that would need an update. I agreed to propose this after I completed my submission to the website.

struct child *next = child->next;
if (c == child->cell->input1) {
// Don't double-free for a compute2 cell.
Copy link
Member Author

Choose a reason for hiding this comment

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

That was my main concern with making this. I thought it would be very tricky to free every cell exactly once, but every compute cell has exactly one input1, so it works out.

Copy link
Member

Choose a reason for hiding this comment

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

I don't really understand the need for this if. Can you explain this for me?

Copy link
Member Author

Choose a reason for hiding this comment

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

let's say it's not there, so that we always call destroy_cell(child->cell).

In that case, even this:

   struct cell *input1 = create_input_cell(r, 1);
   struct cell *input2 = create_input_cell(r, 2);
   struct cell *output = create_compute2_cell(r, input1, input2, plus);

will double-free:

destroy_reactor calls destroy_cell(input1)
destroy_cell(input1) calls destroy_cell(output)
destroy_reactor calls destroy_cell(input2)
destroy_cell(input2) calls destroy_cell(output)

Everything else is correct; it just needed to avoid a second call to destroy_cell(output)

Copy link
Member

Choose a reason for hiding this comment

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

Ahh, that makes much more sense. Thanks!

return c;
}

#define each_child(c) struct child *child = (c)->child; child; child = child->next
Copy link
Member Author

Choose a reason for hiding this comment

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

too obscure? I didn't like repeating myself.

Copy link
Member

Choose a reason for hiding this comment

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

I like it

}
free(child->cell);
struct cb *cb = c->cb;
while (cb) {
Copy link
Member Author

Choose a reason for hiding this comment

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

my submission on the website asks whether it is possible to reduce the duplication between the three while loops

@ryanplusplus
Copy link
Member

Looks good to me outside of a bit of confusion in destroy_cell. I thought that this would be much more complicated than it is, particularly in making sure that callbacks weren't fired too many times, but this is really quite nice.

@arcuru
Copy link
Contributor

arcuru commented Mar 26, 2017

I like that you're updating this 👍, but if you're going to do that I'd suggest also at least adding NULL checks...

At some point I may bring back up the problem, but after I actually write a solution. (I've spent the last 20 minutes just attempting to get it to compile an empty skeleton. The API has 9 functions and 2 structs that need to be defined, and all of their inputs need to be used (to avoid the compiler warnings as errors), before any of the tests can even compile.)

The initial version of react (see #126) let the reactor store all cells
in a linked list, then blindly updated all cells whenever any cell was
set. This was correct but wasteful.

This commit rearchitects the example solution so that:

* reactors only keep track of the input cells
* all other cells keep track of their dependent compute cells
* only dependents get updated when a cell is set

Space cost of:

* one extra `struct child` allocated per compute2 cell created.
* one extra integer field per cell.
@petertseng
Copy link
Member Author

adding NULL checks

Careful - I wouldn't want the maintainers of this track to get in a back-and-forth on #126 (comment) and this, because it's not my place to mediate such a one. But I'm here to let you know that whichever you choose (NULL checks or no NULL checks), it can happen. There is now a commit dedicated to adding the NULL checks, and if y'all decide not to have NULL checks that commit can simply be reverted.

last 20 minutes just attempting to get it to compile an empty skeleton
The API has 9 functions and 2 structs that need to be defined, and all of their inputs need to be used (to avoid the compiler warnings as errors)

I admit that when I was working on this to submit to the site, I removed -Werror from my makefile as that was too annoying. It is possible the track can provide a stub file with (void) on all inputs to make that easier. It would be an exception to #43. Any other exercises with many functions that need to be defined, that could also benefit from that? Ones that come to mind immediately are custom-set and circular-buffer, neither of which this track has.

@petertseng
Copy link
Member Author

Hi! As I explained, it's not necessary to merge this--the only reason to do this is if it's really important that the example solution not do unnecessary work. Given this fact, and the fact that I need to keep my open PRs I've created clean, I need to close this for the sole purpose of getting it out of my queue. It doesn't mean I've given up on the idea! Y'all are free to reopen it for the purpose of merging it of course.

@petertseng petertseng closed this Apr 7, 2017
@ryanplusplus
Copy link
Member

Hey @petertseng I'm sorry this fell off my radar. I think we'd like this, thanks for your help!

@ryanplusplus ryanplusplus reopened this Apr 7, 2017
@ryanplusplus ryanplusplus merged commit a583448 into exercism:master Apr 7, 2017
@petertseng petertseng deleted the react-limited-updates branch April 7, 2017 10:59
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.

3 participants