diff --git a/samples/philosophers/unified/Makefile b/samples/philosophers/unified/Makefile new file mode 100644 index 00000000000..1712727d249 --- /dev/null +++ b/samples/philosophers/unified/Makefile @@ -0,0 +1,5 @@ +KERNEL_TYPE = unified +BOARD ?= qemu_cortex_m3 +CONF_FILE = prj.conf + +include ${ZEPHYR_BASE}/Makefile.inc diff --git a/samples/philosophers/unified/README.txt b/samples/philosophers/unified/README.txt new file mode 100644 index 00000000000..c9f2ba2c734 --- /dev/null +++ b/samples/philosophers/unified/README.txt @@ -0,0 +1,59 @@ +Title: Dining Philosophers + +Description: + +An implementation of a solution to the Dining Philosophers problem +(a classic multi-thread synchronization problem). This particular +implementation demonstrates the usage of nanokernel semaphores and +timers from multiple (6) fibers. + +The philosopher always tries to get the lowest fork first (f1 then f2). When +done, he will give back the forks in the reverse order (f2 then f1). If he +gets two forks, he is EATING. Otherwise, he is THINKING. Transitional states +are shown as well, such as STARVING when the philosopher is hungry but the +forks are not available, and HOLDING ONE FORK when a philosopher is waiting +for the second fork to be available. + +Each Philosopher will randomly alternate between the EATING and THINKING state. + +-------------------------------------------------------------------------------- + +Building and Running Project: + +This nanokernel project outputs to the console. It can be built and executed +on QEMU as follows: + + make qemu + +-------------------------------------------------------------------------------- + +Troubleshooting: + +Problems caused by out-dated project information can be addressed by +issuing one of the following commands then rebuilding the project: + + make clean # discard results of previous builds + # but keep existing configuration info +or + make pristine # discard results of previous builds + # and restore pre-defined configuration info + +-------------------------------------------------------------------------------- + +Sample Output: + +Philosopher 0 [P: 3] HOLDING ONE FORK +Philosopher 1 [P: 2] HOLDING ONE FORK +Philosopher 2 [P: 1] EATING [ 1900 ms ] +Philosopher 3 [P: 0] THINKING [ 2500 ms ] +Philosopher 4 [C:-1] THINKING [ 2200 ms ] +Philosopher 5 [C:-2] THINKING [ 1700 ms ] + + +Demo Description +---------------- +An implementation of a solution to the Dining Philosophers +problem (a classic multi-thread synchronization problem). +This particular implementation demonstrates the usage of multiple +preemptible and cooperative threads of differing priorities, as +well as dynamic mutexes and thread sleeping. diff --git a/samples/philosophers/unified/prj.conf b/samples/philosophers/unified/prj.conf new file mode 100644 index 00000000000..9df69ace72f --- /dev/null +++ b/samples/philosophers/unified/prj.conf @@ -0,0 +1,10 @@ +CONFIG_KERNEL_V2=y +CONFIG_KERNEL_V2_DEBUG=n +CONFIG_MDEF=n +CONFIG_INIT_STACKS=y +CONFIG_COMPILER_OPT="-O0" +CONFIG_SYS_CLOCK_TICKS_PER_SEC=100 +CONFIG_ASSERT=y +CONFIG_ASSERT_LEVEL=2 +CONFIG_NUM_COOP_PRIORITIES=2 +CONFIG_NUM_PREEMPT_PRIORITIES=6 diff --git a/samples/philosophers/unified/src/Makefile b/samples/philosophers/unified/src/Makefile new file mode 100644 index 00000000000..99857c811f9 --- /dev/null +++ b/samples/philosophers/unified/src/Makefile @@ -0,0 +1 @@ +obj-y = main.o diff --git a/samples/philosophers/unified/src/main.c b/samples/philosophers/unified/src/main.c new file mode 100644 index 00000000000..5f46b7ba533 --- /dev/null +++ b/samples/philosophers/unified/src/main.c @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2011-2016 Wind River Systems, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * + * Dining philosophers demo for unified kernel. + * + * The demo can be configured to use different object types for its + * synchronization: SEMAPHORES, MUTEXES, STACKS, FIFOS and LIFOS. To configure + * a specific object, set the value of FORKS to one of these. + * + * By default, the demo uses MUTEXES. + * + * The demo can also be configured to work with static objects or dynamic + * objects. The behaviour will change depending if STATIC_OBJS is set to 0 or + * 1. + * + * By default, the demo uses dynamic objects. + * + * The demo can be configured to work with threads of the same priority or + * not. If using different priorities, two threads will be cooperative + * threads, and the other four will be preemtible threads; if using one + * priority, there will be six preemtible threads of priority 0. This is + * changed via SAME_PRIO. + * + * By default, the demo uses different priorities. + * + * The number of threads is set via NUM_PHIL. The demo has only been tested + * with six threads. In theory it should work with less than six threads, but + * not with more without making changes to the forks[] array in the + * phil_obj_abstract.h header file. + */ + +#include + +#if defined(CONFIG_STDOUT_CONSOLE) +#include +#else +#include +#endif + +#include + +#define SEMAPHORES 1 +#define MUTEXES 2 +#define STACKS 3 +#define FIFOS 4 +#define LIFOS 5 + +/**************************************/ +/* control the behaviour of the demo **/ + +#ifndef DEBUG_PRINTF +#define DEBUG_PRINTF 0 +#endif + +#ifndef NUM_PHIL +#define NUM_PHIL 6 +#endif + +#ifndef STATIC_OBJS +#define STATIC_OBJS 0 +#endif + +#ifndef FORKS +#define FORKS MUTEXES +#if 0 +#define FORKS SEMAPHORES +#define FORKS STACKS +#define FORKS FIFOS +#define FORKS LIFOS +#endif +#endif + +#define SAME_PRIO 0 + +/* end - control behaviour of the demo */ +/***************************************/ + +#define STACK_SIZE 1024 + +/* + * There are multiple tasks doing printfs and they may conflict. + * Therefore use puts() instead of printf(). + */ +#if defined(CONFIG_STDOUT_CONSOLE) +#define PRINTF(...) { char output[256]; \ + sprintf(output, __VA_ARGS__); puts(output); } +#else +#define PRINTF(...) printk(__VA_ARGS__) +#endif + +#if DEBUG_PRINTF +#define PR_DEBUG PRINTF +#else +#define PR_DEBUG(...) +#endif + +#include "phil_obj_abstract.h" + +#define fork(x) (forks[x]) + +extern int k_current_priority_get(void); + +static void set_phil_state_pos(int id) +{ +#if !DEBUG_PRINTF + PRINTF("\x1b[%d;%dH", id + 1, 1); +#endif +} + +#include +static void print_phil_state(int id, const char *fmt, int32_t delay) +{ + int prio = k_current_priority_get(); + + set_phil_state_pos(id); + + PRINTF("Philosopher %d [%s:%s%d] ", + id, prio < 0 ? "C" : "P", + prio < 0 ? "" : " ", + prio); + + if (delay) { + PRINTF(fmt, delay < 1000 ? " " : "", delay); + } else { + PRINTF(fmt); + } + + PRINTF("\n"); +} + +static int32_t get_random_delay(int id) +{ + /* + * The random delay is in tenth of seconds, and is based on the + * philosopher's ID and the current uptime to create some + * pseudo-randomness. It produces a value between 0 and 1500 ms. + */ + int32_t tenth_of_sec = (k_uptime_get_32()/100 * (id + 1)) & 0x1f; + + /* add 1 since we want a delay of at least 100ms */ + int32_t ms = (tenth_of_sec + 1) * 100; + + return ms; +} + +static inline int is_last_philosopher(int id) +{ + return id == (NUM_PHIL - 1); +} + +void philosopher(void *id, void *unused1, void *unused2) +{ + ARG_UNUSED(unused1); + ARG_UNUSED(unused2); + + fork_t fork1; + fork_t fork2; + + int my_id = (int)id; + + /* Djkstra's solution: always pick up the lowest numbered fork first */ + if (is_last_philosopher(my_id)) { + fork1 = fork(0); + fork2 = fork(my_id); + } else { + fork1 = fork(my_id); + fork2 = fork(my_id + 1); + } + + while (1) { + int32_t delay; + + print_phil_state(my_id, " STARVING ", 0); + take(fork1); + print_phil_state(my_id, " HOLDING ONE FORK ", 0); + take(fork2); + + delay = get_random_delay(my_id); + print_phil_state(my_id, " EATING [ %s%d ms ] ", delay); + k_sleep(delay); + + drop(fork2); + print_phil_state(my_id, " DROPPED ONE FORK ", 0); + drop(fork1); + + delay = get_random_delay(my_id); + print_phil_state(my_id, " THINKING [ %s%d ms ] ", delay); + k_sleep(delay); + } + +} + +static int new_prio(int phil) +{ +#if SAME_PRIO + return 0; +#else + return -(phil - (NUM_PHIL/2)); +#endif +} + +static void init_objects(void) +{ +#if !STATIC_OBJS + for (int i = 0; i < NUM_PHIL; i++) { + fork_init(fork(i)); + } +#endif +} + +static void start_threads(void) +{ + /* create two fibers (prios -2/-1) and four tasks: (prios 0-3) */ + for (int i = 0; i < NUM_PHIL; i++) { + int prio = new_prio(i); + + k_thread_spawn(&stacks[i][0], STACK_SIZE, + philosopher, (void *)i, NULL, NULL, prio, 0, 0); + } +} + +#define DEMO_DESCRIPTION \ + "\x1b[2J\x1b[15;1H" \ + "Demo Description\n" \ + "----------------\n" \ + "An implementation of a solution to the Dining Philosophers\n" \ + "problem (a classic multi-thread synchronization problem).\n" \ + "This particular implementation demonstrates the usage of multiple\n" \ + "preemptible and cooperative threads of differing priorities, as\n" \ + "well as %s %s and thread sleeping.\n", obj_init_type, fork_type_str + +static void display_demo_description(void) +{ +#if !DEBUG_PRINTF + PRINTF(DEMO_DESCRIPTION); +#endif +} + +void main(void) +{ + display_demo_description(); + + init_objects(); + start_threads(); +} diff --git a/samples/philosophers/unified/src/phil_obj_abstract.h b/samples/philosophers/unified/src/phil_obj_abstract.h new file mode 100644 index 00000000000..48e0aec3741 --- /dev/null +++ b/samples/philosophers/unified/src/phil_obj_abstract.h @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2016 Wind River Systems, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * + * Object type abstraction. + * + * Each object type that can be used as a "fork" provides: + * + * - a definition for fork_t (a reference to a fork) and fork_obj_t (an + * instance of the fork object) + * - a 'fork_init' function that initializes the object + * - a 'take' function that simulates taking the fork (eg. k_sem_take) + * - a 'drop' function that simulates dropping the fork (eg. k_mutex_unlock) + * - a 'fork_type_str' string defining the object type + * + * When using dynamic objects, the instances of the fork objects are placed + * automatically in the fork_objs[] array . References to these are in turn + * placed automatically in the forks[] array, the array of references to the + * forks. + * + * When using static objects, references to each object must be put by hand in + * the forks[] array. + */ + +#ifndef phil_obj_abstract__h +#define phil_obj_abstract__h + +#define MAGIC 0xa5a5ee11 + +#if (FORKS == FIFOS) || (FORKS == LIFOS) + struct packet { + void *next; + int data; + } orig_packet[NUM_PHIL]; +#endif + +#if FORKS == SEMAPHORES + #define fork_t struct k_sem * + #if STATIC_OBJS + K_SEM_DEFINE(fork0, 1, 1); + K_SEM_DEFINE(fork1, 1, 1); + K_SEM_DEFINE(fork2, 1, 1); + K_SEM_DEFINE(fork3, 1, 1); + K_SEM_DEFINE(fork4, 1, 1); + K_SEM_DEFINE(fork5, 1, 1); + #else + #define fork_obj_t struct k_sem + #define fork_init(x) k_sem_init(x, 1, 1) + #endif + #define take(x) k_sem_take(x, K_FOREVER) + #define drop(x) k_sem_give(x) + #define fork_type_str "semaphores" +#elif FORKS == MUTEXES + #define fork_t struct k_mutex * + #if STATIC_OBJS + K_MUTEX_DEFINE(fork0); + K_MUTEX_DEFINE(fork1); + K_MUTEX_DEFINE(fork2); + K_MUTEX_DEFINE(fork3); + K_MUTEX_DEFINE(fork4); + K_MUTEX_DEFINE(fork5); + #else + #define fork_obj_t struct k_mutex + #define fork_init(x) k_mutex_init(x) + #endif + #define take(x) k_mutex_lock(x, K_FOREVER) + #define drop(x) k_mutex_unlock(x) + #define fork_type_str "mutexes" +#elif FORKS == STACKS + #define fork_t struct k_stack * + #if STATIC_OBJS + #error "not implemented yet." + #else + typedef struct { + char stack_mem[K_STACK_SIZE(1)] __aligned(4); + } fork_obj_t; + #define fork_init(x) do { \ + k_stack_init(x, 1); k_stack_push(x, MAGIC); \ + } while ((0)) + #endif + #define take(x) do { \ + uint32_t data; k_stack_pop(x, &data, K_FOREVER); \ + __ASSERT(data == MAGIC, "data was %x\n", data); \ + } while ((0)) + #define drop(x) k_stack_push(x, MAGIC) + #define fork_type_str "stacks" +#elif FORKS == FIFOS + #define fork_t struct k_fifo * + #if STATIC_OBJS + #error "not implemented yet." + #else + typedef struct { + struct k_fifo fifo; + struct packet data; + } fork_obj_t; + #define fork_init(x) do { \ + k_fifo_init(x); \ + ((fork_obj_t *)(x))->data.data = MAGIC; \ + k_fifo_put(x, &(((fork_obj_t *)(x))->data)); \ + } while ((0)) + #endif + #define take(x) do { \ + struct packet *data; \ + data = k_fifo_get(x, K_FOREVER); \ + __ASSERT(data->data == MAGIC, ""); \ + } while ((0)) + #define drop(x) k_fifo_put(x, &(((fork_obj_t *)(x))->data)) + #define fork_type_str "fifos" +#elif FORKS == LIFOS + #define fork_t struct k_lifo * + #if STATIC_OBJS + #error "not implemented yet." + #else + typedef struct { + struct k_lifo lifo; + struct packet data; + } fork_obj_t; + #define fork_init(x) do { \ + k_lifo_init(x); \ + ((fork_obj_t *)(x))->data.data = MAGIC; \ + k_lifo_put(x, &(((fork_obj_t *)(x))->data)); \ + } while ((0)) + #endif + #define take(x) do { \ + struct packet *data; \ + data = k_lifo_get(x, K_FOREVER); \ + __ASSERT(data->data == MAGIC, ""); \ + } while ((0)) + #define drop(x) k_lifo_put(x, &(((fork_obj_t *)(x))->data)) + #define fork_type_str "lifos" +#else + #error unknown fork type +#endif + +#if STATIC_OBJS + #define obj_init_type "static" +#else + #define obj_init_type "dynamic" + fork_obj_t fork_objs[NUM_PHIL]; +#endif + +static fork_t forks[NUM_PHIL] = { +#if STATIC_OBJS + &fork0, &fork1, &fork2, + &fork3, &fork4, &fork5, +#else + (fork_t)&fork_objs[0], (fork_t)&fork_objs[1], (fork_t)&fork_objs[2], + (fork_t)&fork_objs[3], (fork_t)&fork_objs[4], (fork_t)&fork_objs[5], +#endif +}; + +static char __stack stacks[NUM_PHIL][STACK_SIZE]; + +#endif /* phil_obj_abstract__h */