A timerfd armed against wall-clock time can cross a discontinuous clock adjustment before its deadline. With TFD_TIMER_CANCEL_ON_SET, Linux exposes that event through the descriptor: a subsequent read() fails with ECANCELED rather than presenting the clock jump as an ordinary timer expiration.

The flag is deliberately narrow. It applies only when timerfd_settime() arms an absolute timer on CLOCK_REALTIME or CLOCK_REALTIME_ALARM, and it changes the handling of discontinuous changes to those clocks.

Cancellation is tied to absolute realtime timers

timerfd_create() selects the clock used by the timer. A descriptor created with CLOCK_REALTIME follows the system wall clock, while CLOCK_MONOTONIC follows a clock that is not settable through wall-clock updates.

timerfd_settime() then controls whether the supplied expiration is relative or absolute. TFD_TIMER_ABSTIME interprets it_value as an absolute timestamp on the selected clock.

int fd = timerfd_create(CLOCK_REALTIME, TFD_CLOEXEC);

struct itimerspec spec = {
    .it_value = {
        .tv_sec = deadline,
        .tv_nsec = 0,
    },
};

int rc = timerfd_settime(
    fd,
    TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET,
    &spec,
    NULL
);

For TFD_TIMER_CANCEL_ON_SET to have its defined cancellation effect, the timer must be absolute and use CLOCK_REALTIME or CLOCK_REALTIME_ALARM. The flag is not a general cancellation facility for every timerfd clock.

A discontinuous clock change becomes ECANCELED

Wall-clock time can be changed discontinuously by operations such as clock_settime() or settimeofday(). Such an operation moves the realtime clock to a new position instead of merely letting it progress normally.

When an eligible timerfd is armed with TFD_TIMER_CANCEL_ON_SET, a discontinuous change marks the timer as canceled. A read() from that descriptor then fails with ECANCELED.

CLOCK_REALTIME

10:00 -------- timer armed for 10:30 -------->
  |
  +-- clock changed directly to 11:00
  |
  `-- timerfd read -> ECANCELED

The error is distinct from a normal expiration count. A successful timerfd read returns an unsigned 64-bit integer containing the number of expirations. ECANCELED instead tells the caller that the realtime basis changed discontinuously while the cancellation mode was active.

The flag exposes a clock-domain event, not timer drift

TFD_TIMER_CANCEL_ON_SET does not report every correction made by the kernel timekeeping subsystem. Its documented trigger is a discontinuous change to CLOCK_REALTIME or CLOCK_REALTIME_ALARM for an eligible timer.

This distinction matters because wall-clock synchronization can also involve gradual adjustment. A clock that is being slewed still advances through time; the cancellation contract is centered on discontinuous changes, not on every rate correction or synchronization action.

The interface therefore reports a specific boundary: the absolute timestamp was interpreted in a realtime clock domain whose position was reset while the timer was armed.

Relative timers have different semantics

A relative timer describes a duration from the time it is armed. An absolute realtime timer describes a position on a settable clock. Those are different contracts when the wall clock moves.

relative:  expire after 30 minutes
absolute:  expire at realtime timestamp T

TFD_TIMER_CANCEL_ON_SET is associated with the second contract. It gives software a way to detect that the wall-clock coordinate system changed after an absolute deadline was installed.

For elapsed-time deadlines that must not track wall-clock changes, a monotonic clock is usually the relevant clock domain. CLOCK_MONOTONIC is not affected by discontinuous wall-clock setting, so the realtime cancellation mechanism is not its model.

Cancellation is observable through read semantics

A timerfd is a file descriptor, so expiration state participates in descriptor-oriented event handling. Applications can wait on it with interfaces such as poll, select, and epoll, then consume timer state with read().

The cancellation result appears at the read boundary. Code that assumes every readable timerfd yields only an expiration count can mishandle this mode if it does not check the read() error.

uint64_t expirations;
ssize_t n = read(fd, &expirations, sizeof(expirations));

if (n < 0 && errno == ECANCELED) {
    /* The realtime clock changed discontinuously. */
}

That branch is semantically different from EAGAIN on a nonblocking descriptor. EAGAIN says no expiration is currently available; ECANCELED reports invalidation caused by the qualifying clock change.

Re-arming establishes a new absolute deadline

After software observes cancellation, it can obtain the current clock state, recompute policy as needed, and arm the descriptor again. The kernel does not decide what the original civil-time intent should mean after a clock jump.

That policy boundary is important for schedules tied to wall time. A direct clock correction may place the system before or after the original timestamp. Automatically treating the old deadline as an ordinary expiration would hide the fact that the time coordinate changed.

Cancellation gives the application an explicit decision point. The application can discard the deadline, translate it into a new absolute timestamp, execute associated work, or apply another policy based on its own time model.

Clock choice remains the primary semantic decision

The flag does not make realtime timers equivalent to monotonic timers. It makes one class of realtime disruption visible. Absolute realtime timers still track a settable clock, and monotonic timers still represent a different clock domain.

TFD_TIMER_CANCEL_ON_SET is useful precisely at that boundary. It preserves the meaning of an absolute wall-clock deadline while refusing to silently fold a discontinuous wall-clock reset into normal expiration handling. The resulting ECANCELED lets descriptor-driven code separate timer expiration from invalidation of the clock position on which that expiration was based.