Observing leap seconds

I came across a blog post by Emil Mikulic on the leap second introduced by the IERS on 1 July 2012. Inspired by Emil Mikulic’s blog post, I created the following short programme.

/*
  timekeeping.c --- An implementation inspired by http://unix4lyfe.org/leap-second/

  Copyright © 2013, Trond Endrestøl <Trond.Endrestol@ximalas.info>
  All rights reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this
     list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright notice,
     this list of conditions and the following disclaimer in the documentation
     and/or other materials provided with the distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include <errno.h>
#include <float.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

int main(int argc, char **argv)
{
  struct timespec tv1, tv2;
  long double v1, v2;

  while (1) {
    if (clock_gettime(CLOCK_REALTIME, &tv1) == -1) {
      fprintf(stderr,
              "%s:%s:%d: clock_gettime(CLOCK_REALTIME, ...) = %s (%d)",
              argv[0], __FILE__, __LINE__, strerror(errno), errno);
      return EXIT_FAILURE;
    } // if

    if (clock_gettime(CLOCK_MONOTONIC, &tv2) == -1) {
      fprintf(stderr,
              "%s:%s:%d: clock_gettime(CLOCK_MONOTONIC, ...) = %s (%d)",
              argv[0], __FILE__, __LINE__, strerror(errno), errno);
      return EXIT_FAILURE;
    } // if

    v1 = tv1.tv_sec + (tv1.tv_nsec * 1.0E-9); // CLOCK_REALTIME
    v2 = tv2.tv_sec + (tv2.tv_nsec * 1.0E-9); // CLOCK_MONOTONIC

    printf("%-*.*Lf %-*.*Lf\n", // field widths and precision gathered from arguments
           LDBL_DIG + 1, LDBL_DIG, v1,
           LDBL_DIG + 1, LDBL_DIG, v2);
    fflush(stdout);

    usleep(100000); // sleep 0.1s
  } // while

  return EXIT_SUCCESS;
} // main()

// timekeeping.c

I’d better run this programme the next time a leap second is introduced, which won’t happen this December.

I compiled the above programme using clang 3.3 on FreeBSD/amd64 9.2-STABLE, and it’s very instructive to review the optimized assembly code generated by using the -O3 and -save-temps options.

        .file   "timekeeping.i"
        .section        .rodata.cst8,"aM",@progbits,8
        .align  8
.LCPI0_0:
        .quad   4472406533629990549     # double 1.0E-9
        .text
        .globl  main
        .align  16, 0x90
        .type   main,@function
main:                                   # @main
        .cfi_startproc
# BB#0:                                 # %entry
        pushq   %rbp
.Ltmp3:
        .cfi_def_cfa_offset 16
.Ltmp4:
        .cfi_offset %rbp, -16
        movq    %rsp, %rbp
.Ltmp5:
        .cfi_def_cfa_register %rbp
        pushq   %r15
        pushq   %r14
        pushq   %rbx
        subq    $88, %rsp
.Ltmp6:
        .cfi_offset %rbx, -40
.Ltmp7:
        .cfi_offset %r14, -32
.Ltmp8:
        .cfi_offset %r15, -24
        movq    %rsi, %r14
        leaq    -40(%rbp), %rsi
        xorl    %edi, %edi
        callq   clock_gettime
        cmpl    $-1, %eax
        je      .LBB0_5
# BB#1:
        leaq    -56(%rbp), %r15
        leaq    -40(%rbp), %rbx
        .align  16, 0x90
.LBB0_2:                                # %if.end
                                        # =>This Inner Loop Header: Depth=1
        movl    $4, %edi
        movq    %r15, %rsi
        callq   clock_gettime
        cmpl    $-1, %eax
        je      .LBB0_3
# BB#4:                                 # %if.end13
                                        #   in Loop: Header=BB0_2 Depth=1
        cvtsi2sdq       -48(%rbp), %xmm0
        movsd   .LCPI0_0(%rip), %xmm2
        mulsd   %xmm2, %xmm0
        cvtsi2sdq       -56(%rbp), %xmm1
        addsd   %xmm0, %xmm1
        movsd   %xmm1, -64(%rbp)
        cvtsi2sdq       -40(%rbp), %xmm0
        cvtsi2sdq       -32(%rbp), %xmm1
        fldl    -64(%rbp)
        fstpt   16(%rsp)
        mulsd   %xmm2, %xmm1
        addsd   %xmm0, %xmm1
        movsd   %xmm1, -72(%rbp)
        fldl    -72(%rbp)
        fstpt   (%rsp)
        movl    $.L.str3, %edi
        movl    $19, %esi
        movl    $18, %edx
        movl    $19, %ecx
        movl    $18, %r8d
        xorb    %al, %al
        callq   printf
        movq    __stdoutp(%rip), %rdi
        callq   fflush
        movl    $100000, %edi           # imm = 0x186A0
        callq   usleep
        xorl    %edi, %edi
        movq    %rbx, %rsi
        callq   clock_gettime
        cmpl    $-1, %eax
        jne     .LBB0_2
.LBB0_5:                                # %if.then
        movq    (%r14), %r14
        movq    __stderrp(%rip), %r15
        callq   __error
        movl    (%rax), %edi
        callq   strerror
        movq    %rax, %rbx
        callq   __error
        movl    (%rax), %eax
        movl    %eax, (%rsp)
        movq    %r15, %rdi
        movl    $.L.str, %esi
        movq    %r14, %rdx
        movl    $.L.str1, %ecx
        movl    $45, %r8d
        jmp     .LBB0_6
.LBB0_3:                                # %if.then7
        movq    (%r14), %r14
        movq    __stderrp(%rip), %r15
        callq   __error
        movl    (%rax), %edi
        callq   strerror
        movq    %rax, %rbx
        callq   __error
        movl    (%rax), %eax
        movl    %eax, (%rsp)
        movq    %r15, %rdi
        movl    $.L.str2, %esi
        movq    %r14, %rdx
        movl    $.L.str1, %ecx
        movl    $52, %r8d
.LBB0_6:                                # %if.then
        movq    %rbx, %r9
        xorb    %al, %al
        callq   fprintf
        movl    $1, %eax
        addq    $88, %rsp
        popq    %rbx
        popq    %r14
        popq    %r15
        popq    %rbp
        ret
.Ltmp9:
        .size   main, .Ltmp9-main
        .cfi_endproc

        .type   .L.str,@object          # @.str
        .section        .rodata.str1.1,"aMS",@progbits,1
.L.str:
        .asciz   "%s:%s:%d: clock_gettime(CLOCK_REALTIME, ...) = %s (%d)"
        .size   .L.str, 55

        .type   .L.str1,@object         # @.str1
.L.str1:
        .asciz   "timekeeping.c"
        .size   .L.str1, 14

        .type   .L.str2,@object         # @.str2
.L.str2:
        .asciz   "%s:%s:%d: clock_gettime(CLOCK_MONOTONIC, ...) = %s (%d)"
        .size   .L.str2, 56

        .type   .L.str3,@object         # @.str3
.L.str3:
        .asciz   "%-*.*Lf %-*.*Lf\n"
        .size   .L.str3, 17


        .section        ".note.GNU-stack","",@progbits

Notice how the error handling of the two clock_gettime calls have been changed to separate code paths with only different arguments for each of the calls to fprintf prior the jump to the common group of instructions finishing the calls to fprintf. Also, the main loop has been optimized somewhat, although the call to get CLOCK_REALTIME is duplicated, once prior to the loop and once again after the call to usleep.