Skip to content

Instantly share code, notes, and snippets.

@scivision
Last active July 16, 2025 15:04
Show Gist options
  • Save scivision/dbbbf33c2faf5a16f31fd6d144adc314 to your computer and use it in GitHub Desktop.
Save scivision/dbbbf33c2faf5a16f31fd6d144adc314 to your computer and use it in GitHub Desktop.
nanosleep() for Visual Studio MSVC compiler on Windows

nanosleep() and clock_gettime() for Windows precision timing

The POSIX nanosleep() function is available in Linux, macOS, BSD, Unix, etc. MinGW / MSYS2 implements nanosleep for certain platforms -- x86_64, but not ARM currently. Cygwin implements nanosleep() for x86_64 and ARM. MSVC Visual Studio currently does not implement nanosleep().

nanosleep() provides relatively small precision sleep intervals for a thread. This is useful for games and hardware interfaces among other tasks. Affiliated POSIX functions include clock_gettime() and clock_getres().

Our C implementation of nanosleep(), clock_gettime(), and clock_getres() for Windows (MSVC, Intel oneAPI, MinGW, etc.) uses Waitable Timer Objects and similar elements of the Win32 API.

#if defined(WIN_NANOSLEEP)

is used instead of simply _MSC_VER because as noted above, certain MinGW platforms currently lack nanosleep(). Perhaps over time, these platforms will gain nanosleep() support, so it's a best practice to check dynamically.

API

The C11 standard timespec is used by the win32_time.h function interface. Currently, the CLOCK_MONOTONIC parameter is ignored by our implementation.

Build

cmake -Bbuild
cmake --build build

Specify the sleep time in milliseconds. For example, to sleep for 0.5 second:

build/main 500

The execution time is dependent on system overhead and system load. Accuracy on Windows is lower than other OS generally. Using in a virtual machine also affects accuracy.

Execution time is estimated on Windows PowerShell like:

Measure-Command { build/main.exe 500 }

On other shells like

time build/main 500

C++ reference

Separately, we provide a C++ example that is cross-platform.

We also as an exercise show a main.cpp and win32_time.cpp that implements the same functionality as the C version but in C++.

cmake_minimum_required(VERSION 3.21)
if(NOT CMAKE_BUILD_TYPE AND NOT DEFINED ENV{CMAKE_BUILD_TYPE})
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build.")
endif()
project(SleepDemo LANGUAGES C CXX)
enable_testing()
include(CheckSymbolExists)
set(CMAKE_C_STANDARD 23)
set(WIN_NANOSLEEP 0)
if(WIN32)
check_symbol_exists(nanosleep "time.h" HAVE_NANOSLEEP)
if(NOT HAVE_NANOSLEEP)
set(WIN_NANOSLEEP 1)
add_library(nanosleep_c OBJECT win32_time.c)
target_include_directories(nanosleep_c PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
endif()
endif()
add_executable(main_c main.c)
target_link_libraries(main_c PRIVATE $<$<BOOL:${WIN_NANOSLEEP}>:nanosleep_c>)
target_compile_definitions(main_c PRIVATE
$<$<BOOL:${WIN_NANOSLEEP}>:WIN_NANOSLEEP>
)
add_test(NAME Sleep100msC COMMAND main_c 100)
set_tests_properties(Sleep100msC PROPERTIES TIMEOUT 1)
# --- now a pure C++ version of the same functionality ---
add_library(nanosleep_cpp OBJECT win32_time.cpp)
target_include_directories(nanosleep_cpp PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_features(nanosleep_cpp PUBLIC cxx_std_17)
add_executable(main_cpp main.cpp)
target_link_libraries(main_cpp PRIVATE nanosleep_cpp)
add_test(NAME Sleep100msCPP COMMAND main_cpp 100)
set_tests_properties(Sleep100msCPP PROPERTIES TIMEOUT 1)
# C++ demo, not related to the main program / library
add_executable(sleep_thread_cpp sleep_thread.cpp)
file(GENERATE OUTPUT .gitignore CONTENT "*")
# Utilities
add_executable(types_c types.c)
add_test(NAME TypesC COMMAND types_c)
add_executable(types_cpp types.cpp)
add_test(NAME TypesCPP COMMAND types_cpp)
add_executable(resolution_c resolution.c)
target_link_libraries(resolution_c PRIVATE $<$<BOOL:${WIN_NANOSLEEP}>:nanosleep_c>)
add_test(NAME ResolutionC COMMAND resolution_c)
add_executable(resolution_cpp resolution.cpp)
target_link_libraries(resolution_cpp PRIVATE nanosleep_cpp)
add_test(NAME ResolutionCPP COMMAND resolution_cpp)
#include <time.h>
#ifndef clockid_t
#define clockid_t int
#endif
#ifndef CLOCK_MONOTONIC
#define CLOCK_MONOTONIC 1
#endif
#ifndef CLOCK_REALTIME
#define CLOCK_REALTIME 0
#endif
int nanosleep_cpp(const struct timespec*, struct timespec*);
int clock_gettime_cpp(clockid_t, struct timespec*);
int clock_getres_cpp(clockid_t, struct timespec*);
The MIT License (MIT)
Copyright © 2025 SciVision, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// demonstration program for win32_time.c
// this program isn't necessary, but just demonstrates nanosleep() use.
// MIT License, SciVision, Inc. 2025
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#if defined(WIN_NANOSLEEP)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "win32_time.h"
#endif
#ifndef nullptr
#define nullptr NULL
#endif
double tic_toc_clock(clockid_t clk_id, long req_ms) {
struct timespec treq, tic, toc;
treq.tv_sec = req_ms / 1000;
treq.tv_nsec = (req_ms % 1000) * 1000000;
if(clock_gettime(clk_id, &tic)){
perror("clock_gettime tic");
return EXIT_FAILURE;
}
if(nanosleep(&treq, nullptr) != 0){
perror("nanosleep");
return EXIT_FAILURE;
}
if(clock_gettime(clk_id, &toc)){
perror("clock_gettime toc");
return EXIT_FAILURE;
}
if (tic.tv_nsec > toc.tv_nsec && tic.tv_sec == toc.tv_sec) {
toc.tv_nsec += 1000000000;
toc.tv_sec--;
}
long elapsed_sec = toc.tv_sec - tic.tv_sec;
long elapsed_nsec = toc.tv_nsec - tic.tv_nsec;
return elapsed_sec * 1000 + (double)elapsed_nsec / 1000000;
}
int main(int argc, char* argv[]){
long req_ms = (argc > 1) ? atol(argv[1]) : 100;
double monotonic_ms = tic_toc_clock(CLOCK_MONOTONIC, req_ms);
double realtime_ms = tic_toc_clock(CLOCK_REALTIME, req_ms);
printf("C: Requested, MONOTONIC, REALTIME time (milliseconds): %ld, %.3f, %.3f\n", req_ms, monotonic_ms, realtime_ms);
return EXIT_SUCCESS;
}
// demonstration program for win32_time.cpp
// this program isn't necessary, but just demonstrates nanosleep() use.
// MIT License, SciVision, Inc. 2025
#include <cstdlib>
#include <iostream>
#include "cpp_time.h"
double tic_toc_clock(clockid_t clk_id, long req_ms) {
struct timespec treq, tic, toc;
treq.tv_sec = req_ms / 1000;
treq.tv_nsec = (req_ms % 1000) * 1000000;
if(clock_gettime_cpp(CLOCK_MONOTONIC, &tic)){
std::cerr << "clock_gettime tic\n";
return EXIT_FAILURE;
}
if(nanosleep_cpp(&treq, nullptr) != 0){
std::cerr << "nanosleep\n";
return EXIT_FAILURE;
}
if(clock_gettime_cpp(CLOCK_MONOTONIC, &toc)){
std::cerr << "clock_gettime toc\n";
return EXIT_FAILURE;
}
if (tic.tv_nsec > toc.tv_nsec && tic.tv_sec == toc.tv_sec) {
toc.tv_nsec += 1000000000;
toc.tv_sec--;
}
long elapsed_sec = toc.tv_sec - tic.tv_sec;
long elapsed_nsec = toc.tv_nsec - tic.tv_nsec;
return elapsed_sec * 1000 + (double)elapsed_nsec / 1000000;
}
int main(int argc, char* argv[]){
long req_millisec = (argc > 1) ? atol(argv[1]) : 100;
double monotonic_ms = tic_toc_clock(CLOCK_MONOTONIC, req_millisec);
double realtime_ms = tic_toc_clock(CLOCK_REALTIME, req_millisec);
std::cout << "C++ Requested, MONOTONIC, REALTIME time (milliseconds): ";
std::cout << req_millisec << ", " << monotonic_ms << ", " << realtime_ms << "\n";
return EXIT_SUCCESS;
}
#include "win32_time.h"
#include <stdio.h>
#include <stdlib.h>
int main(void) {
struct timespec t;
if (clock_getres(CLOCK_MONOTONIC, &t) != 0){
perror("could not clock_getres for CLOCK_MONOTONIC");
return EXIT_FAILURE;
}
printf("C: CLOCK_MONOTONIC resolution: %ld nanoseconds\n", t.tv_nsec);
if (clock_getres(CLOCK_REALTIME, &t) != 0){
perror("could not clock_getres for CLOCK_REALTIME");
return EXIT_FAILURE;
}
printf("C: CLOCK_REALTIME resolution: %ld nanoseconds\n", t.tv_nsec);
return EXIT_SUCCESS;
}
#include "cpp_time.h"
#include <iostream>
#include <cstdlib>
int main(void) {
struct timespec t;
if (clock_getres_cpp(CLOCK_MONOTONIC, &t) != 0){
std::cerr << "could not clock_getres for CLOCK_MONOTONIC\n";
return EXIT_FAILURE;
}
printf("C++ CLOCK_MONOTONIC resolution: %ld nanoseconds\n", t.tv_nsec);
if (clock_getres_cpp(CLOCK_REALTIME, &t) != 0){
std::cerr << "could not clock_getres for CLOCK_REALTIME\n";
return EXIT_FAILURE;
}
printf("C++ CLOCK_REALTIME resolution: %ld nanoseconds\n", t.tv_nsec);
}
// reference to compare with win32_time.c
#include <chrono>
#include <iostream>
#include <thread>
#include <cstdlib>
int main(int argc, char* argv[])
{
using namespace std::chrono_literals;
const auto ms_req = (argc > 1) ? std::atol(argv[1]) : 100;
std::cout << "waiting for " << ms_req << " ms." << std::endl;
const auto start = std::chrono::steady_clock::now();
std::this_thread::sleep_for(std::chrono::milliseconds(ms_req));
const auto end = std::chrono::steady_clock::now();
const std::chrono::duration<double, std::milli> elapsed = end - start;
std::cout << "Waited " << elapsed.count() << " ms\n";
return EXIT_SUCCESS;
}
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
int main(void){
printf("long: %d bytes\n", (int) sizeof(long));
printf("long long: %d bytes\n", (int) sizeof(long long));
printf("time_t: %d bytes\n", (int) sizeof(time_t));
printf("timespec tv_nsec: %d bytes\n", (int) sizeof(((struct timespec *)0)->tv_nsec));
printf("timespec tv_sec: %d bytes\n", (int) sizeof(((struct timespec *)0)->tv_sec));
return EXIT_SUCCESS;
}
#include <ctime>
#include <iostream>
#include <cstdlib>
int main(){
std::cout << "long: " << sizeof(long) << " bytes\n";
std::cout << "long long: " << sizeof(long long) << " bytes\n";
std::cout << "time_t: " << sizeof(time_t) << " bytes\n";
std::cout << "timespec tv_nsec: " << sizeof(((struct timespec *)0)->tv_nsec) << " bytes\n";
std::cout << "timespec tv_sec: " << sizeof(((struct timespec *)0)->tv_sec) << " bytes\n";
return EXIT_SUCCESS;
}
// nanosleep(), clock_gettime(), clock_getres() for Windows
// MIT License, SciVision, Inc. 2025
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "win32_time.h"
static const long G = 1000000000L;
// 1 second in nanoseconds
#ifndef nullptr
#define nullptr NULL
#endif
int nanosleep(const struct timespec* ts, struct timespec* rem)
{
// rem is not implemented
rem = nullptr;
HANDLE timer = CreateWaitableTimer(nullptr, TRUE, nullptr);
if(!timer)
return -1;
// SetWaitableTimer() defines interval in 100ns units.
// negative is to indicate relative time.
time_t sec = ts->tv_sec;
long nsec = ts->tv_nsec;
if (sec < 0 || (sec == 0 && nsec <= 0)) {
CloseHandle(timer);
return 0;
}
LARGE_INTEGER delay;
delay.QuadPart = -((LONGLONG)sec * 10000000LL + (LONGLONG)nsec / 100LL);
BOOL ok = SetWaitableTimer(timer, &delay, 0, nullptr, nullptr, FALSE) &&
WaitForSingleObject(timer, INFINITE) == WAIT_OBJECT_0;
CloseHandle(timer);
if(!ok)
return -1;
return 0;
}
static int win32_gettime_monotonic(struct timespec *t){
LARGE_INTEGER count;
static LARGE_INTEGER freq = {-1, -1};
if (freq.QuadPart == -1) {
if (!QueryPerformanceFrequency(&freq))
return -1;
}
if(!QueryPerformanceCounter(&count))
return -1;
t->tv_sec = count.QuadPart / freq.QuadPart;
t->tv_nsec = ((count.QuadPart % freq.QuadPart) * G) / freq.QuadPart;
return 0;
}
static int win32_gettime_realtime(struct timespec *t){
FILETIME ft;
ULARGE_INTEGER ht;
GetSystemTimePreciseAsFileTime(&ft);
ht.LowPart = ft.dwLowDateTime;
ht.HighPart = ft.dwHighDateTime;
// FILETIME is in 100ns units since Jan 1, 1601
// Convert to seconds since Unix epoch (Jan 1, 1970)
t->tv_sec = (time_t)((ht.QuadPart - 116444736000000000ULL) / 10000000ULL);
t->tv_nsec = (long)((ht.QuadPart % 10000000ULL) * 100ULL);
return 0;
}
int clock_gettime(clockid_t clk_id, struct timespec *t){
switch (clk_id) {
case CLOCK_MONOTONIC:
return win32_gettime_monotonic(t);
case CLOCK_REALTIME:
return win32_gettime_realtime(t);
default:
return -1;
}
}
int clock_getres(clockid_t clk_id, struct timespec *res)
{
LARGE_INTEGER freq;
res->tv_sec = 0;
switch (clk_id) {
case CLOCK_MONOTONIC:
if(!QueryPerformanceFrequency(&freq))
return -1;
break;
case CLOCK_REALTIME:
freq.QuadPart = 10000000LL;
// 100ns resolution for FILETIME
// https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime
break;
default:
return -1;
}
res->tv_nsec = (G / freq.QuadPart);
return 0;
}
#include <thread>
#include <chrono>
#include <cstdint>
#include "cpp_time.h"
int nanosleep_cpp(const struct timespec* ts, struct timespec* rem)
{
// rem is not implemented
rem = nullptr;
time_t sec = ts->tv_sec;
long nsec = ts->tv_nsec;
if (sec < 0 || (sec == 0 && nsec <= 0)) {
return 0;
}
// Convert to chrono duration and sleep
auto duration = std::chrono::seconds(sec) + std::chrono::nanoseconds(nsec);
std::this_thread::sleep_for(duration);
return 0;
}
int clock_gettime_cpp(clockid_t clk_id, struct timespec *t) {
// Get the clock time since epoch
std::chrono::nanoseconds duration;
switch (clk_id) {
case CLOCK_MONOTONIC: {
auto now = std::chrono::steady_clock::now();
duration = now.time_since_epoch();
break;
}
case CLOCK_REALTIME: {
auto now = std::chrono::system_clock::now();
duration = now.time_since_epoch();
break;
}
default:
return -1;
}
auto sec = std::chrono::duration_cast<std::chrono::seconds>(duration);
auto nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(duration - sec);
t->tv_sec = sec.count();
t->tv_nsec = nsec.count();
return 0;
}
int clock_getres_cpp(clockid_t clk_id, struct timespec *res) {
// Get the clock resolution
std::intmax_t num, den;
switch (clk_id) {
case CLOCK_MONOTONIC:
num = std::chrono::steady_clock::period::num;
den = std::chrono::steady_clock::period::den;
break;
case CLOCK_REALTIME:
num = std::chrono::system_clock::period::num;
den = std::chrono::system_clock::period::den;
break;
default:
return -1;
}
res->tv_sec = 0;
res->tv_nsec = static_cast<long>(num * 1000000000LL / den);
return 0;
}
#include <time.h>
#ifndef clockid_t
#define clockid_t int
#endif
#ifndef CLOCK_MONOTONIC
#define CLOCK_MONOTONIC 1
#endif
#ifndef CLOCK_REALTIME
#define CLOCK_REALTIME 0
#endif
int nanosleep(const struct timespec*, struct timespec*);
int clock_gettime(clockid_t, struct timespec*);
int clock_getres(clockid_t, struct timespec*);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment