tags:

views:

4864

answers:

11

In traditional embedded programming, we will give a delay function like so:

for(i=0;i<255;i++)
   for(j=0;j<255;j++);

In the microprocessor's view, is this how the sleep() function works?

Is there an alternative for the sleep() function in C?

+13  A: 

The kind of loop you describe is called a "busy wait". In real operating systems, sleeping does not cause a busy wait; it tells the operating system to not schedule the process in until the sleep period is over.

Chris Jester-Young
ok any alternative for sleep()?
Manoj Doubts
there is a place for both. For example Windows kernel KeStallExecutionProcessor is busy wait and KeDelayExecutionThread is non busy wait. There is a situation when you want to wait without loosing context, for example reset hw -> wait few micro -> do rest of hw init
Ilya
I agree there are cases where busy waiting is indeed appropriate and implemented in-kernel. e.g., spin-locking. So I suppose my answer should be, "in real OSs, sleeping doesn't _usually_ cause a busy wait". :-)
Chris Jester-Young
+4  A: 

You talk about "embedded programming" in the OP. If you're doing embedded work and need something like sleep(), there are often hardware counters/timers available. This will vary from architecture to architecture, so have a look at the datasheet.

If you're not doing embedded work, I apologize :)

Tony Arkles
+5  A: 

If you're using for-loops, you'd better know what they compile to and how long those instructions take at your given clock speed, and ensure the CPU runs your instructions and nothing else (this can be done in embedded systems but it's tricky since it disallows interrupts).

Otherwise, you won't be able to tell how long it's really going to take.

Early PC games had this problem - they were built for a 4.7MHz PC and, when the faster computers came along, they were unplayable.

The best way a 'sleep' can work is for the CPU to know what time it is at any given point. Not necessarily the actual time (7:15 am) but at least the relative time (8612 seconds since some point in time).

That way it can apply a delta to the current time and wait in a loop until the current+delta is reached.

Anything that relies on number CPU cycles is inherently unreliable as the CPU may go off to another task and leave your loop hanging.

Let's say you have a memory-mapped 16-bit I/O port which the CPU increments once a second. Let's also assume it's at memory location 0x33 in your embedded system, where ints are also 16 bits. A function called sleep then becomes something like:

void sleep (unsigned int delay) {
    unsigned int target = peek(0x33) + delay;
    while (peek(0x33) != target);
}

You'll have to ensure that peek() returns the memory contents every time (so optimizing compilers don't muck up the logic) and that your while statement runs more than once per second so you don't miss the target, but these are operational issues that don't affect the concept I'm presenting.

paxdiablo
I guess you'd like `while (peek(0x33) < target);`, or you'll have a funny debugging experience when (for some reason) the HW skips the exact value you're interested in.
erikkallen
+6  A: 

Alternatives depend in what you are trying to do and what OS you are on.

If you just want to waste time, then these might help:

On most unix-type systems you'll find a 'usleep' function, which is more or less like sleep with greater resolution. Be careful with that one because it usually can not sleep for just one microsecond.

On some unix-type systems, the select system call can be used with all file descriptor sets zero in order to get a fairly accurate sub-second wait.

On windows systems, you have Sleep, which is pretty much the same, but taking a number of milliseconds.

In a multi-tasking operating system, a sleep function can sometimes be given 0 as a parameter. This generally causes the function to give up it's timeslice, but be re-scheduled immediately if no other task is ready to run.

Michael Kohne
+2  A: 

One common mechanism is to use a select() that is guaranteed to time out and specify the sleep time as the timeout:

// Sleep for 1.5 sec
struct timeval tv;
tv.tv_sec = 1;
tv.tv_usec = 500000;
select(0, NULL, NULL, NULL, &tv);

The select() is typically used to check a set of file descriptors and wait until at least one is ready to perform I/O. If none is ready (or, in this case, if no fds are specified), it will time out.

The advantage of select() over a busy loop is that it consumes very little resources while sleeping, while a busy loop monopolizes the processor as much as permitted by its priority level.

Adam Liss
Probably over-engineering but I thought select() was interruptable by signals. What happens if it's interrupted after 1/2 a second?
paxdiablo
Good catch -- in that case, use pselect() to mask signals, or create your own sleep-aware signal handlers. Thanks!
Adam Liss
+1  A: 

In a unix-derivative OS, you would probably schedule a signal() call, and your code would simply block the code until the signal is raised. Signals are intended for the purpose, and they are very simple and efficient.

le dorfier
Signals are not intended for delay purposes in general, although they can be used for that. They're not the lowest-overhead mechanism, because they are set up for asynchronous interruption. The select() alternative described above is probably more efficient.
bog
+2  A: 

Busy-waiting is for amateurs even in an embedded system, use a real time source.

Paul Betts
+3  A: 

There's more information on how sleep() works here

By the way, busy waiting is not necessarily for amateurs--although it does burn processor that you may want to use for some other purpose. If you are using a time source, you are limited to the granularity of that source. E.G. if you have a 1 ms timer, and want to way 500 uS, you have a problem. If your embedded system can handle the fact that you'll be buzzing in a loop for 500 uSec, that might be acceptable. And even if you have a timer with your desired granularity, you also need to get an interrupt off that timer at the right time...then dispatch ot the interrupt handler...then get to your code. Sometimes a busy loop is the most expedient solution. Sometimes.

bog
+3  A: 

sleep actually interfaces with operating system, where sleeping processes are placed outside of scheduling queue. I usually use:

poll(0, 0, milliseconds);

for POSIX compliant systems. select also works for windows (they must have a native API (probably called Sleep) for that.)

ididak
We found a bug in linux 2.6.11.6 that caused poll to hang the entire system under certain situations. Switching to select() fixed the problem; don't know if it still exists in recent kernels.
Adam Liss
+3  A: 

any decent C compiler would, without extra work, remove your code entirely and the delay would vanish

Scott Evernden
I remember some benchmarks from MANY years ago where a specific compiler-generated executable outperformed other compilers by many orders of magnitude. Turned out to be the compiler realizing that the result of the loop wasn't used anywhere else so it optimized the whole loop out of existence :-).
paxdiablo
+3  A: 

You would not use the code you published to sleep on an embedded system. A decent compiler would entirely remove it, and even if your compiler does not remove it is suboptimal, since running the processor in a tight loop will burn power, which is an issue for embedded system. Even systems not running on battery care about power usage, since lower power usage means cheaper power supplies and cooling.

The way you normally do this is your CPU will implement some sort of IDLE or SLEEP instructions, that will cause it to temporarily stop processing commands. An external interrupt line connected to a timer circuit will wake the processor back up at regular intervals, and which point the CPU checks to see if it has been asleep for long enough, and if not it goes back to sleep.

//Pseudo code
int start = getTime();
int end = start + sleepTime;

while (getTime() < end) {
       asm("SLEEP");
}

The exact details vary from processor to processor. If you are running as a process on an OS the sleep call generally just tells the scheduler to suspend your process, and then the kernel decides whether to schedule another process or to sleep the CPU. Also, the above code will not be adequete for real time systems, which want deadline guarantees, etc. In those cases you will need to get the time in the loop, know the duration of the time interrupt so ou know if you can resleep without blowing the deadline, and potentially reprogram the timer hardware or busy wait.

Louis Gerbarg
What if end < start due to timer rollover?
bk1e
pseudo code, I get to ignore those details, assume an infinite sized int ;-)
Louis Gerbarg