| @@ -74,6 +74,21 @@ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| #include <sys/resource.h> | |||
| #endif | |||
| #ifndef likely | |||
| #ifdef __GNUC__ | |||
| #define likely(x) __builtin_expect(!!(x), 1) | |||
| #else | |||
| #define likely(x) (x) | |||
| #endif | |||
| #endif | |||
| #ifndef unlikely | |||
| #ifdef __GNUC__ | |||
| #define unlikely(x) __builtin_expect(!!(x), 0) | |||
| #else | |||
| #define unlikely(x) (x) | |||
| #endif | |||
| #endif | |||
| #ifdef SMP_SERVER | |||
| #undef MONITOR | |||
| @@ -83,8 +98,6 @@ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| #define ATTRIBUTE_SIZE 128 | |||
| extern void openblas_warning(int verbose, const char * msg); | |||
| /* This is a thread server model implementation. The threads are */ | |||
| /* spawned at first access to blas library, and still remains until */ | |||
| /* destruction routine is called. The number of threads are */ | |||
| @@ -586,6 +599,10 @@ static BLASULONG exec_queue_lock = 0; | |||
| int exec_blas_async(BLASLONG pos, blas_queue_t *queue){ | |||
| #ifdef SMP_SERVER | |||
| // Handle lazy re-init of the thread-pool after a POSIX fork | |||
| if (unlikely(blas_server_avail == 0)) blas_thread_init(); | |||
| #endif | |||
| BLASLONG i = 0; | |||
| blas_queue_t *current = queue; | |||
| #if defined(OS_LINUX) && !defined(NO_AFFINITY) && !defined(PARAMTEST) | |||
| @@ -710,7 +727,11 @@ int exec_blas_async_wait(BLASLONG num, blas_queue_t *queue){ | |||
| /* Execute Threads */ | |||
| int exec_blas(BLASLONG num, blas_queue_t *queue){ | |||
| int (*routine)(blas_arg_t *, void *, void *, double *, double *, BLASLONG); | |||
| #ifdef SMP_SERVER | |||
| // Handle lazy re-init of the thread-pool after a POSIX fork | |||
| if (unlikely(blas_server_avail == 0)) blas_thread_init(); | |||
| #endif | |||
| int (*routine)(blas_arg_t *, void *, void *, double *, double *, BLASLONG); | |||
| #ifdef TIMING_DEBUG | |||
| BLASULONG start, stop; | |||
| @@ -923,17 +944,5 @@ int BLASFUNC(blas_thread_shutdown)(void){ | |||
| return 0; | |||
| } | |||
| /* | |||
| https://github.com/xianyi/OpenBLAS/issues/294 | |||
| Use pthread_atfork to close blas_thread_server before fork. | |||
| Then, re-init blas_thread_server after fork at child and parent. | |||
| */ | |||
| void openblas_fork_handler() | |||
| { | |||
| int err; | |||
| err = pthread_atfork (BLASFUNC(blas_thread_shutdown), blas_thread_init, blas_thread_init); | |||
| if(err != 0) | |||
| openblas_warning(0, "OpenBLAS cannot install fork handler. You may meet hang after fork.\n"); | |||
| } | |||
| #endif | |||
| @@ -315,9 +315,4 @@ int exec_blas(BLASLONG num, blas_queue_t *queue){ | |||
| return 0; | |||
| } | |||
| void openblas_fork_handler() | |||
| { | |||
| } | |||
| #endif | |||
| @@ -498,8 +498,3 @@ void openblas_set_num_threads(int num) | |||
| { | |||
| goto_set_num_threads(num); | |||
| } | |||
| void openblas_fork_handler() | |||
| { | |||
| } | |||
| @@ -143,6 +143,8 @@ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| gotoblas_t *gotoblas = NULL; | |||
| #endif | |||
| extern void openblas_warning(int verbose, const char * msg); | |||
| #ifndef SMP | |||
| #define blas_cpu_number 1 | |||
| @@ -253,6 +255,23 @@ int goto_get_num_procs (void) { | |||
| return blas_cpu_number; | |||
| } | |||
| void openblas_fork_handler() | |||
| { | |||
| // This handler shuts down the OpenBLAS-managed PTHREAD pool when OpenBLAS is | |||
| // built with "make USE_OPENMP=0". | |||
| // Hanging can still happen when OpenBLAS is built against the libgomp | |||
| // implementation of OpenMP. The problem is tracked at: | |||
| // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=60035 | |||
| // In the mean time build with USE_OPENMP=0 or link against another | |||
| // implementation of OpenMP. | |||
| #ifndef OS_WINDOWS | |||
| int err; | |||
| err = pthread_atfork (BLASFUNC(blas_thread_shutdown), NULL, NULL); | |||
| if(err != 0) | |||
| openblas_warning(0, "OpenBLAS Warning ... cannot install fork handler. You may meet hang after fork.\n"); | |||
| #endif | |||
| } | |||
| int blas_get_cpu_number(void){ | |||
| char *p; | |||
| #if defined(OS_LINUX) || defined(OS_WINDOWS) || defined(OS_FREEBSD) || defined(OS_DARWIN) | |||
| @@ -1268,6 +1287,9 @@ void CONSTRUCTOR gotoblas_init(void) { | |||
| if (gotoblas_initialized) return; | |||
| #ifdef SMP | |||
| openblas_fork_handler(); | |||
| #endif | |||
| #ifdef PROFILE | |||
| moncontrol (0); | |||
| @@ -1288,11 +1310,7 @@ void CONSTRUCTOR gotoblas_init(void) { | |||
| #ifdef SMP | |||
| if (blas_cpu_number == 0) blas_get_cpu_number(); | |||
| #ifdef SMP_SERVER | |||
| if (blas_server_avail == 0) { | |||
| blas_thread_init(); | |||
| //deal with pthread and fork. | |||
| openblas_fork_handler(); | |||
| } | |||
| if (blas_server_avail == 0) blas_thread_init(); | |||
| #endif | |||
| #endif | |||
| @@ -11,7 +11,7 @@ CUNIT_LIB=$(CUNIT_DIR)/lib/libcunit.a | |||
| CFLAGS+=-I$(CUNIT_DIR)/include | |||
| OBJS=main.o test_rot.o test_swap.o test_axpy.o test_dotu.o test_rotmg.o test_dsdot.o test_amax.o | |||
| OBJS=main.o test_rot.o test_swap.o test_axpy.o test_dotu.o test_rotmg.o test_dsdot.o test_amax.o test_fork.o | |||
| all : run_test | |||
| @@ -63,4 +63,6 @@ void test_dsdot_n_1(void); | |||
| void test_samax(void); | |||
| void test_fork_safety(void); | |||
| #endif | |||
| @@ -60,6 +60,14 @@ CU_TestInfo test_level1[]={ | |||
| {"Testing dsdot with n == 1",test_dsdot_n_1}, | |||
| {"Testing samax", test_samax}, | |||
| #if !defined(USE_OPENMP) && !defined(OS_WINDOWS) | |||
| // The GNU OpenMP implementation libgomp is not fork-safe (as of 4.8.2): | |||
| // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=60035 | |||
| // Hence skip this test when OpenBLAS is built with OpenMP. | |||
| {"Testing fork safety", test_fork_safety}, | |||
| #endif | |||
| CU_TEST_INFO_NULL, | |||
| }; | |||
| @@ -0,0 +1,123 @@ | |||
| /***************************************************************************** | |||
| Copyright (c) 2014, Lab of Parallel Software and Computational Science,ICSAS | |||
| 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. | |||
| 3. Neither the name of the ISCAS nor the names of its contributors may | |||
| be used to endorse or promote products derived from this software | |||
| without specific prior written permission. | |||
| 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. | |||
| **********************************************************************************/ | |||
| #ifndef OS_WINDOWS | |||
| #include "common_utest.h" | |||
| #include <sys/wait.h> | |||
| #include <cblas.h> | |||
| void* xmalloc(size_t n) | |||
| { | |||
| void* tmp; | |||
| tmp = malloc(n); | |||
| if (tmp == NULL) { | |||
| fprintf(stderr, "You are about to die\n"); | |||
| exit(1); | |||
| } else { | |||
| return tmp; | |||
| } | |||
| } | |||
| void check_dgemm(double *a, double *b, double *result, double *expected, int n) | |||
| { | |||
| int i; | |||
| cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, n, n, n, | |||
| 1.0, a, n, b, n, 0.0, result, n); | |||
| for(i = 0; i < n * n; ++i) { | |||
| CU_ASSERT_DOUBLE_EQUAL(expected[i], result[i], CHECK_EPS); | |||
| } | |||
| } | |||
| void test_fork_safety(void) | |||
| { | |||
| int n = 1000; | |||
| int i; | |||
| double *a, *b, *c, *d; | |||
| size_t n_bytes; | |||
| pid_t fork_pid; | |||
| pid_t fork_pid_nested; | |||
| n_bytes = sizeof(*a) * n * n; | |||
| a = xmalloc(n_bytes); | |||
| b = xmalloc(n_bytes); | |||
| c = xmalloc(n_bytes); | |||
| d = xmalloc(n_bytes); | |||
| // Put ones in a and b | |||
| for(i = 0; i < n * n; ++i) { | |||
| a[i] = 1; | |||
| b[i] = 1; | |||
| } | |||
| // Compute a DGEMM product in the parent process prior to forking to | |||
| // ensure that the OpenBLAS thread pool is initialized. | |||
| cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, n, n, n, | |||
| 1.0, a, n, b, n, 0.0, c, n); | |||
| fork_pid = fork(); | |||
| if (fork_pid == -1) { | |||
| CU_FAIL("Failed to fork process."); | |||
| } else if (fork_pid == 0) { | |||
| // Compute a DGEMM product in the child process to check that the | |||
| // thread pool as been properly been reinitialized after the fork. | |||
| check_dgemm(a, b, d, c, n); | |||
| // Nested fork to check that the pthread_atfork protection can work | |||
| // recursively | |||
| fork_pid_nested = fork(); | |||
| if (fork_pid_nested == -1) { | |||
| CU_FAIL("Failed to fork process."); | |||
| exit(1); | |||
| } else if (fork_pid_nested == 0) { | |||
| check_dgemm(a, b, d, c, n); | |||
| exit(0); | |||
| } else { | |||
| check_dgemm(a, b, d, c, n); | |||
| int child_status = 0; | |||
| pid_t wait_pid = wait(&child_status); | |||
| CU_ASSERT(wait_pid == fork_pid_nested); | |||
| CU_ASSERT(WEXITSTATUS (child_status) == 0); | |||
| exit(0); | |||
| } | |||
| } else { | |||
| check_dgemm(a, b, d, c, n); | |||
| // Wait for the child to finish and check the exit code. | |||
| int child_status = 0; | |||
| pid_t wait_pid = wait(&child_status); | |||
| CU_ASSERT(wait_pid == fork_pid); | |||
| CU_ASSERT(WEXITSTATUS (child_status) == 0); | |||
| } | |||
| } | |||
| #endif | |||