LCOV - code coverage report
Current view: directory - src/shared/platform - nacl_sync_test.c (source / functions) Found Hit Coverage
Test: coverage.lcov Lines: 101 69 68.3 %
Date: 2014-09-25 Functions: 0 0 -

       1                 : /*
       2                 :  * Copyright (c) 2012 The Native Client Authors. All rights reserved.
       3                 :  * Use of this source code is governed by a BSD-style license that can be
       4                 :  * found in the LICENSE file.
       5                 :  */
       6                 : 
       7                 : /*
       8                 :  * Exercise the NaClMutex object.
       9                 :  *
      10                 :  * NB: Some tests are death tests, e.g., ensuring that a NaClMutex
      11                 :  * object really implements a binary semaphore instead of a recursive
      12                 :  * semaphore requires intentionally deadlocking and aborting via a
      13                 :  * test time out.
      14                 :  */
      15                 : #include <string.h>
      16                 : 
      17                 : #include "native_client/src/include/nacl_macros.h"
      18                 : #include "native_client/src/include/portability.h"
      19                 : 
      20                 : #include "native_client/src/shared/platform/platform_init.h"
      21                 : #include "native_client/src/shared/platform/nacl_log.h"
      22                 : #include "native_client/src/shared/platform/nacl_sync.h"
      23                 : #include "native_client/src/shared/platform/nacl_sync_checked.h"
      24                 : #include "native_client/src/shared/platform/nacl_threads.h"
      25                 : #include "native_client/src/shared/platform/nacl_time.h"
      26                 : 
      27                 : #define TIMEOUT_THREAD_STACK_SIZE (8192)
      28                 : #define ON_TIMEOUT_EXIT_SUCCESS ((void *) 0)
      29                 : #define ON_TIMEOUT_EXIT_FAILURE ((void *) 1)
      30                 : 
      31                 : /*
      32                 :  * This test contains several subtests.  The goal is to check that
      33                 :  * NaClMutex objects actually implement binary mutexes.  To do so, the
      34                 :  * subtests check that:
      35                 :  *
      36                 :  * Lock followed by another Lock leads to a deadlock;
      37                 :  *
      38                 :  * TryLock followed by a Lock leads to a deadlock;
      39                 :  *
      40                 :  * Lock followed by a TryLock leads to an NACL_SYNC_EBUSY return; and
      41                 :  *
      42                 :  * TryLock followed by another TryLock leads to an NACL_SYNC_EBUSY return.
      43                 :  *
      44                 :  * The way that deadlock detection works is not by alarm(3) timeouts,
      45                 :  * since that's not cross platform (not available in Windows).
      46                 :  * Instead, we spawn a separate thread via the NaClThread abstraction
      47                 :  * prior to invoking the expected-to-deadlock operation.  That thread
      48                 :  * will use NaClNanosleep (which is the cross platform version of
      49                 :  * nanosleep(2)) to suspend itself for a while, and then invoke
      50                 :  * exit(0) to shut down the whole process, which includes getting rid
      51                 :  * of the deadlocked thread.  On the other hand, if the
      52                 :  * expected-to-deadlock operation didn't actually cause a deadlock,
      53                 :  * the main thread immediately exits with a non-zero exit status.
      54                 :  *
      55                 :  * This test strategy is subject to scheduler races.  It may be the
      56                 :  * case that we would get a false negative -- the test would report
      57                 :  * that there is no bug, because the scheduler paused the thread that
      58                 :  * invoked the expected-to-deadlock operation long enough that the
      59                 :  * deadlock detection thread fires.  This is expected to be extremely
      60                 :  * rare, since the timeout value (defaulting to 500ms) is much larger
      61                 :  * than a single scheduling quantum on any host operating system, and
      62                 :  * the likelihood that the scheduler happens to interrupt the thread
      63                 :  * at precisely the wrong place ought to be low.  (We expect that the
      64                 :  * largest scheduling quantum that we would encounter to be ~16.7ms or
      65                 :  * 60Hz.)  In any case, a false negative will not create false alarm.
      66                 :  *
      67                 :  * In the case where the second lock operation is a TryLock, we do not
      68                 :  * expect the TryLock to block and just verify proper behavior based
      69                 :  * on the return value.  We detect regressions where the TryLock
      70                 :  * blocks by spawning a timeout thread that would cause the test to
      71                 :  * exit with a non-zero exit status.  In these cases, we can have a
      72                 :  * false positive due to scheduler problems on an extremely heavily
      73                 :  * loaded machine, but in practice this should not occur.
      74                 :  */
      75                 : 
      76                 : 
      77                 : /*
      78                 :  * A second thread is spawned to cause the whole process to exit after
      79                 :  * g_timeout_milliseconds.
      80                 :  */
      81                 : uint32_t g_timeout_milliseconds = 500;
      82                 : 
      83                 : /*
      84                 :  * TimeOutThread is responsible for doing deadlock detection.  If the
      85                 :  * main thread hits a deadlock, then this thread will time out and
      86                 :  * exit with the status specified by the thread_state argument.
      87                 :  *
      88                 :  * If the caller expect a deadlock in the main thread, the
      89                 :  * thread_state value will be 0 for success to indicate that the
      90                 :  * expected deadlock occurred; if caller does not expect a deadlock in
      91                 :  * the main thread, the thread_state value should be non-zero.
      92                 :  */
      93               1 : void WINAPI TimeOutThread(void *thread_state) {
      94                 :   struct nacl_abi_timespec ts;
      95               1 :   int time_out_exit_status = (int) (uintptr_t) thread_state;
      96                 : 
      97               1 :   ts.tv_sec = g_timeout_milliseconds / 1000;
      98               1 :   ts.tv_nsec = (g_timeout_milliseconds % 1000) * 1000 * 1000;
      99                 : 
     100               1 :   while (0 != NaClNanosleep(&ts, &ts)) {
     101                 :     /*
     102                 :      * On POSIX operating systems nanosleep (which underlies
     103                 :      * NaClNanosleep) can return early with errno equal to
     104                 :      * NACL_ABI_EINTR if, for example, a signal interrupted its sleep.
     105                 :      * When this occurs, the second timespec argument (if non-NULL) is
     106                 :      * overwritten with the remaining time to sleep, and so we can
     107                 :      * immediately sleep again.  We also expose the EINVAL and EFAULT
     108                 :      * error returns (translated to NACL_ABI_), but since we set the
     109                 :      * initial timespec value those should never occur.
     110                 :      */
     111               0 :     continue;
     112                 :   }
     113                 :   /*
     114                 :    * If we reach here, we assume that the main thread has deadlocked
     115                 :    * and so we optimistically report that via the exit status.
     116                 :    */
     117               1 :   exit(time_out_exit_status);
     118                 : }
     119                 : 
     120                 : /*
     121                 :  * The following tests do not Dtor the NaClMutex objects, since we
     122                 :  * only run one test before exiting.  This is a hard requirement for
     123                 :  * deadlock detection where the second lock is a NaClXMutexLock (the
     124                 :  * trylock case could be simpler, though we also spawn a time-out
     125                 :  * thread in that case in case the error is a deadlock rather than an
     126                 :  * immediate return with NACL_SYNC_BUSY).
     127                 :  */
     128                 : 
     129               1 : int TestLockLock(void) {
     130                 :   struct NaClMutex mu;
     131                 :   struct NaClThread nt;
     132               1 :   printf("TestLockLock\n");
     133               1 :   printf("Constructing mutex\n");
     134               1 :   if (!NaClMutexCtor(&mu)) return 1;
     135               1 :   printf("Locking mutex\n");
     136               1 :   NaClXMutexLock(&mu);
     137               1 :   printf("Spawning timeout thread\n");
     138                 :   /* NULL is exit status if timeout occurs */
     139                 :   if (!NaClThreadCtor(&nt, TimeOutThread,
     140               1 :                       ON_TIMEOUT_EXIT_SUCCESS, TIMEOUT_THREAD_STACK_SIZE)) {
     141               0 :     return 1;
     142                 :   }
     143               1 :   printf("Locking mutex again\n");
     144               1 :   NaClXMutexLock(&mu);
     145                 :   /* should deadlock and timeout thread should exit process */
     146               0 :   printf("ERROR: Double locking succeeded?!?\n");
     147               0 :   return 1;
     148               0 : }
     149                 : 
     150               1 : int TestLockTrylock(void) {
     151                 :   struct NaClMutex mu;
     152                 :   struct NaClThread nt;
     153               1 :   printf("TestLockTrylock\n");
     154               1 :   printf("Constructing mutex\n");
     155               1 :   if (!NaClMutexCtor(&mu)) return 1;
     156               1 :   printf("Locking mutex\n");
     157               1 :   NaClXMutexLock(&mu);
     158               1 :   printf("Spawning timeout thread\n");
     159                 :   /* 1 is exit status if timeout occurs */
     160                 :   if (!NaClThreadCtor(&nt, TimeOutThread,
     161               1 :                       ON_TIMEOUT_EXIT_FAILURE, TIMEOUT_THREAD_STACK_SIZE)) {
     162               0 :     return 1;
     163                 :   }
     164               1 :   printf("Trylocking mutex\n");
     165                 :   /*
     166                 :    * Here the NaClMutexTryLock should not block, and the role of the
     167                 :    * timeout thread is to detect implementation flaws where a trylock
     168                 :    * blocks.
     169                 :    */
     170               1 :   if (NaClMutexTryLock(&mu) == NACL_SYNC_BUSY) {
     171               1 :     printf("OK: trylock failed\n");
     172               1 :     return 0;
     173                 :   }
     174               0 :   printf("ERROR: Trylock succeeded?!?\n");
     175               0 :   return 1;
     176               1 : }
     177                 : 
     178               1 : int TestTrylockLock(void) {
     179                 :   struct NaClMutex mu;
     180                 :   struct NaClThread nt;
     181               1 :   printf("TestLockTrylock\n");
     182               1 :   printf("Constructing mutex\n");
     183               1 :   if (!NaClMutexCtor(&mu)) return 1;
     184               1 :   printf("Trylocking mutex\n");
     185               1 :   if (NaClMutexTryLock(&mu) != NACL_SYNC_OK) {
     186               0 :     printf("ERROR: Trylock failed\n");
     187               0 :     return 1;
     188                 :   }
     189               1 :   printf("Spawning timeout thread\n");
     190                 :   if (!NaClThreadCtor(&nt, TimeOutThread,
     191               1 :                       ON_TIMEOUT_EXIT_SUCCESS, TIMEOUT_THREAD_STACK_SIZE)) {
     192               0 :     return 1;
     193                 :   }
     194               1 :   printf("Locking mutex\n");
     195               1 :   NaClXMutexLock(&mu);
     196               0 :   printf("ERROR: Lock succeeded?!?\n");
     197               0 :   return 1;
     198               0 : }
     199                 : 
     200               1 : int TestTrylockTrylock(void) {
     201                 :   struct NaClMutex mu;
     202                 :   struct NaClThread nt;
     203               1 :   printf("TestLockTrylock\n");
     204               1 :   printf("Constructing mutex\n");
     205               1 :   if (!NaClMutexCtor(&mu)) return 1;
     206               1 :   printf("Trylocking mutex\n");
     207               1 :   if (NaClMutexTryLock(&mu) != NACL_SYNC_OK) {
     208               0 :     printf("ERROR: Trylock failed\n");
     209               0 :     return 1;
     210                 :   }
     211               1 :   printf("Spawning timeout thread\n");
     212                 :   if (!NaClThreadCtor(&nt, TimeOutThread,
     213               1 :                       ON_TIMEOUT_EXIT_FAILURE, TIMEOUT_THREAD_STACK_SIZE)) {
     214               0 :     return 1;
     215                 :   }
     216               1 :   printf("Trylocking mutex again\n");
     217               1 :   if (NaClMutexTryLock(&mu) == NACL_SYNC_BUSY) {
     218               1 :     printf("OK: trylock failed\n");
     219               1 :     return 0;
     220                 :   }
     221               0 :   printf("ERROR: Trylock succeeded?!?\n");
     222               0 :   return 1;
     223               1 : }
     224                 : 
     225                 : struct Tests {
     226                 :   char const *name;
     227                 :   int (*test)(void);
     228                 : };
     229                 : 
     230                 : struct Tests const tests[] = {
     231                 :   { "lock_lock", TestLockLock, },
     232                 :   { "lock_trylock", TestLockTrylock, },
     233                 :   { "trylock_lock", TestTrylockLock, },
     234                 :   { "trylock_trylock", TestTrylockTrylock, },
     235                 : };
     236                 : 
     237               0 : void usage(void) {
     238                 :   size_t ix;
     239                 : 
     240                 :   fprintf(stderr,
     241                 :           "Usage: nacl_sync_test [-t timeout_milliseconds] [-T test]\n"
     242               0 :           "       where <test> is one of\n");
     243               0 :   for (ix = 0; ix < NACL_ARRAY_SIZE(tests); ++ix) {
     244               0 :     fprintf(stderr, "         %s\n", tests[ix].name);
     245               0 :   }
     246               0 : }
     247                 : 
     248               1 : int main(int ac, char **av) {
     249                 :   int opt;
     250               1 :   int (*test_fn)(void) = NULL;
     251                 :   size_t ix;
     252                 :   int retcode;
     253                 : 
     254               1 :   while (-1 != (opt = getopt(ac, av, "t:T:"))) {
     255               1 :     switch (opt) {
     256                 :       case 't':
     257               0 :         g_timeout_milliseconds = strtoul(optarg, (char **) NULL, 0);
     258               0 :         break;
     259                 :       case 'T':
     260               1 :         for (ix = 0; ix < NACL_ARRAY_SIZE(tests); ++ix) {
     261               1 :           if (!strcmp(optarg, tests[ix].name)) {
     262               1 :             test_fn = tests[ix].test;
     263               1 :             break;
     264                 :           }
     265               1 :         }
     266               1 :         break;
     267                 :       default:
     268               0 :         usage();
     269               0 :         return 1;
     270                 :     }
     271               1 :   }
     272               1 :   if (test_fn == NULL) {
     273               0 :     fprintf(stderr, "No test specified\n");
     274               0 :     usage();
     275               0 :     return 1;
     276                 :   }
     277               1 :   NaClPlatformInit();
     278               1 :   retcode = (*test_fn)();
     279               1 :   NaClPlatformFini();
     280               1 :   return retcode;
     281               1 : }

Generated by: LCOV version 1.7