# OS Detection

[`./000 core.h`](https://facil.io/0.8.x/api/000-core/) detects the host OS and defines a few portable helpers so the rest of the library can use one code path per platform.

## Platform macros

| Macro | POSIX value | Windows value | Notes |
|---|---|---|---|
| `FIO_OS_POSIX` | `1` | `0` | Set on Linux, macOS, BSD, and other `__unix__`/`__linux__`/`__APPLE__` systems. |
| `FIO_OS_WIN` | `0` | `1` | Set on Windows, including MinGW, MSYS2, Cygwin, and Borland. |

The Windows check is evaluated first, because MinGW/MSYS2 define both `_WIN32` and `__unix__`. Treating the system as Windows avoids calling POSIX socket functions on Winsock `SOCKET` handles.

After detection, `FIO_OS_WIN` always wins over `FIO_OS_POSIX`:

```c
#if FIO_OS_WIN
#undef FIO_OS_POSIX
#define FIO_OS_POSIX 0
#endif
```

## Unix-tools level

`FIO_HAVE_UNIX_TOOLS` tells the library how Unix-like the environment is:

- `0` — no Unix tools (pure MSVC).
- `1` — full POSIX.
- `2` — MinGW.
- `3` — Cygwin.

It is used to decide whether functions such as `pipe()`, `fcntl()`, and POSIX signal APIs are available.

## Process helpers

| Macro | POSIX | Windows |
|---|---|---|
| `fio_getpid` | `getpid` | `_getpid` |
| `FIO_KILL_SELF()` | `kill(0, SIGINT)` | `GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0)` |

`FIO_KILL_SELF` is guarded by `#ifndef`, so you can define your own version before including the header. The default implementation is used by the assertion/debug machinery to deliver a signal to the running process.

## Thread sleep and yield

| Macro | POSIX | Windows |
|---|---|---|
| `FIO_THREAD_WAIT(nano_sec)` | `nanosleep` | `Sleep` |
| `FIO_THREAD_YIELD()` | see below | see below |
| `FIO_THREAD_RESCHEDULE()` | `FIO_THREAD_WAIT(4)` | `FIO_THREAD_WAIT(4)` |

`FIO_THREAD_WAIT(nano_sec)` sleeps the current thread for the requested number of nanoseconds. On POSIX it builds a `struct timespec` and calls `nanosleep`. On Windows it converts the interval to milliseconds and calls `Sleep`; any non-zero request is rounded up to at least 1 ms.

`FIO_THREAD_YIELD()` hints the processor that the thread is in a spin loop. On x86/x86-64 with GCC or Clang it emits a `pause` instruction; on ARM/AArch64 with GCC or Clang it emits a `yield` instruction; with MSVC it calls `YieldProcessor()`; otherwise it falls back to `sched_yield()` on POSIX.

`FIO_THREAD_RESCHEDULE()` yields the thread by sleeping for 4 nanoseconds (`FIO_THREAD_WAIT(4)`). It is used by the lock code to back off busy waits without fully suspending the thread.

## Portable type patches

On pure MSVC `ssize_t` is missing, so the core header defines it from `SSIZE_T`:

```c
typedef SSIZE_T ssize_t;
```

`SSIZE_MAX` and `SSIZE_MIN` are also provided if the system headers do not:

```c
#ifndef SSIZE_MAX
#define SSIZE_MAX ((ssize_t)((~(size_t)0) >> 1))
#endif
#ifndef SSIZE_MIN
#define SSIZE_MIN ((ssize_t)(~((~(size_t)0) >> 1)))
#endif
```

## `printf`-style format checking

The `FIO___PRINTF_STYLE(string_index, check_index)` macro is defined differently on each toolchain so the compiler can check `printf`-style arguments. MinGW/Cygwin use the MinGW format attribute, pure MSVC leaves it empty, and POSIX/GCC/Clang use the standard `printf` format attribute. It is documented in [`./001 compiler attributes.md`](https://facil.io/0.8.x/compiler-attributes/).
