Quantcast
Channel: The Old New Thing
Viewing all 24428 articles
Browse latest View live

Async-Async: Consequences for ordering of multiple calls in flight simultaneously

$
0
0

The feature known as Async-Async makes asynchronous operations even more asynchronous by pretending that they started before they actually did. As I noted earlier, you might notice a difference if you have been breaking the rules and getting away with it. We saw last time what happens if you mutate a parameter that was passed to an asynchronous method. (Short answer: Don’t do that until the asynchronous method completes.)

Another place you may notice a difference is if you race multiple calls against each other.

// Code in italics is wrong.

// Queue two animations in sequence.

// Queue the first animation.
var task1 = widget.QueueAnimationAsync(source1, curve1);

// Queue the second animation.
var task2 = widget.QueueAnimationAsync(source2, curve2);

// Wait for the tasks to complete.
await Task.WhenAll(task1, task2);

This code “knows” that the Widget.Queue­Animation­Async method chooses the point at which the animation will be added before it returns with an IAsync­Operation. It therefore “knows” that it can queue two items in order by starting the Queue­Animation­Async operations in order, and waiting for both of them to complete.

This code does not work when Async-Async is enabled because the two calls to Widget.Queue­Animation­Async are not required to start on the server in the same order that the client issued them. The fake IAsync­Operation issues a request to start the operation on the server, but does not wait for the server to acknowledge the start of the operation. If you start two operations, they race to the server, and the second one may reach the server first, in which case the operations will be started on the server in the opposite order.

Of course, proper client code should not have had this dependency on the order of asynchronous operations in the first place. After all, the server might decide not to choose the position of the animations until later in the asynchronous operation, and the second operation may have raced to the decision point ahead of the first operation. For example, the internal behavior of Queue­Animation­Async may have been

  1. Create a new animation from the source and curve parameters.
  2. Add that animation to the list of animations.

If you start two Queue­Animation­Async operations in parallel, you don’t really know which one will reach step 2 first.

If the order of queueing is important, then you need to wait until the first animation is definitely queued before you queue the second one. You’ll have to run the operations in series, rather than in parallel.

// Queue two animations in sequence.

// Queue the first animation.
await widget.QueueAnimationAsync(source1, curve1);

// Queue the second animation.
await widget.QueueAnimationAsync(source2, curve2);

In a sense, this is another case of “mutating a parameter passed to an asynchronous method”: The parameter that is being mutated is the widget itself! Well, more specifically, the animation queue of the widget.

Next time, we’ll look at another consequence of Async-Async that you may notice if you have been cheating the rules.

The post Async-Async: Consequences for ordering of multiple calls in flight simultaneously appeared first on The Old New Thing.


Async-Async: Consequences for exceptions

$
0
0

As we’ve been learning, the feature known as Async-Async makes asynchronous operations even more asynchronous by pretending that they started before they actually did. The effect of Async-Async is transparent to properly-written applications, but if you have been breaking the rules, you may notice some changes to behavior. Today we’ll look at exceptions.

// Code in italics is wrong.

Task task1 = null;
Task task2 = null;
try
{
    task1 = DoSomethingAsync(arg1);
    task2 = DoSomethingAsync(arg2);
}
catch (ArgumentException ex)
{
    // One of the arguments was invalid.
    return;
}

// Wait for the operations to complete.
await Task.WhenAll(task1, task2);

This code “knows” that the invalid parameter exception is raised as part of initiating the asynchronous operation, so it catches the exception only at that point.

With Async-Async, the call to Do­Something­Async returns a fake IAsync­Operation immediately, before sending the call to the server. If the server returns an error in response to the operation, it’s too late to report that error to the client as the return value of Do­Something­Async. Because, y’know, time machine.

The exception is instead reported to the completion handler for the IAsync­Operation. In the above case, it means that the exception is reported when you await the task, rather than when you create the task.

try
{
    Task task1 = DoSomethingAsync(arg1);
    Task task2 = DoSomethingAsync(arg2);

    // Wait for the operations to complete.
    await Task.WhenAll(task1, task2);
}
catch (ArgumentException ex)
{
    // One of the arguments was invalid.
    return;
}

Again, this is not something that should affect a properly-written program, because you don’t know when the server is going to do its parameter validation. It might do parameter validation before creating the IAsync­Operation, or it might defer doing the parameter validation until later for performance reasons. You need to be prepared for the exception to be generated at either point.

In practice, this is unlikely to be something people stumble across because Windows Runtime objects generally reserve exceptions for fatal errors, so you have no need to try to catch them.

The post Async-Async: Consequences for exceptions appeared first on The Old New Thing.

Stupid git commit-tree tricks, Part 1: Building a commit manually out of a tree

$
0
0

When it comes time to make a public update to the UWP samples repo, I take a snapshot of what’s in our internal staging repo and push it to the public repo. All of the intermediate steps are squashed out, so that the public repo isn’t cluttered with noisy history.

I accomplish this with a local repo with two remotes. One remote is the public repo and the other repo is our private repo. When it’s time to make a public release, I go through these steps.

First: Freshen my local master with the latest public. This technically isn’t necessary because nobody should be pushing to public except me, so my local master should always be up to date. But hey, it doesn’t hurt to check, because occasionally somebody else will push something to master.

git checkout master
git pull --ff-only public

Next, I update my private remote so it has a copy of the source code that we want to make public.

git fetch private

Now the fun part: Commit the latest private tree onto the latest public tree.

git commit-tree private/master^{tree} -p master -m "comment"
(this prints a hash)

Note: If using the Windows CMD command prompt, you need to type

git commit-tree private/master^^{tree} -p master -m "comment"

The doubling of the ^ character is necessary because ^ is the escape character for CMD.EXE. The point is that you want the first parameter to commit-tree to be private/master^{tree} after the command line has had its say of escaping.

(It’s too bad that commit-tree requires a tree. Would be nice if they could relax this requirement so it can accept any tree-ish.)

I can then fast-forward to the newly-created commit and bingo, it’s as if I did a super-squash of the private repo onto the public repo.

git merge --ff-only 〈hash〉

In reality, my workflow is a little funkier than this, and I have to update multiple branches and make each one merge into the next. We’ll look some more at the workflow next time.

 

The post Stupid git commit-tree tricks, Part 1: Building a commit manually out of a tree appeared first on The Old New Thing.

Stupid git commit-tree tricks, Part 2: Building a merge commit manually out of a tree

$
0
0

When it comes time to make a public update to the UWP samples repo, I usually have to update multiple branches. Each branch encompasses the changes from the previous-version branch, and then adds new changes specific to that branch.

For the oldest branch, I create a plain commit from a tree and a parent commit, like we saw last time.

git checkout --detach

git commit-tree public/win10-1507^{tree} -p win10-1507 -m "comment"
(this prints a hash)

git fetch . 〈hash〉:win10-1507

First I detach the head from whatever branch it happens to be on. This allows me to update branches without getting any complaints of the form “You can’t do that to the current branch.” Since the head is detached, there is no current branch. Detaching the head also means that any branch updates do not alter the working directory.

The second command is the one we saw last time. It creates a commit from a tree and a parent. (If your command shell is CMD.EXE, don’t forget to double the ^ character because ^ is CMD.EXE‘s escape character.)

The third command updates the win10-1507 branch to the specified hash. This is equivalent to

git branch -f win10-1507 〈hash〉

except that it verifies that the new branch head is a descendant of the current branch head. (In other words, it’s like a --ff-only.) I use this alternate version as a safety check.

For the second oldest branch win10-1511, I want the commit to be a merge of win10-1507 and the changes specific to that branch. To do this, I use the commit-tree command, but provide multiple parents. The first parent is the previous commit for the branch, and the second parent is the incoming changes from its ancestor branch.

git commit-tree private/win10-1511^{tree} -p win10-1511 -p win10-1507 -m "comment"
(this prints a hash)

git fetch . 〈hash〉:win10-1511

The next branches follow the same pattern as win10-1511.

git commit-tree private/win10-1607^{tree} -p win10-1607 -p win10-1511 -m "comment"
(this prints a hash)

git fetch . 〈hash〉:win10-1607

git commit-tree private/master^{tree} -p master -p win10-1607 -m "comment"
(this prints a hash)

git fetch . 〈hash〉:master

git commit-tree private/dev^{tree} -p dev -p master -m "comment"
(this prints a hash)

git fetch . 〈hash〉:dev

At this point the branches are ready to be pushed to the public repo.

Note that at no point did we update the working directory. All we are doing is creating commits from existing trees and updating branches. These are fast operations since they manipulate relatively little data, and no files on disk need to be updated.

The post Stupid git commit-tree tricks, Part 2: Building a merge commit manually out of a tree appeared first on The Old New Thing.

Stupid git commit-tree tricks, Part 3: Building a throwaway commit in order to perform a combined cherry-pick-squash

$
0
0

Suppose you have a series of commits you want to cherry-pick and squash onto another branch.

The traditional way of doing this is to cherry-pick the series of commits with the -n option so that they all stack on top of each other, and then perform a big git commit when you’re done. However, this mechanism churns the local hard drive with copies of each of the intermediate commits, and if there are merge conflicts, you may end up having to resolve the conflict in the same block of code over and over again.

For example, suppose that you want to cherry-pick a series of commits that look like this:

base A B revert A B2 B3 C

Suppose you are cherry-picking it onto a branch which contains changes that conflict with A and B. The conflict may not be serious, but it could be tedious.

For example, maybe the target branch changed one line of code, and A changed the indentation of a large section of code that encompasses that one line. This results in a large merge conflict. It’s not hard to resolve, but it can be tedious verifying that the only change from A was indentation and therefore the correct merge is to take the one modified line from the target branch and fix its indentation.

And then the cherry-pick reverts A and you have to go through the same exercise.

Similarly, if B conflicts with a change in the target branch, you are going to have to deal with that conflict three times. Once when B is cherry-picked, and once again each for the follow-up commits.

What you want to do is squash all the commits together and then cherry-pick that one commit. That way, you have to deal with the conflicts only once. (Or, in the case of a conflict that was later reverted, you won’t have to deal with it at all!)

So let’s do that. Squash all the commits from A to C (inclusive) into a single commit:

git commit-tree C^{tree} -p A~ -m "squasheroo!"
(this prints a hash)

Note: If using the Windows CMD command prompt, you need to type

git commit-tree C^^{tree} -p A~ -m "squasheroo!"

for reasons discussed earlier.

The hash that got printed out is a dangling commit that is the squash of everything from A to C inclusive.

base A B revert A B2 B2 C
  ↖︎
  A + B + revert A + B2 + B3 + C

Note that you don’t have to be checked out to the branch that contains the series of commits you want to cherry-pick. You are creating a commit with an explicit parent, so there’s no reference to HEAD and therefore no need to be on a particular branch. (And in practice, you are already checked out to the branch that you want to cherry-pick into.)

Now you can cherry-pick the dangling commit and deal with the conflicts only once.

git checkout target-branch [but you are probably there already]
git cherry-pick 〈that hash that was printed by git commit-tree〉

This is similar to git diff A~ C | git apply, but has the following advantages:

  • The git cherry-pick can take advantage of the full contents of the base version to help resolve the conflict.
  • The output of git diff may be problematic for binary content.
  • If there are any merge conflicts, the git apply will just throw away the conflicting chunks, rather than leaving merge markers in the output. You’ll have to go dig into the patch to find the pieces that were not successfully applied and then try to merge them yourself.
  • The git cherry-pick keeps track of the conflicting files and will remind you of them when you do a git status.
  • You can use your regular merge conflict workflow (such as git mergetool) to resolve the conflicts.
  • If you decide that the merge conflicts are too nasty, you can abandon the git cherry-pick. A git apply cannot be abandoned.

The post Stupid git commit-tree tricks, Part 3: Building a throwaway commit in order to perform a combined cherry-pick-squash appeared first on The Old New Thing.

Stupid git commit-tree tricks, Part 4: Changing a squash to a merge

$
0
0

Suppose you performed a git merge --squash, and then later realized that you meant it to be a true merge rather than a squash merge.

In diagrams, suppose you have this:

A M1 ← ← ← M2   master
  ↖︎      
    F1 F2       feature

From a starting commit A, the master branch continues with a commit M1. From that same starting commit A, a feature branch adds commits F1 and F2. Now you decide to take the feature branch into the master branch, and you resolve the merge as a squash. This means that the resulting commit M2 technically has only a single parent M1. The other alleged parent F2 is just a dotted line, indicating that it is just a figment of your imagination.

You then realize that you should have accepted the feature branch as a true merge rather than a squash. What can you do?

Naturally, you could hard reset the master branch back to M1 and redo the merge. But that means you have to redo all the merge conflicts, and that may have been quite an ordeal. And if that was a large merge, then even in the absence of conflicts, you still have a lot of files being churned in your working directory.

Much faster is simply to create the commit you want and reset to it.

git commit-tree HEAD^{tree} -p HEAD~ -p F2 -m comment

Note: If using the Windows CMD command prompt, you need to type

git commit-tree HEAD^^{tree} -p HEAD~ -p F2 -m comment

for reasons discussed earlier.

This creates a tree identical to what is currently checked in (which is the merge result), whose parents are M1 and F2. In other words, it colors in the dotted line.

A M1 ← ← ← *   master
  ↖︎       ↙︎
    F1 F2       feature

The result of the git commit-tree command is a hash printed to stdout. You can git reset to that hash to make that the branch head.

The post Stupid git commit-tree tricks, Part 4: Changing a squash to a merge appeared first on The Old New Thing.

Stupid git commit-tree tricks, Part 5: Squashing without git rebase

$
0
0

Suppose you’ve made a bunch of changes.

    X WIP   almost   rename
method
  silly typo   X works   update Y   update Z  
base A B C D E F G

You started by trying to get the X component working. I subscribe to the theory of commit early and commit often. I don’t wait until all of X is done before committing. I’ll commit every time I reach a point where I have built up enough work that I don’t want to lose it, especially if I might wind up breaking it in the next stage of work. Think of it as save game for source code.

After four tries, you finally got component X working. Next step is to update components Y and Z to use the new component.

Okay, you’re ready to create your pull request. Now, a pull request is a story, so you need to decide how you want to tell the story of your work to others, so that they can review it. For this story, we want to say “First, I wrote this awesome bug-free X component. Then I updated the Y component to use the X component. Finally, I did the same with Z.” To tell this story, we want to do some internal squashing.

    X WIP   almost   rename
method
  silly typo   X works   update Y   update Z  
base A B C D E F G
  ↖︎  
    AE′ F′ G′

The conventional way to do this is to check out the branch and perform an interactive rebase, squashing together commits A through E to form a new commit AE′, and then picking commits F and G, producing F′ and G′.

However, the conventional way may not be the convenient way. You may have moved on and checked out a different branch to to some other work, and returning to this branch for some squashing action would churn your working directory, forcing unwanted rebuilds.

Or you might still be on that branch, but rewinding back to base is going to churn so many files that it will invalidate all the build collateral you’ve created, forcing you to rebuild them pointlessly. For example, part of the work in adding the X component may have involved changing a centrally-used header file, which means that your entire project will have to rebuild.

Since all of the commits we want to squash are consecutive, we can do all this squashing by simply committing trees.

git commit-tree E^{tree} -p base -m "Write component X"

Note: As before, if using the Windows CMD command prompt, you need to double the ^ character because it is the CMD escape character.

This command prints out a hash, which is our AE′.

Now we can stack F and G on top of it:

git commit-tree F^{tree} -p AE′ -m "Update Y"

This prints a hash, which is our F′.

git commit-tree G^{tree} -p F′ -m "Update Z"

This prints a hash, which is our G′.

We can now reset the local branch to that commit, and then push it.

If the branch you are “virtually rebasing” is the current branch, you can reset to it.

git reset --soft G′

Since the trees for G and G′ are identical, this has no effect on your index. Any files that were staged remain staged, with exactly the same changes.

If you are virtually rebasing a non-checked-out branch, then you can update it, and even push it, without checking it out:

git branch -f that-branch G′
git push -f origin that-branch

Or we could bypass our local branch and push directly to the remote.

git push -f origin G′:that-branch

The point is that we were able to rewrite a branch without touching any files in the working directory.

The post Stupid git commit-tree tricks, Part 5: Squashing without git rebase appeared first on The Old New Thing.

Stupid git commit-tree tricks, Part 6: Resetting by reusing an earlier tree

$
0
0

Suppose you have a branch in which you have been doing a bunch of work, you’ve been merging regularly from the main branch to stay up to date, and you realize that your work should be abandoned, and the branch reset to a state as if it had been freshly-created from the main branch.

For most people, that would mean simply abandoning the branch and creating a new one.

Unfortunately, Windows hasn’t quite reached the point where it can use trunk-based development. Thousands of developers working in a branch with three million files means that there would be commits going into the main branch pretty much continuously. Instead, Windows uses a variant of the dictator-lieutenant workflow. Setting up a new lieutenant involves a lot of paperwork,¹ and teams often want to avoid that paperwork by finding an existing no-longer-needed lieutenant and giving it a new purpose.

When you do that, you want to clean out any changes from the lieutenant that were part of its former purpose, so that the new purpose gets a fresh start, as if it were using a branch new lieutenant.

This is where git commit-tree once again comes in handy.

M1 M2 M3 M4 M5
  ↖︎       ↖︎
    L1 L2 L3 L4

Suppose this is the state of the project at the time the team decides to repurpose its lieutenant. It had been doing some work in that branch (L1 through L4) that it wants to abandon and pretend never happened.

Windows has a policy that official branches may not rewrite history,² so a hard-reset is not permitted.

Find the most recent commit in the main branch which has been merged into the lieutenant branch. You can look into the lieutenant’s record keeping, or the git merge-base command will tell you. In our case, the commit is M3.

You can now wipe out all of the work done in commits L1 through L4 by committing the tree that matches the commit you most recently merged from the main branch.

git commit-tree M3^{tree} -p L4 -m "Reset to M3"

This prints a hash that you can fast-forward to.

git merge --ff-only 〈hash〉

This tree-based merge is the trick I referred to some time ago in the context of forcing a patch branch to look like a nop. In that diagram, we started with a commit A and cherry-picked a patch P so we could use it to patch the master branch. Meanwhile, we also want a nop to go into the feature branch. We did it with a git revert, but you can also do it in a no-touch way by committing trees.

git commit-tree A^{tree} -p P -m "Revert P"

By doing it this way, you are guaranteed that the trees A and P2 are absolutely identical, because we created them that way.

Note that in both of these cases, if you are already checked out to the branch you want to roll back, you can use a different command sequence:

git read-tree 〈hash〉
git commit -m "Reset to 〈hash〉"

An alternative that uses more conventional commands (but which temporarily moves your HEAD):

git reset --hard 〈hash〉
git reset --soft @{1}
git commit -m "Reset to 〈hash〉"

However you manage to do it, once you get your branch reset, you can submit a pull request back to the main branch with your freshly-reset lieutenant. This should result in a nop change (no files changed), with a payload consisting of L1 through L4, plus a massive commit of that wipes out all the custom changes.

Merging back to the main branch serves a few purposes:

  • The emptiness of the pull request validates that your lieutenant branch has been properly reset.
  • Payload tracking tools will report that your (now-abandoned) L1 through L4 payload has reached the main branch. Putting them all inside a “nothing happened” pull request makes it easier to prove that those changes were indeed abandoned.
  • Your next pull request from the lieutenant to the main branch will consist solely of the new work.

¹ Part of the reason for the paperwork is that a lieutenant doesn’t get just a branch. A lieutenant also gets build resources, artifact retention policy, automated testing resources, a feature flag environment, and lots of other goodies. Besides, you need to know some basic information, like who to contact if there is a problem with their branch.

² I suspect many organizations have similar policies, You need to be able to recover the source code that produced any particular build. You also have to be able to identify all the changes that went into a particular build (say, when investigating a regression). Rewriting history undermines those principles.

The post Stupid git commit-tree tricks, Part 6: Resetting by reusing an earlier tree appeared first on The Old New Thing.


Stupid git tricks: Combining two files into one while preserving line history

$
0
0

Suppose you have two files that you want to combine into one. Let’s set up a scratch repo to demonstrate. I’ve omitted the command prompts so you can copy-paste this into your shell of choice and play along at home. (The timestamps and commit hashes will naturally be different.)

git init

>fruits echo apple
git add fruits
git commit --author="Alice <alice>" -m "create fruits"
>>fruits echo grape
git commit --author="Bob <bob>"     -am "add grape"
>>fruits echo orange
git commit --author="Carol <carol>" -am "add orange"

>veggies echo celery
git add veggies
git commit --author="David <david>" -m "create veggies"
>>veggies echo lettuce
git commit --author="Eve <eve>"     -am "add lettuce"
>>veggies echo peas
git commit --author="Frank <frank>" -am "add peas"

git tag ready

We now have two files, one with fruits and one with vegetables. Each has its own history, and the git blame command can attribute each line to the commit that introduced it.

git blame fruits

^adbef3a (Alice 2019-05-14 07:00:00 -0700 1) apple
8312990f (Bob   2019-05-14 07:00:01 -0700 2) grape
2259ff53 (Carol 2019-05-14 07:00:02 -0700 3) orange

git blame veggies

2f11bacc (David 2019-05-14 07:00:03 -0700 1) celery
2d7b11e8 (Eve   2019-05-14 07:00:04 -0700 2) lettuce
8c8cf113 (Frank 2019-05-14 07:00:05 -0700 3) peas

Now you decide that fruits and vegetables should be combined into a single file called produce. How do you do this while still preserving the commit and histories of the contributing files?

The naïve way of combining the files would be to do it in a single commit:

cat fruits veggies > produce
git rm fruits veggies
git add produce
git commit --author="Greg <greg>" -m "combine"

The resulting file gets blamed like this:

eefddfb1 produce (Greg  2019-05-14 07:01:00 -0700 1) apple
eefddfb1 produce (Greg  2019-05-14 07:01:00 -0700 2) grape
eefddfb1 produce (Greg  2019-05-14 07:01:00 -0700 3) orange
7a542f13 veggies (David 2019-05-14 07:00:03 -0700 4) celery
2c258db0 veggies (Eve   2019-05-14 07:00:04 -0700 5) lettuce
87296161 veggies (Frank 2019-05-14 07:00:05 -0700 6) peas

The history from veggies was preserved, but the history from produce was not. What git saw in the commit was that one file appeared and two files vanished. The rename detection machinery kicked in and decided that since the majority of the produce file matches the veggies file, it infers that what you did was delete the fruits file, renamed the veggies file to produce, and then added three new lines to the top of produce.

You can tweak the git blame algorithms with options like -M and -C to get it to try harder, but in practice, you don’t often have control over those options: The git blame may be performed on a server, and the results reported back to you on a web page. Or the git blame is performed by a developer sitting at another desk (whose command line options you don’t get to control), and poor Greg has to deal with all the tickets that get assigned to him from people who used the git blame output to figure out who introduced the line that’s causing problems.

What we want is a way to get git blame to report the correct histories for both the fruits and the vegetables.

The trick is to use a merge. Let’s reset back to the original state.

git reset --hard ready

We set up two branches. In one branch, we rename veggies to produce. In the other branch, we rename fruits to produce.

git checkout -b rename-veggies
git mv veggies produce
git commit --author="Greg <greg>" -m "rename veggies to produce"

git checkout -
git mv fruits produce
git commit --author="Greg <greg>" -m "rename fruits to produce"

git merge -m "combine fruits and veggies" rename-veggies

The merge fails with a rename-rename conflict:

CONFLICT (rename/rename):
Rename fruits->produce in HEAD.
Rename veggies->produce in rename-veggies

Renaming fruits to produce~HEAD
and veggies to produce~rename-veggies instead

Automatic merge failed; fix conflicts and then commit the result.

At this point, you create the combined produce file from produce~HEAD and produce~rename-veggies:

cat "produce~HEAD" "produce~rename-veggies" >produce
git add produce
git merge --continue

The resulting produce file was created by a merge, so git knows to look in both parents of the merge to learn what happened. And that’s where it sees that each parent contributed half of the file, and it also sees that the files in each branch were themselves created via renames of other files, so it can chase the history back into both of the original files.

^fa19403 fruits  (Alice 2019-05-14 07:00:00 -0700 1) apple
00ef7240 fruits  (Bob   2019-05-14 07:00:01 -0700 2) grape
10e90730 fruits  (Carol 2019-05-14 07:00:02 -0700 3) orange
7a542f13 veggies (David 2019-05-14 07:00:03 -0700 4) celery
2c258db0 veggies (Eve   2019-05-14 07:00:04 -0700 5) lettuce
87296161 veggies (Frank 2019-05-14 07:00:05 -0700 6) peas

Magic! Greg is nowhere to be found in the blame history. Each line is correctly attributed to the person who introduced it in the original file, whether it’s fruits or veggies. People investigating the produce file get a more accurate history of who last touched each line of the file.

Greg might need to do some editing to the two files before committing. Maybe the results need to be sorted, and maybe Greg figures he should add a header to remind people to keep it sorted.

>produce echo # keep sorted
cat "produce~HEAD" "produce~rename-veggies" | sort >>produce
git add produce
git merge --continue

git blame produce

057507c7 produce (Greg  2019-05-14 07:01:00 -0700 1) # keep sorted
^943c65d fruits  (Alice 2019-05-14 07:00:00 -0700 2) apple
cfce62ae veggies (David 2019-05-14 07:00:03 -0700 3) celery
43c9aeb6 fruits  (Bob   2019-05-14 07:00:01 -0700 4) grape
5f60490e veggies (Eve   2019-05-14 07:00:04 -0700 5) lettuce
143eb20f fruits  (Carol 2019-05-14 07:00:02 -0700 6) orange
75a1ad0c veggies (Frank 2019-05-14 07:00:05 -0700 7) peas

For best results, your rename commit should be a pure rename. Resist the tempotation to edit the file’s contents at the same time you rename it. A pure rename ensure that git’s rename detection will find the match. If you edit the file in the same commit as the rename, then whether the rename is detected as such will depend on git’s “similar files” heuristic.¹ If you need to edit the file as well as rename it, do it in two separate commits: One for the rename, another for the edit.

Wait, we didn’t use git commit-tree yet. What’s this doing in the Stupid git commit-tree tricks series?

We’ll add commit-tree to the mix next time. Today was groundwork, but this is a handy technique to keep in your bag of tricks, even if you never get around to the commit-tree part.

¹ If you cross the merge.renameLimit, then git won’t look for similar files; it requires exact matches. The Windows repo is so large that the rename limit is easily exceeded. The “similar files” detector is O(n²) in the number of files changed, and when your repo has 3 million files, that quadratic growth becomes a problem.

The post Stupid git tricks: Combining two files into one while preserving line history appeared first on The Old New Thing.

Stupid git commit-tree tricks, Part 7: Combining more than two files into one while preserving line history, manual octopus merging

$
0
0

Last time, we saw how to combine two files to form a third file, while preserving line history. But what if you need to combine more than two files? For example, maybe you want to take a whole bunch of csv files and merge them into one big file, but still track the origin of each line.

Let’s set up a scratch repo to try it out.

git init

>fruits echo apple
>>fruits echo grape
>>fruits echo orange
git add fruits
git commit --author="Alice <alice>" -m "create fruits"

>veggies echo celery
>>veggies echo lettuce
>>veggies echo peas
git add veggies
git commit --author="Bob <bob>" -m "create veggies"

>dairy echo cheese
>>dairy echo eggs
>>dairy echo milk
git add dairy
git commit --author="Carol <carol>" -m "create dairy"

git tag ready

We can use the trick from last time to merge two files, and extend it to three files by performing an octopus merge.

git checkout -b d2f
git mv dairy food
git commit -m "dairy to food"
git checkout -

git checkout -b f2f
git mv fruits food
git commit -m "fruits to food"
git checkout -

git checkout -b v2f
git mv veggies food
git commit -m "veggies to food"
git checkout -

git merge d2f f2f v2f

Except that it doesn’t work. In fact, it explodes quite spectacularly.

Fast-forwarding to: d2f
Trying simple merge with f2f
Simple merge did not work, trying automatic merge.
Added food in both, but differently.
fatal: unable to read blob object e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
error: Could not stat : No such file or directory
ERROR: content conflict in food
fatal: merge program failed
Automated merge did not work.
Should not be doing an octopus.
Merge with strategy octopus failed.

I like that second-to-last line that scolds you for attempting this sort of thing in the first place.

Let’s clean up the work that merge had left in progress. You normally would do this with a git merge --abort, but octopus merges are not abortable because they don’t record enough information to permit an abort. (This is arguably a bug in git, but it’s merely an annoyance, and not something normal people are going to encounter.)

git reset --hard

The problem is that octopus merges work only if there are no conflicts. We’re going to have to build our own octopus merge.

cat dairy fruits veggies | sort >food
git rm dairy fruits veggies
git add food
git write-tree

The git write-tree creates a tree from the index. It’s the tree that a git commit would great, but we don’t want to do a normal commit. This is the tree we want to commit, but we need to set custom parents, so we’ll ask git write-tree for the tree that would be committed, so we can build our custom commit.

git commit-tree 〈tree-hash〉 -p HEAD -p d2f -p f2f -p v2f -m "combine dairy, fruits, and veggies"

The commit-tree will print another hash. This is the hash of the manually-constructed octopus merge.

git merge --ff-only 〈commit-hash〉

I like to use --ff-only to make sure that I really am just moving forward.

git blame food

^7c5ae53 fruits  (Alice 2019-05-15 07:00:00 -0700 1) apple
03c4572c veggies (Bob   2019-05-15 07:00:01 -0700 2) celery
65430aff dairy   (Carol 2019-05-15 07:00:02 -0700 3) cheese
65430aff dairy   (Carol 2019-05-15 07:00:02 -0700 4) eggs
^7c5ae53 fruits  (Alice 2019-05-15 07:00:00 -0700 5) grape
03c4572c veggies (Bob   2019-05-15 07:00:01 -0700 6) lettuce
65430aff dairy   (Carol 2019-05-15 07:00:02 -0700 7) milk
^7c5ae53 fruits  (Alice 2019-05-15 07:00:00 -0700 8) orange
03c4572c veggies (Bob   2019-05-15 07:00:01 -0700 9) peas

There are other ways we could have produced the same result. For example, we could have performed a series of two-files-into-one merges, but this way gives us a single commit on the trunk that captures the “combine multiple files into one”.

The post Stupid git commit-tree tricks, Part 7: Combining more than two files into one while preserving line history, manual octopus merging appeared first on The Old New Thing.

Why are Bluetooth functions exported from the infrared control panel?

$
0
0

Remember infrared communications?

Okay, maybe you don’t. It was a thing for a while in the late 1990’s and early 2000’s, but it never really caught on, and it became overshadowed by other short-range wireless communication mechanisms like Wi-Fi and Bluetooth.

If you peek inside the the infrared control panel irprops.cpl, you’ll see that it exports a ton of Bluetooth functions. What is Bluetooth doing in the infrared control panel?

Rewind to 2004. Windows XP Service Pack 2 was under development, and one of the features being added was support for Bluetooth. However, at the time, the service pack rules prevented adding new DLLs to the system. The Bluetooth folks had to squeeze their new APIs into the existing infrared control panel.

The restriction on introducing new binaries to the system was subsequently lifted, but the functions had to remain in irprops.cpl for compatibility. Those functions nowadays merely forward to the implementation in the real Bluetooth DLL bthprops.cpl.

For over a decade, the documentation has exhorted people to use bthprops.lib rather than irprops.lib.

And finally, the Windows SDK pulled the plug. It no longer includes a copy of irprops.lib, thereby removing the temptation to link with the wrong library. If you want to call those Bluetooth functions, link with bthprops.lib already.

Note that if you do this, your program won’t run on Windows XP Service Pack 2, because Windows XP Service Pack 2 doesn’t have a bthprops.cpl. For the hopefully none of you that are developing software to run on Windows XP Service Pack 2 (and really, who are you people?), you can copy irprops.lib from your Windows XP SDK and link with that.

Bonus chatter: Why not just ship irprops.lib as a duplicate of bthprops.lib? That way, people are silently upgraded to the correct library.

Sure, that would silently upgrade people to the correct library, but it also means that if you are one of those hopefully none of you that is writing code that intends to run on Windows XP Service Pack 2, your code silently broke: Your program that used to run on Windows XP Service Pack 2 no longer works, and there was no warning or error message anywhere along the way.

We decided that it was better to make the break obvious.

The post Why are Bluetooth functions exported from the infrared control panel? appeared first on The Old New Thing.

When would CopyFile succeed but produce a file filled with zeroes?

$
0
0

A customer reported that they very rarely encounter cases where they used the Copy­File function to copy a file from a network location to the local system, the function reports success, but when they go to look at the file, it’s filled with zeroes instead of actual file data. They were wondering what could cause this situation. They suspected that the computer may have rebooted and wanted to know whether file contents are flushed to disk under those conditions.

When the Copy­File function returns success, it does not mean that the data has reached physical media. It means that the data was written to the cache. The customer didn’t specify whether the reboot was a crash or an organized restart. If the system crashed, then any unwritten data in the cache is lost. On the other hand, if the reboots through the normal shutdown-and-reboot process, then the cache will be written out as part of the shutdown.

The customer wondered whether passing the COPY_FILE_NO_BUFFERING would cover the crash scenario.

A member of the file system team explained that the COPY_FILE_NO_BUFFERING flag tells the system not to keep data in memory, but rather send it straight to the device. This flag was recommended for large files (which don’t fit in RAM anyway), but it’s a bad idea for small files, since every file will have to wait for the device to respond before the system can move on to the next file. You’ll have to experiment to find the breakeven point for your specific data set and device.

Note, however, that the COPY_FILE_NO_BUFFERING doesn’t solve the problem.

For example, the system might crash while the file copy is still in progress. The Copy­File function creates a 4GB file (say), but manages to copy only 1GB of data into it before crashing. The other 3GB was never copied even though the file claims to be 4GB in size.

Another possibility is that all the file data makes it to the device, but the metadata does not get written to the device before the crash. The Copy­File returned success, but all of the bookkeeping didn’t make it to the device.

Even if you call Flush­File­Buffers, the system could crash before Flush­File­Buffers returns.

One possible way to address these problems is to copy the file to a temporary name, flush the file buffers, and then rename the file to its final name. The downside of this is that it forces synchronous writes to the device, which slows down your overall workflow, so it’s not a cheap algorithm to use.

But let’s step back. Is there a way to avoid slowing down the common case just because there’s a rare problem case?¹

A higher-level solution may be in order: The next time your program runs, you can detect that it did not shut down properly. When that happens, hash the file contents and compare it to the expected value. This moves the expensive operation to the rare case, allowing the file copy to proceed at normal speed.

¹ We’re assuming that system crashes are rare. If they’re not rare, then I think you have bigger problems.

The post When would CopyFile succeed but produce a file filled with zeroes? appeared first on The Old New Thing.

If each thread’s TEB is referenced by the fs selector, does that mean that the 80386 is limited to 1024 threads?

$
0
0

Commenter Waleri Todorov recalled that the global descriptor table (GDT), which is one of the places that selectors are defined, is limited to 1024 selectors. Does that mean that there is a hard limit of 1024 threads?

The question was in the context of how Windows NT for the 80386 managed the thread environment block (TEB), namely, by using the fs register to point to the per-thread data. The point is that there are at most 1024 possible distinct values for the fs register to have, so does this implicitly limit the number of threads to 1024?

No, it doesn’t, because nobody said that the distinct values had to be different simultaneously.

Let’s start with a single-processor system. That single processor is executing only one thread at a time, so there needs to be only one valid value for fs at a time. When the processor changes threads, the definition of that selector is updated to refer to the TEB for the incoming thread. Using selectors to access another thread’s TEB is not part of the ABI; all that is required is that you can use fs to access your own TEB.

You can see this in the debugger. Break into a multithreaded program and look at the value of the fs register. On my system it’s 0x0053. Switch to another thread and look at the value of the fs register. It’s the same value: 0x0053. Every thread has the same selector in fs. What happens is that each time the processor changes threads, the GDT entry for 0x0053 is updated to refer to the TEB of the thread that is being scheduled.¹

This trick works even on multiprocessor systems. Each processor has its own GDTR internal register, so instead of sharing a single GDT for all processors, you can give each processor its own GDT.

So I guess this puts the theoretical maximum number of processors supported by an x86-based system at around twenty-four million, because that would exhaust all of kernel mode address space just for GDTs.²

No, wait, that’s still not the limit, because each processor also gets its own page table. After all, that’s how two processors can be executing threads from different processes (and therefore in different address spaces). So the theoretical limit is basically until you run out of memory.

But I suspect you’ll run into other problems long before you add that twenty-four-millionth processor.

¹ Bit 2 is clear for GDT selectors and set for LDT selectors, so you can infer that 0x0053 is a GDT selector.

² I calculated this by dividing 2³¹ by 0x60, which is my presumed minimum size for a GDT. A selector whose numeric value is 0x0053 implies that the GDT is at least 0x0058 bytes in size, because that’s how big you need to be to get to a selector value of 0x0053 in the first place.

The post If each thread’s TEB is referenced by the fs selector, does that mean that the 80386 is limited to 1024 threads? appeared first on The Old New Thing.

The secret signal that tells Windows Runtime events that the event recipient no longer exists

$
0
0

There is a convention in the Windows Runtime that if an event handler returns the error code RPC_E_DISCONNECTED, then that means that the event recipient no longer exists and can be removed from the list of handlers. The C++/CX name for this error code is Platform::Disconnected­Exception. C# doesn’t have a specific name for it; it’s just a special case of COMException.

By convention, the RPC_E_DISCONNECTED is returned in the case where you subscribe to an event with a weak reference to the event recipient, but when the event is raised, the weak reference fails to resolve to an object, meaning that the object no longer exists. It is a convention in the Windows Runtime that the event source auto-unregisters the handler when it learns that the receipient no longer exists. In other words, once you report that the event recipient is gone, it cannot magically resurrect itself.

In practice, you get this error in two cases. The first is the case where the event recipient forgot to unregister itself from the event before it destructed. In that case, the handler is never going to be deregistered (the object forgot to clean up prior to destruction), and automatically unregistering the handler avoids a leak.

The second is the case where the event recipient unregistered the handler, but an event was in flight at the time the handler was unregistered, so you hit a race condition where the event source was all primed to call the handler just as you unregistered it. In that case, the event handler was already removed, so the auto-unregistration is redundant and harmless.

The WRL event source goes one step further and gives this auto-unregister treatment to the RPC_S_SERVER_UNAVAILABLE error code as well. And the winrt::event goes two steps further and also auto-unregisters upon receipt of JSCRIPT_E_CANT­EXECUTE.

What this means for you is that you should avoid making this secret signal accidentally. If you return one of these secret error codes (or throw the corresponding exceptions), you are going to tell the event source that your handler is unrecoverably dead. The most common case where you might do it by mistake is if you in turn call out to another object, and that other object returns one of these error codes or throws one of these exception, and you propagate the error to the event or allow the exception to escape your event handler.

Next time, we’ll look at how event handlers interact with garbage collection.

Bonus chatter: Many years ago, one of my friends accidentally invoked a secret code. When she and her friends ordered drinks, she asked for a sidecar, and was perplexed when she received something that didn’t resemble the cocktail at all. It turns out that in that area, the term sidecar was a secret code meaning that you’d like an additional shot of alcohol served with your existing drink. (I assume it was a secret code because strict liquor laws prevented you from asking for the extra shot explicitly.)

 

The post The secret signal that tells Windows Runtime events that the event recipient no longer exists appeared first on The Old New Thing.

Windows Runtime delegates and object lifetime in C# and other GC languages

$
0
0

In C# and other GC languages such as JavaScript, delegates (most typically used as event handlers) capture strong references to objects in their closures. This means that you can create reference cycles that are beyond the ability of the GC to collect.

using Windows.Devices.Enumeration;

class Circular
{
  DeviceWatcher watcher;

  public Circular()
  {
    watcher = DeviceInformation.CreateWatcher();
    watcher.Added += OnDeviceAdded;
  }

  void OnDeviceAdded(DeviceWatcher sender, DeviceInformation info)
  {
    ...
  }
}

The Circular class contains a reference to a Device­Watcher, which in turn contains a reference (via the delegate) back to the Circular. This circular reference will never be collected because one of the participants is a DeviceWatcher, which is beyond the knowledge of the garbage collector.

From the garbage collector’s point of view, the system looks like this:

? delegate Circular DeviceWatcher

The garbage collector knows that there is an outstanding reference to the delegate from some external source, and it knows that that delegate has a reference to the Circular object, and it knows that the Circular object has a reference to the Device­Watcher object, but it has no insight into what the Device­Watcher object may have references to. It has no idea that the Device­Watcher was in fact the question mark the whole time.¹

To avoid a memory leak, you will have to break this circular reference. Ideally, there is some natural place to do this cleanup. For example, if you are a Page, you can clean up in your On­Navigated­From method, or in response to the Unloaded event. Less ideally, you could add a cleanup method, possibly codified in the IDisposable pattern.

There is a special case: The XAML framework has a secret deal with the CLR, whereby XAML shares more detailed information about the references it holds. This information makes it possible for the CLR to break certain categories of circular references that are commonly-encounted in XAML code. For example, this circular reference can be detected by the CLR with the assistance of information provided by the XAML framework:

<!-- XAML -->
<Page x:Name="AwesomePage" ...>
  ...
  <Button x:Name="SomeNamedButton" ... >
  ...
</Page>

// C# code-behind
partial class AwesomePage : Page
{
  AwesomePage()
  {
    InitializeComponent();
    SomeNamedButton.Click += SomeNamedButton_Click;
  }

  void SomeNamedButton_Click(object sender, RoutedEventArgs e)
  {
    ...
  }
}

There is a circular reference here between the Awesome­Page and the Some­Named­Button, but the extra information provided by the XAML framework gives the CLR enough information to recognize the cycle and collect it when it becomes garbage.

¹ “It was the question mark all along” sounds like the spoiler to a bad M. Night Shyamalan movie.

The post Windows Runtime delegates and object lifetime in C# and other GC languages appeared first on The Old New Thing.


Windows Runtime delegates and object lifetime in C++/CX

$
0
0

In C++/CX, there are two ways to create event handlers: As an (object, method) pair, or as a lambda. And the lifetime rules are different depending on how you do it.

When you create a delegate with an object and a method, the object is captured by weak reference. When the delegate is invoked, the runtime first attempts to resolve the weak reference back to a strong reference. If successful, then it calls the method. If not successful, it raises the Platform::Disconnected­Exception, which we saw earlier is a signal to the event source that the delegate should be auto-unregistered because it will never succeed again.

When you create a delegate with a lambda, you capture objects into your lambda, and those objects remain alive for as long as the delegate exists. For event handlers, the delegate generally¹ continues to exist until the handler is unregistered. In C++/CX, hat pointers are strong references, so if you capture a hat pointer, you captured a strong reference to the object. In particular, in methods on ref classes, this is a hat pointer to the enclosing class, so capturing this captures a strong reference to the enclosing class. This is called out in the documentation:

A named function captures the “this” pointer by weak reference, but a lambda captures it by strong reference and creates a circular reference.

The documentation here is being a bit presumptive that the object captured in the lambda in turn contains a reference to the object that holds the delegate. If that’s not the case, then you don’t have an (immediate) circular reference.

using namespace Windows::Devices::Enumeration;

ref class Circular
{
  DeviceWatcher^ watcher;

public:
  Circular()
  {
    watcher = DeviceInformation::CreateWatcher();
    watcher.Added += ref new TypedEventHandler<
        DeviceWatcher^, DeviceInformation^>(
          [this](DeviceWatcher^ sender, DeviceInformation^ info)
          {
             ...
          });
  }
};

The above example creates a C++/CX delegate with a lambda, and that lambda captured this. Since this is a ref class, a strong reference was captured into the lambda, and we have created a circular reference:

    delegate    
  ↗︎   ↘︎  
DeviceWatcher     Circular

As with C#, you will have to break this circular reference manually. You can’t break the arrow from the delegate to the Circular object (since it’s captured inside the lambda), so your choices are to unregister the delegate from the event (breaking the arrow from the Device­Watcher to the delegate) or to null out the Circular object’s reference to the Device­Watcher.

On the other hand, this version creates the delegate with an object and a method pointer:

ref class Circular
{
  DeviceWatcher^ watcher;

public:
  Circular()
  {
    watcher = DeviceInformation::CreateWatcher();
    watcher.Added += ref new TypedEventHandler<
        DeviceWatcher^, DeviceInformation^>(
          this, &Circular::OnDeviceAdded);
  }

private:
  void OnDeviceAdded(DeviceWatcher^ sender, DeviceInformation^ info)
  {
     ...
  }
};

The object is captured by weak reference into the delegate, which means that there is no circular reference.

Note that capturing the object by weak reference means that the delegate will not keep the object alive. If you want the object to remain alive, you’ll have to keep it alive yourself.

A final note is that when an event handler is created via XAML markup, the resulting delegate is of the (object, method) variety.

<!-- XAML -->
<Page x:Name="AwesomePage" ...>
  ...
  <Button Click="Button_Click" >
  ...
</Page>

When you write the above XAML, the delegate is created as if you had written

thatButton.Click += ref new EventHandler<RoutedEventArgs^>
    (this, &AwesomePage::Button_Click);

So you don’t have to worry about circular references created by XAML markup event handlers.

¹ Naturally, you can extend the lifetime of the delegate by keeping an explicit reference to the delegate after you create it. But people rarely do that, and if you do, you know what you signed up for.

The post Windows Runtime delegates and object lifetime in C++/CX appeared first on The Old New Thing.

Windows Runtime delegates and object lifetime in C++/WinRT

$
0
0

In C++/WinRT, there are four ways to create delegates for event handlers:

  • As a raw pointer with a method pointer.
  • As a strong pointer with a method pointer.
  • As a weak pointer with a method pointer.
  • As a lambda.
// raw pointer with method pointer.
MyButton.Event({ this, &AwesomePage::Button_Click });

// strong pointer with method pointer.
MyButton.Event({ get_strong(), &AwesomePage::Button_Click });

// weak pointer with method pointer.
MyButton.Event({ get_weak(), &AwesomePage::Button_Click });

// lambda.
MyButton.Event([...](auto&& sender, auto&& args) { ... });

The first three are all very similar. They call the method on the object. The only difference is how they obtain the object.

  • Raw pointer: The method is invoked on the raw pointer.
  • Strong pointer: The method is invoked on the strong pointer.
  • Weak pointer: The weak pointer is resolved to a strong pointer. If successful, the method is invoked on the strong pointer. If unsuccessful, nothing happens.¹

The lambda case is the same as always: The lambda is invoked. It is up to the lambda to determine what objects were captured, how they were captured, and what happens when the lambda is invoked.

The tricky part is deciding which of these mechanisms is most appropriate for your use case.

Use the strong pointer if you need the object referenced by the strong pointer to remain alive for as long as the event handler is registered. This is not a common scenario; usually, you are keeping the object alive by other means.

Before discussing the scenarios where you would use the weak or raw pointer, let’s think about the problems these options are trying to solve.

The underlying problem is the event that is raised after the object has been destructed. Can you guarantee that this will not happen? If you can guarantee it, then you can use a raw pointer. If you cannot guarantee it, then you must use a weak pointer. (If you’re not sure, it is always okay to use a weak pointer.)

Under what conditions can you guarantee that the event won’t be raised after the object has been destructed? Well, one obvious requirement is that you have to stop the event handler from being invoked. The standard way of doing this is by unregistering the event handler. A less common way is destroying the event source. (It’s less common because it assumes that nobody else has taken a reference to the event source and is thereby keeping it alive!)

Preventing the event source from calling your handler is completely on you, so let’s assume you remember to do that, and that you do it correctly.

Even if you remember to unregister the event handler, it’s possible to receive an event after unregistering if the event can be raised from another thread, There’s an unavoidable race condition, because the event may be in flight at the time you unregister the handler. The only way to be sure that you won’t receive any events after unregistering is when the event is always raised from the thread doing the unregistering. This guarantee is available only for objects with thread affinity, and in the cases where the event is raised synchronously in response to the event trigger. In practice, this means that you have this guarantee only for UI objects, such as XAML elements.

Okay, so now that we understand the scenarios, we can write the guidance.

If the event source has thread affinity (typical of UI objects), then you have the option of using either the weak pointer or raw pointer version. The raw pointer is more efficient, but it counts on you having done your analysis correctly. And of course when your object is destroyed, you have to remember to stop the event handler from being invoked.

Use the weak pointer for all the other cases where you are choosing between a weak pointer and a raw pointer, namely if the event source does not have thread affinity, which basically means that the event source is not a UI object.

When an event handler is created via XAML markup, the resulting delegate is created with a raw pointer and method pointer.

<!-- XAML -->
<Page x:Name="AwesomePage" ...>
  ...
  <Button Click="Button_Click" >
  ...
</Page>

When you write the above XAML, the delegate is created as if you had written

thatButton.Click({ this, &AwesomePage::Button_Click });

The XAML compiler knows that it’s safe to do this because it’s hooking up the handler to a XAML element, which is a UI object with thread affinity. It therefore knows that it can unregister its handlers at destruction without risk of events coming in late.

Additional reading:

¹ Note that the “nothing happens” also means that it doesn’t even return the magic RPC_E_DISCONNECTED error code to tell the event source that the handler is permanently dead.

The post Windows Runtime delegates and object lifetime in C++/WinRT appeared first on The Old New Thing.

Programming puzzle: Creating a map of command handlers given only the function pointer

$
0
0

Suppose you have some sort of communication protocol that sends packets of binary-encoded information. There is some information at the start of the packet that describe what command is being sent, and the rest of the bytes in the packet describes the parameters to the command.

The puzzle is to come up with a generic dispatcher that accepts command / handler pairs and does the work of extracting the command parameters from the packet and calling the handler.

You are given this class:

class Packet
{
public:
 int32_t ReadInt32();
 uint32_t ReadUInt32();
 int8_t ReadInt8();
 std::string ReadString();
 ... and so on ...
};

The Read methods parse the next bytes in the packet and produces a corresponding object. Sometimes the object is simple, like an integer. Sometimes it’s complicated, like a string. Don’t worry about the details of the parsing; the Packet object will do it.

The puzzle is to implement the Dispatcher class:

class Dispatcher
{
public:
  void AddHandler(uint32_t command, ??? SOMETHING ???);
  void DispatchCommand(uint32_t command, Packet& packet);
};

The intended usage is like this:

// Some handler functions
void HandleFoo(int32_t, int32_t);
void HandleBar(int32_t);
void HandleBaz(int32_t, std::string);

// Command 0 is the "Foo" command that takes
// two 32-bit integers.
dispatcher.AddHandler(0, HandleFoo);

// Command 1 is the "Bar" command that takes
// one 32-bit integer.
dispatcher.AddHandler(1, HandleBar);

// Command 4 is the "Baz" command that takes
// a 32-bit integer and a string.
dispatcher.AddHandler(4, HandleBaz);

// We received a packet. Dispatch it to a handler.
dispatcher.DispatchCommand(command, packet);

The Dispatch­Command method looks up the commandId and executes the corresponding handler. In this case, the effect would be as if the Dispatch­Command were written like this:

void DispatchCommand(uint32_t command, Packet& packet)
{
 switch (command) {
 case 0:
  {
   auto param1 = packet.ReadInt32();
   auto param2 = packet.ReadInt32();
   HandleFoo(param1, param2);
   break;
  }
 case 1:
  {
   auto param1 = packet.ReadInt32();
   HandleBar(param1);
   break;
  }
 case 4:
  {
   auto param1 = packet.ReadInt32();
   auto param2 = packet.ReadString();
   HandleFoo(param1, param2);
   break;
  }

 default: std::terminate();
 }
}

For the purpose of the puzzle, we won’t worry too much about the case where an invalid command is received. The puzzle is really about the dispatching of valid commands.

Okay, let’s roll up our sleeves. One way to attack this problem is to do it in a way similar to how we implemented message crackers for Windows messages: Write a custom dispatcher for each function signature.

class Dispatcher
{
 std::map<uint32_t, std::function<void(Packet&)>> commandMap;

public:
 void AddHandler(uint32_t command, void (*func)(int32_t, int32_t))
 {
  commandMap.emplace(command, [func](Packet& packet) {
   auto param1 = packet.ReadInt32();
   auto param2 = packet.ReadInt32();
   func(param1, param2);
  });
 }

 void AddHandler(uint32_t command, void (*func)(int32_t))
 {
  commandMap.emplace(command, [func](Packet& packet) {
   auto param1 = packet.ReadInt32();
   func(param1);
  });
 }

 void AddHandler(uint32_t command, void (*func)(int32_t, std::string))
 {
  commandMap.emplace(command, [func](Packet& packet) {
   auto param1 = packet.ReadInt32();
   auto param2 = packet.ReadString();
   func(param1, param2);
  });
 }

 ... and so on ...

 void DispatchCommand(uint32_t command, Packet& packet)
 {
  auto it = commandMap.find(command);
  if (it == commandMap.end()) std::terminate();
  it->second(packet);
 }
};

We write a version of Add­Handler for each function signature we care about, and adding a handler consists of creating a lambda which which extracts the relevant parameters from the packet and then calls the handler. These lambdas are captured into a std::function and saved in the map for future lookup.

The problem with this technique is that it’s tedious writing all the lambdas, and the Dispatcher class needs to know up front all of the possible function signatures, so it can had an appropriate Add­Handler overload. What would be better is if the compiler could write the lambdas automatically based on the parameters to the function. This avoids having to write out all the lambdas, and it means that the Dispatcher can handle arbitrary function signatures, not just the ones that were hard-coded into it.

First, we write some helper functions so we can invoke the Read methods more template-y-like.

template<typename T> T Read(Packet& packet) = delete;

template<> int32_t Read<int32_t>(Packet& packet)
    { return packet.ReadInt32(); }

template<> uint32_t Read<uint32_t>(Packet& packet)
    { return packet.ReadUInt32(); }

template<> int8_t Read<int8_t>(Packet& packet)
    { return packet.ReadInt8(); }

template<> std::string Read<std::string>(Packet& packet)
    { return packet.ReadString(); }

... and so on ...

If somebody needs to read a different kind of thing from a packet, they can add their own specialization of the Read function template. They don’t need to come back to you to ask you to change your Dispatcher class.

Now the hard part: Autogenerating the lambdas.

We want a local variable for each parameter. The template parameter pack syntax doesn’t let us create a variable number of variables, but we can fake it by putting all the variables into a tuple.

template <typename... Args>
void AddHandler(uint32_t command, void(*func)(Args...))
{
  commandMap.emplace(command, [func](Packet& packet) {
    auto args = std::make_tuple(Read<Args>(packet)...);
    std::apply(func, args);
  };
}

The idea here is that we create a tuple, each of whose components is the next parameter read from the packet. The templatized Read method extracts the parameter from the packet. We take all those parameters, bundle them up into a tuple, and then std::apply the function to the tuple, which calls the function with the tuple as arguments.

Unfortunately, this doesn’t work because it relies on left-to-right order of evaluation of parameters, which C++ does not guarantee. (And in practice, it often isn’t.)

We need to build up the tuple one component at a time.

template<typename First, typename... Rest>
std::tuple<First, Rest...>
read_tuple(Packet& packet)
{
  auto first = std::make_tuple(Read<First>(packet));
  return std::tuple_cat(first, read_tuple<Rest>(packet));
}

std::tuple<> read_tuple(Packet& packet)
{
  return std::tuple<>();
}

template <typename... Args>
void AddHandler(uint32_t command, void(*func)(Args...))
{
  commandMap.emplace(command, [func](Packet& packet) {
    auto args = read_tuple(packet);
    std::apply(func, args);
  };
}

We use the standard template metaprogramming technique of employing recursion to process each template parameter one at a time. You must resist the temptation to simplify

  auto first = std::make_tuple(Read<First>(packet));
  return std::tuple_cat(first, read_tuple<Rest>(packet));

to

  return std::tuple_cat(std::make_tuple(Read<First>(packet)),
                        read_tuple<Rest>(packet));

because that reintroduces the order-of-evaluation problem the read_tuple function was intended to solve!

The attempted solution doesn’t compile because you can’t do this sort of recursive template stuff with functions. (I’m not sure why.) So we’ll have to wrap it inside a templatized helper class.

template<typename... Args>
struct tuple_reader;

template<>
struct tuple_reader<>
{
  static std::tuple<> read(Packet&) { return {}; }
};

template<typename First, typename... Rest>
struct tuple_reader<First, Rest...>
{
  static std::tuple<First, Rest...> read(Packet& packet)
  {
    auto first = std::make_tuple(Read<First>(packet));
    return std::tuple_cat(first,
                 tuple_reader<Rest...>::read(packet));
  }
};

template <typename... Args>
void AddHandler(uint32_t command, void(*func)(Args...))
{
  commandMap.emplace(command, [func](Packet& packet) {
    auto args = tuple_reader<Args...>::read(packet);
    std::apply(func, args);
  };
}

We start by defining our tuple_reader helper template class as one with a variable number of template parameters.

Next comes the base case: There are no parameters at all. In that case, we return an empty tuple.

Otherwise, we have the recursive case: We peel off the first template parameter and use it to Read the corresponding actual parameter from the packet. Then we recursively call ourselves to read the remaining parameters from the packet. And finally, we combine our actual parameter with the tuple produced by the remaining parameters, resulting in the complete tuple.

The std::tuple_cat function requires tuples, so we take our first parameter and put it in a one-element tuple, so that we can concatenate the second tuple to it.

Now I’m going to pull a sneaky trick and combine the forward declaration with the recursion base case:

// Delete 
//
// template<typename... Args>
// struct tuple_reader;
//
// template<>
// struct tuple_reader<>
// {
//   static std::tuple<> read(Packet&) { return {}; }
// };

template<typename... Args>
struct tuple_reader
{
  static std::tuple<> read(Packet&) { return {}; }
};

This trick works because the only thing that will match the template instantiation is the zero-parameter case. If there is one or more parameter, then the First, Rest... version will be the one chosen by the compiler.

We’re almost there. If one of the parameters is non-copyable, the above solution won’t work because the first is passed by copy to std::tuple_cat, and the args is passed by copy to std::apply.

Even if the parameters are all copyable, the std::move is helpful because it avoids unnecessary copies. For example, if a very large string was passed in the packet, we don’t want to make a copy of the large string just so we can pass it to the handler function. We just want to let the handler function use the string we already read.

To fix that, we do some judicious std::moveing.

template<typename... Args>
struct tuple_reader
{
  static std::tuple<> read(Packet&) { return {}; }
};

template<typename First, typename... Rest>
struct tuple_reader<First, Rest...>
{
  static std::tuple<First, Rest...> read(Packet& packet)
  {
    auto first = std::make_tuple(Read<First>(packet));
    return std::tuple_cat(std::move(first), // moved
                 tuple_reader<Rest...>::read(packet));
  }
};

template <typename... Args>
void AddHandler(uint32_t command, void(*func)(Args...))
{
  commandMap.emplace(command, [func](Packet& packet) {
    auto args = tuple_reader<Args...>::read(packet);
    std::apply(func, std::move(args)); // moved
  };
}

The Add­Handler method could be condensed slightly, which also saves us the trouble of having to std::move the tuple explicitly.

    std::apply(func, tuple_reader<Args...>::read(packet));

Exercise 1: Change the tuple_reader so it evaluates the template parameters from right to left.

Exercise 2: Suppose the Packet has methods for sending a response to the caller. In that case, the handler should receive a Packet& as its first parameter, before the other optional parameters. Extend the above solution to support that.

Exercise 3: (Harder.) Extend the above solution to support passing an arbitrary function object as a handler, such as a lambda or std::function.

The post Programming puzzle: Creating a map of command handlers given only the function pointer appeared first on The Old New Thing.

Why does Explorer’s New menu take so long to appear the first time I open it?

$
0
0

When you right-click on an empty space in an Explorer folder and select the New menu item, there is sometimes a delay of up to two seconds before the menu appears. What’s going on during that delay?

The items in the New menu are discovered by looking for Shell­New subkeys in HKEY_CLASSES_ROOT.

This search takes some time, seeing as HKEY_CLASSES_ROOT is a rather large registry key. The New menu starts a background task to collect all the information and waits up to two seconds for it to report results. If the task doesn’t finish within two seconds, then the New menu gives up waiting and merely shows the result from the previous time the task ran, hoping that no new items were added in the meantime.

Meanwhile, the task continues to run and eventually completes with a list of items. If it finishes in time, the New menu will use it right away. Otherwise, it is saved for the New menu to use next time.

This is the similar sort of nonsense we went through with the COM component category catalog.

The post Why does Explorer’s <I>New</I> menu take so long to appear the first time I open it? appeared first on The Old New Thing.

In times of uncertainty, take your cue from the janitor

$
0
0

A friend of mine told a story of a time his flight to a foreign country was delayed by several hours, and the flight finally arrived shortly before the airport closed for the night. He retrieved his bags from the baggage carousel and waited for his hosts to come pick him up.

While he waited, the closing time of the airport passed. He was the only person left in the airport, except for a janitor who was busy mopping the floor.

And then there was a commotion at the other end of the baggage claim. A team of police with their weapons drawn scurried into the building, hiding behind walls and other structures, as if they were searching for an armed intruder.

My friend was rather worried at this display of force and wondered what was going on.

Then he looked over at the janitor. The janitor continued to mop the floor, unphased by the hubbub surrounding him.

My friend breathed a sigh of relief. The fact that the janitor remained unperturbed told him that this was probably some sort of training exercise that the police did every night after closing, or at least frequently enough that the janitor knew that it was nothing to be concerned about.

 

The post In times of uncertainty, take your cue from the janitor appeared first on The Old New Thing.

Viewing all 24428 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>