/*
* This file is part of flex.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE.
*/
%{
/* A scanner file to build "scanner.c".
Input language is any text made of spaces, newlines, and alphanumerics.
We create N_THREADS number of threads. Each thread has it's own scanner.
Each thread selects one of the files specified in ARGV, scans it, then
closes it. This is repeated N_SCANS number of times for each thread.
The idea is to press the scanner to break under threads.
If we see "Scanner Jammed", then we know
*/
#include <stdio.h>
#include <stdlib.h>
#include <config.h>
#ifdef HAVE_PTHREAD_H
#include <pthread.h>
#endif
/* A naive test for segfaults when accessing yytext. */
static int process_text(char* s, yyscan_t scanner);
%}
%option 8bit prefix="test"
%option nounput nomain nodefault noinput
%option yywrap
%option reentrant
%option warn
/* Arbitrary states.*/
%x STATE_1
%x STATE_2
%%
#define NUMBER 200
#define WORD 201
<INITIAL>[[:digit:]]+ { BEGIN(STATE_1); process_text(yytext,yyscanner); return NUMBER; }
<INITIAL>[[:alpha:]]+ { BEGIN(STATE_2); process_text(yytext,yyscanner); return WORD; }
<STATE_1>[[:alpha:]]+ { BEGIN(0); process_text(yytext,yyscanner); return WORD; }
<STATE_1>[[:digit:]]+ { BEGIN(0); process_text(yytext,yyscanner); return NUMBER; }
<STATE_2>[[:alpha:]]+ { BEGIN(0); process_text(yytext,yyscanner); return WORD; }
<STATE_2>[[:digit:]]+ { BEGIN(0); process_text(yytext,yyscanner); return NUMBER; }
<INITIAL,STATE_1,STATE_2>" "|\t|\r|\n { process_text(yytext,yyscanner); }
<INITIAL,STATE_1,STATE_2>[^[:alnum:][:space:]\t\r\n] {
/*fprintf(stderr,"*** Error: bad input char '%c'.\n", yytext[0]); */
yyterminate();
}
<INITIAL,STATE_1,STATE_2>[[:space:]\r\n]+ { }
%%
int testwrap( yyscan_t scanner) {
(void)scanner;
return 1;
}
static int process_text(char* s, yyscan_t scanner)
{
(void)scanner;
return (int)(*s) + (int) *(s + testget_leng(scanner)-1);
}
int main(int ARGC, char *ARGV[]);
#ifndef HAVE_PTHREAD_H
int main (int ARGC, char *ARGV[]) {
puts(
"TEST ABORTED because pthread library not available \n"
"-- This is expected on some systems. It is not a flex error.");
/* Exit status for a skipped test */
return 77;
}
#else
#define N_THREADS 4
#define N_SCANS 20
/* Each thread selects the next file to scan in round-robin fashion.
If there are less files than threads, some threads may block. */
static pthread_mutex_t next_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t go_ahead = PTHREAD_MUTEX_INITIALIZER;
static int n_files, next_file;
static pthread_mutex_t *file_locks;
static char **filenames;
static void * thread_func ( void* arg )
{
int i;
(void)arg;
/* Wait for go-ahead. */
pthread_mutex_lock( &go_ahead);
pthread_mutex_unlock(&go_ahead);
for( i =0 ; i < N_SCANS ; i++ )
{
int next;
yyscan_t scanner;
FILE * fp;
pthread_mutex_lock ( &next_lock );
next = (next_file++) % n_files;
pthread_mutex_unlock ( &next_lock );
pthread_mutex_lock ( &file_locks[ next ] );
testlex_init( &scanner );
/*printf("Scanning file %s #%d\n",filenames[next],i); fflush(stdout); */
if((fp = fopen(filenames[next],"r"))==NULL) {
perror("fopen");
return NULL;
}
testset_in(fp,scanner);
while( testlex( scanner) != 0)
{
}
fclose(fp);
testlex_destroy(scanner);
pthread_mutex_unlock ( &file_locks[ next ] );
}
return NULL;
}
int main (int ARGC, char *ARGV[])
{
int i;
pthread_t threads[N_THREADS];
if( ARGC < 2 ) {
fprintf(stderr,"*** Error: No filenames specified.\n");
exit(-1);
}
/* Allocate and initialize the locks. One for each filename in ARGV. */
file_locks = malloc((size_t) (ARGC-1) * sizeof(pthread_mutex_t));
for( i = 0; i < ARGC-1; i++)
pthread_mutex_init( &file_locks[i], NULL );
n_files = ARGC -1;
filenames = ARGV + 1;
next_file = 0;
/* prevent threads from starting until all threads have been created. */
pthread_mutex_lock(&go_ahead);
/* Create N threads then wait for them. */
for(i =0; i < N_THREADS ; i++ ) {
if( pthread_create( &threads[i], NULL, thread_func, NULL) != 0)
{
fprintf(stderr, "*** Error: pthread_create failed.\n");
exit(-1);
}
printf("Created thread %d.\n",i); fflush(stdout);
}
/* Tell threads to begin. */
pthread_mutex_unlock(&go_ahead);
for(i =0; i < N_THREADS ; i++ ) {
pthread_join ( threads[i], NULL );
printf("Thread %d done.\n", i ); fflush(stdout);
}
for( i = 0; i < ARGC-1; i++)
pthread_mutex_destroy( &file_locks[i] );
pthread_mutex_destroy( &next_lock );
pthread_mutex_destroy( &go_ahead );
free( file_locks );
printf("TEST RETURNING OK.\n");
return 0;
}
#endif /* HAVE_PTHREAD_H */