Who cares how it's implemented? Manually using a lock is a smell in the same way that goto is a smell. Wait for a task to finish is not a smell in the same way that while is not a smell.
"Manually using a lock is a smell in the same way that goto is a smell."
The presence of a lock tells you nothing about whether a program is well-designed. In fact, the whole idea that you can argue about the correctness of a program from the presence or absence of "code smells" is absurdly simplistic. I have seen plenty of bad code littered with while statements.
Code smells (I don't like the name either) are just warning signs. They do not necessarily indicate an actual problem.
Incorrect use of abstraction levels, such as using tools that are too low-level and error-prone for the task at hand (as in the majority of business applications) IS one of those warning signs. It usually indicates a conceptual problem, and makes the code needlessly complex.
There isn't really that big of a difference between a lock and a wait, which is the point that was being made. It either way will "lock" up the thread until condition X is full filled and since it depends on other threads to finish, it's still very possible to get deadlocks and a like.
Besides that, depending on the task, it's very important how something is implemented.
The presence of a lock tells you nothing about whether a program is well-designed. In fact, the whole idea that you can argue about the correctness of a program from the presence or absence of "code smells" is absurdly simplistic. I have seen plenty of bad code littered with while statements.