/* * Copyright (c) 2008-2012 Zmanda, Inc. All Rights Reserved. * Copyright (c) 2013-2016 Carbonite, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Contact information: Carbonite Inc., 756 N Pastoria Ave * Sunnyvale, CA 94085, or: http://www.zmanda.com */ #include "amanda.h" #include "amsemaphore.h" #include "testutils.h" #include "amutil.h" /* * test that decrement waits properly */ struct test_decr_wait_data { amsemaphore_t *sem; gboolean increment_called; }; static gpointer test_decr_wait_thread(gpointer datap) { struct test_decr_wait_data *data = datap; /* should block */ amsemaphore_decrement(data->sem, 20); /* if increment hasn't been called yet, that's an error. */ if (!data->increment_called) return GINT_TO_POINTER(0); return GINT_TO_POINTER(1); } static gboolean test_decr_wait(void) { GThread *th; struct test_decr_wait_data data = { NULL, FALSE }; int rv; data.sem = amsemaphore_new_with_value(10), th = g_thread_create(test_decr_wait_thread, (gpointer)&data, TRUE, NULL); /* sleep to give amsemaphore_decrement() a chance to block (or not). */ g_usleep(G_USEC_PER_SEC / 4); /* and then increment the semaphore enough that the decrement can succeed */ data.increment_called = TRUE; amsemaphore_increment(data.sem, 10); /* join the thread and see how it fared. */ rv = GPOINTER_TO_INT(g_thread_join(th)); amsemaphore_free(data.sem); return (rv == 1); } /* * test that amsemaphore_wait_empty waits properly */ static gpointer test_wait_empty_thread(gpointer datap) { amsemaphore_t *sem = datap; /* should block */ amsemaphore_decrement(sem, 20); /* value should be 10 now (decremented from 30) */ if (sem->value != 10) return GINT_TO_POINTER(1); /* sleep for a bit */ g_usleep(G_USEC_PER_SEC / 4); /* decrement those last 10, which should trigger the zero */ amsemaphore_decrement(sem, 10); return GINT_TO_POINTER(0); } static gboolean test_wait_empty(void) { GThread *th; amsemaphore_t *sem = amsemaphore_new_with_value(10); int rv; th = g_thread_create(test_wait_empty_thread, (gpointer)sem, TRUE, NULL); /* sleep to give amsemaphore_decrement() a chance to block (or not). */ g_usleep(G_USEC_PER_SEC / 4); /* add another 10, so decrement can hit zero next time it's called */ amsemaphore_increment(sem, 10); /* and wait on the semaphore emptying */ amsemaphore_wait_empty(sem); /* join the thread and see how it fared. */ rv = GPOINTER_TO_INT(g_thread_join(th)); amsemaphore_free(sem); return (rv == 1); } /* * test that amsemaphore_force_adjust correctly wakes both * amsemaphore_decrement and amsemaphore_wait_empty. */ static gpointer test_force_adjust_thread(gpointer datap) { amsemaphore_t *sem = datap; /* this should block */ amsemaphore_decrement(sem, 20); /* and this should block, too - it's fun */ amsemaphore_wait_empty(sem); return NULL; } static gboolean test_force_adjust(void) { GThread *th; amsemaphore_t *sem = amsemaphore_new_with_value(10); th = g_thread_create(test_force_adjust_thread, (gpointer)sem, TRUE, NULL); /* sleep to give amsemaphore_decrement() a chance to block (or not). */ g_usleep(G_USEC_PER_SEC / 4); /* add another 20, so decrement can proceed, but leave the value at 10 */ amsemaphore_force_adjust(sem, 20); /* sleep to give amsemaphore_wait_empty() a chance to block (or not). */ g_usleep(G_USEC_PER_SEC / 4); /* and empty out the semaphore */ amsemaphore_force_adjust(sem, -10); g_thread_join(th); amsemaphore_free(sem); /* it we didn't hang yet, it's all good */ return TRUE; } /* * test that amsemaphore_force_set correctly wakes both * amsemaphore_decrement and amsemaphore_wait_empty. */ static gpointer test_force_set_thread(gpointer datap) { amsemaphore_t *sem = datap; /* this should block */ amsemaphore_decrement(sem, 20); /* and this should block, too - it's fun */ amsemaphore_wait_empty(sem); return NULL; } static gboolean test_force_set(void) { GThread *th; amsemaphore_t *sem = amsemaphore_new_with_value(10); th = g_thread_create(test_force_set_thread, (gpointer)sem, TRUE, NULL); /* sleep to give amsemaphore_decrement() a chance to block (or not). */ g_usleep(G_USEC_PER_SEC / 4); /* set it to 30, so decrement can proceed, but leave the value at 10 */ amsemaphore_force_set(sem, 30); /* sleep to give amsemaphore_wait_empty() a chance to block (or not). */ g_usleep(G_USEC_PER_SEC / 4); /* and empty out the semaphore */ amsemaphore_force_set(sem, 0); g_thread_join(th); amsemaphore_free(sem); /* it we didn't hang yet, it's all good */ return TRUE; } /* * Main loop */ int main(int argc, char **argv) { #if defined(G_THREADS_ENABLED) && !defined(G_THREADS_IMPL_NONE) static TestUtilsTest tests[] = { TU_TEST(test_decr_wait, 90), TU_TEST(test_wait_empty, 90), TU_TEST(test_force_adjust, 90), TU_TEST(test_force_set, 90), TU_END() }; glib_init(); return testutils_run_tests(argc, argv, tests); #else g_fprintf(stderr, "No thread support on this platform -- nothing to test\n"); return 0; #endif }