C language - sleep usleep nsleep - Windows Unix MacOS - Page 1
Forum section: electronics / general

author: Tantratron
author_rank: Frequent Contributor
timestamp: May 26, 2025, 10:01:19 am
content: Hello, I've a C programming question about how to use same Makefile and time delay sleep function inside C source file and compiler.A few of us have been trying to rationalize or fuse different old C source file initially working under Windows NT so it would be used either under Linux or MacOS (unix part).If looking at the original C source files, the previous author long ago then new author had to write again the subroutine usleep to be then compiled whereas this is not necessary if compiling with Unix or MacOS (under terminal mode).As a reference my GitHub repositoryhereand under Windowthis file.Would you know a simple solution to not have any need to write or rewrite these sleep() usleep() or nsleep() functions so they would be known by any Windows compiler ?Thank you in advance, Albert

author: radiolistener
author_rank: Super Contributor
timestamp: May 26, 2025, 11:01:38 am
content: The simplest and most robust solution is to avoid using any form of sleep(), usleep(), or nanosleep() altogether. In well-designed, portable, and efficient code, explicit sleeping is rarely necessary, especially for precise timing.However, if you do choose to use sleep functions, use sleep() only for relatively long delays (typically 10-20 milliseconds or more). These are suitable for coarse timing, throttling, or waiting for external events.If you find yourself needing sleep intervals shorter than 1-10 ms, it's usually a sign that something is fundamentally wrong with your algorithm or design. Code relying on sub-millisecond sleeps will almost certainly face serious portability, performance, and timing issues, especially on general-purpose operating systems. You may not realize it yet, but this approach leads to unpredictable behavior and difficult-to-diagnose bugs down the road.In certain scenarios, particularly in real-time systems, delays shorter than 10 ms may indeed be necessary. This is typically implemented using spin locks, where a thread actively waits in a tight loop, continuously checking a condition. While this approach is inefficient in terms of CPU usage, it can be justified in specific high-performance or low-latency contexts.That said, such use cases are quite specialized. If you're working in this domain, it's likely you already have a solid understanding of thread scheduling and low-level system behavior. In that case, the trade-offs and implications of using spin waits are something you're already well aware of.However, for most general-purpose applications, you should avoid using delays shorter than 10 milliseconds. In typical scenarios, such short sleeps offer no real benefit and are more likely to introduce subtle bugs, timing issues, and performance problems, especially if you’re not fully aware of when and why such delays might be justified. Simply put, it’s best to steer clear of them unless you have a compelling and well-understood reason.Well-designed code typically avoids the use of explicit delays altogether. Instead, it relies on event-driven architectures, callbacks (such as lambdas), and other asynchronous mechanisms to handle timing and coordination efficiently and responsively. This approach leads to more maintainable, scalable, and portable code.In short, just avoid using any kind of sleep altogether. If you find yourself reaching for it, take it as a red flag - it’s usually a strong indication that something in your design or logic needs to be re-evaluated.

author: colorburst
author_rank: Regular Contributor
timestamp: May 26, 2025, 12:26:49 pm
content: I'm not aware of a usleep() equivalent on Windows. Case in point, later Windows-based Tek scopes implement their own microsecond delays with a busy loop. Nothing wrong with it, put your implementation inside an #ifdef _WIN32 and don't worry about it.That said, 10us delays are probably unnecessary in your case since you're going through GPIB every time. I'm guessing the GPIB library will yield thread execution on some wait and you won't be woken up till many milliseconds later. I'd keep them for documentation purposes, but that's yet another reason not to lose sleep over it (pun intended).

author: Tantratron
author_rank: Frequent Contributor
timestamp: May 26, 2025, 12:27:32 pm
content: Many thanks@radiolistenerfor this wisdom answer, I understand the concern since myself come from embedded real software.The problem I'm faced, actually another member@madaowith myself is that we're trying to fuse or simplify or extend a sequence of previous C software developped under Windows long time ago by different members in Germany and Sweden. Furthermore the other problem is the nature of these sofwtare, maybe you know of TDSxxx oscilloscope memory map (flash NVRAM and EEPROM). It is kind of hacking through the GPIB interface of the processor board. Some people like myself use GPIB-USB interface with its own latency, other GPIB-ISA card (much faster bandwidth) then how the GPIB protocol is programmed via C function to actually distance read, write, flash remote memories.My approach in general is to look always for universal C code able to be cross-compiled whatever the plateform (Windows, MacOS or Linux) because if blockage in the Makefile then it shows an improvment route or a serious issue as you have warned. As for sleep msleep usleep nsleep functions, I kind of assumed they were self-normalized versus the computer, its CPU speed so provided say an absolute correct delay. So technically this concept works for MacOS (Unix) and Linux butappears to not work with Windows.Attached for yoru information different usleep code introduced as source file by previous user working on tektronix TDSxxx memory manipulation when doing Windows NT or othersCode:void usleep(__int64 usec){HANDLE timer;LARGE_INTEGER ft;ft.QuadPart = -(10*usec); // Convert to 100 nanosecond interval, negative value indicates relative timetimer = CreateWaitableTimer(NULL, TRUE, NULL);SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0);WaitForSingleObject(timer, INFINITE);CloseHandle(timer);}Code:static void my_usleep(uint32_t us) {#if defined(__MSDOS__) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)Sleep((us / 1000) + 1); // actually only millisecond resolution, but that should be OK#elseint ret = -1;struct timespec rqt, rmt;rqt.tv_sec = us / 1000000;rqt.tv_nsec = 1000 * (us % 1000000);while (ret && !abort_requested) {ret = nanosleep(&rqt, &rmt);rqt = rmt;}#endif}Please note that myself have been using MacOS (Unix terminal) which I prefer for other historical reasons plus the GPIB-USB interface made by National Instruments is fully compatible with the 3 platforms (Windows, MacOS and Linux)/Now if we make abstraction of where is the usleep() function, it is used few times when flashing and erasing old memories 28F010SA 28F020SA 28F008SA 28F016SA and 28F160S5 found through different TDSxxx. Again we're in the old days in the 90's trying to hack and manipulate old tektronix firmware and hardware via GPIB gate.In the Windows NT code, there are calls indeed in the us (microseconds) hence tricky as you have warned, typically part of the program usleep(10), usleep(6), usleep (20000)...One thing I wonder now and might try with my iMac platform is to removed these small usleep which might not be necessary due to the latency of GPIB-USB translator, no idea.Sorry for the rambling, I just wanted to explain the story behind this project plus the fact we are remoetly modifying memories through a higher level of protocol so we cannot just apply an understanding of low level transaction say in assembly language or from the 68020/68040 processor in the TDSxxx board.

author: Tantratron
author_rank: Frequent Contributor
timestamp: May 26, 2025, 12:41:45 pm
content: Quote from: colorburst on May 26, 2025, 12:26:49 pmSome members like myself use GPIB-USB from NI or HP but others uses old ISA-GPIB card much faster. I'll try to remove the usleep in my own code then try since I only have GPIB-USB but do you think fast ISA-GPIB card could be say too fast hence we need to slow down via xsleep routine ?Thank you for your time

author: radiolistener
author_rank: Super Contributor
timestamp: May 26, 2025, 01:03:43 pm
content: Quote from: Tantratron on May 26, 2025, 12:27:32 pmWhile your code appears syntactically correct and uses the appropriate Windows API, in practice it is unlikely to function as expected for microsecond-level delays.The main issue lies in how the Windows scheduler operates. Although you're specifying the delay in 100-nanosecond units via SetWaitableTimer, the actual resolution is limited by the system timer granularity, which in standard configurations is around 15-20 milliseconds. As a result, the thread will not be resumed until the next scheduler tick, regardless of the precision of the time you requested.This means your function won't be able to reliably produce delays shorter than ~15-20 ms under normal system conditions.The core issue is that waiting on an event using WaitForSingleObject cannot guarantee thread wake-up times faster than the scheduler’s typical 15-20 ms granularity. Therefore, if your application requires responding to events with lower latency, this synchronization method is inherently unsuitable.While it's technically possible to improve timer resolution using timeBeginPeriod(1) (also called Windows "real-time mode"), even then the minimum achievable delay is still 1 ms, and doing so comes at a cost - reduced power efficiency and increased CPU usage across the entire system. Higher scheduler timer resolution forces the processor to handle interrupts more frequently, which increases the overhead of context switching and results in more CPU time being wasted on managing thread transitions rather than executing useful work.So in essence, despite the low-level look of your implementation, the delay granularity is still bounded by the scheduler. As such, the code becomes useless for delays less than 20 ms and may be misleading in terms of its actual precision.I'm not familiar with latest Windows versions, so it's possible that some improvements have been made in recent versions of Windows, but I think the fundamental behavior of the scheduler hasn't significantly changed in this regard.If you want microsecond interval delays on windows, you need to use spin locks, something like this (I didn't tested exactly this code, just a pseudo-code to explain the main idea):Code:void usleep(__int64 usec) {LARGE_INTEGER freq, start, current;QueryPerformanceFrequency(&freq);QueryPerformanceCounter(&start);LONGLONG ticks = freq.QuadPart * usec / 1000000;do {QueryPerformanceCounter(&current);} while ((current.QuadPart - start.QuadPart) < ticks);}On some systems, it is possible to read the real-time timestamp counter directly from a CPU register. However, doing so typically requires elevated privileges and can yield inconsistent results across different hardware architectures and multi-threaded scheduling environments. For these reasons, I would generally advise against relying on this approach in portable or production code.

author: Tantratron
author_rank: Frequent Contributor
timestamp: May 26, 2025, 01:14:15 pm
content: Quote from: radiolistener on May 26, 2025, 01:03:43 pmMany thanks again@radiolistenerwhere by the way, please note these windows usleep function are not my code because I don't touch any Windows for 2 decades but rather go Unix with MacOS.Maybe member@madaowill try your suggestion since he is more Windows and we working together to enhance globally the TDSxxx memory management software about flash file memories.Maybe one question about Unix and Linux since both do offer de-facto internaly sleep msleep usleep nsleep built-in routine. In my MacOS case, I just call usleep (10) and the compiler knows where to get via the appropriate .h file. Do you know if Unix or Lunix xsleep routines are reliable, precise or suffer form the same latency risk you have warned from the start ?

author: colorburst
author_rank: Regular Contributor
timestamp: May 26, 2025, 01:57:31 pm
content: I doubt an ISA card would make a difference on Windows as the same mechanisms are at play. However, if you intend to support DOS, this would likely be an issue there.radiolistener touches on an important point, any delay you introduce on Windows or Linux (any system with preemptive scheduling) only provides a lower bound on the wait time. Whatever you do, your thread can be paused by the scheduler at any random moment and resume many milliseconds later. Certain API calls, which you're likely to find inside the GPIB library, can trigger this pause early.The usleepdocumentationfrom Linux calls this out explicitly: "The usleep() function suspends execution of the calling thread for(at least)usec microseconds.The sleep may be lengthened slightly by any system activityor by the time spent processing the call or by the granularity of system timers."There is no way to control the upper bound, but hey the tool works, so waiting too long doesn't seem to be a problem.

author: radiolistener
author_rank: Super Contributor
timestamp: May 26, 2025, 02:08:01 pm
content: Yes, Linux provides built-in sleep functions like usleep() and nanosleep(). However, their precision and reliability are generally subject to similar limitations as on Windows. Technically these functions guarantee that the delaywill not be shorter than requested, but they do not guarantee it won't be longer. Typically, the requested time is rounded up to the system timer resolution, which depends on the specific system and kernel configuration.On Linux systems, the timer resolution is usually higher than on Windows, so these functions can deliver better accuracy, especially on modern kernels. However, the actual precision still heavily depends on kernel configuration and hardware.In any case, WaitForSingleObject is a relatively slow synchronization method and is unsuitable for scenarios requiring response times faster than the typical 15-20 ms scheduler interval.

author: Siwastaja
author_rank: Super Contributor
timestamp: May 26, 2025, 02:09:45 pm
content: Yeah, do not use Windows or even linux (although latter is a bit better, especially if carefully tuned) for timing-sensitive stuff. They are just not designed for that. For occasional testing (internal automation) where failure is not catastrophic and retries are acceptable, this is OK. Otherwise than that, 1ms can simply sometimes become 10ms, or even 1000ms. Maybe not very often, but it can happen.Solution is, offload the timing-critical parts to a system capable of real-time stuff. Depending on what you are comfortable with, that might mean industrial automation PLC, Arduino, or custom MCU board. Then the communication with the PC happens at higher abstraction level - like, "do this task, which involves this and this, and report results when done".

author: nctnico
author_rank: Super Contributor
timestamp: May 26, 2025, 03:00:01 pm
content: Quote from: Siwastaja on May 26, 2025, 02:09:45 pmBollocks. If an OS doesn't support doing time sensitive stuff, then it is useless for realtime tasks like (for example) playing video and music. Windows (for example) has a special multi-media timer with 1 milli-second precision. Linux also has provisions to perform realtime tasks:https://wiki.gentoo.org/wiki/Project:Sound/How_to_Enable_Realtime_for_Multimedia_ApplicationsThe bottom line is that sleep() isn't the way to achieve precise timing, but it doesn't mean there are no other means to achieve precise timing.

author: radiolistener
author_rank: Super Contributor
timestamp: May 26, 2025, 03:07:51 pm
content: Quote from: nctnico on May 26, 2025, 03:00:01 pmYes, but as previously mentioned, using a higher timer resolution comes at a cost. It leads to increased power consumption and reduced performance due to more frequent context switches, which waste more CPU time. The higher timer resolution results in greater system overhead. This is a fundamental limitation, and it cannot be bypassed without incurring trade-offs.

author: Tantratron
author_rank: Frequent Contributor
timestamp: May 26, 2025, 04:52:05 pm
content: Thanks to all contributors and food for thought.I'll try later this week removing all usleep() calls in my own MacOS version C files then observe if any issue when remote GPIB-USB flashing three TDSxxx processor boards (B serie, C serie and D serie).

author: Siwastaja
author_rank: Super Contributor
timestamp: May 26, 2025, 05:15:45 pm
content: Quote from: nctnico on May 26, 2025, 03:00:01 pmAnd yet, video playback sometimes stutters; everybody has witnessed this.It's all about what is the cost of occasional timing misses. There are completely separate product lines of OSes for guaranteed timing. Multimedia is considered non-critical application, as long as it does not miss frames so often it starts to annoy customers.This is the first time I have heard someone call video playback on Windows a "realtime task".Tuning linux for minimized timing jitter for MIDI is similar case of tuning linux for LinuxCNC. It works to a certain extent. Yet, musicians buy dedicated hardware, and LinuxCNC "parallel port bitbang mode" is nearly dead in favor of microcontroller-based controllers which eat gcode directly (higher level instructions). I personally tried both, music/MIDI thing and linuxCNC in the past, and gave up.