does a coroutine body execute forever even if a condition in the body is false unless I yield break?

I’m wondering if the way In which I write a coroutine implies that it’s condition will be tested forever (until the coroutine is explicitly stopped for instance), even if the condition is false. That is, for a given condition inside the coroutine body.

So I tried a simple case of updating a value 10 times. And I wrote 3 variations on it:

    IEnumerator updateValue1() {
		Debug.Log("*");
		while (value < 10) {
			value++;
			Debug.Log(value);
			yield return null;
		}
	}
	
	IEnumerator updateValue2() {
		Debug.Log("*");
		while (value < 10) {
			value++;
			Debug.Log(value);
			yield return null;
		}
		
		yield break;
	}
	
	IEnumerator updateValue3() {
		Debug.Log("*");
		if (value < 10) {
			value++;
			Debug.Log(value);
			yield return null;
		}
		else {
			yield break;
		}
	}

The interesting thing is that all 3 variations seem to behave the same: they print * a single time and the value 10 times. Which kind of throws me off cause I’d expect the first case to print * forever, even if value < 10 is no longer true and the two others to print * 10 times along with the value. Which makes me wonder:

  1. Does the body of the coroutine execute forever unless I explictly yield break out of it, only that it happens behind the scenes? Or does it magically destroy itself or stops getting called after a condition in its body no longer holds?
  2. Assuming the coroutine should never execute again after it “completes” (whatever measure of completion being encoded in some condition in its body), is there any efficiency (cycles or memory) gain in doing a yield break?

That third function shouldn’t print all 10 values unless you are repeatedly calling the coroutine.
A coroutine will follow the same logic of any function except the yield return line will cause it to pause execution, in this case until the following frame update. Yield break is used to terminate the coroutine prematurely before the end of the function has been reached. The yield break in there is redundant as you would fall out the function anyway.
Normally a function would execute in a single frame. Using a coroutine just allows you to pause execution for a while and resume at a later frame.