1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
|
// From forum question about a homework assignment
// http://www.cplusplus.com/forum/general/242972
//
// The assignment is given in the PDF at
// http://www2.cs.uh.edu/~acheng/hw1.f18.pdf
//
// This is -a- possible solution, knowing nothing more than what is given in that PDF and
// additional commentary by the student.
//
// The assignment's goal appears to be to create a static dependency tree (directed acyclic graph)
// by parsing the unfriendly argv[0] input and then instantiating it as subprocesses (nodes) and
// pipes (edges) and reporting the resulting calculations on stdout.
//
// The stated learning objective(s) is to understand how concurrent processes synchronize
// using pipes.
//
// IM(NS)HO, however, it failed that. The effective learning result is how to parse a dependency
// graph. There is no requirement to observe how I/O wait states affect the flow of data through
// the tree. If you wish to it here, change the following line to have a value of '1', compile,
// and execute the binary a few times.
#define SHOW_DATA_FLOW 0
//-------------------------------------------------------------------------------------------------
// REQUIRED INCLUDES
//-------------------------------------------------------------------------------------------------
#define _POSIX_C_SOURCE 200809L
#include <ctype.h>
#include <iso646.h>
#include <math.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <unistd.h>
//-------------------------------------------------------------------------------------------------
// TOTALLY UNNECESSARY STUFF TO SHOW THE DATA FLOW
//-------------------------------------------------------------------------------------------------
#if SHOW_DATA_FLOW
#define DECL char ivars[ 10 ][ 10 ]; char ovars[ 10 ][ 10 ];
#define SEND( me, x, to ) printf( "%-6s sends %12g to %s\n", me, x, to );
#define GOT( me, x, from ) printf( "%-6s got %12g from %s\n", me, x, from );
#define DONE( me ) printf( "%-6s done\n", me );
#define LINK( output, input, iname ) \
strcpy( input->name, iname ); \
strcpy( output->ovars[ output->noutputs ], input ->name ); \
strcpy( input ->ivars[ input ->ninputs ], output->name );
#else
#define DECL
#define SEND( me, x, to )
#define GOT( me, x, from )
#define DONE( me )
#define LINK( output, input, iname )
#endif
//-------------------------------------------------------------------------------------------------
// USAGE AND ERROR REPORTING
//-------------------------------------------------------------------------------------------------
const char* basename( const char* path )
{
const char* result = strchr( path, '\0' );
while (result != path)
if (strchr( "/\\", *--result))
return result + 1;
return result;
}
void usage( const char* argv0 )
{
printf( "%s%s%s",
"usage:\n"
" ", basename( argv0 ), " DATAGRAPHFILE < INPUTFILE\n"
"\n"
"See hw1.f18.pdf for details.\n\n"
);
exit( 0 );
}
void error( int line_number, const char* message, ... )
{
va_list args;
fprintf( stderr, "[%d] ", line_number );
va_start( args, message );
vfprintf( stderr, message, args );
fprintf( stderr, "\n" );
va_end( args );
exit( 1 );
}
#define error( ... ) error( __LINE__, __VA_ARGS__ )
//-------------------------------------------------------------------------------------------------
// I/O UTILITIES
//-------------------------------------------------------------------------------------------------
bool readline( FILE* f, char* s, int n, char delim )
{
int c, nread = 0;
while (true)
{
c = fgetc( f );
if ((c == EOF) or (c == delim)) break;
*s++ = c;
if (++nread == n) error( "input line too long" );
}
*s = '\0';
return !!nread;
}
double read_double( FILE* f )
{
double result;
if (!fscanf( f, "%lf", &result )) result = NAN;
return result;
}
int skip( FILE* f, const char* chars_to_skip )
{
int c;
while (true)
{
c = fgetc( f );
if (c == EOF) error( "unexpected EOF" );
if (strchr( chars_to_skip, c ) == NULL) break;
}
ungetc( c, f );
return c;
}
//-------------------------------------------------------------------------------------------------
// PIPE WRAPPER
//-------------------------------------------------------------------------------------------------
typedef struct
{
FILE* reader;
FILE* writer;
}
Pipe;
Pipe create_pipe()
{
int fd[ 2 ];
Pipe result = { NULL, NULL };
if (pipe( fd ) == 0)
{
result.reader = fdopen( fd[ 0 ], "r" );
result.writer = fdopen( fd[ 1 ], "w" );
}
else error( "pipe() failed!" );
if (!result.reader or !result.writer)
error( "could not create pipe" );
return result;
}
//-------------------------------------------------------------------------------------------------
// VAR TYPE
//-------------------------------------------------------------------------------------------------
typedef enum { input_var, internal_var, write_var } vartype;
const char* vartype_names[] = { "input_var", "internal_var", "write" };
typedef struct var
{
char name[ 10 ]; DECL
vartype type;
FILE* inputs [ 10 ]; // read end of a pipe
int ninputs;
FILE* outputs[ 10 ]; // write end of a pipe
int noutputs;
// input_var
double value;
// internal_var
char mathops[ 10 ];
}
var;
var create_var( const char* name, vartype type )
{
var result;
strncpy( result.name, name, 9 );
result.name[ 9 ] = '\0';
result.type = type;
result.ninputs = 0;
result.noutputs = 0;
return result;
}
var* find_var( const char* name, var* vars, int nvars )
{
while (nvars--)
if (strcmp( vars[ nvars ].name, name ) == 0)
return vars + nvars;
return NULL;
}
//-------------------------------------------------------------------------------------------------
// CHILD PROCESSES
//-------------------------------------------------------------------------------------------------
int input_var_proc( var* v )
//
// An 'input_var' proces simply prints its stored value to all of its output pipes and terminates
//
{
while (v->noutputs--)
{ SEND( v->name, v->value, v->ovars[ v->noutputs ] )
fprintf( v->outputs[ v->noutputs ], "%g\n", v->value ); // '\n' is important!
} DONE( v->name )
return 0;
}
int internal_var_proc( var* v )
//
// An 'internal_var' process reads each input pipe, in order, updating value as indicated.
// Once done it outputs that value to all its output pipes, so we just reuse the 'input_var' proc.
//
{
v->value = 0.0;
for (int n = 0; n < v->ninputs; n++)
{
double value = read_double( v->inputs[ n ] ); GOT( v->name, value, v->ivars[ n ] )
switch (v->mathops[ n ])
{
case ' ': v->value = value; break;
case '+': v->value += value; break;
case '-': v->value -= value; break;
case '*': v->value *= value; break;
case '/': v->value /= value; break;
default: v->value = NAN;
}
}
return input_var_proc( v );
}
| |