LCOV - code coverage report
Current view: directory - tests/lock_manager - nacl_test_util_repl.c (source / functions) Found Hit Coverage
Test: coverage.lcov Lines: 905 559 61.8 %
Date: 2014-06-18 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                 :            q->type, q->thread_id, q->file_id);
     137               0 :     q = q->next;
     138               0 :   }
     139               0 : }
     140                 : 
     141               0 : static void crash_node(struct WorkState *ws,
     142               0 :                        char const *reason,
     143               0 :                        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               0 : }
     150                 : 
     151               0 : static void crash_cons(struct WorkState *ws,
     152               0 :                        char const *reason,
     153               0 :                        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               0 : }
     160                 : 
     161               0 : static void error_node(struct WorkState *ws,
     162               0 :                        char const *reason,
     163               0 :                        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               0 :                        char const *reason,
     181               0 :                        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                 : static void NaClFileLockTestSetFileIdentityDataWrapper(
     200              74 :     struct NaClFileLockEntry *entry,
     201              74 :     int desc) {
     202              74 :   struct ThreadState *ts;
     203                 : 
     204              74 :   ts = (struct ThreadState *) pthread_getspecific(gNaClTestTsdKey);
     205             222 :   CHECK(NULL != ts);
     206              74 :   (*ts->ws->test_if->set_identity)(ts->ws->test_if,
     207                 :                                    ts->ws->orig_set_identity,
     208                 :                                    entry, desc);
     209              74 : }
     210                 : 
     211              30 : static void NaClFileLockTestTakeFileLockWrapper(int desc) {
     212              30 :   struct ThreadState *ts;
     213                 : 
     214              30 :   ts = (struct ThreadState *) pthread_getspecific(gNaClTestTsdKey);
     215              90 :   CHECK(NULL != ts);
     216              30 :   if ((*ts->ws->test_if->take_lock)(ts->ws->test_if,
     217                 :                                     ts->ws->orig_take_lock,
     218                 :                                     ts->this_thread, desc)) {
     219              30 :     EventOccurred(ts, kLocked, desc);
     220              30 :   }
     221              30 : }
     222                 : 
     223              30 : static void NaClFileLockTestDropFileLockWrapper(int desc) {
     224              30 :   struct ThreadState *ts;
     225                 : 
     226              30 :   ts = (struct ThreadState *) pthread_getspecific(gNaClTestTsdKey);
     227              90 :   CHECK(NULL != ts);
     228              30 :   if ((*ts->ws->test_if->drop_lock)(ts->ws->test_if,
     229                 :                                     ts->ws->orig_drop_lock,
     230                 :                                     ts->this_thread, desc)) {
     231              30 :     EventOccurred(ts, kUnlocked, desc);
     232              30 :   }
     233              30 : }
     234                 : 
     235               8 : void WorkStateCtor(struct WorkState *ws,
     236               8 :                    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               8 :   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               0 :   }
     263               8 :   NaClFileLockManagerDtor(&ws->flm);
     264               8 : }
     265                 : 
     266             147 : size_t EventQueueLengthMu(struct WorkState *ws) {
     267             147 :   struct Event *p;
     268             147 :   size_t count = 0;
     269             442 :   for (p = ws->q; NULL != p; p = p->next) {
     270              74 :     ++count;
     271              74 :   }
     272             147 :   return count;
     273                 : }
     274                 : 
     275              60 : void EnqueueEvent(struct WorkState *ws, struct Event *ev) {
     276             180 :   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             180 :   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              60 :   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               2 :     ++tspec->tv_sec;
     294               2 :     tspec->tv_nsec -= 1000000000;
     295               2 :   }
     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              68 : int WaitForEventMu(struct WorkState *ws, struct timespec *abs_timeout) {
     303              68 :   int timed_out_failure = 0;
     304                 : 
     305              68 :   if (NULL != abs_timeout) {
     306              68 :     if (pthread_cond_timedwait(&ws->cv, &ws->mu, abs_timeout) == ETIMEDOUT) {
     307              16 :       timed_out_failure = 1;
     308              16 :     }
     309              68 :   } else {
     310               0 :     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               0 :     }
     315                 :   }
     316                 : 
     317              68 :   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             180 :   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             180 :   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              60 :   struct Event *p;
     338              60 :   struct Event *rest;
     339                 : 
     340             180 :   DPRINTF(("EventQueueDiscardMu\n"));
     341             240 :   for (p = ws->q; p != NULL; p = rest) {
     342              60 :     rest = p->next;
     343              60 :     free(p);
     344              60 :   }
     345              60 :   ws->q = NULL;
     346              60 :   ws->qend = &ws->q;
     347              60 : }
     348                 : 
     349              82 : int GetWork(struct ThreadState *ts,
     350              82 :             void (**functor_out)(void *functor_state),
     351              82 :             void **functor_state_out) {
     352              82 :   int actor_thread;
     353              82 :   int has_work;
     354                 : 
     355             246 :   DPRINTF(("GetWork(%d)\n", ts->this_thread));
     356              82 :   pthread_mutex_lock(&ts->ws->mu);
     357             710 :   while (WORK_ENDED != (actor_thread = ts->ws->actor_thread) &&
     358                 :          actor_thread != ts->this_thread) {
     359             729 :     DPRINTF(("GetWork(%d) waiting\n", ts->this_thread));
     360             243 :     pthread_cond_wait(&ts->ws->cv, &ts->ws->mu);
     361             243 :   }
     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              60 :   }
     368              82 :   pthread_mutex_unlock(&ts->ws->mu);
     369             246 :   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              60 :              void (*action_functor)(void *functor_state),
     375              60 :              void *functor_state) {
     376             180 :   DPRINTF(("PutWork(thread=%d)\n", actor_thread));
     377              60 :   pthread_mutex_lock(&ws->mu);
     378             172 :   while (ws->actor_thread != WORK_PENDING &&
     379                 :          ws->actor_thread != WORK_ACCEPTED) {
     380               0 :     pthread_cond_wait(&ws->cv, &ws->mu);
     381               0 :   }
     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             180 :   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               0 :   }
     395               0 :   pthread_mutex_unlock(&ws->mu);
     396               0 : }
     397                 : 
     398               8 : void AnnounceEndOfWork(struct WorkState *ws) {
     399              24 :   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                 :          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               0 :   }
     409               8 :   ws->actor_thread = WORK_ENDED;
     410              24 :   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              22 :   void (*functor)(void *functor_state);
     418              22 :   void *functor_state;
     419                 : 
     420                 :   /* set ts in TSD */
     421              22 :   pthread_setspecific(gNaClTestTsdKey, ts);
     422                 : 
     423              66 :   DPRINTF(("thread %d\n", ts->this_thread));
     424             104 :   while (GetWork(ts, &functor, &functor_state)) {
     425              60 :     (*functor)(functor_state);
     426              60 :   }
     427              22 :   return NULL;
     428                 : }
     429                 : 
     430               8 : void SpawnThreadsMu(struct WorkState *ws, size_t num_threads) {
     431               8 :   size_t ix;
     432               8 :   int err;
     433                 : 
     434              24 :   CHECK(0 < num_threads);
     435               8 :   ws->num_workers = num_threads;
     436               8 :   ws->workers = malloc(num_threads * sizeof *ws->workers);
     437              24 :   CHECK(NULL != ws->workers);
     438                 : 
     439              60 :   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                 :                                    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              22 :   }
     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             180 :   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               8 :   size_t ix;
     473                 : 
     474               8 :   if (ws->num_workers > 0) {
     475              60 :     for (ix = 0; ix < ws->num_workers; ++ix) {
     476              22 :       pthread_join(ws->workers[ix].tid, (void **) NULL);
     477              22 :     }
     478               8 :   }
     479               8 : }
     480                 : 
     481                 : struct NaClSexpNode *Eval(struct NaClSexpNode *n, struct WorkState *ws);
     482                 : 
     483               8 : struct NaClSexpNode *SetThreadsImpl(struct NaClSexpCons *cons,
     484               8 :                                     struct WorkState *ws) {
     485               8 :   int num_threads;
     486                 : 
     487               8 :   if (NaClSexpListLength(cons) != 2) {
     488               0 :     crash_cons(ws, "set-threads should have 1 argument", cons);
     489               0 :   }
     490               8 :   if (!NaClSexpIntp(cons->cdr->car)) {
     491               0 :     crash_cons(ws, "set-threads should have 1 numeric argument", cons);
     492               0 :   }
     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               0 :   }
     497               8 :   if (num_threads >= MAX_THREADS) {
     498               0 :     crash_cons(ws, "program specifies too many threads", cons);
     499               0 :   }
     500               8 :   pthread_mutex_lock(&ws->mu);
     501              24 :   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               8 :                                   struct WorkState *ws) {
     509               8 :   int num_files;
     510                 : 
     511               8 :   if (NaClSexpListLength(cons) != 2) {
     512               0 :     crash_cons(ws, "set-files should have 1 argument", cons);
     513               0 :   }
     514               8 :   if (!NaClSexpIntp(cons->cdr->car)) {
     515               0 :     crash_cons(ws, "set-files should have 1 numeric argument", cons);
     516               0 :   }
     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               0 :   }
     521               8 :   if (num_files >= MAX_FILES) {
     522               0 :     crash_cons(ws, "program specifies too many files", cons);
     523               0 :   }
     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               0 :   }
     528                 : 
     529               8 :   return NaClSexpNodeWrapInt(num_files);
     530                 : }
     531                 : 
     532               4 : struct NaClSexpNode *QuoteImpl(struct NaClSexpCons *cons,
     533               4 :                                struct WorkState *ws) {
     534               8 :   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               4 : }
     541                 : 
     542               0 : struct NaClSexpNode *CarImpl(struct NaClSexpCons *cons,
     543               0 :                              struct WorkState *ws) {
     544               0 :   struct NaClSexpNode *p;
     545               0 :   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               0 :   } else {
     555               0 :     arg = NaClSexpNodeToCons(p);
     556               0 :     if (NULL != arg) {
     557               0 :       result = NaClSexpDupNode(arg->car);
     558               0 :     }
     559                 :   }
     560               0 :   return result;
     561               0 : }
     562                 : 
     563               0 : struct NaClSexpNode *CdrImpl(struct NaClSexpCons *cons,
     564               0 :                              struct WorkState *ws) {
     565               0 :   struct NaClSexpNode *p;
     566               0 :   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               0 :   } else {
     576               0 :     arg = NaClSexpNodeToCons(p);
     577               0 :     if (NULL != arg) {
     578               0 :       result = NaClSexpNodeWrapCons(NaClSexpDupCons(arg->cdr));
     579               0 :     }
     580                 :   }
     581               0 :   return result;
     582               0 : }
     583                 : 
     584               0 : struct NaClSexpNode *ConsImpl(struct NaClSexpCons *cons,
     585               0 :                               struct WorkState *ws) {
     586               0 :   struct NaClSexpNode *first;
     587               0 :   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               0 :   } else {
     599               0 :     result = NaClSexpNodeWrapCons(
     600               0 :         NaClSexpConsCons(NaClSexpDupNode(first),
     601               0 :                          NaClSexpDupCons(NaClSexpNodeToCons(second))));
     602                 :   }
     603               0 :   NaClSexpFreeNode(first);
     604               0 :   NaClSexpFreeNode(second);
     605               0 :   return result;
     606               0 : }
     607                 : 
     608               0 : struct NaClSexpNode *ListImpl(struct NaClSexpCons *cons,
     609               0 :                                struct WorkState *ws) {
     610               0 :   struct NaClSexpCons *result = NULL;
     611               0 :   struct NaClSexpCons **addpos = &result;
     612               0 :   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               0 :   }
     618               0 :   return NaClSexpNodeWrapCons(result);
     619                 : }
     620                 : 
     621               4 : struct NaClSexpNode *NthImpl(struct NaClSexpCons *cons,
     622               4 :                              struct WorkState *ws) {
     623               4 :   struct NaClSexpCons *result = NULL;
     624               4 :   struct NaClSexpCons *p;
     625               4 :   struct NaClSexpNode *arg_selector;
     626               4 :   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               0 :   }
     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               0 :   } else {
     639               4 :     ix = NaClSexpNodeToInt(arg_selector);
     640              12 :     for (p = NaClSexpNodeToCons(arg_list);
     641                 :          NULL != p && ix > 0;
     642               0 :          p = p->cdr, --ix) {
     643               0 :       continue;
     644                 :     }
     645               4 :     if (ix == 0) {
     646               4 :       result = p;
     647               4 :     }
     648                 :   }
     649              12 :   return NaClSexpDupNode(NULL == result ? NULL : result->car);
     650                 : }
     651                 : 
     652               0 : struct NaClSexpNode *AppendImpl(struct NaClSexpCons *cons,
     653               0 :                                 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               0 :   }
     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               0 :   } else {
     668               0 :     result = NaClSexpNodeWrapCons(NaClSexpAppend(
     669               0 :                                       NaClSexpNodeToCons(first),
     670               0 :                                       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               0 :                               struct WorkState *ws) {
     683               0 :   int first = 1;
     684               0 :   int value = 0;
     685               0 :   struct NaClSexpNode *v = NULL;
     686               0 :   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               0 :     } else {
     698               0 :       value *= NaClSexpNodeToInt(v);
     699                 :     }
     700               0 :     NaClSexpFreeNode(v);
     701               0 :   }
     702               0 :   return NaClSexpNodeWrapInt(value);
     703               0 : }
     704                 : 
     705               0 : struct NaClSexpNode *DivImpl(struct NaClSexpCons *cons,
     706               0 :                               struct WorkState *ws) {
     707               0 :   int first = 1;
     708               0 :   int value = 0;
     709               0 :   struct NaClSexpNode *v = NULL;
     710               0 :   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               0 :     } else {
     722               0 :       value /= NaClSexpNodeToInt(v);
     723                 :     }
     724               0 :     NaClSexpFreeNode(v);
     725               0 :   }
     726               0 :   return NaClSexpNodeWrapInt(value);
     727               0 : }
     728                 : 
     729               0 : struct NaClSexpNode *AddImpl(struct NaClSexpCons *cons,
     730               0 :                               struct WorkState *ws) {
     731               0 :   int first = 1;
     732               0 :   int value = 0;
     733               0 :   struct NaClSexpNode *v = NULL;
     734               0 :   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               0 :     } else {
     746               0 :       value += NaClSexpNodeToInt(v);
     747                 :     }
     748               0 :     NaClSexpFreeNode(v);
     749               0 :   }
     750               0 :   return NaClSexpNodeWrapInt(value);
     751               0 : }
     752                 : 
     753               0 : struct NaClSexpNode *SubImpl(struct NaClSexpCons *cons,
     754               0 :                               struct WorkState *ws) {
     755               0 :   int first = 1;
     756               0 :   int value = 0;
     757               0 :   struct NaClSexpNode *v = NULL;
     758               0 :   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               0 :     } else {
     770               0 :       value -= NaClSexpNodeToInt(v);
     771                 :     }
     772               0 :     NaClSexpFreeNode(v);
     773               0 :   }
     774               0 :   return NaClSexpNodeWrapInt(value);
     775               0 : }
     776                 : 
     777             108 : static struct NaClSexpNode *MatcherResult(int64_t matcher_position,
     778             108 :                                           uint64_t matched_bitset) {
     779             324 :   DPRINTF(("MatcherResult(%"NACL_PRId64", 0x%"NACL_PRIx64")\n",
     780                 :            matcher_position, matched_bitset));
     781             108 :   return NaClSexpNodeWrapCons(
     782             216 :       NaClSexpConsCons(NaClSexpNodeWrapInt(matcher_position),
     783             108 :                        NaClSexpConsCons(NaClSexpNodeWrapInt(matched_bitset),
     784                 :                                         NULL)));
     785                 : }
     786                 : 
     787             140 : static int MatchResultExtract(int64_t *matcher_pos,
     788             140 :                               uint64_t *matched_bitset,
     789             140 :                               struct NaClSexpNode *result) {
     790             140 :   struct NaClSexpCons *result_cons;
     791             140 :   if (NULL == result) {
     792              32 :     return 0;
     793                 :   }
     794             324 :   CHECK(NaClSexpConsp(result));
     795             108 :   result_cons = NaClSexpNodeToCons(result);
     796             324 :   CHECK(2 == NaClSexpListLength(result_cons));
     797             324 :   CHECK(NaClSexpIntp(result_cons->car));
     798             324 :   CHECK(NaClSexpIntp(result_cons->cdr->car));
     799             108 :   if (NULL != matcher_pos) {
     800             104 :     *matcher_pos = NaClSexpNodeToInt(result_cons->car);
     801             104 :   }
     802             108 :   if (NULL != matched_bitset) {
     803             108 :     *matched_bitset = NaClSexpNodeToInt(result_cons->cdr->car);
     804             108 :   }
     805             108 :   return 1;
     806             140 : }
     807                 : 
     808              16 : struct NaClSexpNode *EpsilonMatcherImpl(struct NaClSexpCons *cons,
     809              16 :                                         struct WorkState *ws) {
     810              16 :   if (NaClSexpListLength(cons) != 1) {
     811               0 :     crash_cons(ws, "epsilon built-in should have no arguments", cons);
     812               0 :   }
     813              48 :   DPRINTF(("Epsilon\n"));
     814                 :   /*
     815                 :    * Check that the event queue is empty.
     816                 :    */
     817              16 :   if (EventQueueLengthMu(ws) == 0) {
     818              48 :     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              16 : }
     824                 : 
     825               4 : struct NaClSexpNode *PrognImpl(struct NaClSexpCons *cons,
     826               4 :                                struct WorkState *ws) {
     827               4 :   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              16 :     } else {
     836               0 :       crash_node(ws, "Not a list", node);
     837                 :     }
     838              16 :   }
     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              60 :                                  enum WorkType type,
     855              60 :                                  int desc) {
     856              60 :   struct WorkItem *wi = malloc(sizeof *wi);
     857             180 :   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             180 :   DPRINTF(("WorkOnWorkItem: entered\n"));
     868             120 :   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             180 :   DPRINTF(("WorkOnWorkItem: done\n"));
     877              60 :   free(wi);
     878              60 : }
     879                 : 
     880              60 : struct NaClSexpNode *LockUnlock(struct NaClSexpCons *cons,
     881              60 :                                 struct WorkState *ws,
     882              60 :                                 enum WorkType op) {
     883              60 :   struct NaClSexpNode *p;
     884              60 :   int actor_thread;
     885              60 :   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             120 :   PutWork(ws, actor_thread,
     910              60 :           WorkOnWorkItem, WorkItemFactory(ws, op, file_desc));
     911              60 :   return NULL;
     912              60 : }
     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              30 :                                 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              87 : struct NaClSexpNode *EventQueueEventMatcher(struct NaClSexpCons *cons,
     933              87 :                                             struct WorkState *ws,
     934              87 :                                             enum EventType expected_event) {
     935              87 :   struct NaClSexpNode *n;
     936              87 :   int expected_thread_id;
     937              87 :   int expected_file_id;
     938              87 :   size_t list_pos = 0;
     939              87 :   struct NaClSexpNode *p = NULL;
     940              87 :   struct Event *event_entry;
     941                 : 
     942              87 :   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              87 :   n = Eval(cons->cdr->car, ws);
     949              87 :   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              87 :   expected_thread_id = NaClSexpNodeToInt(n);
     957              87 :   NaClSexpFreeNode(n);
     958              87 :   n = Eval(cons->cdr->cdr->car, ws);
     959              87 :   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              87 :   expected_file_id = NaClSexpNodeToInt(n);
     967              87 :   NaClSexpFreeNode(n);
     968                 : 
     969             261 :   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             205 :   for (event_entry = ws->q;
     974                 :        NULL != event_entry;
     975              31 :        ++list_pos, event_entry = event_entry->next) {
     976             247 :     if (event_entry->type == expected_event &&
     977                 :         event_entry->thread_id == expected_thread_id &&
     978                 :         event_entry->file_id == expected_file_id) {
     979              72 :       if (list_pos > 8 * sizeof(int64_t)) {
     980               0 :         crash_cons(ws, "event queue too deep", cons);
     981               0 :       }
     982              72 :       p = MatcherResult(-1, 1 << list_pos);
     983              72 :       break;
     984                 :     }
     985              31 :   }
     986              87 :   return p;
     987              87 : }
     988                 : 
     989              45 : struct NaClSexpNode *LockedMatcherImpl(struct NaClSexpCons *cons,
     990              45 :                                        struct WorkState *ws) {
     991                 :   /*
     992                 :    * (locked t f) -- look for event (kLocked t f) in event queue.
     993                 :    */
     994              45 :   return EventQueueEventMatcher(cons, ws, kLocked);
     995                 : }
     996                 : 
     997              42 : struct NaClSexpNode *UnlockedMatcherImpl(struct NaClSexpCons *cons,
     998              42 :                                          struct WorkState *ws) {
     999                 :   /*
    1000                 :    * (unlocked t f) -- look for event (kUnlocked t f) in event queue.
    1001                 :    */
    1002              42 :   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               4 :                               struct WorkState *ws) {
    1016               4 :   struct NaClSexpNode *n;
    1017               4 :   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               4 : }
    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             108 : static int CheckMatchers(struct WorkState *ws,
    1112             108 :                          struct NaClSexpCons *matcher_list,
    1113             108 :                          struct SymbolTable **matcher_entries) {
    1114             108 :   size_t mx;
    1115             108 :   size_t ix;
    1116             108 :   struct NaClSexpCons *matchers;
    1117             108 :   struct NaClSexpNode *cur;
    1118             108 :   struct NaClSexpCons *submatcher;
    1119             108 :   char const *submatcher_name;
    1120                 : 
    1121             361 :   for (mx = 0, matchers = matcher_list;
    1122                 :        NULL != matchers;
    1123             145 :        ++mx, matchers = matchers->cdr) {
    1124             145 :     cur = matchers->car;
    1125             145 :     if (!NaClSexpConsp(cur)) {
    1126               0 :       error_node(ws, "all: submatcher not a list", cur);
    1127               0 :       return 0;
    1128                 :     }
    1129             145 :     submatcher = NaClSexpNodeToCons(cur);
    1130             290 :     if (NULL == submatcher || !NaClSexpTokenp(submatcher->car)) {
    1131               0 :       error_node(ws, "all: submatcher not a matcher", cur);
    1132               0 :       return 0;
    1133                 :     }
    1134             145 :     submatcher_name = NaClSexpNodeToToken(submatcher->car);
    1135            6026 :     for (ix = 0; ix < sizeof g_symtab/sizeof g_symtab[0]; ++ix) {
    1136            3013 :       if (!strcmp(g_symtab[ix].name, submatcher_name)) {
    1137                 :         /* found! */
    1138             145 :         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             145 :         matcher_entries[mx] = &g_symtab[ix];
    1144             145 :         break;
    1145                 :       }
    1146            2868 :     }
    1147             145 :     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             145 :   }
    1152             108 :   return 1;
    1153             108 : }
    1154                 : 
    1155              29 : struct NaClSexpNode *AllMatcherImpl(struct NaClSexpCons *cons,
    1156              29 :                                     struct WorkState *ws) {
    1157              29 :   int64_t match_index = -1;
    1158              29 :   uint64_t match_pos = 0;
    1159              29 :   size_t num_matchers = NaClSexpListLength(cons) - 1;
    1160              29 :   size_t mx;
    1161              29 :   struct NaClSexpCons *matchers;
    1162              29 :   struct NaClSexpNode *cur;
    1163              29 :   struct SymbolTable **matcher_entries = NULL;
    1164              29 :   struct NaClSexpNode *match_result;
    1165              29 :   int64_t submatch_index;
    1166              29 :   uint64_t submatch_pos;
    1167                 : 
    1168              29 :   if (gVerbosity > 2) {
    1169               0 :     printf("AllMatcherImpl: %"NACL_PRIdS" submatchers\n", num_matchers);
    1170               0 :   }
    1171              29 :   if (0 == num_matchers) {
    1172               0 :     error_cons(ws, "all must have at least one sub-matcher", cons);
    1173               0 :     return NULL;
    1174                 :   }
    1175                 :   matcher_entries = (struct SymbolTable **)
    1176              29 :       malloc(num_matchers * sizeof *matcher_entries);
    1177              29 :   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             102 :   for (mx = 0, matchers = cons->cdr;
    1187                 :        NULL != matchers;
    1188              44 :        ++mx, matchers = matchers->cdr) {
    1189              57 :     cur = matchers->car;
    1190              57 :     match_result = (*matcher_entries[mx]->fn)(NaClSexpNodeToCons(cur), ws);
    1191                 : 
    1192              57 :     if (gVerbosity > 2) {
    1193               0 :       printf("submatcher "); NaClSexpPrintNode(stdout,cur); printf(" --> ");
    1194               0 :       NaClSexpPrintNode(stdout, match_result);
    1195               0 :       printf("\n");
    1196               0 :     }
    1197                 : 
    1198              57 :     if (!MatchResultExtract(&submatch_index, &submatch_pos, match_result)) {
    1199              39 :       DPRINTF(("submatcher failed\n"));
    1200              13 :       NaClSexpFreeNode(match_result);
    1201              13 :       free(matcher_entries);
    1202              13 :       return NULL;
    1203                 :     }
    1204              44 :     NaClSexpFreeNode(match_result);
    1205                 : 
    1206              44 :     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              88 :     if (-1 == match_index && -1 != submatch_index) {
    1218               2 :       match_index = submatch_index;
    1219               2 :     }
    1220              44 :     match_pos |= submatch_pos;
    1221              44 :   }
    1222              16 :   free(matcher_entries);
    1223              16 :   return MatcherResult(match_index, match_pos);
    1224              29 : }
    1225                 : 
    1226               8 : struct NaClSexpNode *AnyMatcherImpl(struct NaClSexpCons *cons,
    1227               8 :                                     struct WorkState *ws) {
    1228               8 :   uint64_t submatch_pos = 0;
    1229               8 :   size_t num_matchers = NaClSexpListLength(cons) - 1;
    1230               8 :   size_t mx;
    1231               8 :   struct NaClSexpCons *matchers;
    1232               8 :   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               0 :   }
    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                 :   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               0 :     }
    1264                 : 
    1265              12 :     if (MatchResultExtract((int64_t *) NULL, &submatch_pos, match_result)) {
    1266              12 :       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               8 :   }
    1273               8 :   free(matcher_entries);
    1274               8 :   return match_result;
    1275               8 : }
    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              71 : struct NaClSexpNode *MatchMatcherImpl(struct NaClSexpCons *cons,
    1286              71 :                                       struct WorkState *ws) {
    1287                 : 
    1288              71 :   struct SymbolTable *matcher[1];
    1289              71 :   struct NaClSexpNode *result;
    1290              71 :   int64_t which_match;
    1291              71 :   uint64_t match_set;
    1292                 : 
    1293             213 :   DPRINTF(("MatchMatcherImpl\n"));
    1294              71 :   if (NaClSexpListLength(cons) != 2) {
    1295               0 :     error_cons(ws, "match takes a single matcher as argument", cons);
    1296               0 :     return NULL;
    1297                 :   }
    1298              71 :   if (!CheckMatchers(ws, cons->cdr, matcher)) {
    1299               0 :     error_cons(ws, "match argument should be a matcher", cons);
    1300               0 :     return NULL;
    1301                 :   }
    1302              71 :   if (!NaClSexpConsp(cons->cdr->car)) {
    1303               0 :     error_cons(ws, "match argument not a list", cons);
    1304               0 :     return NULL;
    1305                 :   }
    1306              71 :   result = (*matcher[0]->fn)(NaClSexpNodeToCons(cons->cdr->car), ws);
    1307              71 :   if (!MatchResultExtract(&which_match, &match_set, result)) {
    1308              33 :     DPRINTF(("did not match\n"));
    1309              11 :     NaClSexpFreeNode(result);
    1310              11 :     result = NULL;
    1311              71 :   } 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               0 :   } else {
    1317             180 :     DPRINTF(("matched all events\n"));
    1318              60 :     EventQueueDiscardMu(ws);
    1319              60 :     NaClSexpFreeNode(result);
    1320              60 :     result = NaClSexpNodeWrapInt(which_match);
    1321                 :   }
    1322              71 :   return result;
    1323              71 : }
    1324                 : 
    1325             446 : struct NaClSexpNode *Eval(struct NaClSexpNode *n, struct WorkState *ws) {
    1326             446 :   struct NaClSexpCons *cons;
    1327             446 :   size_t ix;
    1328             446 :   struct NaClSexpNode *car;
    1329             446 :   char const *token;
    1330             446 :   struct NaClSexpNode *val = NULL;
    1331             446 :   struct timespec timeout_time;
    1332             446 :   int wait_for_more;
    1333             446 :   int last_match;
    1334                 : 
    1335             446 :   if (!NaClSexpConsp(n)) {
    1336                 :     /* for now; symbol table lookup when there are symbols with values */
    1337             294 :     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               0 :   }
    1350             152 :   token = NaClSexpNodeToToken(car);
    1351             456 :   DPRINTF(("function %s\n", token));
    1352            4476 :   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              92 :       } 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             180 :         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             131 :         while (!last_match) {
    1396             213 :           DPRINTF(("Checking EventQueueLengthMu -> %d\n",
    1397                 :                    (int) EventQueueLengthMu(ws)));
    1398              85 :           if (EventQueueLengthMu(ws) == 0 || wait_for_more) {
    1399             204 :             DPRINTF(("Waiting for event\n"));
    1400              68 :             if (!WaitForEventMu(ws, &timeout_time)) {
    1401              48 :               DPRINTF(("Event timed out\n"));
    1402              16 :               last_match = 1;
    1403              16 :             }
    1404              68 :           }
    1405                 :           /*
    1406                 :            * Run matcher on event queue; matchers are invoked while
    1407                 :            * holding the lock.
    1408                 :            */
    1409              71 :           val = (*g_symtab[ix].fn)(cons, ws);
    1410              71 :           if (gVerbosity > 1) {
    1411               0 :             printf("matcher returned: ");
    1412               0 :             NaClSexpPrintNode(stdout,val);
    1413               0 :             putchar('\n');
    1414               0 :           }
    1415              71 :           if (NULL != val) {
    1416                 :             /* successful match! */
    1417              60 :             break;
    1418                 :           }
    1419                 : 
    1420              11 :           if (last_match) {
    1421               0 :             error_cons(ws, "match failed; timed out", cons);
    1422               0 :             val = NULL;
    1423               0 :             break;
    1424                 :           }
    1425              33 :           DPRINTF(("match failed, waiting for more events\n"));
    1426              11 :           wait_for_more = 1;
    1427              11 :         }
    1428                 : 
    1429              60 :         pthread_mutex_unlock(&ws->mu);
    1430                 :       }
    1431             152 :       return val;
    1432                 :     }
    1433            2086 :   }
    1434               0 :   fprintf(stderr, "No such function: %s\n", token);
    1435               0 :   return NULL;
    1436             446 : }
    1437                 : 
    1438               8 : void ReadEvalPrintLoop(struct NaClSexpIo *input,
    1439               8 :                        int interactive,
    1440               8 :                        int verbosity,
    1441               8 :                        size_t epsilon_delay_nanos,
    1442               8 :                        struct NaClFileLockTestInterface *test_if) {
    1443               8 :   struct WorkState ws;
    1444               8 :   struct NaClSexpNode *n;
    1445               8 :   struct NaClSexpNode *val;
    1446               8 :   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               0 :   }
    1453               8 :   gEpsilonDelayNanos = epsilon_delay_nanos;
    1454                 : 
    1455               8 :   err = pthread_key_create(&gNaClTestTsdKey, (void (*)(void *)) NULL);
    1456              24 :   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               0 :     }
    1465                 : 
    1466             120 :     if (gVerbosity) {
    1467               0 :       NaClSexpPrintNode(stdout, n);
    1468               0 :       printf(" => ");
    1469               0 :     }
    1470             120 :     val = Eval(n, &ws);
    1471             120 :     NaClSexpFreeNode(n);
    1472             120 :     NaClSexpPrintNode(stdout, val);
    1473             120 :     printf("\n");
    1474             120 :     NaClSexpFreeNode(val);
    1475             120 :   }
    1476               8 :   AnnounceEndOfWork(&ws);
    1477               8 :   ReapThreads(&ws);
    1478               8 :   WorkStateDtor(&ws);
    1479               8 : }

Generated by: LCOV version 1.7