Enforce that only one version of folly is loaded at the same time
authorTudor Bosman <tudorb@fb.com>
Mon, 21 Jul 2014 22:18:02 +0000 (15:18 -0700)
committerChip Turner <chip@fb.com>
Fri, 25 Jul 2014 16:06:26 +0000 (09:06 -0700)
Test Plan: folly/test, OSS build, check the macro in separate file

Reviewed By: dancol@fb.com

FB internal diff: D1448086

folly/Makefile.am
folly/Version.cpp [new file with mode: 0644]
folly/VersionCheck.h [new file with mode: 0644]
folly/configure.ac

index a69b0abb939382cb480a8d52c640481221db2e14..401b849a69705be99d8c8cb2f224103fc84fecb1 100644 (file)
@@ -245,6 +245,7 @@ libfolly_la_SOURCES = \
        ThreadCachedArena.cpp \
        TimeoutQueue.cpp \
        Uri.cpp \
+       Version.cpp \
        wangle/InlineExecutor.cpp \
        wangle/ManualExecutor.cpp \
        wangle/ThreadGate.cpp \
diff --git a/folly/Version.cpp b/folly/Version.cpp
new file mode 100644 (file)
index 0000000..7878616
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/VersionCheck.h>
+
+namespace folly { namespace detail {
+
+FOLLY_VERSION_CHECK(folly, FOLLY_VERSION)
+
+}}  // namespaces
diff --git a/folly/VersionCheck.h b/folly/VersionCheck.h
new file mode 100644 (file)
index 0000000..a15675d
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FOLLY_VERSIONCHECK_H_
+#define FOLLY_VERSIONCHECK_H_
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include <folly/Portability.h>
+#include <folly/Preprocessor.h>
+
+/**
+ * Check if the currently loaded version of a library is what you expect.
+ *
+ * It is possible for multiple versions of the same shared library to end up
+ * being loaded simultaneously in the same address space, usually with
+ * disastrous results.
+ *
+ * For example, let's say you have a shared library (foo) that doesn't keep
+ * binary compatbility between releases, and so each version is distributed as
+ * a SO with different SONAME. Let's say you build another shared library, bar
+ * that depends on version 1 of foo: libbar.so depends on libfoo1.so.
+ * Your main executable now (baz) depends on version 2 of foo, and also
+ * depends on bar: baz depends on libfoo2.so and libbar.so.
+ *
+ * At load time, baz loads libfoo2.so first, then libbar.so; libbar.so will
+ * load libfoo1.so, but, as this is normal dynamic loading (and not explicit
+ * dlopen calls with RTLD_DEEPBIND), any symbols from libfoo1.so that are
+ * also present in libfoo2.so will be satisfied from the (already loaded)
+ * libfoo2.so.
+ *
+ * But foo does not preserve binary compatibility between versions, so all
+ * hell breaks loose (the symbols from libfoo2.so are not necessarily direct
+ * replacements of the identically-named symbols in libfoo1.so).
+ *
+ * It is better to crash with a helpful error message instead, which is what
+ * this macro provides. FOLLY_VERSION_CHECK verifies at load time that
+ * the compiled-in version is the same as the currently loaded version.
+ *
+ * Usage: use this macro at namespace scope in a .cpp file (IMPORTANT: NOT
+ * in the unnamed namespace):
+ *
+ * FOLLY_VERSION_CHECK(mylib, "1")
+ *
+ * The first argument identifies your library; the second argument is a
+ * string literal containing the desired version string.
+ *
+ * In order to avoid changing the file for each version, the version string
+ * could be provided on the compiler command line with -D:
+ *
+ * FOLLY_VERSION_CHECK(mylib, MYLIB_VERSION)
+ *
+ * ... and then commpile your file with -DMYLIB_VERSION=\"1\"
+ */
+// Note that this is carefully crafted: PRODUCT##Version must have external
+// linkage (so it collides among versions), versionCheck must have internal
+// linkage (so it does NOT collide between versions); if we're trying to have
+// multiple versions loaded at the same time, they must each run their copy
+// of versionCheck, but share the PRODUCT##Version variable.
+#define FOLLY_VERSION_CHECK(PRODUCT, VERSION) \
+  const char* PRODUCT##Version = VERSION; \
+  namespace { \
+  __attribute__((constructor(101))) void versionCheck() { \
+    if (strcmp(PRODUCT##Version, VERSION)) { \
+      fprintf(stderr, \
+              "Invalid %s version: desired [%s], currently loaded [%s]\n", \
+              FB_STRINGIZE(PRODUCT), PRODUCT##Version, VERSION); \
+      abort(); \
+    } \
+  } \
+  }
+
+#endif /* FOLLY_VERSIONCHECK_H_ */
index 99b4fbbe6380c0ab6346a1833730751e36b79778..93bb5d03949d30346ebedd34c9f6e5b173f87902 100644 (file)
@@ -3,12 +3,13 @@
 # Process this file with autoconf to produce a configure script.
 
 AC_PREREQ(2.59)
-AC_INIT(folly, 0.1, folly@fb.com)
 
-m4_define([folly_libtool_current], [1])
+m4_define([folly_version_str], m4_esyscmd_s([cat VERSION]))
+
+AC_INIT([folly], m4_translit(folly_version_str, [:], [.]), [folly@fb.com])
 
 # We assume all revisions are backwards incompatible.
-LT_VERSION=folly_libtool_current:0:0
+LT_VERSION=folly_version_str:0
 AC_SUBST(LT_VERSION)
 
 AC_CONFIG_SRCDIR([Likely.h])