LCOV - code coverage report
Current view: directory - tests/lock_manager - nacl_test_util_repl.c (source / functions) Found Hit Coverage
Test: coverage.lcov Lines: 692 431 62.3 %
Date: 2014-07-02 Functions: 0 0 -

       1                 : /*
       2                 :  * Copyright (c) 2013 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                 :  * This file contains the Read-Eval-Print loop.  It defines the test
       9                 :  * language embedded in the s-expressions.
      10                 :  */
      11                 : 
      12                 : #include <ctype.h>
      13                 : #include <errno.h>
      14                 : #include <stdint.h>
      15                 : #include <stdio.h>
      16                 : #include <stdlib.h>
      17                 : #include <string.h>
      18                 : #include <pthread.h>
      19                 : #include <unistd.h> /* portability.h */
      20                 : 
      21                 : #define MAX_THREADS 1024
      22                 : #define MAX_FILES   1024
      23                 : #define MIN_EPSILON_DELAY_NANOS 1000
      24                 : 
      25                 : #include "native_client/tests/lock_manager/nacl_test_util_repl.h"
      26                 : 
      27                 : #include "native_client/src/shared/platform/nacl_check.h"
      28                 : #include "native_client/src/shared/platform/posix/nacl_file_lock.h"
      29                 : #include "native_client/src/shared/platform/posix/nacl_file_lock_intern.h"
      30                 : #include "native_client/tests/lock_manager/nacl_test_util_sexp.h"
      31                 : 
      32                 : /*
      33                 :  * To test the NaClFileLockManager object's API, we have to invoke the
      34                 :  * API from multiple threads -- since the NaClFileLockManagerLock
      35                 :  * function can block -- and ensure that locking and unlocking happens
      36                 :  * in an acceptable order.  The output sequence -- "output" occurs
      37                 :  * when a lock is taken or released, i.e., at lock state transitions
      38                 :  * -- can be serialized, but the test will be inherently racy, e.g.,
      39                 :  * dropping a lock in one thread while two threads are already blocked
      40                 :  * attempting to get the lock has two valid/acceptable output
      41                 :  * sequences.  Instead of handling this, we avoid the mess: the test
      42                 :  * driver will be multithreaded, but will (attempt to) coordinate the
      43                 :  * threads so that only one operation is done by one thread at any
      44                 :  * particular time slot -- the sequence of operations will be strictly
      45                 :  * sequential.  Because threads must acknowledge that they are about
      46                 :  * to perform the requested operation before they actually do it, we
      47                 :  * introduce a microsleep testing quantum in the execution of the test
      48                 :  * machine, so that the next test step should not proceed prior to the
      49                 :  * thread actually having done its task, even if the task would not
      50                 :  * cause any events to fire.
      51                 :  *
      52                 :  * To handle the possibility that two or more threads will wake up at
      53                 :  * the same time -- and as a result, output "wakeup" messages -- the
      54                 :  * output matcher accepts a specification that is petri-net-like: the
      55                 :  * expected output can be a list of outputs which all must occur, but
      56                 :  * may occur in any order.  To handle races, alternation must be
      57                 :  * possible.  This is done using a matcher-of-matcher that returns the
      58                 :  * index of the matcher that succeeded, in combination with nth,
      59                 :  * quote, and eval to run the continuation for the match.  See
      60                 :  * test_input.txt for one example.
      61                 :  *
      62                 :  * The lisp-like execution environment does not include any bindings
      63                 :  * for variables, so the static vs dynamic scoping question does not
      64                 :  * apply.  If/when we include parametric matching, this will have to
      65                 :  * be addressed, in addition to deciding on how the bound variables
      66                 :  * are made available to the match's continuation.
      67                 :  *
      68                 :  * There is no garbage collection.  Instead, we copy lists (too much)
      69                 :  * to avoid leaking memory.
      70                 :  */
      71                 : 
      72                 : static int gVerbosity = 0;
      73                 : static int gInteractive = 0;  /* do not crash on error if set */
      74                 : static size_t gEpsilonDelayNanos = 0;
      75                 : 
      76                 : static pthread_key_t gNaClTestTsdKey;
      77                 : 
      78                 : #define DPRINTF(args)                           \
      79                 :   do {                                          \
      80                 :     if (gVerbosity > 1) {                       \
      81                 :       printf args;                              \
      82                 :     }                                           \
      83                 :   } while (0)
      84                 : 
      85                 : 
      86                 : struct ThreadState;
      87                 : 
      88                 : enum EventType {
      89                 :   kLocked = 0,
      90                 :   kUnlocked = 1,
      91                 : };
      92                 : 
      93                 : struct Event {
      94                 :   struct Event *next;
      95                 :   enum EventType type;
      96                 :   int thread_id;
      97                 :   int file_id;
      98                 : };
      99                 : 
     100                 : struct WorkState {
     101                 :   /* initialized before going threaded */
     102                 :   size_t num_workers;
     103                 :   struct ThreadState *workers;
     104                 : 
     105                 :   /* protects rest */
     106                 :   pthread_mutex_t mu;
     107                 :   pthread_cond_t cv;
     108                 : 
     109                 :   int actor_thread;
     110                 : #define WORK_PENDING  (-1)
     111                 : #define WORK_ACCEPTED (-2)
     112                 : #define WORK_ENDED    (-3)
     113                 :   void (*action_functor)(void *functor_state);
     114                 :   void *functor_state;
     115                 : 
     116                 :   struct Event *q, **qend;
     117                 : 
     118                 :   struct NaClFileLockManager flm;
     119                 :   struct NaClFileLockTestInterface *test_if;
     120                 : 
     121                 :   void (*orig_set_identity)(struct NaClFileLockEntry *, int desc);
     122                 :   void (*orig_take_lock)(int desc);
     123                 :   void (*orig_drop_lock)(int desc);
     124                 : };
     125                 : 
     126                 : /*
     127                 :  * Error / abort handling function.  We do not grab locks here, since
     128                 :  * this may be called in an error context where the lock is already
     129                 :  * held.  Since this is a debugging aid, we tolerate the possibility
     130                 :  * of inconsistent / undefined behavior.
     131                 :  */
     132               0 : static void NaClReplDumpWs(struct WorkState *ws) {
     133               0 :   struct Event *q = ws->q;
     134               0 :   while (NULL != q) {
     135               0 :     printf("event type %d, tid %d, fid %d\n",
     136               0 :            q->type, q->thread_id, q->file_id);
     137               0 :     q = q->next;
     138                 :   }
     139               0 : }
     140                 : 
     141               0 : static void crash_node(struct WorkState *ws,
     142                 :                        char const *reason,
     143                 :                        struct NaClSexpNode *n) {
     144               0 :   fprintf(stderr, "Runtime error: %s\n", reason);
     145               0 :   NaClSexpPrintNode(stderr, n);
     146               0 :   putc('\n', stderr);
     147               0 :   NaClReplDumpWs(ws);
     148               0 :   exit(1);
     149                 : }
     150                 : 
     151               0 : static void crash_cons(struct WorkState *ws,
     152                 :                        char const *reason,
     153                 :                        struct NaClSexpCons *c) {
     154               0 :   fprintf(stderr, "Runtime error: %s\n", reason);
     155               0 :   NaClSexpPrintCons(stderr, c);
     156               0 :   putc('\n', stderr);
     157               0 :   NaClReplDumpWs(ws);
     158               0 :   exit(1);
     159                 : }
     160                 : 
     161               0 : static void error_node(struct WorkState *ws,
     162                 :                        char const *reason,
     163                 :                        struct NaClSexpNode *n) {
     164               0 :   printf("Error: %s\n", reason);
     165               0 :   NaClSexpPrintNode(stdout, n);
     166               0 :   putchar('\n');
     167               0 :   NaClReplDumpWs(ws);
     168                 :   /*
     169                 :    * Eventually, with proper garbage collection, we may distinguish
     170                 :    * between batch execution and interactive execution, with errors
     171                 :    * aborting batch execution but letting errors longjmp back to the
     172                 :    * REPL for interactive use.
     173                 :    */
     174               0 :   if (!gInteractive) {
     175               0 :     exit(1);
     176                 :   }
     177               0 : }
     178                 : 
     179               0 : static void error_cons(struct WorkState *ws,
     180                 :                        char const *reason,
     181                 :                        struct NaClSexpCons *c) {
     182               0 :   printf("Error: %s\n", reason);
     183               0 :   NaClSexpPrintCons(stdout, c);
     184               0 :   putchar('\n');
     185               0 :   NaClReplDumpWs(ws);
     186               0 :   if (!gInteractive) {
     187               0 :     exit(1);
     188                 :   }
     189               0 : }
     190                 : 
     191                 : void EventOccurred(struct ThreadState *ts, enum EventType type, int file_id);
     192                 : 
     193                 : struct ThreadState {
     194                 :   pthread_t tid;
     195                 :   int this_thread;
     196                 :   struct WorkState *ws;
     197                 : };
     198                 : 
     199              74 : static void NaClFileLockTestSetFileIdentityDataWrapper(
     200                 :     struct NaClFileLockEntry *entry,
     201                 :     int desc) {
     202                 :   struct ThreadState *ts;
     203                 : 
     204              74 :   ts = (struct ThreadState *) pthread_getspecific(gNaClTestTsdKey);
     205              74 :   CHECK(NULL != ts);
     206             148 :   (*ts->ws->test_if->set_identity)(ts->ws->test_if,
     207              74 :                                    ts->ws->orig_set_identity,
     208                 :                                    entry, desc);
     209              74 : }
     210                 : 
     211              30 : static void NaClFileLockTestTakeFileLockWrapper(int desc) {
     212                 :   struct ThreadState *ts;
     213                 : 
     214              30 :   ts = (struct ThreadState *) pthread_getspecific(gNaClTestTsdKey);
     215              30 :   CHECK(NULL != ts);
     216              60 :   if ((*ts->ws->test_if->take_lock)(ts->ws->test_if,
     217              30 :                                     ts->ws->orig_take_lock,
     218                 :                                     ts->this_thread, desc)) {
     219              30 :     EventOccurred(ts, kLocked, desc);
     220                 :   }
     221              30 : }
     222                 : 
     223              30 : static void NaClFileLockTestDropFileLockWrapper(int desc) {
     224                 :   struct ThreadState *ts;
     225                 : 
     226              30 :   ts = (struct ThreadState *) pthread_getspecific(gNaClTestTsdKey);
     227              30 :   CHECK(NULL != ts);
     228              60 :   if ((*ts->ws->test_if->drop_lock)(ts->ws->test_if,
     229              30 :                                     ts->ws->orig_drop_lock,
     230                 :                                     ts->this_thread, desc)) {
     231              30 :     EventOccurred(ts, kUnlocked, desc);
     232                 :   }
     233              30 : }
     234                 : 
     235               8 : void WorkStateCtor(struct WorkState *ws,
     236                 :                    struct NaClFileLockTestInterface *test_if) {
     237               8 :   ws->num_workers = 0;  /* unknown */
     238               8 :   ws->workers = NULL;
     239               8 :   pthread_mutex_init(&ws->mu, (pthread_mutexattr_t *) NULL);
     240               8 :   pthread_cond_init(&ws->cv, (pthread_condattr_t *) NULL);
     241               8 :   ws->q = NULL;
     242               8 :   ws->qend = &ws->q;
     243               8 :   ws->actor_thread = WORK_PENDING;
     244               8 :   NaClFileLockManagerCtor(&ws->flm);
     245               8 :   ws->orig_set_identity = ws->flm.set_file_identity_data;
     246               8 :   ws->orig_take_lock = ws->flm.take_file_lock;
     247               8 :   ws->orig_drop_lock = ws->flm.drop_file_lock;
     248               8 :   ws->test_if = test_if;
     249               8 :   ws->flm.set_file_identity_data = NaClFileLockTestSetFileIdentityDataWrapper;
     250               8 :   ws->flm.take_file_lock = NaClFileLockTestTakeFileLockWrapper;
     251               8 :   ws->flm.drop_file_lock = NaClFileLockTestDropFileLockWrapper;
     252               8 : }
     253                 : 
     254               8 : void WorkStateDtor(struct WorkState *ws) {
     255                 :   struct Event *p;
     256               8 :   free(ws->workers);
     257               8 :   pthread_mutex_destroy(&ws->mu);
     258               8 :   pthread_cond_destroy(&ws->cv);
     259              16 :   while (NULL != (p = ws->q)) {
     260               0 :     ws->q = p->next;
     261               0 :     free(p);
     262                 :   }
     263               8 :   NaClFileLockManagerDtor(&ws->flm);
     264               8 : }
     265                 : 
     266             148 : size_t EventQueueLengthMu(struct WorkState *ws) {
     267                 :   struct Event *p;
     268             148 :   size_t count = 0;
     269             223 :   for (p = ws->q; NULL != p; p = p->next) {
     270              75 :     ++count;
     271                 :   }
     272             148 :   return count;
     273                 : }
     274                 : 
     275              60 : void EnqueueEvent(struct WorkState *ws, struct Event *ev) {
     276              60 :   DPRINTF(("EnqueueEvent: %d %d %d\n", ev->type, ev->thread_id, ev->file_id));
     277              60 :   pthread_mutex_lock(&ws->mu);
     278              60 :   ev->next = NULL;
     279              60 :   *ws->qend = ev;
     280              60 :   ws->qend = &ev->next;
     281              60 :   pthread_cond_broadcast(&ws->cv);
     282              60 :   DPRINTF(("EventQueueLength -> %d\n", (int) EventQueueLengthMu(ws)));
     283              60 :   pthread_mutex_unlock(&ws->mu);
     284              60 : }
     285                 : 
     286              60 : void ComputeAbsTimeout(struct timespec *tspec, int timeout_nanoseconds) {
     287                 :   struct timeval now;
     288                 : 
     289              60 :   gettimeofday(&now, (struct timezone *) NULL);
     290              60 :   tspec->tv_sec = now.tv_sec;
     291              60 :   tspec->tv_nsec = now.tv_usec * 1000 + timeout_nanoseconds;
     292              60 :   if (tspec->tv_nsec > 1000000000) {
     293               5 :     ++tspec->tv_sec;
     294               5 :     tspec->tv_nsec -= 1000000000;
     295                 :   }
     296              60 : }
     297                 : 
     298                 : /*
     299                 :  * WaitForEventMu: wait for event, with a timeout. May spuriously
     300                 :  * return without timing out.  Returns 0 on timeout.
     301                 :  */
     302              69 : int WaitForEventMu(struct WorkState *ws, struct timespec *abs_timeout) {
     303              69 :   int timed_out_failure = 0;
     304                 : 
     305              69 :   if (NULL != abs_timeout) {
     306              69 :     if (pthread_cond_timedwait(&ws->cv, &ws->mu, abs_timeout) == ETIMEDOUT) {
     307              16 :       timed_out_failure = 1;
     308                 :     }
     309                 :   } else {
     310                 :     int err;
     311                 : 
     312               0 :     if (0 != (err = pthread_cond_wait(&ws->cv, &ws->mu))) {
     313               0 :       fprintf(stderr, "WaitForEventMu: error %d\n", err);
     314                 :     }
     315                 :   }
     316                 : 
     317              69 :   return !timed_out_failure;
     318                 : }
     319                 : 
     320              60 : struct Event *EventFactory(enum EventType type, int thread_id, int file_id) {
     321              60 :   struct Event *ev = malloc(sizeof *ev);
     322              60 :   CHECK(NULL != ev);
     323              60 :   ev->next = NULL;
     324              60 :   ev->type = type;
     325              60 :   ev->thread_id = thread_id;
     326              60 :   ev->file_id = file_id;
     327              60 :   DPRINTF(("EventFactory(%d, %d, %d)\n", type, thread_id, file_id));
     328              60 :   return ev;
     329                 : }
     330                 : 
     331              60 : void EventOccurred(struct ThreadState *ts, enum EventType type, int file_id) {
     332              60 :   struct Event *ev = EventFactory(type, ts->this_thread, file_id);
     333              60 :   EnqueueEvent(ts->ws, ev);
     334              60 : }
     335                 : 
     336              60 : void EventQueueDiscardMu(struct WorkState *ws) {
     337                 :   struct Event *p;
     338                 :   struct Event *rest;
     339                 : 
     340              60 :   DPRINTF(("EventQueueDiscardMu\n"));
     341             120 :   for (p = ws->q; p != NULL; p = rest) {
     342              60 :     rest = p->next;
     343              60 :     free(p);
     344                 :   }
     345              60 :   ws->q = NULL;
     346              60 :   ws->qend = &ws->q;
     347              60 : }
     348                 : 
     349              82 : int GetWork(struct ThreadState *ts,
     350                 :             void (**functor_out)(void *functor_state),
     351                 :             void **functor_state_out) {
     352                 :   int actor_thread;
     353                 :   int has_work;
     354                 : 
     355              82 :   DPRINTF(("GetWork(%d)\n", ts->this_thread));
     356              82 :   pthread_mutex_lock(&ts->ws->mu);
     357             604 :   while (WORK_ENDED != (actor_thread = ts->ws->actor_thread) &&
     358             250 :          actor_thread != ts->this_thread) {
     359             190 :     DPRINTF(("GetWork(%d) waiting\n", ts->this_thread));
     360             190 :     pthread_cond_wait(&ts->ws->cv, &ts->ws->mu);
     361                 :   }
     362              82 :   has_work = (actor_thread == ts->this_thread);
     363              82 :   if (has_work) {
     364              60 :     *functor_out = ts->ws->action_functor;
     365              60 :     *functor_state_out = ts->ws->functor_state;
     366              60 :     ts->ws->actor_thread = WORK_ACCEPTED;
     367                 :   }
     368              82 :   pthread_mutex_unlock(&ts->ws->mu);
     369              82 :   DPRINTF(("GetWork(%d) returning %d\n", ts->this_thread, has_work));
     370              82 :   return has_work;
     371                 : }
     372                 : 
     373              60 : void PutWork(struct WorkState *ws, int actor_thread,
     374                 :              void (*action_functor)(void *functor_state),
     375                 :              void *functor_state) {
     376              60 :   DPRINTF(("PutWork(thread=%d)\n", actor_thread));
     377              60 :   pthread_mutex_lock(&ws->mu);
     378             172 :   while (ws->actor_thread != WORK_PENDING &&
     379              52 :          ws->actor_thread != WORK_ACCEPTED) {
     380               0 :     pthread_cond_wait(&ws->cv, &ws->mu);
     381                 :   }
     382              60 :   ws->actor_thread = actor_thread;
     383              60 :   ws->action_functor = action_functor;
     384              60 :   ws->functor_state = functor_state;
     385              60 :   pthread_cond_broadcast(&ws->cv);
     386              60 :   pthread_mutex_unlock(&ws->mu);
     387              60 :   DPRINTF(("PutWork(thread=%d) done\n", actor_thread));
     388              60 : }
     389                 : 
     390               0 : void WaitForWorkAcceptance(struct WorkState *ws) {
     391               0 :   pthread_mutex_lock(&ws->mu);
     392               0 :   while (ws->actor_thread != WORK_ACCEPTED) {
     393               0 :     pthread_cond_wait(&ws->cv, &ws->mu);
     394                 :   }
     395               0 :   pthread_mutex_unlock(&ws->mu);
     396               0 : }
     397                 : 
     398               8 : void AnnounceEndOfWork(struct WorkState *ws) {
     399               8 :   DPRINTF(("AnnounceEndOfWork\n"));
     400               8 :   pthread_mutex_lock(&ws->mu);
     401                 :   /*
     402                 :    * ensure all previous work is accepted, allowing for no-work programs.
     403                 :    */
     404              16 :   while (ws->actor_thread != WORK_ACCEPTED &&
     405               0 :          ws->actor_thread != WORK_PENDING) {
     406               0 :     DPRINTF(("AnnounceEndOfWork: waiting for work to drain\n"));
     407               0 :     pthread_cond_wait(&ws->cv, &ws->mu);
     408                 :   }
     409               8 :   ws->actor_thread = WORK_ENDED;
     410               8 :   DPRINTF(("AnnounceEndOfWork: broadcast!\n"));
     411               8 :   pthread_cond_broadcast(&ws->cv);
     412               8 :   pthread_mutex_unlock(&ws->mu);
     413               8 : }
     414                 : 
     415              22 : void *ThreadFunc(void *vstate) {
     416              22 :   struct ThreadState *ts = (struct ThreadState *) vstate;
     417                 :   void (*functor)(void *functor_state);
     418                 :   void *functor_state;
     419                 : 
     420                 :   /* set ts in TSD */
     421              22 :   pthread_setspecific(gNaClTestTsdKey, ts);
     422                 : 
     423              22 :   DPRINTF(("thread %d\n", ts->this_thread));
     424             104 :   while (GetWork(ts, &functor, &functor_state)) {
     425              60 :     (*functor)(functor_state);
     426                 :   }
     427              22 :   return NULL;
     428                 : }
     429                 : 
     430               8 : void SpawnThreadsMu(struct WorkState *ws, size_t num_threads) {
     431                 :   size_t ix;
     432                 :   int err;
     433                 : 
     434               8 :   CHECK(0 < num_threads);
     435               8 :   ws->num_workers = num_threads;
     436               8 :   ws->workers = malloc(num_threads * sizeof *ws->workers);
     437               8 :   CHECK(NULL != ws->workers);
     438                 : 
     439              30 :   for (ix = 0; ix < num_threads; ++ix) {
     440              22 :     ws->workers[ix].this_thread = (int) ix;
     441              22 :     ws->workers[ix].ws = ws;
     442              22 :     if (0 != (err = pthread_create(&ws->workers[ix].tid,
     443                 :                                    (pthread_attr_t *) NULL,
     444              22 :                                    ThreadFunc, (void *) &ws->workers[ix]))) {
     445               0 :       fprintf(stderr, "nacl_file_lock_test: pthread_create failed, %d\n", err);
     446               0 :       exit(1);
     447                 :     }
     448                 :   }
     449               8 : }
     450                 : 
     451                 : #define CHECK_PROG(ws, expr, cons)                                       \
     452                 :   do {                                                                  \
     453                 :     if (!(expr)) {                                                      \
     454                 :       fprintf(stderr,                                                   \
     455                 :               "nacl_file_lock_test: %s does not hold at program:\n",    \
     456                 :               #expr);                                                   \
     457                 :       crash_cons(ws, "internal error", cons);                            \
     458                 :     }                                                                   \
     459                 :   } while (0)
     460                 : 
     461              60 : void CheckStateMu(struct NaClSexpCons *cons, struct WorkState *ws) {
     462              60 :   CHECK_PROG(ws, ws->num_workers != 0, cons);
     463              60 : }
     464                 : 
     465              60 : void CheckState(struct NaClSexpCons *cons, struct WorkState *ws) {
     466              60 :   pthread_mutex_lock(&ws->mu);
     467              60 :   CheckStateMu(cons, ws);
     468              60 :   pthread_mutex_unlock(&ws->mu);
     469              60 : }
     470                 : 
     471               8 : void ReapThreads(struct WorkState *ws) {
     472                 :   size_t ix;
     473                 : 
     474               8 :   if (ws->num_workers > 0) {
     475              30 :     for (ix = 0; ix < ws->num_workers; ++ix) {
     476              22 :       pthread_join(ws->workers[ix].tid, (void **) NULL);
     477                 :     }
     478                 :   }
     479               8 : }
     480                 : 
     481                 : struct NaClSexpNode *Eval(struct NaClSexpNode *n, struct WorkState *ws);
     482                 : 
     483               8 : struct NaClSexpNode *SetThreadsImpl(struct NaClSexpCons *cons,
     484                 :                                     struct WorkState *ws) {
     485                 :   int num_threads;
     486                 : 
     487               8 :   if (NaClSexpListLength(cons) != 2) {
     488               0 :     crash_cons(ws, "set-threads should have 1 argument", cons);
     489                 :   }
     490               8 :   if (!NaClSexpIntp(cons->cdr->car)) {
     491               0 :     crash_cons(ws, "set-threads should have 1 numeric argument", cons);
     492                 :   }
     493               8 :   num_threads = NaClSexpNodeToInt(cons->cdr->car);
     494               8 :   if (num_threads < 1) {
     495               0 :     crash_cons(ws, "program specifies too few threads", cons);
     496                 :   }
     497               8 :   if (num_threads >= MAX_THREADS) {
     498               0 :     crash_cons(ws, "program specifies too many threads", cons);
     499                 :   }
     500               8 :   pthread_mutex_lock(&ws->mu);
     501               8 :   CHECK_PROG(ws, ws->num_workers == 0, cons);
     502               8 :   SpawnThreadsMu(ws, num_threads);
     503               8 :   pthread_mutex_unlock(&ws->mu);
     504               8 :   return NaClSexpNodeWrapInt(num_threads);
     505                 : }
     506                 : 
     507               8 : struct NaClSexpNode *SetFilesImpl(struct NaClSexpCons *cons,
     508                 :                                   struct WorkState *ws) {
     509                 :   int num_files;
     510                 : 
     511               8 :   if (NaClSexpListLength(cons) != 2) {
     512               0 :     crash_cons(ws, "set-files should have 1 argument", cons);
     513                 :   }
     514               8 :   if (!NaClSexpIntp(cons->cdr->car)) {
     515               0 :     crash_cons(ws, "set-files should have 1 numeric argument", cons);
     516                 :   }
     517               8 :   num_files = NaClSexpNodeToInt(cons->cdr->car);
     518               8 :   if (num_files < 1) {
     519               0 :     crash_cons(ws, "program specifies too few files", cons);
     520                 :   }
     521               8 :   if (num_files >= MAX_FILES) {
     522               0 :     crash_cons(ws, "program specifies too many files", cons);
     523                 :   }
     524                 : 
     525               8 :   if (!(*ws->test_if->set_num_files)(ws->test_if, num_files)) {
     526               0 :     crash_cons(ws, "set-files error", cons);
     527                 :   }
     528                 : 
     529               8 :   return NaClSexpNodeWrapInt(num_files);
     530                 : }
     531                 : 
     532               4 : struct NaClSexpNode *QuoteImpl(struct NaClSexpCons *cons,
     533                 :                                struct WorkState *ws) {
     534                 :   UNREFERENCED_PARAMETER(ws);
     535               4 :   if (NaClSexpListLength(cons) != 2) {
     536               0 :     error_cons(ws, "quote takes a single argument", cons);
     537               0 :     return NULL;
     538                 :   }
     539               4 :   return NaClSexpDupNode(cons->cdr->car);
     540                 : }
     541                 : 
     542               0 : struct NaClSexpNode *CarImpl(struct NaClSexpCons *cons,
     543                 :                              struct WorkState *ws) {
     544                 :   struct NaClSexpNode *p;
     545                 :   struct NaClSexpCons *arg;
     546               0 :   struct NaClSexpNode *result = NULL;
     547               0 :   if (NaClSexpListLength(cons) != 2) {
     548               0 :     error_cons(ws, "car takes a single argument", cons);
     549               0 :     return NULL;
     550                 :   }
     551               0 :   p = Eval(cons->cdr->car, ws);
     552               0 :   if (NULL == p || !NaClSexpConsp(p)) {
     553               0 :     error_cons(ws, "car: argument must evaluate to a list", cons);
     554                 :   } else {
     555               0 :     arg = NaClSexpNodeToCons(p);
     556               0 :     if (NULL != arg) {
     557               0 :       result = NaClSexpDupNode(arg->car);
     558                 :     }
     559                 :   }
     560               0 :   return result;
     561                 : }
     562                 : 
     563               0 : struct NaClSexpNode *CdrImpl(struct NaClSexpCons *cons,
     564                 :                              struct WorkState *ws) {
     565                 :   struct NaClSexpNode *p;
     566                 :   struct NaClSexpCons *arg;
     567               0 :   struct NaClSexpNode *result = NULL;
     568               0 :   if (NaClSexpListLength(cons) != 2) {
     569               0 :     error_cons(ws, "cdr takes a single argument", cons);
     570               0 :     return NULL;
     571                 :   }
     572               0 :   p = Eval(cons->cdr->car, ws);
     573               0 :   if (NULL == p || !NaClSexpConsp(p)) {
     574               0 :     error_cons(ws, "cdr: argument must evaluate to a list", cons);
     575                 :   } else {
     576               0 :     arg = NaClSexpNodeToCons(p);
     577               0 :     if (NULL != arg) {
     578               0 :       result = NaClSexpNodeWrapCons(NaClSexpDupCons(arg->cdr));
     579                 :     }
     580                 :   }
     581               0 :   return result;
     582                 : }
     583                 : 
     584               0 : struct NaClSexpNode *ConsImpl(struct NaClSexpCons *cons,
     585                 :                               struct WorkState *ws) {
     586                 :   struct NaClSexpNode *first;
     587                 :   struct NaClSexpNode *second;
     588               0 :   struct NaClSexpNode *result = NULL;
     589                 : 
     590               0 :   if (NaClSexpListLength(cons) != 3) {
     591               0 :     error_cons(ws, "cons take two arguments", cons);
     592               0 :     return NULL;
     593                 :   }
     594               0 :   first = Eval(cons->cdr->car, ws);
     595               0 :   second = Eval(cons->cdr->cdr->car, ws);
     596               0 :   if (!NaClSexpConsp(second)) {
     597               0 :     error_cons(ws, "cons: second argument must evaluate to a list", cons);
     598                 :   } else {
     599               0 :     result = NaClSexpNodeWrapCons(
     600                 :         NaClSexpConsCons(NaClSexpDupNode(first),
     601                 :                          NaClSexpDupCons(NaClSexpNodeToCons(second))));
     602                 :   }
     603               0 :   NaClSexpFreeNode(first);
     604               0 :   NaClSexpFreeNode(second);
     605               0 :   return result;
     606                 : }
     607                 : 
     608               0 : struct NaClSexpNode *ListImpl(struct NaClSexpCons *cons,
     609                 :                                struct WorkState *ws) {
     610               0 :   struct NaClSexpCons *result = NULL;
     611               0 :   struct NaClSexpCons **addpos = &result;
     612                 :   struct NaClSexpCons *p;
     613                 : 
     614               0 :   for (p = cons->cdr; NULL != p; p = p->cdr) {
     615               0 :     *addpos = NaClSexpConsWrapNode(Eval(p->car, ws));
     616               0 :     addpos = &((*addpos)->cdr);
     617                 :   }
     618               0 :   return NaClSexpNodeWrapCons(result);
     619                 : }
     620                 : 
     621               4 : struct NaClSexpNode *NthImpl(struct NaClSexpCons *cons,
     622                 :                              struct WorkState *ws) {
     623               4 :   struct NaClSexpCons *result = NULL;
     624                 :   struct NaClSexpCons *p;
     625                 :   struct NaClSexpNode *arg_selector;
     626                 :   struct NaClSexpNode *arg_list;
     627               4 :   size_t ix = 0;
     628                 : 
     629               4 :   if (NaClSexpListLength(cons) != 3) {
     630               0 :     crash_cons(ws, "nth takes exactly two arguments", cons);
     631                 :   }
     632               4 :   arg_selector = Eval(cons->cdr->car, ws);
     633               4 :   arg_list = Eval(cons->cdr->cdr->car, ws);
     634               4 :   if (!NaClSexpIntp(arg_selector)) {
     635               0 :     error_cons(ws, "nth: first arg does not evaluate to an integer", cons);
     636               4 :   } else if (!NaClSexpConsp(arg_list)) {
     637               0 :     error_cons(ws, "nth: second arg does not evaluate to a list", cons);
     638                 :   } else {
     639               4 :     ix = NaClSexpNodeToInt(arg_selector);
     640               8 :     for (p = NaClSexpNodeToCons(arg_list);
     641               4 :          NULL != p && ix > 0;
     642               0 :          p = p->cdr, --ix) {
     643               0 :       continue;
     644                 :     }
     645               4 :     if (ix == 0) {
     646               4 :       result = p;
     647                 :     }
     648                 :   }
     649               4 :   return NaClSexpDupNode(NULL == result ? NULL : result->car);
     650                 : }
     651                 : 
     652               0 : struct NaClSexpNode *AppendImpl(struct NaClSexpCons *cons,
     653                 :                                 struct WorkState *ws) {
     654               0 :   struct NaClSexpNode *first = NULL;
     655               0 :   struct NaClSexpNode *second = NULL;
     656               0 :   struct NaClSexpNode *result = NULL;
     657                 : 
     658               0 :   if (NaClSexpListLength(cons) != 3) {
     659               0 :     crash_cons(ws, "append take exactly two arguments", cons);
     660                 :   }
     661               0 :   first = Eval(cons->cdr->car, ws);
     662               0 :   second = Eval(cons->cdr->cdr->car, ws);
     663               0 :   if (!NaClSexpConsp(first)) {
     664               0 :     error_cons(ws, "append: first arg does not evaluate to a list", cons);
     665               0 :   } else if (!NaClSexpConsp(second)) {
     666               0 :     error_cons(ws, "append: second arg does not evaluate to a list", cons);
     667                 :   } else {
     668               0 :     result = NaClSexpNodeWrapCons(NaClSexpAppend(
     669                 :                                       NaClSexpNodeToCons(first),
     670                 :                                       NaClSexpNodeToCons(second)));
     671                 :   }
     672               0 :   NaClSexpFreeNode(first);
     673               0 :   NaClSexpFreeNode(second);
     674               0 :   return result;
     675                 : }
     676                 : 
     677                 : /*
     678                 :  * The arithmetic functions are just for fun.  And to test that the
     679                 :  * basic evaluation framework makes sense.
     680                 :  */
     681               0 : struct NaClSexpNode *MultImpl(struct NaClSexpCons *cons,
     682                 :                               struct WorkState *ws) {
     683               0 :   int first = 1;
     684               0 :   int value = 0;
     685               0 :   struct NaClSexpNode *v = NULL;
     686                 :   struct NaClSexpCons *p;
     687                 : 
     688               0 :   for (p = cons->cdr; NULL != p; p = p->cdr) {
     689               0 :     v = Eval(p->car, ws);
     690               0 :     if (!NaClSexpIntp(v)) {
     691               0 :       error_node(ws, "argument not integral", v);
     692               0 :       return NULL;
     693                 :     }
     694               0 :     if (first) {
     695               0 :       value = NaClSexpNodeToInt(v);
     696               0 :       first = 0;
     697                 :     } else {
     698               0 :       value *= NaClSexpNodeToInt(v);
     699                 :     }
     700               0 :     NaClSexpFreeNode(v);
     701                 :   }
     702               0 :   return NaClSexpNodeWrapInt(value);
     703                 : }
     704                 : 
     705               0 : struct NaClSexpNode *DivImpl(struct NaClSexpCons *cons,
     706                 :                               struct WorkState *ws) {
     707               0 :   int first = 1;
     708               0 :   int value = 0;
     709               0 :   struct NaClSexpNode *v = NULL;
     710                 :   struct NaClSexpCons *p;
     711                 : 
     712               0 :   for (p = cons->cdr; NULL != p; p = p->cdr) {
     713               0 :     v = Eval(p->car, ws);
     714               0 :     if (!NaClSexpIntp(v)) {
     715               0 :       error_node(ws, "argument not integral", v);
     716               0 :       return NULL;
     717                 :     }
     718               0 :     if (first) {
     719               0 :       value = NaClSexpNodeToInt(v);
     720               0 :       first = 0;
     721                 :     } else {
     722               0 :       value /= NaClSexpNodeToInt(v);
     723                 :     }
     724               0 :     NaClSexpFreeNode(v);
     725                 :   }
     726               0 :   return NaClSexpNodeWrapInt(value);
     727                 : }
     728                 : 
     729               0 : struct NaClSexpNode *AddImpl(struct NaClSexpCons *cons,
     730                 :                               struct WorkState *ws) {
     731               0 :   int first = 1;
     732               0 :   int value = 0;
     733               0 :   struct NaClSexpNode *v = NULL;
     734                 :   struct NaClSexpCons *p;
     735                 : 
     736               0 :   for (p = cons->cdr; NULL != p; p = p->cdr) {
     737               0 :     v = Eval(p->car, ws);
     738               0 :     if (!NaClSexpIntp(v)) {
     739               0 :       error_node(ws, "argument not integral", v);
     740               0 :       return NULL;
     741                 :     }
     742               0 :     if (first) {
     743               0 :       value = NaClSexpNodeToInt(v);
     744               0 :       first = 0;
     745                 :     } else {
     746               0 :       value += NaClSexpNodeToInt(v);
     747                 :     }
     748               0 :     NaClSexpFreeNode(v);
     749                 :   }
     750               0 :   return NaClSexpNodeWrapInt(value);
     751                 : }
     752                 : 
     753               0 : struct NaClSexpNode *SubImpl(struct NaClSexpCons *cons,
     754                 :                               struct WorkState *ws) {
     755               0 :   int first = 1;
     756               0 :   int value = 0;
     757               0 :   struct NaClSexpNode *v = NULL;
     758                 :   struct NaClSexpCons *p;
     759                 : 
     760               0 :   for (p = cons->cdr; NULL != p; p = p->cdr) {
     761               0 :     v = Eval(p->car, ws);
     762               0 :     if (!NaClSexpIntp(v)) {
     763               0 :       error_node(ws, "argument not integral", v);
     764               0 :       return NULL;
     765                 :     }
     766               0 :     if (first) {
     767               0 :       value = NaClSexpNodeToInt(v);
     768               0 :       first = 0;
     769                 :     } else {
     770               0 :       value -= NaClSexpNodeToInt(v);
     771                 :     }
     772               0 :     NaClSexpFreeNode(v);
     773                 :   }
     774               0 :   return NaClSexpNodeWrapInt(value);
     775                 : }
     776                 : 
     777             109 : static struct NaClSexpNode *MatcherResult(int64_t matcher_position,
     778                 :                                           uint64_t matched_bitset) {
     779             109 :   DPRINTF(("MatcherResult(%"NACL_PRId64", 0x%"NACL_PRIx64")\n",
     780                 :            matcher_position, matched_bitset));
     781             109 :   return NaClSexpNodeWrapCons(
     782                 :       NaClSexpConsCons(NaClSexpNodeWrapInt(matcher_position),
     783                 :                        NaClSexpConsCons(NaClSexpNodeWrapInt(matched_bitset),
     784                 :                                         NULL)));
     785                 : }
     786                 : 
     787             143 : static int MatchResultExtract(int64_t *matcher_pos,
     788                 :                               uint64_t *matched_bitset,
     789                 :                               struct NaClSexpNode *result) {
     790                 :   struct NaClSexpCons *result_cons;
     791             143 :   if (NULL == result) {
     792              34 :     return 0;
     793                 :   }
     794             109 :   CHECK(NaClSexpConsp(result));
     795             109 :   result_cons = NaClSexpNodeToCons(result);
     796             109 :   CHECK(2 == NaClSexpListLength(result_cons));
     797             109 :   CHECK(NaClSexpIntp(result_cons->car));
     798             109 :   CHECK(NaClSexpIntp(result_cons->cdr->car));
     799             109 :   if (NULL != matcher_pos) {
     800             105 :     *matcher_pos = NaClSexpNodeToInt(result_cons->car);
     801                 :   }
     802             109 :   if (NULL != matched_bitset) {
     803             109 :     *matched_bitset = NaClSexpNodeToInt(result_cons->cdr->car);
     804                 :   }
     805             109 :   return 1;
     806                 : }
     807                 : 
     808              16 : struct NaClSexpNode *EpsilonMatcherImpl(struct NaClSexpCons *cons,
     809                 :                                         struct WorkState *ws) {
     810              16 :   if (NaClSexpListLength(cons) != 1) {
     811               0 :     crash_cons(ws, "epsilon built-in should have no arguments", cons);
     812                 :   }
     813              16 :   DPRINTF(("Epsilon\n"));
     814                 :   /*
     815                 :    * Check that the event queue is empty.
     816                 :    */
     817              16 :   if (EventQueueLengthMu(ws) == 0) {
     818              16 :     DPRINTF(("epsilon: success -- empty event queue\n"));
     819              16 :     return MatcherResult(-1, 0);
     820                 :   }
     821               0 :   DPRINTF(("epsilon: fail -- non-zero length event queue\n"));
     822               0 :   return NULL;
     823                 : }
     824                 : 
     825               4 : struct NaClSexpNode *PrognImpl(struct NaClSexpCons *cons,
     826                 :                                struct WorkState *ws) {
     827                 :   struct NaClSexpNode *node;
     828               4 :   struct NaClSexpNode *val = NULL;
     829                 : 
     830              24 :   while (NULL != (cons = cons->cdr)) {
     831              16 :     node = cons->car;
     832              16 :     if (NaClSexpConsp(node)) {
     833              16 :       NaClSexpFreeNode(val);
     834              16 :       val = Eval(node, ws);
     835                 :     } else {
     836               0 :       crash_node(ws, "Not a list", node);
     837                 :     }
     838                 :   }
     839               4 :   return val;
     840                 : }
     841                 : 
     842                 : enum WorkType {
     843                 :   kTakeLock = 0,
     844                 :   kDropLock = 1,
     845                 : };
     846                 : 
     847                 : struct WorkItem {
     848                 :   struct WorkState *ws;
     849                 :   enum WorkType type;
     850                 :   int desc;
     851                 : };
     852                 : 
     853              60 : struct WorkItem *WorkItemFactory(struct WorkState *ws,
     854                 :                                  enum WorkType type,
     855                 :                                  int desc) {
     856              60 :   struct WorkItem *wi = malloc(sizeof *wi);
     857              60 :   CHECK(NULL != wi);
     858              60 :   wi->ws = ws;
     859              60 :   wi->type = type;
     860              60 :   wi->desc = desc;
     861              60 :   return wi;
     862                 : }
     863                 : 
     864              60 : static void WorkOnWorkItem(void *functor_state) {
     865              60 :   struct WorkItem *wi = (struct WorkItem *) functor_state;
     866                 : 
     867              60 :   DPRINTF(("WorkOnWorkItem: entered\n"));
     868              60 :   switch (wi->type) {
     869                 :     case kTakeLock:
     870              30 :       NaClFileLockManagerLock(&wi->ws->flm, wi->desc);
     871              30 :       break;
     872                 :     case kDropLock:
     873              30 :       NaClFileLockManagerUnlock(&wi->ws->flm, wi->desc);
     874              30 :       break;
     875                 :   }
     876              60 :   DPRINTF(("WorkOnWorkItem: done\n"));
     877              60 :   free(wi);
     878              60 : }
     879                 : 
     880              60 : struct NaClSexpNode *LockUnlock(struct NaClSexpCons *cons,
     881                 :                                 struct WorkState *ws,
     882                 :                                 enum WorkType op) {
     883                 :   struct NaClSexpNode *p;
     884                 :   int actor_thread;
     885                 :   int file_desc;
     886                 : 
     887              60 :   CheckState(cons, ws);
     888              60 :   if (NaClSexpListLength(cons) != 3) {
     889               0 :     error_cons(ws,
     890                 :                "lock/unlock takes 2 arguments: thread-id and file-id", cons);
     891               0 :     return NULL;
     892                 :   }
     893              60 :   p = Eval(cons->cdr->car, ws);
     894              60 :   if (!NaClSexpIntp(p)) {
     895               0 :     error_cons(ws,
     896                 :                "lock/unlock thread-id argument evaluate to an integer", cons);
     897               0 :     return NULL;
     898                 :   }
     899              60 :   actor_thread = NaClSexpNodeToInt(p);
     900              60 :   NaClSexpFreeNode(p);
     901              60 :   p = Eval(cons->cdr->cdr->car, ws);
     902              60 :   if (!NaClSexpIntp(p)) {
     903               0 :     error_cons(ws,
     904                 :                "lock/unlock file-id argument evaluate to an integer", cons);
     905               0 :     return NULL;
     906                 :   }
     907              60 :   file_desc = NaClSexpNodeToInt(p);
     908              60 :   NaClSexpFreeNode(p);
     909              60 :   PutWork(ws, actor_thread,
     910              60 :           WorkOnWorkItem, WorkItemFactory(ws, op, file_desc));
     911              60 :   return NULL;
     912                 : }
     913                 : 
     914              30 : struct NaClSexpNode *LockImpl(struct NaClSexpCons *cons, struct WorkState *ws) {
     915                 :   /*
     916                 :    * (lock t f) -- tell thread t to take lock f.
     917                 :    */
     918              30 :   return LockUnlock(cons, ws, kTakeLock);
     919                 : }
     920                 : 
     921              30 : struct NaClSexpNode *UnlockImpl(struct NaClSexpCons *cons,
     922                 :                                 struct WorkState *ws) {
     923                 :   /*
     924                 :    * (unlock t f) -- tell thread t to drop lock f.
     925                 :    */
     926              30 :   return LockUnlock(cons, ws, kDropLock);
     927                 : }
     928                 : 
     929                 : /*
     930                 :  * Called by matchers, so ws->mu is held already.
     931                 :  */
     932              89 : struct NaClSexpNode *EventQueueEventMatcher(struct NaClSexpCons *cons,
     933                 :                                             struct WorkState *ws,
     934                 :                                             enum EventType expected_event) {
     935                 :   struct NaClSexpNode *n;
     936                 :   int expected_thread_id;
     937                 :   int expected_file_id;
     938              89 :   size_t list_pos = 0;
     939              89 :   struct NaClSexpNode *p = NULL;
     940                 :   struct Event *event_entry;
     941                 : 
     942              89 :   if (NaClSexpListLength(cons) != 3) {
     943               0 :     error_cons(ws,
     944                 :                "locked/unlocked takes exactly two arguments, thread_id and"
     945                 :                " lock_id", cons);
     946               0 :     return NULL;
     947                 :   }
     948              89 :   n = Eval(cons->cdr->car, ws);
     949              89 :   if (!NaClSexpIntp(n)) {
     950               0 :     error_cons(ws,
     951                 :                "locked/unlocked thread_id argument must eval to an integer",
     952                 :                cons);
     953               0 :     NaClSexpFreeNode(n);
     954               0 :     return NULL;
     955                 :   }
     956              89 :   expected_thread_id = NaClSexpNodeToInt(n);
     957              89 :   NaClSexpFreeNode(n);
     958              89 :   n = Eval(cons->cdr->cdr->car, ws);
     959              89 :   if (!NaClSexpIntp(n)) {
     960               0 :     error_cons(ws,
     961                 :                "locked/unlocked file_id argument must eval to an integer",
     962                 :                cons);
     963               0 :     NaClSexpFreeNode(n);
     964               0 :     return NULL;
     965                 :   }
     966              89 :   expected_file_id = NaClSexpNodeToInt(n);
     967              89 :   NaClSexpFreeNode(n);
     968                 : 
     969              89 :   DPRINTF(("locked/unlocked matcher: %s, thread %d, file %d\n",
     970                 :            (expected_event == kLocked) ? "locked" : "unlocked",
     971                 :            expected_thread_id, expected_file_id));
     972                 : 
     973             210 :   for (event_entry = ws->q;
     974                 :        NULL != event_entry;
     975              32 :        ++list_pos, event_entry = event_entry->next) {
     976             178 :     if (event_entry->type == expected_event &&
     977             146 :         event_entry->thread_id == expected_thread_id &&
     978              73 :         event_entry->file_id == expected_file_id) {
     979              73 :       if (list_pos > 8 * sizeof(int64_t)) {
     980               0 :         crash_cons(ws, "event queue too deep", cons);
     981                 :       }
     982              73 :       p = MatcherResult(-1, 1 << list_pos);
     983              73 :       break;
     984                 :     }
     985                 :   }
     986              89 :   return p;
     987                 : }
     988                 : 
     989              46 : struct NaClSexpNode *LockedMatcherImpl(struct NaClSexpCons *cons,
     990                 :                                        struct WorkState *ws) {
     991                 :   /*
     992                 :    * (locked t f) -- look for event (kLocked t f) in event queue.
     993                 :    */
     994              46 :   return EventQueueEventMatcher(cons, ws, kLocked);
     995                 : }
     996                 : 
     997              43 : struct NaClSexpNode *UnlockedMatcherImpl(struct NaClSexpCons *cons,
     998                 :                                          struct WorkState *ws) {
     999                 :   /*
    1000                 :    * (unlocked t f) -- look for event (kUnlocked t f) in event queue.
    1001                 :    */
    1002              43 :   return EventQueueEventMatcher(cons, ws, kUnlocked);
    1003                 : }
    1004                 : 
    1005                 : struct NaClSexpNode *AllMatcherImpl(struct NaClSexpCons *cons,
    1006                 :                                     struct WorkState *ws);
    1007                 : 
    1008                 : struct NaClSexpNode *AnyMatcherImpl(struct NaClSexpCons *cons,
    1009                 :                                     struct WorkState *ws);
    1010                 : 
    1011                 : struct NaClSexpNode *Eval(struct NaClSexpNode *n,
    1012                 :                           struct WorkState *ws);
    1013                 : 
    1014               4 : struct NaClSexpNode *EvalImpl(struct NaClSexpCons *cons,
    1015                 :                               struct WorkState *ws) {
    1016                 :   struct NaClSexpNode *n;
    1017                 :   struct NaClSexpNode *eval_n;
    1018                 :   /*
    1019                 :    * Check that there is a single argument, the invoke Eval on it.
    1020                 :    */
    1021               4 :   if (NaClSexpListLength(cons) != 2) {
    1022               0 :     error_cons(ws, "eval takes exactly one argument", cons);
    1023               0 :     return NULL;
    1024                 :   }
    1025               4 :   n = Eval(cons->cdr->car, ws);
    1026               4 :   eval_n = Eval(n, ws);
    1027               4 :   NaClSexpFreeNode(n);
    1028               4 :   return eval_n;
    1029                 : }
    1030                 : 
    1031                 : struct NaClSexpNode *MatchMatcherImpl(struct NaClSexpCons *cons,
    1032                 :                                       struct WorkState *ws);
    1033                 : 
    1034                 : struct SymbolTable {
    1035                 :   char const *name;
    1036                 :   int is_matcher;
    1037                 : 
    1038                 :   /*
    1039                 :    * The |fn| member may be a built-in function, or a matcher as
    1040                 :    * specified by |is_matcher|.  If !is_matcher, then it returns an
    1041                 :    * s-expression that is the value of the function.  If it is a
    1042                 :    * matcher, then at the lisp level the "apparent" return value is an
    1043                 :    * integer.  Internally, successful matchers return the following:
    1044                 :    *
    1045                 :    *   ( sub-matcher-index event-pos-bitset )
    1046                 :    *
    1047                 :    * For non-composite matchers, sub-matcher-index is NULL.  The list
    1048                 :    * of event-pos-bitset is an integer with a non-zero bit for each
    1049                 :    * events that were matched, with the bit position corresponding to
    1050                 :    * their positions in the event queue.  NB: it is a test
    1051                 :    * specification error if any sub-matchers used in the (any ...)
    1052                 :    * special form matches a subset of the events matched by another
    1053                 :    * sub-matcher, or sub-matchers used in the (all ...)  special form
    1054                 :    * overlap.
    1055                 :    *
    1056                 :    * A matcher that failed to match returns NULL.
    1057                 :    */
    1058                 :   struct NaClSexpNode *(*fn)(struct NaClSexpCons *cons, struct WorkState *ws);
    1059                 : };
    1060                 : 
    1061                 : struct SymbolTable g_symtab[] = {
    1062                 :   {
    1063                 :     "set-threads", 0, SetThreadsImpl
    1064                 :   }, {
    1065                 :     "set-files", 0, SetFilesImpl
    1066                 :   }, {
    1067                 :     "quote", 0, QuoteImpl
    1068                 :   }, {
    1069                 :     "car", 0, CarImpl
    1070                 :   }, {
    1071                 :     "cdr", 0, CdrImpl
    1072                 :   }, {
    1073                 :     "cons", 0, ConsImpl
    1074                 :   }, {
    1075                 :     "list", 0, ListImpl
    1076                 :   }, {
    1077                 :     "nth", 0, NthImpl
    1078                 :   }, {
    1079                 :     "append", 0, AppendImpl
    1080                 :   }, {
    1081                 :     "eval", 0, EvalImpl
    1082                 :   }, {
    1083                 :     "*", 0, MultImpl
    1084                 :   }, {
    1085                 :     "/", 0, DivImpl
    1086                 :   }, {
    1087                 :     "+", 0, AddImpl
    1088                 :   }, {
    1089                 :     "-", 0, SubImpl
    1090                 :   }, {
    1091                 :     "progn", 0, PrognImpl
    1092                 :   }, {
    1093                 :     "lock", 0, LockImpl
    1094                 :   }, {
    1095                 :     "unlock", 0, UnlockImpl
    1096                 :   }, {
    1097                 :     "match", 1, MatchMatcherImpl
    1098                 :   }, {
    1099                 :     "epsilon", 1, EpsilonMatcherImpl
    1100                 :   }, {
    1101                 :     "locked", 1, LockedMatcherImpl
    1102                 :   }, {
    1103                 :     "unlocked", 1, UnlockedMatcherImpl
    1104                 :   }, {
    1105                 :     "all", 1, AllMatcherImpl
    1106                 :   }, {
    1107                 :     "any", 1, AnyMatcherImpl
    1108                 :   }
    1109                 : };
    1110                 : 
    1111             110 : static int CheckMatchers(struct WorkState *ws,
    1112                 :                          struct NaClSexpCons *matcher_list,
    1113                 :                          struct SymbolTable **matcher_entries) {
    1114                 :   size_t mx;
    1115                 :   size_t ix;
    1116                 :   struct NaClSexpCons *matchers;
    1117                 :   struct NaClSexpNode *cur;
    1118                 :   struct NaClSexpCons *submatcher;
    1119                 :   char const *submatcher_name;
    1120                 : 
    1121             368 :   for (mx = 0, matchers = matcher_list;
    1122                 :        NULL != matchers;
    1123             148 :        ++mx, matchers = matchers->cdr) {
    1124             148 :     cur = matchers->car;
    1125             148 :     if (!NaClSexpConsp(cur)) {
    1126               0 :       error_node(ws, "all: submatcher not a list", cur);
    1127               0 :       return 0;
    1128                 :     }
    1129             148 :     submatcher = NaClSexpNodeToCons(cur);
    1130             148 :     if (NULL == submatcher || !NaClSexpTokenp(submatcher->car)) {
    1131               0 :       error_node(ws, "all: submatcher not a matcher", cur);
    1132               0 :       return 0;
    1133                 :     }
    1134             148 :     submatcher_name = NaClSexpNodeToToken(submatcher->car);
    1135            3076 :     for (ix = 0; ix < sizeof g_symtab/sizeof g_symtab[0]; ++ix) {
    1136            3076 :       if (!strcmp(g_symtab[ix].name, submatcher_name)) {
    1137                 :         /* found! */
    1138             148 :         if (!g_symtab[ix].is_matcher) {
    1139               0 :           error_node(ws, "all: submatcher not a matcher", cur);
    1140               0 :           return 0;
    1141                 :         }
    1142                 :         /* save pointer for later use */
    1143             148 :         matcher_entries[mx] = &g_symtab[ix];
    1144             148 :         break;
    1145                 :       }
    1146                 :     }
    1147             148 :     if (ix == sizeof g_symtab/sizeof g_symtab[0]) {
    1148               0 :       error_node(ws, "all: submatcher not found", cur);
    1149               0 :       return 0;
    1150                 :     }
    1151                 :   }
    1152             110 :   return 1;
    1153                 : }
    1154                 : 
    1155              30 : struct NaClSexpNode *AllMatcherImpl(struct NaClSexpCons *cons,
    1156                 :                                     struct WorkState *ws) {
    1157              30 :   int64_t match_index = -1;
    1158              30 :   uint64_t match_pos = 0;
    1159              30 :   size_t num_matchers = NaClSexpListLength(cons) - 1;
    1160                 :   size_t mx;
    1161                 :   struct NaClSexpCons *matchers;
    1162                 :   struct NaClSexpNode *cur;
    1163              30 :   struct SymbolTable **matcher_entries = NULL;
    1164                 :   struct NaClSexpNode *match_result;
    1165                 :   int64_t submatch_index;
    1166                 :   uint64_t submatch_pos;
    1167                 : 
    1168              30 :   if (gVerbosity > 2) {
    1169               0 :     printf("AllMatcherImpl: %"NACL_PRIdS" submatchers\n", num_matchers);
    1170                 :   }
    1171              30 :   if (0 == num_matchers) {
    1172               0 :     error_cons(ws, "all must have at least one sub-matcher", cons);
    1173               0 :     return NULL;
    1174                 :   }
    1175              30 :   matcher_entries = (struct SymbolTable **)
    1176              30 :       malloc(num_matchers * sizeof *matcher_entries);
    1177              30 :   if (!CheckMatchers(ws, cons->cdr, matcher_entries)) {
    1178               0 :     error_cons(ws, "all: submatcher error", cons);
    1179               0 :     free(matcher_entries);
    1180               0 :     return NULL;
    1181                 :   }
    1182                 :   /*
    1183                 :    * Invoke all submatchers.  If any fail, we fail.  Accumulate event
    1184                 :    * queue indices.
    1185                 :    */
    1186             105 :   for (mx = 0, matchers = cons->cdr;
    1187                 :        NULL != matchers;
    1188              45 :        ++mx, matchers = matchers->cdr) {
    1189              59 :     cur = matchers->car;
    1190              59 :     match_result = (*matcher_entries[mx]->fn)(NaClSexpNodeToCons(cur), ws);
    1191                 : 
    1192              59 :     if (gVerbosity > 2) {
    1193               0 :       printf("submatcher "); NaClSexpPrintNode(stdout,cur); printf(" --> ");
    1194               0 :       NaClSexpPrintNode(stdout, match_result);
    1195               0 :       printf("\n");
    1196                 :     }
    1197                 : 
    1198              59 :     if (!MatchResultExtract(&submatch_index, &submatch_pos, match_result)) {
    1199              14 :       DPRINTF(("submatcher failed\n"));
    1200              14 :       NaClSexpFreeNode(match_result);
    1201              14 :       free(matcher_entries);
    1202              14 :       return NULL;
    1203                 :     }
    1204              45 :     NaClSexpFreeNode(match_result);
    1205                 : 
    1206              45 :     if ((match_pos & submatch_pos) != 0) {
    1207               0 :       error_cons(ws, "all: overlapping matchers", cons);
    1208               0 :       free(matcher_entries);
    1209               0 :       return NULL;
    1210                 :     }
    1211                 :     /*
    1212                 :      * Allow, for example, the (all (unlocked 0 0) (any (locked 1 0)
    1213                 :      * (locked 2 0))) to propagate the submatch index from the (any
    1214                 :      * ...) form up.  This does not generalize to more than one (any
    1215                 :      * ...) sub-sexpressions in the (all ...) form.
    1216                 :      */
    1217              45 :     if (-1 == match_index && -1 != submatch_index) {
    1218               2 :       match_index = submatch_index;
    1219                 :     }
    1220              45 :     match_pos |= submatch_pos;
    1221                 :   }
    1222              16 :   free(matcher_entries);
    1223              16 :   return MatcherResult(match_index, match_pos);
    1224                 : }
    1225                 : 
    1226               8 : struct NaClSexpNode *AnyMatcherImpl(struct NaClSexpCons *cons,
    1227                 :                                     struct WorkState *ws) {
    1228               8 :   uint64_t submatch_pos = 0;
    1229               8 :   size_t num_matchers = NaClSexpListLength(cons) - 1;
    1230                 :   size_t mx;
    1231                 :   struct NaClSexpCons *matchers;
    1232                 :   struct NaClSexpNode *cur;
    1233               8 :   struct SymbolTable **matcher_entries = NULL;
    1234               8 :   struct NaClSexpNode *match_result = NULL;
    1235                 : 
    1236               8 :   if (gVerbosity > 2) {
    1237               0 :     printf("AnyMatcherImpl: %"NACL_PRIdS" submatchers\n", num_matchers);
    1238                 :   }
    1239               8 :   if (0 == num_matchers) {
    1240               0 :     error_cons(ws, "any must have at least one sub-matcher", cons);
    1241               0 :     return NULL;
    1242                 :   }
    1243               8 :   matcher_entries = (struct SymbolTable **)
    1244               8 :       malloc(num_matchers * sizeof *matcher_entries);
    1245               8 :   if (!CheckMatchers(ws, cons->cdr, matcher_entries)) {
    1246               0 :     error_cons(ws, "any: submatcher error", cons);
    1247               0 :     free(matcher_entries);
    1248               0 :     return NULL;
    1249                 :   }
    1250                 :   /*
    1251                 :    * Invoke all submatchers.  If any succeed, we succeed.
    1252                 :    */
    1253              24 :   for (mx = 0, matchers = cons->cdr;
    1254                 :        NULL != matchers;
    1255               8 :        ++mx, matchers = matchers->cdr) {
    1256              12 :     cur = matchers->car;
    1257              12 :     match_result = (*matcher_entries[mx]->fn)(NaClSexpNodeToCons(cur), ws);
    1258                 : 
    1259              12 :     if (gVerbosity > 2) {
    1260               0 :       printf("submatcher "); NaClSexpPrintNode(stdout,cur); printf(" --> ");
    1261               0 :       NaClSexpPrintNode(stdout, match_result);
    1262               0 :       printf("\n");
    1263                 :     }
    1264                 : 
    1265              12 :     if (MatchResultExtract((int64_t *) NULL, &submatch_pos, match_result)) {
    1266               4 :       DPRINTF(("submatcher succeeded\n"));
    1267               4 :       NaClSexpFreeNode(match_result);
    1268               4 :       match_result = MatcherResult(mx, submatch_pos);
    1269               4 :       break;
    1270                 :     }
    1271               8 :     NaClSexpFreeNode(match_result);
    1272                 :   }
    1273               8 :   free(matcher_entries);
    1274               8 :   return match_result;
    1275                 : }
    1276                 : 
    1277                 : /*
    1278                 :  * Single arg must be a matcher.  Evaluate it.  If successful, check
    1279                 :  * that all events in the event queue were matched; if so, discard all
    1280                 :  * events in event queue, and return the match index.  If not
    1281                 :  * successful or not all events were matched, leave the event queue
    1282                 :  * alone and return -1.  Typically returned value is used by (nth ...)
    1283                 :  * form.
    1284                 :  */
    1285              72 : struct NaClSexpNode *MatchMatcherImpl(struct NaClSexpCons *cons,
    1286                 :                                       struct WorkState *ws) {
    1287                 : 
    1288                 :   struct SymbolTable *matcher[1];
    1289                 :   struct NaClSexpNode *result;
    1290                 :   int64_t which_match;
    1291                 :   uint64_t match_set;
    1292                 : 
    1293              72 :   DPRINTF(("MatchMatcherImpl\n"));
    1294              72 :   if (NaClSexpListLength(cons) != 2) {
    1295               0 :     error_cons(ws, "match takes a single matcher as argument", cons);
    1296               0 :     return NULL;
    1297                 :   }
    1298              72 :   if (!CheckMatchers(ws, cons->cdr, matcher)) {
    1299               0 :     error_cons(ws, "match argument should be a matcher", cons);
    1300               0 :     return NULL;
    1301                 :   }
    1302              72 :   if (!NaClSexpConsp(cons->cdr->car)) {
    1303               0 :     error_cons(ws, "match argument not a list", cons);
    1304               0 :     return NULL;
    1305                 :   }
    1306              72 :   result = (*matcher[0]->fn)(NaClSexpNodeToCons(cons->cdr->car), ws);
    1307              72 :   if (!MatchResultExtract(&which_match, &match_set, result)) {
    1308              12 :     DPRINTF(("did not match\n"));
    1309              12 :     NaClSexpFreeNode(result);
    1310              12 :     result = NULL;
    1311              60 :   } else if (match_set != ((((uint64_t) 1) << EventQueueLengthMu(ws)) - 1)) {
    1312                 :     /* did not match all entries */
    1313               0 :     DPRINTF(("match set incomplete\n"));
    1314               0 :     NaClSexpFreeNode(result);
    1315               0 :     result = NULL;
    1316                 :   } else {
    1317              60 :     DPRINTF(("matched all events\n"));
    1318              60 :     EventQueueDiscardMu(ws);
    1319              60 :     NaClSexpFreeNode(result);
    1320              60 :     result = NaClSexpNodeWrapInt(which_match);
    1321                 :   }
    1322              72 :   return result;
    1323                 : }
    1324                 : 
    1325             450 : struct NaClSexpNode *Eval(struct NaClSexpNode *n, struct WorkState *ws) {
    1326                 :   struct NaClSexpCons *cons;
    1327                 :   size_t ix;
    1328                 :   struct NaClSexpNode *car;
    1329                 :   char const *token;
    1330             450 :   struct NaClSexpNode *val = NULL;
    1331                 :   struct timespec timeout_time;
    1332                 :   int wait_for_more;
    1333                 :   int last_match;
    1334                 : 
    1335             450 :   if (!NaClSexpConsp(n)) {
    1336                 :     /* for now; symbol table lookup when there are symbols with values */
    1337             298 :     return NaClSexpDupNode(n);
    1338                 :   }
    1339             152 :   cons = NaClSexpNodeToCons(n);
    1340             152 :   if (NULL == cons) {
    1341               0 :     return NaClSexpDupNode(n);
    1342                 :   }
    1343                 : 
    1344             152 :   car = cons->car;
    1345             152 :     if (!NaClSexpTokenp(car)) {
    1346               0 :     crash_cons(ws,
    1347                 :                "nacl_file_lock_test: car of list should be a built-in function",
    1348                 :                cons);
    1349                 :   }
    1350             152 :   token = NaClSexpNodeToToken(car);
    1351             152 :   DPRINTF(("function %s\n", token));
    1352            2238 :   for (ix = 0; ix < sizeof g_symtab / sizeof g_symtab[0]; ++ix) {
    1353            2238 :     if (!strcmp(token, g_symtab[ix].name)) {
    1354             152 :       if (!g_symtab[ix].is_matcher) {
    1355              92 :         val = (*g_symtab[ix].fn)(cons, ws);
    1356                 :       } else {
    1357                 :         /*
    1358                 :          * A "matcher" special form is a bit weird/tricky.  These are
    1359                 :          * not blocking functions, since matchers can be used in
    1360                 :          * conjunction with other matchers in (all ...) or (any ...)
    1361                 :          * forms.  What we do is the following: we wait for events,
    1362                 :          * and as events arrive in the event queue, we run the
    1363                 :          * matcher, which must immediately return whether a match
    1364                 :          * against the contents of the event queue occurred.  If there
    1365                 :          * is no match, we wait for another event, until the timeout
    1366                 :          * occurs; if timeout occurs and the matcher did not match,
    1367                 :          * then it is an error and we abort the test program.  For the
    1368                 :          * (all ...) form, the AllMatcherImpl (essentially AND) will
    1369                 :          * check that all arguments are themselves matchers and run
    1370                 :          * them to see if all succeeds, and AllMatcherImpl only
    1371                 :          * succeeds if all succeeded.  The AnyMatcherImpl (OR) will
    1372                 :          * succeed if any of the argument matchers succeed.  Matchers
    1373                 :          * return the position in the event queue where the match
    1374                 :          * occurred, so the composite matchers can remove the events
    1375                 :          * appropriately.  Matching and removal is done with the event
    1376                 :          * list locked, so worker threads that wake up etc cannot add
    1377                 :          * new events.  Position is relative to the head, so even if
    1378                 :          * this were not the case, we would not get confused about
    1379                 :          * which event was matched.
    1380                 :          *
    1381                 :          * Later we may consider matchers that introduce a variable to
    1382                 :          * be bound to an event parameter, to be used later by
    1383                 :          * subsequent matchers.  This can be used to cut down on the
    1384                 :          * combinatoric explosion that occurs with possible futures
    1385                 :          * when, for example, several threads might wake up when a
    1386                 :          * lock becomes available.  We need to think about scoping in
    1387                 :          * that case.
    1388                 :          */
    1389              60 :         DPRINTF(("Matcher found\n"));
    1390              60 :         pthread_mutex_lock(&ws->mu);
    1391              60 :         last_match = 0;
    1392              60 :         ComputeAbsTimeout(&timeout_time, gEpsilonDelayNanos);
    1393                 : 
    1394              60 :         wait_for_more = 0;
    1395             132 :         while (!last_match) {
    1396              72 :           DPRINTF(("Checking EventQueueLengthMu -> %d\n",
    1397                 :                    (int) EventQueueLengthMu(ws)));
    1398              72 :           if (EventQueueLengthMu(ws) == 0 || wait_for_more) {
    1399              69 :             DPRINTF(("Waiting for event\n"));
    1400              69 :             if (!WaitForEventMu(ws, &timeout_time)) {
    1401              16 :               DPRINTF(("Event timed out\n"));
    1402              16 :               last_match = 1;
    1403                 :             }
    1404                 :           }
    1405                 :           /*
    1406                 :            * Run matcher on event queue; matchers are invoked while
    1407                 :            * holding the lock.
    1408                 :            */
    1409              72 :           val = (*g_symtab[ix].fn)(cons, ws);
    1410              72 :           if (gVerbosity > 1) {
    1411               0 :             printf("matcher returned: ");
    1412               0 :             NaClSexpPrintNode(stdout,val);
    1413               0 :             putchar('\n');
    1414                 :           }
    1415              72 :           if (NULL != val) {
    1416                 :             /* successful match! */
    1417              60 :             break;
    1418                 :           }
    1419                 : 
    1420              12 :           if (last_match) {
    1421               0 :             error_cons(ws, "match failed; timed out", cons);
    1422               0 :             val = NULL;
    1423               0 :             break;
    1424                 :           }
    1425              12 :           DPRINTF(("match failed, waiting for more events\n"));
    1426              12 :           wait_for_more = 1;
    1427                 :         }
    1428                 : 
    1429              60 :         pthread_mutex_unlock(&ws->mu);
    1430                 :       }
    1431             152 :       return val;
    1432                 :     }
    1433                 :   }
    1434               0 :   fprintf(stderr, "No such function: %s\n", token);
    1435               0 :   return NULL;
    1436                 : }
    1437                 : 
    1438               8 : void ReadEvalPrintLoop(struct NaClSexpIo *input,
    1439                 :                        int interactive,
    1440                 :                        int verbosity,
    1441                 :                        size_t epsilon_delay_nanos,
    1442                 :                        struct NaClFileLockTestInterface *test_if) {
    1443                 :   struct WorkState ws;
    1444                 :   struct NaClSexpNode *n;
    1445                 :   struct NaClSexpNode *val;
    1446                 :   int err;
    1447                 : 
    1448               8 :   gInteractive = interactive;
    1449               8 :   gVerbosity = verbosity;
    1450               8 :   if (epsilon_delay_nanos < MIN_EPSILON_DELAY_NANOS) {
    1451               0 :     epsilon_delay_nanos = MIN_EPSILON_DELAY_NANOS;
    1452                 :   }
    1453               8 :   gEpsilonDelayNanos = epsilon_delay_nanos;
    1454                 : 
    1455               8 :   err = pthread_key_create(&gNaClTestTsdKey, (void (*)(void *)) NULL);
    1456               8 :   CHECK(0 == err);
    1457                 : 
    1458               8 :   WorkStateCtor(&ws, test_if);
    1459                 : 
    1460             136 :   while (NULL != (n = NaClSexpReadSexp(input))) {
    1461             120 :     if (gVerbosity > 1 && n->type == kNaClSexpCons) {
    1462               0 :       printf("Program list length: %"NACL_PRIdS"\n",
    1463               0 :              NaClSexpListLength(n->u.cval));
    1464                 :     }
    1465                 : 
    1466             120 :     if (gVerbosity) {
    1467               0 :       NaClSexpPrintNode(stdout, n);
    1468               0 :       printf(" => ");
    1469                 :     }
    1470             120 :     val = Eval(n, &ws);
    1471             120 :     NaClSexpFreeNode(n);
    1472             120 :     NaClSexpPrintNode(stdout, val);
    1473             120 :     printf("\n");
    1474             120 :     NaClSexpFreeNode(val);
    1475                 :   }
    1476               8 :   AnnounceEndOfWork(&ws);
    1477               8 :   ReapThreads(&ws);
    1478               8 :   WorkStateDtor(&ws);
    1479               8 : }

Generated by: LCOV version 1.7