24

If I have a function that never returns (it contains an infinite loop) how do I declare/communicate that it will never return to the user of the function? The user does not see the source code, only the function signature and the documentation.

So the user will know to call my function in a new thread else his program will be blocked forever and the code below my function will never get executed.

Example function:

public void Foo()
{
    while (true)
    {
        _player.Play("scary_sound.wav");
        Thread.Sleep(TimeSpan.FromMinutes(5));
    }
}

Should I declare that as part of the documentation?

/// <summary>Plays scary sounds!</summary>
/// <remarks>This function never returns. Call it on a new thread.</remarks>
public void Foo()

Or name it with a "Run" prefix and "Loop" suffix or something?

public void RunFooLoop()

What ways are there to declare/communicate this? Any common practices? Any examples?

Fred
  • 509

9 Answers9

64

This is one of those cases where infinite loops are so predominantly a bad idea that there's no real support/convention around using them.

Disclaimer
My experience is with C# (as is your code example, seems to be), but I would think that this is language-agnostic, unless there are languages which automatically wrap method calls in separate threads.

Based on the code you posted, what you've created here is a honeytrap. You can call the method any time you like, but you can never leave (I couldn't resist)

What you have here is the equivalent of asking your local government what signs you need to put up in your garden to tell people that you buried mines in your garden. The answer is that you shouldn't be burying mines in your garden.

The only practical use case for an infinite loop is for an application whose runtime process will be killed by the user (e.g. ping 8.8.8.8 -t is such an example). This is most commonly encountered in always-on services that are expected to run indefinitely.

However, playing a sound isn't something that you want your application to hang on. It's something you want to have happen concurrently. Therefore, you should design your code to work with it.

In essence, you should make a class that behaves like a media player. Maybe for you it's enough if this media player only has a start button with no way of ever turning it off again. I'll leave that up to you to decide.

But the play button click event is not an infinite loop. The sound should keep playing, but the click event should return.

This function never returns. Call it on a new thread.

Instead of pushing that responsibility to your consumer, just develop a class that, when called creates this thread and starts playing the sound.

This isn't a matter of "how do I tell my consumer that he should do this". It's significantly easier to do it for them. It promotes code reuse, lowers the change of bugs and unexpected behavior, and the documentation becomes easier to write as well.


Also, as an aside, you may want to look into using async/tasks instead of threads. It helps keep down the amount of active threads. But that's a different discussion.

Flater
  • 58,824
46

This depends on the type system and the language.

Many languages have a type for this, for example never in TypeScript, Nothing in Scala and Kotlin, or Void in Haskell.

C has a _Noreturn function specifier keyword.

In languages that support annotations (e.g. Java, C#), you could create a custom annotation that communicates your intent. Some IDE vendors or static analyzers might already have invented one for you.

Otherwise, documentation is the best thing you can do.

A type such as this is often a bottom type, meaning a type that is a subtype of every other type, and it is a type that can never have any instance. (This is called an "uninhabited" type.) Since it cannot have any instances, declaring the return type of a function as this type clearly communicates that the function never returns, since there is nothing it could return.

Bergi
  • 1,368
Jörg W Mittag
  • 104,619
13

I will answer from the point of view of C++, which has not been addressed in other answer at the time of writing.

Since C++11, there is an attribute, noreturn, that you can use to signal that a function will never actually give back control to the caller.

Before C++11, each compiler could decide to have such an extension (for example, GCC on this page you can find the description of the attribute used for this)

In embedded systems, I have seen such functions used as traps when an error or fault is detected, so the the processor can keep running and the developer can debug the problem. It might be useful in production too, but I've not seen such a use yet (and I guess it depends on the application.)

bracco23
  • 429
6

Naming

Or name it with a "Run" prefix and "Loop" suffix or something?

Instead of "Run" or "Loop", I'd prefer "Forever" here. As in, ForeverFoo() or FooForever().

There's a readability cost to long identifiers, but for rarely used ones this cost is negligible. I suspect the clarity outweighs the loss of brevity in this case. Do explain it in the documentation though, just to be explicit.

Factor out the pattern

If you have multiple of these infinite functions, consider factoring out the forever pattern to its own function/template/macro. Depending on your language, this might be tricky if Foo() requires arguments, but it would definitely make things cleaner and more natural.

In Python, for example, I might do something like this:

import time

def forever(func, args, sleep_time=0, kwargs): while True: func(args, **kwargs) time.sleep(sleep_time)

forever(print, 'hi', end='#\n', sleep_time=1)

Then you might end up with a call like Forever(Foo), which is both readable and DRY.

This approach also lends itself well to other wrappers, for example Repeat() to play your scary sound a fixed number of times.

marcelm
  • 161
4

Provide low-level access and an expected-use wrapper

This is a frustrating bug waiting to happen. However, some users will want to control their own threading model. You can get the best of both worlds by creating a low-level function:

/** 
 * Play scary sounds in an infinite loop which never returns. 
 *  
 * See `PlayScarySounds` for a function which doesn't hijack the
 * current thread.
 */ 
public void PlayScarySoundsForever() {
    while (True) {
        _player.Play("scary_sound.wav");
        Thread.Sleep(TimeSpan.FromMinutes(5));
    }
}

And a wrapper function with your expected "spawn new thread" use case:

/** 
 * Spawn a new thread which plays scary sounds in an infinite loop.
 *
 * Wrapper around `PlayScarySoundForever`. 
 */
public void PlayScarySounds() {
    Thread.Spawn(() -> PlayScarySoundsForever());
}

Users might skim past the documentation, but they're less likely to skim over the list of methods and take note of a pattern there. The important part is that you give the "less scary" function the "less scary" name, since some users will still look at the method names and pick the one that looks most fitting.

Of course, there's also the question of "do you really not want to have some way of terminating this?" But that's, I think, a different question.

Phoenix
  • 758
1

Swift has types "Void" and "Never". "Void" has only one value (and needs 0 bits to store it). "Never" has zero values. Therefore a function cannot return a value of type "Never". Therefore a function declared to return "Never" cannot return.

gnasher729
  • 49,096
1

tl;dr Use asynchronous coding if you want asynchronous operation. Don't write sequential code unless:

  1. you actually want things to happen in the specific order; or/and

  2. you're confident that the earlier steps will return quickly enough for it to work out.

If you follow this, then it doesn't matter if a method returns because there's no practical distinction between non-returning methods and other types of methods that don't reliably return quickly (e.g., long-running methods and conditionally-returning methods).


Don't worry about if a method returns.

Programmers shouldn't be worried about stuff like if a method will ever return.

When you write procedural code, you're specifying a sequence of things that should happen. You shouldn't worry about if a method will ever return because it shouldn't matter; you don't want things after the method to happen before that method's done.

For example, if you write

PlaySounds();
SendEmail("Hey, I heard all the sounds!");

, it should be understood that the email won't send until the sounds are done playing.

By contrast,

BeginPlayingSounds();
SendEmail("Hey, I've begun listening to the sounds!");

will allow the email to be sent once sounds have started to play (which can mean different things), even if the sounds go on forever.


Discussion: False synchronicity as a performance hack.

Say you want to calculate both x*x and y*y.

  • Conceptually, unless you want these things to happen in a specific order, you shouldn't specify them as happening in a specific order.

  • Practically, these are very fast operations, so it's not usually worth the verbosity or computational overhead to write them as asynchronous. Modern compilers and CPU's may try to reorder such operations anyway.

In other words, while it's conceptually wrong to write non-synchronous logic as synchronous, it's often very practical to do so.

This disconnect between intent and practical coding is a deeper problem in modern programming practice. We're hesitant to do something like adopt conventions for non-returning methods because those wouldn't really fix the underlying problem.

For working programmers today, I'd think:

  1. By default, use asynchronous coding whenever you don't actually require things to happen sequentially. Use sequential only if you actually want things to happen sequentially.

  2. As a performance/brevity hack, reduce asynchronous code to falsely synchronous code according to your best judgement.


Switch focus to qualifying if methods will return quickly.

As discussed above, there're two reasons to write synchronous code:

  1. We want sequential operation.

  2. As a hack to dodge the verbosity/overhead of asynchronous code/execution.

So if you're going to call a method, the big thing you want to know is if it'll return quickly. Because if it does, then you might use falsely synchronous code.

Non-returning methods are just a subset of methods that don't necessarily return quickly. As such, they're not particularly interesting to programming practice.

Nat
  • 1,101
  • 1
  • 8
  • 12
1

Don't make a function that never returns. Don't ask the caller to put that function on its own thread.

Put the logic on its own thread within the function. Then name it appropriately for whatever it does.

For example, in C#:

public void PlaySounds()
{
    new Thread(() =>
    {
        while (true)
        {
            _player.Play("scary_sound.wav");
            Thread.Sleep(TimeSpan.FromMinutes(5));
        }
    }).Start();
}

However, as stated in other answers, an infinite loop is not the ideal way to go. Especially in this case, since the thread will never be killed. So maybe something like this would be more appropriate:

private static bool playSounds;
public void PlaySounds()
{
    playSounds = true;
    new Thread(() =>
    {
        while (playSounds)
        {
            _player.Play("scary_sound.wav");
            Thread.Sleep(TimeSpan.FromMinutes(5));
        }
    }).Start();
}

public void StopSounds() { playSounds = false; }

Evorlor
  • 1,563
  • 3
  • 18
  • 23
0

In Python, you can use typing.NoReturn to indicate that a function never returns. e.g.

from typing import NoReturn

def stop() -> NoReturn: raise RuntimeError('no way')

laike9m
  • 135