traffic.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  1. /*
  2. * traffic.c
  3. *
  4. * 09-2015: KMS: created
  5. *
  6. */
  7. /*
  8. * CS350 Students Note!
  9. *
  10. * You may not modify the code in this file in any way!
  11. *
  12. */
  13. /*
  14. *
  15. * Includes
  16. *
  17. */
  18. #include <types.h>
  19. #include <lib.h>
  20. #include <test.h>
  21. #include <clock.h>
  22. #include <thread.h>
  23. #include <synch.h>
  24. #include <synchprobs.h>
  25. #include <lamebus/ltimer.h>
  26. #include <kern/errno.h>
  27. /*
  28. * Problem parameters
  29. *
  30. * Values for these parameters are set by the main driver
  31. * function based on the problem parameters
  32. * that are passed in from the kernel menu command or
  33. * kernel command line.
  34. *
  35. * These are only ever modified by one thread, when the simulation
  36. * is being initialized, so they do not need to be volatile.
  37. */
  38. #define MAX_THREADS 10
  39. static int NumIterations = 100; // number of vehicle arrivals per thread
  40. static int NumThreads = 10; // number of concurrent simulation threads
  41. static int InterArrivalTime = 1; // time between vehicle arrivals
  42. static int ServiceTime = 1; // time in the intersection
  43. static int DirectionBias = 0; // 0 = unbiased, 1 = biased
  44. /*
  45. * Once the main driver function has created the
  46. * simulation threads, it uses this semaphore to block until all of the
  47. * simulation threads are finished.
  48. */
  49. static struct semaphore *SimulationWait;
  50. /*
  51. *
  52. * shared simulation state
  53. *
  54. * note: this is state should be used only by the
  55. * functions in this file, hence the static declarations
  56. *
  57. */
  58. /* note: this typedef is defined here because it is used only
  59. within this file, and is not exposed externally */
  60. typedef struct Vehicles
  61. {
  62. Direction origin;
  63. Direction destination;
  64. } Vehicle;
  65. /* vehicles in the intersection */
  66. /* note: this is an array of volatile pointers */
  67. static Vehicle * volatile vehicles[MAX_THREADS];
  68. /* semaphore to protect vehicles array */
  69. static struct semaphore *mutex;
  70. /* performance statistics, one set per origin direction */
  71. static volatile time_t total_wait_secs[4];
  72. static volatile uint32_t total_wait_nsecs[4];
  73. static volatile int wait_count[4];
  74. static volatile time_t max_wait_secs[4];
  75. static volatile uint32_t max_wait_nsecs[4];
  76. /* mutex to provide mutual exclusion to performance stats */
  77. static struct semaphore *perf_mutex;
  78. /* number of milliseconds per timer tick (LT_GRANULARITY is in usec) */
  79. #define TICK_MILLISEC (LT_GRANULARITY/1000)
  80. /* simulation start and end time */
  81. time_t start_sec, end_sec;
  82. uint32_t start_nsec, end_nsec;
  83. /* bias direction, for arrival biasing */
  84. Direction heavy_direction;
  85. /* functions defined and used internally */
  86. static void initialize_state(void);
  87. static void cleanup_state(void);
  88. static void print_direction(Direction d);
  89. static void print_perf_stats(void);
  90. static void vehicle_simulation(void *ptr, unsigned long thread_num);
  91. static bool right_turn(Vehicle *v);
  92. static void check_constraints(int thread_num);
  93. /*
  94. * print_direction()
  95. *
  96. * Purpose:
  97. * print a direction
  98. *
  99. * Arguments:
  100. * a direction
  101. *
  102. * Returns:
  103. * nothing
  104. */
  105. static void
  106. print_direction(Direction d) {
  107. switch (d)
  108. {
  109. case north:
  110. kprintf("N");
  111. break;
  112. case east:
  113. kprintf("E");
  114. break;
  115. case south:
  116. kprintf("S");
  117. break;
  118. case west:
  119. kprintf("W");
  120. break;
  121. }
  122. }
  123. /*
  124. * choose_direction()
  125. *
  126. * Purpose:
  127. * randomly choose a direction, with or without bias
  128. *
  129. * Arguments:
  130. * none
  131. *
  132. * Returns:
  133. * a direction
  134. */
  135. static Direction
  136. choose_direction(void) {
  137. int x;
  138. if (DirectionBias) {
  139. /* this gives 70% arrivals from the heavy direction,
  140. and 10% from each of the other directions */
  141. x = random()%10;
  142. if (x >= 4) {
  143. /* 60% from the heavy direction */
  144. return heavy_direction;
  145. }
  146. else {
  147. /* otherwise choose any direction (including heavy) with equal prob */
  148. return x;
  149. }
  150. }
  151. else {
  152. return random()%4;
  153. }
  154. }
  155. /*
  156. * print_perf_stats()
  157. *
  158. * Purpose:
  159. * print the performance statistics
  160. *
  161. * Arguments:
  162. * none
  163. *
  164. * Returns:
  165. * nothing
  166. */
  167. static void
  168. print_perf_stats(void) {
  169. int i;
  170. int max_allowed_ms;
  171. int wait_msecs,mean_wait_msecs,max_wait_msecs;
  172. int total_wait_msecs = 0;
  173. int total_count = 0;
  174. int sim_msec;
  175. time_t run_sec;
  176. uint32_t run_nsec;
  177. /* report maximum permitted wait time */
  178. /* arbitrary standard: we are willing to wait one service time
  179. for per other thread in the simulation */
  180. max_allowed_ms = (NumThreads-1)*ServiceTime*TICK_MILLISEC;
  181. kprintf("max permitted wait:\t%d.%03d seconds\n",
  182. max_allowed_ms/1000,max_allowed_ms%1000);
  183. /* print average wait time for each direction of origin */
  184. for(i=0;i<4;i++) {
  185. print_direction((Direction)i);
  186. kprintf(":\t");
  187. if (wait_count[i] > 0) {
  188. wait_msecs = (total_wait_secs[i]*1000+total_wait_nsecs[i]/1000000);
  189. total_wait_msecs += wait_msecs;
  190. // some rounding error here, in millisecond range
  191. mean_wait_msecs = wait_msecs/wait_count[i];
  192. total_count += wait_count[i];
  193. max_wait_msecs = max_wait_secs[i]*1000+max_wait_nsecs[i]/1000000;
  194. kprintf("%d vehicles, average wait %d.%03d seconds, max wait %d.%03d seconds\n",
  195. wait_count[i], mean_wait_msecs/1000,mean_wait_msecs%1000,
  196. max_wait_msecs/1000,max_wait_msecs%1000);
  197. } else {
  198. kprintf("0 vehicles, average wait 0.000 seconds, max wait 0.000 seconds\n");
  199. }
  200. }
  201. /* then the average wait time for all vehicles */
  202. if (total_count > 0) {
  203. kprintf("all:\t%d vehicles, average %d.%03d seconds waiting\n",total_count,
  204. (total_wait_msecs/total_count)/1000,
  205. (total_wait_msecs/total_count)%1000);
  206. } else{
  207. kprintf("all:\t0 vehicles, average 0.000 seconds waiting\n");
  208. }
  209. /* finally, overall simulation run-time and throughput */
  210. getinterval(start_sec,start_nsec,end_sec,end_nsec,&run_sec,&run_nsec);
  211. sim_msec = run_sec*1000;
  212. sim_msec += run_nsec/1000000;
  213. kprintf("Simulation: %d.%03d seconds, %d vehicles\n",
  214. sim_msec/1000,
  215. sim_msec%1000,
  216. total_count);
  217. }
  218. /*
  219. * bool right_turn()
  220. *
  221. * Purpose:
  222. * predicate that checks whether a vehicle is making a right turn
  223. *
  224. * Arguments:
  225. * a pointer to a Vehicle
  226. *
  227. * Returns:
  228. * true if the vehicle is making a right turn, else false
  229. *
  230. * Note: written this way to avoid a dependency on the specific
  231. * assignment of numeric values to Directions
  232. */
  233. bool
  234. right_turn(Vehicle *v) {
  235. KASSERT(v != NULL);
  236. if (((v->origin == west) && (v->destination == south)) ||
  237. ((v->origin == south) && (v->destination == east)) ||
  238. ((v->origin == east) && (v->destination == north)) ||
  239. ((v->origin == north) && (v->destination == west))) {
  240. return true;
  241. } else {
  242. return false;
  243. }
  244. }
  245. /*
  246. * check_constraints()
  247. *
  248. * Purpose:
  249. * checks whether the entry of a vehicle into the intersection violates
  250. * any synchronization constraints. Causes a kernel panic if so, otherwise
  251. * returns silently
  252. *
  253. * Arguments:
  254. * number of the simulation thread that moved the vehicle into the intersection
  255. *
  256. * Returns:
  257. * nothing
  258. */
  259. void
  260. check_constraints(int thread_num) {
  261. int i;
  262. KASSERT(thread_num < NumThreads);
  263. /* compare newly-added vehicle to each other vehicles in in the intersection */
  264. for(i=0;i<NumThreads;i++) {
  265. if ((i==thread_num) || (vehicles[i] == NULL)) continue;
  266. /* no conflict if both vehicles have the same origin */
  267. if (vehicles[i]->origin == vehicles[thread_num]->origin) continue;
  268. /* no conflict if vehicles go in opposite directions */
  269. if ((vehicles[i]->origin == vehicles[thread_num]->destination) &&
  270. (vehicles[i]->destination == vehicles[thread_num]->origin)) continue;
  271. /* no conflict if one makes a right turn and
  272. the other has a different destination */
  273. if ((right_turn(vehicles[i]) || right_turn(vehicles[thread_num])) &&
  274. (vehicles[thread_num]->destination != vehicles[i]->destination)) continue;
  275. /* Houston, we have a problem! */
  276. /* print info about the two vehicles found to be in conflict,
  277. then cause a kernel panic */
  278. kprintf("Vehicle A: ");
  279. print_direction(vehicles[i]->origin);
  280. kprintf("->");
  281. print_direction(vehicles[i]->destination);
  282. kprintf("\n");
  283. kprintf("Vehicle B: ");
  284. print_direction(vehicles[thread_num]->origin);
  285. kprintf("->");
  286. print_direction(vehicles[thread_num]->destination);
  287. kprintf("\n");
  288. panic("intersection synchronization constraint violation!\n");
  289. }
  290. }
  291. /*
  292. * initialize_state()
  293. *
  294. * Purpose:
  295. * initializes simulation
  296. *
  297. * Arguments:
  298. * none
  299. *
  300. * Returns:
  301. * nothing
  302. */
  303. static void
  304. initialize_state(void)
  305. {
  306. int i;
  307. for(i=0;i<MAX_THREADS;i++) {
  308. vehicles[i] = (Vehicle * volatile)NULL;
  309. }
  310. for(i=0;i<4;i++) {
  311. total_wait_secs[i] = total_wait_nsecs[i] = wait_count[i] = 0;
  312. max_wait_secs[i] = max_wait_nsecs[i] = 0;
  313. }
  314. mutex = sem_create("Vehicle Mutex",1);
  315. if (mutex == NULL) {
  316. panic("could not create vehicle mutex semaphore\n");
  317. }
  318. perf_mutex = sem_create("PerfMutex",1);
  319. if (perf_mutex == NULL) {
  320. panic("could not create perf_mutex semaphore\n");
  321. }
  322. SimulationWait = sem_create("SimulationWait",0);
  323. if (SimulationWait == NULL) {
  324. panic("could not create SimulationWait semaphore\n");
  325. }
  326. heavy_direction = random()%4;
  327. /* initialization for synchronization code */
  328. intersection_sync_init();
  329. }
  330. /*
  331. * cleanup_state()
  332. *
  333. * Purpose:
  334. * Releases simulation resources
  335. *
  336. * Arguments:
  337. * None
  338. *
  339. * Returns:
  340. * Nothing
  341. */
  342. static void
  343. cleanup_state(void)
  344. {
  345. sem_destroy(mutex);
  346. sem_destroy(perf_mutex);
  347. sem_destroy(SimulationWait);
  348. intersection_sync_cleanup();
  349. }
  350. /*
  351. * in_intersection()
  352. *
  353. * simulate time spent passing through the intersection
  354. *
  355. * Arguments:
  356. * none
  357. *
  358. * Return:
  359. * nothing
  360. */
  361. static void
  362. in_intersection(void) {
  363. clocknap(ServiceTime);
  364. }
  365. /*
  366. * vehicle_simulation()
  367. *
  368. * Simulates the arrival of vehicles to the intersection
  369. * from one direction.
  370. *
  371. * Arguments:
  372. * void * unusedpointer: currently unused.
  373. * unsigned long origin: vehicle arrival Direction
  374. *
  375. * Returns:
  376. * nothing.
  377. *
  378. * Notes:
  379. * Each simulation thread runs this function
  380. *
  381. */
  382. static
  383. void
  384. vehicle_simulation(void * unusedpointer,
  385. unsigned long thread_num)
  386. {
  387. int i;
  388. Vehicle v;
  389. time_t before_sec, after_sec, wait_sec;
  390. uint32_t before_nsec, after_nsec, wait_nsec;
  391. int sleeptime;
  392. /* avoid unused variable warnings. */
  393. (void) unusedpointer;
  394. KASSERT((long)thread_num < NumThreads);
  395. for(i=0;i<NumIterations;i++) {
  396. /* mix things up a bit: actual interarrival time is
  397. InterArrivalTime +/- 1 */
  398. sleeptime = InterArrivalTime + random()%3 - 1;
  399. KASSERT(sleeptime >= InterArrivalTime-1);
  400. KASSERT(sleeptime <= InterArrivalTime+1);
  401. /* wait for the next vehicle to arrive */
  402. clocknap(sleeptime);
  403. /* try to mix things up a bit more */
  404. if (random()%NumThreads < thread_num) {
  405. thread_yield();
  406. }
  407. /* choose where this vehicle is coming from */
  408. v.origin = choose_direction();
  409. /* choose where this vehicle is heading */
  410. v.destination = v.origin + (random()%3) + 1;
  411. if (v.destination >= 4) {
  412. v.destination = v.destination % 4;
  413. }
  414. KASSERT(4 > v.origin);
  415. KASSERT(4 > v.destination);
  416. KASSERT(v.origin != v.destination);
  417. /* invoke entry synchronization function */
  418. /* this should block until it is OK for this vehicle to
  419. enter the intersection */
  420. /* we also measure the time spent blocked */
  421. gettime(&before_sec,&before_nsec);
  422. intersection_before_entry(v.origin, v.destination);
  423. gettime(&after_sec,&after_nsec);
  424. /* enter the intersection */
  425. /* note: we are setting a global pointer to point to a local
  426. structure (v) here. In general, this is dangerous.
  427. However, we will be unsetting the
  428. pointer before the local structure goes out of scope */
  429. P(mutex);
  430. KASSERT(vehicles[thread_num] == NULL);
  431. vehicles[thread_num] = &v;
  432. /* check to make sure that the synchronization constraints have
  433. not been violated */
  434. /* this call will panic if a constraint has been violated */
  435. check_constraints(thread_num);
  436. V(mutex);
  437. /* spend some time in the intersection */
  438. in_intersection();
  439. /* leave the intersection */
  440. /* here, we are unsetting the global pointer */
  441. P(mutex);
  442. KASSERT(vehicles[thread_num] == &v);
  443. vehicles[thread_num] = NULL;
  444. V(mutex);
  445. /* invoke synchronization exit function */
  446. intersection_after_exit(v.origin, v.destination);
  447. getinterval(before_sec,before_nsec,after_sec,after_nsec,&wait_sec,&wait_nsec);
  448. P(perf_mutex);
  449. total_wait_secs[v.origin] += wait_sec;
  450. total_wait_nsecs[v.origin] += wait_nsec;
  451. if (total_wait_nsecs[v.origin] > 1000000000) {
  452. total_wait_nsecs[v.origin] -= 1000000000;
  453. total_wait_secs[v.origin] ++;
  454. }
  455. wait_count[v.origin]++;
  456. if (wait_sec > max_wait_secs[v.origin]) {
  457. max_wait_secs[v.origin] = wait_sec;
  458. max_wait_nsecs[v.origin] = wait_nsec;
  459. }
  460. else if ((wait_sec == max_wait_secs[v.origin])&&
  461. (wait_nsec > max_wait_nsecs[v.origin])) {
  462. max_wait_nsecs[v.origin] = wait_nsec;
  463. }
  464. V(perf_mutex);
  465. }
  466. /* indicate that this simulation is finished */
  467. V(SimulationWait);
  468. }
  469. /*
  470. * traffic_simulation()
  471. *
  472. * Arguments:
  473. * int nargs: should be 1 or 5
  474. * char ** args: args[1] = number of threads
  475. * args[2] = number of iterations per thread
  476. * args[3] = vehicle interarrival time (seconds)
  477. * args[4] = vehicle intersection time (seconds)
  478. * args[5] = direction bias (0=unbiased, 1=biased)
  479. *
  480. * Returns:
  481. * 0 on success, otherwise an error code from kern/errno.h
  482. *
  483. * Notes:
  484. * Driver code to start up simulation threads and wait for them
  485. * to finish
  486. */
  487. int
  488. traffic_simulation(int nargs,
  489. char ** args)
  490. {
  491. int i;
  492. int error;
  493. if ((nargs != 1) && (nargs != 6)) {
  494. kprintf("Usage: <command> [threads iterations interarrivaltime servicetime directionbias\n");
  495. return EINVAL; // return failure indication
  496. }
  497. if (nargs == 6) {
  498. NumThreads = atoi(args[1]);
  499. if (NumThreads <= 0 || NumThreads > MAX_THREADS) {
  500. kprintf("invalid number of threads: %d\n",NumThreads);
  501. return EINVAL;
  502. }
  503. NumIterations = atoi(args[2]);
  504. if (NumIterations < 0) {
  505. kprintf("invalid number of iterations per thread: %d\n",NumIterations);
  506. return EINVAL;
  507. }
  508. InterArrivalTime = atoi(args[3]);
  509. if (InterArrivalTime < 0) {
  510. kprintf("invalid interarrival time: %d\n",InterArrivalTime);
  511. return EINVAL;
  512. }
  513. ServiceTime = atoi(args[4]);
  514. if (ServiceTime < 0) {
  515. kprintf("invalid service time: %d\n",ServiceTime);
  516. return EINVAL;
  517. }
  518. DirectionBias = atoi(args[5]);
  519. if ((DirectionBias != 0) && (DirectionBias != 1)) {
  520. kprintf("Direction Bias (%d) must be 0 (unbiased) or 1 (biased)\n",DirectionBias);
  521. return EINVAL;
  522. }
  523. }
  524. kprintf("Threads: %d Iterations: %d Interarrival time: %d Service time: %d Bias?: %s\n",
  525. NumThreads,NumIterations,InterArrivalTime,ServiceTime,DirectionBias?"yes":"no");
  526. /* initialize our simulation state */
  527. initialize_state();
  528. /* get simulation start time */
  529. gettime(&start_sec,&start_nsec);
  530. for (i = 0; i < NumThreads; i++) {
  531. error = thread_fork("vehicle_simulation thread", NULL, vehicle_simulation, NULL, i);
  532. if (error) {
  533. panic("traffic_simulation: thread_fork failed: %s\n", strerror(error));
  534. }
  535. }
  536. /* wait for all of the vehicle simulations to finish before terminating */
  537. for(i=0;i<NumThreads;i++) {
  538. P(SimulationWait);
  539. }
  540. /* get simulation end time */
  541. gettime(&end_sec,&end_nsec);
  542. /* clean up the simulation state */
  543. cleanup_state();
  544. /* display performance stats */
  545. print_perf_stats();
  546. return 0;
  547. }
  548. /*
  549. * End of traffic.c
  550. */