Skip to content

Commit

Permalink
configury: fix for MacOS clock_gettime(3) tomfoolery
Browse files Browse the repository at this point in the history
clock_gettime(3) on MacOS is... complicated.

1. CLOCK_REALTIME (and friends) may or may not exist
2. clockid_t may or may not exist
3. clock_gettime, as a symbol, may or may not exist
4. Even if clock_gettime exists as a symbol, it may be a weak symbol
   that has no strong symbol behind it (which causes a run-time linker
   error when you call it).

Separate off the clock_gettime(3) checks into their own .m4 to keep
the insanity limited.

The m4 probably could have been written a bit more compactly, but I
tried for maximum clarity instead (i.e., small, simple macros that
call each other).

Thanks to Homebrew/homebrew-core#6551 for
identifying the issue.

Signed-off-by: Jeff Squyres <jsquyres@cisco.com>
  • Loading branch information
jsquyres committed Nov 8, 2016
1 parent 7f2feb4 commit b150883
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 17 deletions.
112 changes: 112 additions & 0 deletions config/fi_clock_gettime.m4
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
dnl
dnl Copyright (c) 2016 Cisco Systems, Inc. All rights reserved.
dnl

dnl
dnl 1. Some OS's have clock_gettime(3), some do not. This is easy to
dnl detect.
dnl
dnl 2. Regardless of the answer to #1, some OS's have the constants
dnl associated with clock_gettime(3) (e.g., CLOCK_REALTIME), some do
dnl not. Fortunately, this is also easy to detect.
dnl
dnl 3. Some environments have a weak symbol for clock_gettime(3), but
dnl do not actually have a strong symbol that defines it. Meaning:
dnl you can compile and link code that uses clock_gettime(3), but if
dnl you try to call it at run time, your program will abort due to its
dnl inability to find a corresponding strong symbol. Sigh.
dnl
dnl So we check for a few things:
dnl
dnl A. Check for CLOCK_REALTIME. If we don't have it, don't bother
dnl checking anything else -- go ahead and activate the libfabric
dnl clock_gettime(3) emulation.
dnl
dnl B. If we have CLOCK_REALTIME, check for clock_gettime. If we
dnl don't have it, activate the libfabric clock_gettime(3) emulation.
dnl
dnl C. If we *do* have clock_gettime, see if we have the clockid_t
dnl type. Because of the way we prototype things in osd.h, it's
dnl slightly simpler to have a separate check / declaration to
dnl determine whether we need to declare this type.
dnl
dnl D. If we *do* have clock_gettime, run a trivial program to make
dnl sure that it actually works. If it does, great. If it does not,
dnl activate the libfabric clock_gettime(3) emulation.
dnl
dnl This could be written as one giant, nested m4 block. Break it up
dnl into a few m4 macros just for ease of readability.

dnl ---------------------------------------------------------

dnl Even if we have clock_gettime(3), make sure that it actually works
AC_DEFUN([_FI_CHECK_IF_CLOCK_GETTIME_WORKS],[
AC_MSG_CHECKING([if clock_gettime(3) actually works])
AC_LANG([C])
AC_RUN_IFELSE(
[AC_LANG_PROGRAM([dnl
#include <time.h>
],[
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);])],
[AC_MSG_RESULT([yes])
have_clock_gettime=1],
[AC_MSG_RESULT([no])])
])

dnl Check if we have the clockid_t type
AC_DEFUN([_FI_CHECK_CLOCKID_T],[
AC_CHECK_TYPE([clockid_t], [have_clockid_t=1], [], [dnl
#include <time.h>
])
])

dnl Check if we have the clock_gettime symbol
AC_DEFUN([_FI_CHECK_CLOCK_GETTIME],[
AC_SEARCH_LIBS([clock_gettime], [rt],
[_FI_CHECK_CLOCKID_T
_FI_CHECK_IF_CLOCK_GETTIME_WORKS])
])

dnl Check for CLOCK_REALTIME
AC_DEFUN([_FI_CHECK_CLOCK_REALTIME],[
AC_CHECK_DECL([CLOCK_REALTIME],
[have_clock_realtime=1
_FI_CHECK_CLOCK_GETTIME],
[],
[dnl
#include <time.h>
])
])

dnl If we do not have a working clock_gettime(), on MacOS, we
dnl need host_get_clock_service() to emulate clock_gettime().
AC_DEFUN([_FI_SETUP_CLOCK_GETTIME_EMULATION],[
AS_IF([test $macos -eq 1],
[AC_CHECK_FUNCS([host_get_clock_service],
[],
[AC_MSG_ERROR([clock_gettime or host_get_clock_service not found.])])
],
[AC_MSG_ERROR([clock_gettime(3) not found; do not know how to emulate it])])
])

dnl
dnl Main entry point to this .m4 file
dnl
AC_DEFUN([FI_CLOCK_GETTIME],[
have_clockid_t=0
have_clock_realtime=0
have_clock_gettime=0
_FI_CHECK_CLOCK_REALTIME
AS_IF([test $have_clock_gettime -eq 0],
[_FI_SETUP_CLOCK_GETTIME_EMULATION])
AC_DEFINE_UNQUOTED([HAVE_CLOCK_REALTIME], [$have_clock_realtime],
[Whether the symbol CLOCK_REALTIME is present or not])
AC_DEFINE_UNQUOTED([HAVE_CLOCKID_T], [$have_clockid_t],
[Whether we have the clockid_t type or not])
AC_DEFINE_UNQUOTED([HAVE_CLOCK_GETTIME], [$have_clock_gettime],
[Define to 1 if clock_gettime is available.])
AM_CONDITIONAL(HAVE_CLOCK_GETTIME, [test $have_clock_gettime -eq 1])
])
15 changes: 3 additions & 12 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -125,23 +125,14 @@ fi
AC_DEFINE_UNQUOTED([PT_LOCK_SPIN], [$have_spinlock],
[Define to 1 if pthread_spin_init is available.])

have_clock_gettime=0

AC_CHECK_FUNCS([epoll_create])
if test "$ac_cv_func_epoll_create" = yes; then
AC_DEFINE([HAVE_EPOLL], [1], [Define if you have epoll support.])
fi

AC_SEARCH_LIBS([clock_gettime],[rt],
[have_clock_gettime=1],
[AC_CHECK_FUNCS([host_get_clock_service],
[],
[AC_MSG_ERROR([clock_gettime or host_get_clock_service
not found.])])])

AC_DEFINE_UNQUOTED(HAVE_CLOCK_GETTIME, [$have_clock_gettime],
[Define to 1 if clock_gettime is available.])
AM_CONDITIONAL(HAVE_CLOCK_GETTIME, [test $have_clock_gettime -eq 1])
dnl The portability of clock_gettime(3) is... complicated.
dnl See config/fi_clock_gettime.m4
FI_CLOCK_GETTIME

dnl Check for gcc atomic intrinsics
AC_MSG_CHECKING(compiler support for c11 atomics)
Expand Down
23 changes: 18 additions & 5 deletions include/osx/osd.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,19 +58,32 @@
#define HOST_NAME_MAX 255
#endif

/* Some versions of MacOS/XCode define the clock_gettime(3) constants
* (e.g., CLOCK_REALTIME), even though they don't have a functioning
* clock_gettime(3) (!). Hence, we have a block for defining the
* CLOCK_* constants that is separate from the block for prototyping
* clock_gettime(3).
*/
#if !HAVE_CLOCK_REALTIME
#define CLOCK_REALTIME CALENDAR_CLOCK
#define CLOCK_MONOTONIC SYSTEM_CLOCK
#else /* !HAVE_CLOCK_REALTIME */
#include <time.h>
#endif /* !HAVE_CLOCK_REALTIME */

#ifdef __cplusplus
extern "C" {
#endif

/* macOS Sierra added clock_gettime to libc. This implementation should only
#if !HAVE_CLOCKID_T
typedef int clockid_t;
#endif /* !HAVE_CLOCKID_T */

/* MacOS Sierra added clock_gettime to libc. This implementation should only
* take effect if it is not available.
*/
#if !HAVE_CLOCK_GETTIME

#define CLOCK_REALTIME CALENDAR_CLOCK
#define CLOCK_MONOTONIC SYSTEM_CLOCK

typedef int clockid_t;
int clock_gettime(clockid_t clk_id, struct timespec *tp);

#endif
Expand Down

0 comments on commit b150883

Please sign in to comment.