The reason Emacs "starts to undo after all of the possible redos are done" is that, after you interrupted the original undo sequence with
C-g, every change you had made up until that point became part of the tree to be undone. Those changes include not only the initial text you entered ("aa bb cc") but also the undoing of those changes. As the manual explains: "Any command other than an undo command breaks the sequence of undo commands. Starting from that moment, the entire sequence of undo commands that you have just performed are themselves placed into the undo record."
This is not how
undo works in most other text editors, and some people find this behavior counterintuitive. However, this type of
undo is very powerful, since it allows you to recover any past state of a buffer. You may want to try the package undo-tree, which replaces the native
undo Emacs feature with a more intuitive equivalent, without sacrificing its power. Its commands
undo-tree-redo will behave like the
redo you are used to, but you can also see a visualization of the undo tree branches (
undo-tree-visualize), and switch branches if needed (
ADDED: Check the comments to this answer by ideasman42 and Phil Hudson for some issues with
undo-tree, and note that as ideasman42 mentions in another answer, Emacs 28 offers the command
undo-redo out of the box.