/******************************************************************************
*
* 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;
}