/****************************************************************************** * * Copyright © International Business Machines Corp., 2007, 2008 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * NAME * sched_football.c * * DESCRIPTION * This is a scheduler test that uses a football analogy. * The premise is that we want to make sure that lower priority threads * (defensive team). The offense is trying to increment the balls position, * while the defense is trying to block that from happening. * And the ref (highest priority thread) will blow the wistle if the * ball moves. Finally, we have crazy fans (higer prority) that try to * distract the defense by occasionally running onto the field. * * Steps: * - Create a fixed number of offense threads (lower priority) * - Create a referee thread (highest priority) * - Once everyone is on the field, the offense thread increments the * value of 'the_ball' and yields. The defense thread tries to block * the ball by never letting the offense players get the CPU (it just * does a sched_yield). * - The refree threads wakes up regularly to check if the game is over :) * - In the end, if the value of 'the_ball' is >0, the test is considered * to have failed. * * USAGE: * Use run_auto.sh script in current directory to build and run test. * * AUTHOR * John Stultz <johnstul@xxxxxxxxx > * * HISTORY * 2006-03-16 Reduced verbosity, non binary failure reporting, removal of * crazy_fans thread, added game_length argument by Darren Hart. * 2007-08-01 Remove all thread cleanup in favor of simply exiting.Various * bugfixes and cleanups. -- Josh Triplett * 2009-06-23 Simplified atomic startup mechanism, avoiding thundering herd * scheduling at the beginning of the game. -- Darren Hart * *****************************************************************************/ #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <time.h> #include <string.h> #include <pthread.h> #include <sched.h> #include <errno.h> #include <sys/syscall.h> #include <unistd.h> #include <sys/time.h> #include <librttest.h> #define DEF_GAME_LENGTH 5 /* Here's the position of the ball */ volatile int the_ball; static int players_per_team = 0; static int game_length = DEF_GAME_LENGTH; static atomic_t players_ready; void usage(void) { rt_help(); printf("sched_football specific options:\n"); printf(" -nPLAYERS players per team (defaults to num_cpus)\n"); printf(" -lGAME_LENGTH game length in seconds (defaults to %d s)\n", DEF_GAME_LENGTH); } int parse_args(int c, char *v) { int handled = 1; switch (c) { case 'h': usage(); exit(0); case 'n': players_per_team = atoi(v); break; case 'l': game_length = atoi(v); break; default: handled = 0; break; } return handled; } /* This is the defensive team. They're trying to block the offense */ void *thread_defense(void *arg) { atomic_inc(&players_ready); /*keep the ball from being moved */ while (1) { sched_yield(); /* let other defenders run */ } return NULL; } /* This is the offensive team. They're trying to move the ball */ void *thread_offense(void *arg) { atomic_inc(&players_ready); while (1) { the_ball++; /* move the ball ahead one yard */ sched_yield(); /* let other offensive players run */ } return NULL; } int referee(int game_length) { struct timeval start, now; int final_ball; printf("Game On (%d seconds)!\n", game_length); gettimeofday(&start, NULL); now = start; /* Start the game! */ the_ball = 0; /* Watch the game */ while ((now.tv_sec - start.tv_sec) < game_length) { sleep(1); gettimeofday(&now, NULL); } /* Blow the whistle */ printf("Game Over!\n"); final_ball = the_ball; printf("Final ball position: %d\n", final_ball); return final_ball != 0; } int main(int argc, char *argv[]) { struct sched_param param; int priority; int i; int result; setup(); rt_init("n:l:h", parse_args, argc, argv); if (players_per_team == 0) players_per_team = sysconf(_SC_NPROCESSORS_ONLN); atomic_set(0, &players_ready); printf("Running with: players_per_team=%d game_length=%d\n", players_per_team, game_length); /* We're the ref, so set our priority right */ param.sched_priority = sched_get_priority_min(SCHED_FIFO) + 80; sched_setscheduler(0, SCHED_FIFO, ¶m); /* * Start the offense * They are lower priority than defense, so they must be started first. */ priority = 15; printf("Starting %d offense threads at priority %d\n", players_per_team, priority); for (i = 0; i < players_per_team; i++) create_fifo_thread(thread_offense, NULL, priority); /* Wait for the offense threads to start */ while (atomic_get(&players_ready) < players_per_team) usleep(100); /* Start the defense */ priority = 30; printf("Starting %d defense threads at priority %d\n", players_per_team, priority); for (i = 0; i < players_per_team; i++) create_fifo_thread(thread_defense, NULL, priority); /* Wait for the defense threads to start */ while (atomic_get(&players_ready) < players_per_team * 2) usleep(100); /* Ok, everyone is on the field, bring out the ref */ printf("Starting referee thread\n"); result = referee(game_length); printf("Result: %s\n", result ? "FAIL" : "PASS"); return result; }