This commit is contained in:
Mark Qvist 2014-04-03 22:21:37 +02:00
commit c898b090dd
1049 changed files with 288572 additions and 0 deletions

32
bertos/kern/coop.c Normal file
View file

@ -0,0 +1,32 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* \note This file is deprecated and kept only for backward compatibility.
*
* -->
*/

79
bertos/kern/irq.c Normal file
View file

@ -0,0 +1,79 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2008 Bernie Innocenti <bernie@codewiz.org>
* -->
*
* \brief Process scheduler (public interface).
*
* \author Bernie Innocenti <bernie@codewiz.org>
*
* Still in development, disable nightly test for now
* notest: avr
* notest: arm
*/
#include "irq.h"
#include <cfg/module.h>
#include <kern/proc_p.h>
#include <kern/proc.h>
#include "cfg/cfg_proc.h"
#include <unistd.h> // FIXME: move POSIX stuff to irq_posix.h
MOD_DEFINE(irq)
// FIXME
static void (*irq_handlers[100])(void);
/* signal handler */
void irq_entry(int signum)
{
irq_handlers[signum]();
}
void irq_register(int irq, void (*callback)(void))
{
irq_handlers[irq] = callback;
}
void irq_init(void)
{
struct sigaction act;
act.sa_handler = irq_entry;
sigemptyset(&act.sa_mask);
//sigaddset(&act.sa_mask, irq);
act.sa_flags = SA_RESTART; // | SA_SIGINFO;
sigaction(SIGUSR1, &act, NULL);
sigaction(SIGALRM, &act, NULL);
MOD_INIT(irq);
}

43
bertos/kern/irq.h Normal file
View file

@ -0,0 +1,43 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2008 Bernie Innocenti <bernie@codewiz.org>
* -->
*
* \brief Process scheduler (public interface).
*
* \author Bernie Innocenti <bernie@codewiz.org>
*/
#ifndef KERN_IRQ_H
#define KERN_IRQ_H
void irq_entry(int irq);
void irq_register(int irq, void (*handler)(void));
void irq_init(void);
#endif // KERN_IRQ_H

37
bertos/kern/kfile.h Normal file
View file

@ -0,0 +1,37 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
* notest: all
*
*/
#warning "This file is deprecated, include the new <io/kfile.h>"
#include <io/kfile.h>

166
bertos/kern/monitor.c Normal file
View file

@ -0,0 +1,166 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2004, 2008 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief Monitor to check for stack overflows
*
* \author Giovanni Bajo <rasky@develer.com>
*/
#include "monitor.h"
#if CONFIG_KERN_MONITOR
#include "proc_p.h"
#include <cfg/macros.h>
#include <cfg/debug.h>
#include <struct/list.h>
#include <drv/timer.h>
#include <kern/proc.h>
#include <cpu/frame.h> /* CPU_STACK_GROWS_UPWARD */
/* Access to this list must be protected against the scheduler */
static List MonitorProcs;
void monitor_init(void)
{
LIST_INIT(&MonitorProcs);
}
void monitor_add(Process *proc, const char *name)
{
proc->monitor.name = name;
PROC_ATOMIC(ADDTAIL(&MonitorProcs, &proc->monitor.link));
}
void monitor_remove(Process *proc)
{
PROC_ATOMIC(REMOVE(&proc->monitor.link));
}
void monitor_rename(Process *proc, const char *name)
{
proc->monitor.name = name;
}
size_t monitor_checkStack(cpu_stack_t *stack_base, size_t stack_size)
{
cpu_stack_t *beg;
cpu_stack_t *cur;
cpu_stack_t *end;
int inc;
size_t sp_free;
beg = stack_base;
end = stack_base + stack_size / sizeof(cpu_stack_t);
inc = +1;
if (CPU_STACK_GROWS_UPWARD)
{
SWAP(beg, end);
inc = -1;
}
cur = beg;
while (cur != end)
{
if (*cur != CONFIG_KERN_STACKFILLCODE)
break;
cur += inc;
}
sp_free = ABS(cur - beg) * sizeof(cpu_stack_t);
return sp_free;
}
void monitor_report(void)
{
Node *node;
int i;
proc_forbid();
kprintf("%-9s%-9s%-9s%-9s%s\n", "TCB", "SPbase", "SPsize", "SPfree", "Name");
for (i = 0; i < 56; i++)
kputchar('-');
kputchar('\n');
FOREACH_NODE(node, &MonitorProcs)
{
Process *p = containerof(node, Process, monitor.link);
size_t free = monitor_checkStack(p->stack_base, p->stack_size);
kprintf("%-9p%-9p%-9zu%-9zu%s\n",
p, p->stack_base, p->stack_size, free, p->monitor.name);
}
proc_permit();
}
static void NORETURN monitor(void)
{
Node *node;
for (;;)
{
proc_forbid();
FOREACH_NODE(node, &MonitorProcs)
{
Process *p = containerof(node, Process, monitor.link);
size_t free = monitor_checkStack(p->stack_base, p->stack_size);
if (p->stack_base && free < 0x20)
kprintf("MONITOR: Free stack of process '%s' is only %u chars\n",
p->monitor.name, (unsigned int)free);
}
proc_permit();
/* Give some rest to the system */
timer_delay(500);
}
}
void monitor_start(size_t stacksize, cpu_stack_t *stack)
{
struct Process *p = proc_new(monitor, NULL, stacksize, stack);
proc_setPri(p, -10);
}
#endif /* CONFIG_KERN_MONITOR */

77
bertos/kern/monitor.h Normal file
View file

@ -0,0 +1,77 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2004 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief Monitor to check for stack overflows
*
*
* \author Giovanni Bajo <rasky@develer.com>
*
* $WIZ$ module_name = "monitor"
* $WIZ$ module_depends = "kernel"
* $WIZ$ module_configuration = "bertos/cfg/cfg_monitor.h"
*/
#ifndef KERN_MONITOR_H
#define KERN_MONITOR_H
#include "cfg/cfg_monitor.h"
#include <cpu/types.h>
/**
* Start the kernel monitor. It is a special process which checks every second the stacks of the
* running processes trying to detect stack overflows.
*
* \param stacksize Size of stack in chars
* \param stack Pointer to the stack that will be used by the monitor
*
* \note The stack is provided by the caller so that there is no wasted space if the monitor
* is not used.
*/
void monitor_start(size_t stacksize, cpu_stack_t *stack);
/**
* Manually check if a given stack has overflown. This is used to check for stacks
* of processes handled externally form the kernel, or for other stacks (for instance
* the interrupt supervisor stack).
*
* \note For this function to work, the stack must have been filled at startup with
* CONFIG_KERN_STACKFILLCODE.
*/
size_t monitor_checkStack(cpu_stack_t *stack_base, size_t stack_size);
/** Print a report of the stack status through kdebug */
void monitor_report(void);
#endif /* KERN_MONITOR_H */

300
bertos/kern/msg.h Normal file
View file

@ -0,0 +1,300 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2004 Develer S.r.l. (http://www.develer.com/)
* Copyright 1999,2001 Bernie Innocenti <bernie@codewiz.org>
*
* -->
*
*
* \defgroup kern_msg Message box IPC
* \ingroup kern
* \{
*
* \brief Simple inter-process messaging system
*
* This module implements a common system for executing
* a user defined action calling a hook function.
*
* A message port is an abstraction used to exchange information
* asynchronously between processes or other entities such as
* interrupts and call-back functions.
*
* This form of IPC is higher-level than bare signals and
* semaphores, because it sets a policy for exchanging
* structured data with well-defined synchronization and
* ownership semantics.
*
* Before using it, a message port must be initialized by
* calling msg_initPort(), which associates the port with
* an Event object, which can be setup to signal a process
* or invoke a call-back hook.
*
* A process or interrupt routine can deliver messages to any
* message port by calling msg_put(). By sending a message,
* the sender temporarly or permanently transfers ownership
* of its associated data to the receiver.
*
* Queuing a message to a port automatically triggers the
* associated Event to notify the receiver. When the
* receiver wakes up, it usually invokes msg_get() to pick
* the next message from the port.
*
* \note
* When you put a message into a port, such message becomes
* unavailable until you retrieve it using msg_get(), eg.
* you must not delete it or put it into another port.
*
* Message ports can hold any number of pending messages,
* and receivers usually process them in FIFO order.
* Other scheduling policies are possible, but not implemented
* in this API.
*
* After the receiver has done processing a message, it replies
* it back to the sender with msg_reply(), which transfer
* ownership back to the original sender. Replies are delivered
* to a reply port, which is nothing more than another MsgPort
* structure designated by the sender.
*
* Returning messages to senders is not mandatory, but it provides
* a convenient way to provide some kind of result and simplify
* the resource allocation scheme at the same time.
*
* When using signals to receive messages in a process, you
* call sig_wait() in an event-loop to wake up when messages
* are delivered to any of your ports. When your process
* wakes up with the port signal active, multiple messages
* may already have queued up at the message port, and the
* process must process them all before returning to sleep.
* Signals don't keep a nesting count.
*
* A simple message loop works like this:
*
* \code
* // Our message port.
* static MsgPort test_port;
*
* // A test message with two parameters and a result.
* typedef struct
* {
* Msg msg;
*
* int x, y;
* int result;
* } TestMsg;
*
*
* PROC_DEFINE_STACK(sender_stack, KERN_MINSTACKSIZE);
*
* // A process that sends two messages and waits for replies.
* static void sender_proc(void)
* {
* MsgPort test_reply_port;
* TestMsg msg1;
* TestMsg msg2;
* Msg *reply;
*
* msg_initPort(&test_reply_port,
* event_createSignal(proc_current(), SIG_SINGLE);
*
* // Fill-in first message and send it out.
* msg1.x = 3;
* msg1.y = 2;
* msg1.msg.replyPort = &test_reply_port;
* msg_put(&test_port, &msg1.msg);
*
* // Fill-in second message and send it out too.
* msg2.x = 5;
* msg2.y = 4;
* msg2.msg.replyPort = &test_reply_port;
* msg_put(&test_port, &msg2.msg);
*
* // Wait for a reply...
* sig_wait(SIG_SINGLE);
*
reply = containerof(msg_get(&test_reply_port), TestMsg, msg);
* ASSERT(reply != NULL);
* ASSERT(reply->result == 5);
*
* // Get reply to second message.
* while (!(reply = containerof(msg_get(&test_reply_port), TestMsg, msg)))
* {
* // Not yet, be patient and wait some more.
* sig_wait(SIG_SINGLE);
* }
*
* ASSERT(reply->result == 9);
* }
*
*
* // Receive messages and do something boring with them.
* static void receiver_proc(void)
* {
* msg_initPort(&test_port,
* event_createSignal(proc_current(), SIG_EXAMPLE);
*
* proc_new(sender_proc, NULL,sizeof(sender_stack), sender_stack);
*
* for (;;)
* {
* sigmask_t sigs = sig_wait(SIG_EXAMPLE | more_signals);
*
* if (sigs & SIG_EXAMPLE)
* {
* TestMsg *emsg;
* while((emsg = containerof(msg_get(&test_port), TestMsg, msg)))
* {
* // Do something with the message
* emsg->result = emsg->x + emsg->y;
* msg_reply(emsg->msg);
* }
* }
* }
* }
* \endcode
*
* \author Bernie Innocenti <bernie@codewiz.org>
*
* $WIZ$ module_name = "msg"
* $WIZ$ module_depends = "event", "signal", "kernel"
*/
#ifndef KERN_MSG_H
#define KERN_MSG_H
#include <mware/event.h>
#include <struct/list.h>
#include <kern/proc.h>
typedef struct MsgPort
{
List queue; /**< Messages queued at this port. */
Event event; /**< Event to trigger when a message arrives. */
} MsgPort;
typedef struct Msg
{
Node link; /**< Link into message port queue. */
MsgPort *replyPort; /**< Port to which the msg is to be replied. */
/* User data may follow */
} Msg;
/**
* Lock a message port.
*
* This is required before reading or manipulating
* any field of the MsgPort structure.
*
* \note Ports may be locked multiple times and each
* call to msg_lockPort() must be paired with
* a corresponding call to msg_unlockPort().
*
* \todo Add a configurable policy for locking against
* interrupts and locking with semaphorse.
*
* \see msg_unlockPort()
*/
INLINE void msg_lockPort(UNUSED_ARG(MsgPort *, port))
{
proc_forbid();
}
/**
* Unlock a message port.
*
* \see msg_lockPort()
*/
INLINE void msg_unlockPort(UNUSED_ARG(MsgPort *, port))
{
proc_permit();
}
/** Initialize a message port */
INLINE void msg_initPort(MsgPort *port, Event event)
{
LIST_INIT(&port->queue);
port->event = event;
}
/** Queue \a msg into \a port, triggering the associated event */
INLINE void msg_put(MsgPort *port, Msg *msg)
{
msg_lockPort(port);
ADDTAIL(&port->queue, &msg->link);
msg_unlockPort(port);
event_do(&port->event);
}
/**
* Get the first message from the queue of \a port.
*
* \return Pointer to the message or NULL if the port was empty.
*/
INLINE Msg *msg_get(MsgPort *port)
{
Msg *msg;
msg_lockPort(port);
msg = (Msg *)list_remHead(&port->queue);
msg_unlockPort(port);
return msg;
}
/** Peek the first message in the queue of \a port, or NULL if the port is empty. */
INLINE Msg *msg_peek(MsgPort *port)
{
Msg *msg;
msg_lockPort(port);
msg = (Msg *)port->queue.head.succ;
if (LIST_EMPTY(&port->queue))
msg = NULL;
msg_unlockPort(port);
return msg;
}
/** Send back (reply) \a msg to its sender. */
INLINE void msg_reply(Msg *msg)
{
msg_put(msg->replyPort, msg);
}
/** \} */ //defgroup kern_msg
int msg_testRun(void);
int msg_testSetup(void);
int msg_testTearDown(void);
#endif /* KERN_MSG_H */

291
bertos/kern/msg_test.c Normal file
View file

@ -0,0 +1,291 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2004, 2008 Develer S.r.l. (http://www.develer.com/)
* Copyright 1999, 2000, 2001 Bernie Innocenti <bernie@codewiz.org>
* -->
*
* \brief Message test.
*
*
* \author Daniele Basile <asterix@develer.com>
*
* $test$: cp bertos/cfg/cfg_proc.h $cfgdir/
* $test$: echo "#undef CONFIG_KERN" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN 1" >> $cfgdir/cfg_proc.h
* $test$: cp bertos/cfg/cfg_signal.h $cfgdir/
* $test$: echo "#undef CONFIG_KERN_SIGNALS" >> $cfgdir/cfg_signal.h
* $test$: echo "#define CONFIG_KERN_SIGNALS 1" >> $cfgdir/cfg_signal.h
*/
#include "cfg/cfg_timer.h"
#include <cfg/debug.h>
#include <cfg/test.h>
#include <cfg/compiler.h>
#include <kern/msg.h>
#include <kern/proc.h>
#include <kern/signal.h>
#include <mware/event.h>
#include <drv/timer.h>
/*
* In the nightly build test, signals are disables, so this
* code won't compile.
* Since this code is used when we run "make check" it will be
* compiled and therefor tested there.
*/
#if CONFIG_KERN_SIGNALS
// Global settings for the test.
#define MAX_GLOBAL_COUNT 11040
#define TEST_TIME_OUT_MS 5000
#define DELAY 5
// Settings for the test message.
//Process 0
#define INC_PROC_T0 1
#define DELAY_PROC_T0 INC_PROC_T0*DELAY
//Process 1
#define INC_PROC_T1 3
#define DELAY_PROC_T1 INC_PROC_T1*DELAY
//Process 2
#define INC_PROC_T2 5
#define DELAY_PROC_T2 INC_PROC_T2*DELAY
//Process 3
#define INC_PROC_T3 7
#define DELAY_PROC_T3 INC_PROC_T3*DELAY
//Process 4
#define INC_PROC_T4 11
#define DELAY_PROC_T4 INC_PROC_T4*DELAY
//Process 5
#define INC_PROC_T5 13
#define DELAY_PROC_T5 INC_PROC_T5*DELAY
/*
* These macros generate the code needed to create the test process functions.
*/
#define RECV_PROC(num, sig) \
static NORETURN void receiver_proc##num(void) \
{ \
TestMsg *rec_msg; \
for(;;) \
{ \
sig_wait(sig); \
kprintf("Proc[%d]..get message\n", num); \
rec_msg = containerof(msg_get(&test_port##num), TestMsg, msg); \
timer_delay(rec_msg->delay); \
rec_msg->result += rec_msg->val; \
kprintf("Proc[%d]..process message val[%d],delay[%d],res[%d]\n", num, rec_msg->val, rec_msg->delay, rec_msg->result); \
msg_reply(&rec_msg->msg); \
process_num++; \
kprintf("Proc[%d] reply\n", num); \
} \
}
#define SEND_MSG(num) \
do { \
kprintf("Main send message to proc[%d]\n", num); \
msg##num.msg.replyPort = &test_portMain; \
msg_put(&test_port##num, &msg##num.msg); \
} while(0)
#define RECV_STACK(num) PROC_DEFINE_STACK(receiver_stack##num, KERN_MINSTACKSIZE * 2)
#define RECV_INIT_PROC(num) proc_new(receiver_proc##num, NULL, sizeof(receiver_stack##num), receiver_stack##num)
#define RECV_INIT_MSG(num, proc, sig) msg_initPort(&test_port##num, event_createSignal(proc, sig))
// A test message with the parameters and a result.
typedef struct
{
Msg msg;
int val;
int delay;
int result;
} TestMsg;
// Global count to check if the test is going ok.
static int count = 0;
static int process_num;
// Our message port.
static MsgPort test_port0;
static MsgPort test_port1;
static MsgPort test_port2;
static MsgPort test_port3;
static MsgPort test_port4;
static MsgPort test_port5;
/*
* Generate the process to test message.
*/
RECV_PROC(0, SIG_USER0)
RECV_PROC(1, SIG_USER1)
RECV_PROC(2, SIG_USER2)
RECV_PROC(3, SIG_USER3)
RECV_PROC(4, SIG_SYSTEM5)
RECV_PROC(5, SIG_SYSTEM6)
/*
* These signal are already use from
* main process and the sig_waitWithTimeout functions, so we don't
* use it.
*
* RECV_PROC(6, SIG_SINGLE)
* RECV_PROC(7, SIG_TIMEOUT)
*/
RECV_STACK(0);
RECV_STACK(1);
RECV_STACK(2);
RECV_STACK(3);
RECV_STACK(4);
RECV_STACK(5);
/*
* Help function to fill the message to send
*/
static void fill_msg(TestMsg *msg, int val, int delay, int res)
{
msg->val = val;
msg->delay = delay;
msg->result = res;
}
/**
* Run signal test
*/
int msg_testRun(void)
{
MsgPort test_portMain;
TestMsg msg0;
TestMsg msg1;
TestMsg msg2;
TestMsg msg3;
TestMsg msg4;
TestMsg msg5;
TestMsg *reply;
// Allocate and start the test process
struct Process *recv0 = RECV_INIT_PROC(0);
struct Process *recv1 = RECV_INIT_PROC(1);
struct Process *recv2 = RECV_INIT_PROC(2);
struct Process *recv3 = RECV_INIT_PROC(3);
struct Process *recv4 = RECV_INIT_PROC(4);
struct Process *recv5 = RECV_INIT_PROC(5);
kprintf("Run Message test..\n");
// Init port and message
RECV_INIT_MSG(Main, proc_current(), SIG_SINGLE);
RECV_INIT_MSG(0, recv0, SIG_USER0);
RECV_INIT_MSG(1, recv1, SIG_USER1);
RECV_INIT_MSG(2, recv2, SIG_USER2);
RECV_INIT_MSG(3, recv3, SIG_USER3);
RECV_INIT_MSG(4, recv4, SIG_SYSTEM5);
RECV_INIT_MSG(5, recv5, SIG_SYSTEM6);
// Fill-in first message and send it out.
fill_msg(&msg0, INC_PROC_T0, DELAY_PROC_T0, 0);
fill_msg(&msg1, INC_PROC_T1, DELAY_PROC_T1, 0);
fill_msg(&msg2, INC_PROC_T2, DELAY_PROC_T2, 0);
fill_msg(&msg3, INC_PROC_T3, DELAY_PROC_T3, 0);
fill_msg(&msg4, INC_PROC_T4, DELAY_PROC_T4, 0);
fill_msg(&msg5, INC_PROC_T5, DELAY_PROC_T5, 0);
// Send and wait the message
for (int i = 0; i < 23; ++i)
{
process_num = 0;
SEND_MSG(0);
SEND_MSG(1);
SEND_MSG(2);
SEND_MSG(3);
SEND_MSG(4);
SEND_MSG(5);
while(1)
{
sigmask_t sigs = sig_waitTimeout(SIG_SINGLE, ms_to_ticks(TEST_TIME_OUT_MS));
if (sigs & SIG_SINGLE)
{
// Wait for a reply...
while ((reply = (TestMsg *)msg_get(&test_portMain)))
{
count += reply->result;
kprintf("Main recv[%d] count[%d]\n", reply->result, count);
}
}
if (process_num == 6)
break;
if (sigs & SIG_TIMEOUT)
{
kputs("Main: sig timeout\n");
goto error;
}
}
}
if(count == MAX_GLOBAL_COUNT)
{
kprintf("Message test finished..ok!\n");
return 0;
}
error:
kprintf("Message test finished..fail!\n");
return -1;
}
int msg_testSetup(void)
{
kdbg_init();
kprintf("Init Timer..");
timer_init();
kprintf("Done.\n");
kprintf("Init Process..");
proc_init();
kprintf("Done.\n");
return 0;
}
int msg_testTearDown(void)
{
kputs("TearDown Message test.\n");
return 0;
}
TEST_MAIN(msg);
#endif /* CONFIG_KERN_SIGNALS */

32
bertos/kern/preempt.c Normal file
View file

@ -0,0 +1,32 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* \note This file is deprecated and kept only for backward compatibility.
*
* -->
*/

678
bertos/kern/proc.c Normal file
View file

@ -0,0 +1,678 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* \brief Simple preemptive multitasking scheduler.
*
* Preemption is explicitly regulated at the exit of each interrupt service
* routine (ISR). Each task obtains a time quantum as soon as it is scheduled
* on the CPU and its quantum is decremented at each clock tick. The frequency
* of the timer determines the system tick granularity and CONFIG_KERN_QUANTUM
* the time sharing interval.
*
* When the quantum expires the handler proc_needPreempt() checks if the
* preemption is enabled and in this case proc_schedule() is called, that
* possibly replaces the current running thread with a different one.
*
* The preemption can be disabled or enabled via proc_forbid() and
* proc_permit() primitives. This is implemented using a global atomic counter.
* When the counter is greater than 0 the task cannot be preempted; only when
* the counter reaches 0 the task can be preempted again.
*
* Preemption-disabled sections may be nested. The preemption will be
* re-enabled when the outermost preemption-disabled section completes.
*
* The voluntary preemption still happens via proc_switch() or proc_yield().
* The first one assumes the current process has been already added to a
* private wait queue (e.g., on a semaphore or a signal), while the second one
* takes care of adding the process into the ready queue.
*
* Context switch is done by CPU-dependent support routines. In case of a
* voluntary preemption the context switch routine must take care of
* saving/restoring only the callee-save registers (the voluntary-preemption is
* actually a function call). The kernel-preemption always happens inside a
* signal/interrupt context and it must take care of saving all registers. For
* this, in the entry point of each ISR the caller-save registers must be
* saved. In the ISR exit point, if the context switch must happen, we switch
* to user-context and call the same voluntary context switch routine that take
* care of saving/restoring also the callee-save registers. On resume from the
* switch, the interrupt exit point moves back to interrupt-context, resumes
* the caller-save registers (saved in the ISR entry point) and return from the
* interrupt-context.
*
* \note Thread priority (if enabled by CONFIG_KERN_PRI) defines the order in
* the \p proc_ready_list and the capability to deschedule a running process. A
* low-priority thread can't preempt a high-priority thread.
*
* A high-priority process can preempt a low-priority process immediately (it
* will be descheduled and replaced in the interrupt exit point). Processes
* running at the same priority can be descheduled when they expire the time
* quantum.
*
* \note Sleeping while preemption is disabled fallbacks to a busy-wait sleep.
* Voluntary preemption when preemption is disabled raises a kernel bug.
*
* -->
*
* \brief Simple cooperative and preemptive multitasking scheduler.
*
* \author Bernie Innocenti <bernie@codewiz.org>
* \author Stefano Fedrigo <aleph@develer.com>
* \author Andrea Righi <arighi@develer.com>
*/
#include "proc_p.h"
#include "proc.h"
#include "cfg/cfg_proc.h"
#define LOG_LEVEL KERN_LOG_LEVEL
#define LOG_FORMAT KERN_LOG_FORMAT
#include <cfg/log.h>
#include "cfg/cfg_monitor.h"
#include <cfg/macros.h> // ROUND_UP2
#include <cfg/module.h>
#include <cfg/depend.h> // CONFIG_DEPEND()
#include <cpu/irq.h>
#include <cpu/types.h>
#include <cpu/attr.h>
#include <cpu/frame.h>
#if CONFIG_KERN_HEAP
#include <struct/heap.h>
#endif
#include <string.h> /* memset() */
#define PROC_SIZE_WORDS (ROUND_UP2(sizeof(Process), sizeof(cpu_stack_t)) / sizeof(cpu_stack_t))
/*
* The scheduer tracks ready processes by enqueuing them in the
* ready list.
*
* \note Access to the list must occur while interrupts are disabled.
*/
REGISTER List proc_ready_list;
/*
* Holds a pointer to the TCB of the currently running process.
*
* \note User applications should use proc_current() to retrieve this value.
*/
REGISTER Process *current_process;
/** The main process (the one that executes main()). */
static struct Process main_process;
#if CONFIG_KERN_HEAP
/**
* Local heap dedicated to allocate the memory used by the processes.
*/
static HEAP_DEFINE_BUF(heap_buf, CONFIG_KERN_HEAP_SIZE);
static Heap proc_heap;
/*
* Keep track of zombie processes (processes that are exiting and need to
* release some resources).
*
* \note Access to the list must occur while kernel preemption is disabled.
*/
static List zombie_list;
#endif /* CONFIG_KERN_HEAP */
/*
* Check if the process context switch can be performed directly by the
* architecture-dependent asm_switch_context() or if it must be delayed
* because we're in the middle of an ISR.
*
* Return true if asm_switch_context() can be executed, false
* otherwise.
*
* NOTE: if an architecture does not implement IRQ_RUNNING() this function
* always returns true.
*/
#define CONTEXT_SWITCH_FROM_ISR() (!IRQ_RUNNING())
/*
* Save context of old process and switch to new process.
*/
static void proc_context_switch(Process *next, Process *prev)
{
cpu_stack_t *dummy;
if (UNLIKELY(next == prev))
return;
/*
* If there is no old process, we save the old stack pointer into a
* dummy variable that we ignore. In fact, this happens only when the
* old process has just exited.
*/
asm_switch_context(&next->stack, prev ? &prev->stack : &dummy);
}
static void proc_initStruct(Process *proc)
{
/* Avoid warning for unused argument. */
(void)proc;
#if CONFIG_KERN_SIGNALS
proc->sig.recv = 0;
proc->sig.wait = 0;
#endif
#if CONFIG_KERN_HEAP
proc->flags = 0;
#endif
#if CONFIG_KERN_PRI
proc->link.pri = 0;
# if CONFIG_KERN_PRI_INHERIT
proc->orig_pri = proc->inh_link.pri = proc->link.pri;
proc->inh_blocked_by = NULL;
LIST_INIT(&proc->inh_list);
# endif
#endif
}
MOD_DEFINE(proc);
void proc_init(void)
{
LIST_INIT(&proc_ready_list);
#if CONFIG_KERN_HEAP
LIST_INIT(&zombie_list);
heap_init(&proc_heap, heap_buf, sizeof(heap_buf));
#endif
/*
* We "promote" the current context into a real process. The only thing we have
* to do is create a PCB and make it current. We don't need to setup the stack
* pointer because it will be written the first time we switch to another process.
*/
proc_initStruct(&main_process);
current_process = &main_process;
#if CONFIG_KERN_MONITOR
monitor_init();
monitor_add(current_process, "main");
#endif
MOD_INIT(proc);
}
#if CONFIG_KERN_HEAP
/**
* Free all the resources of all zombie processes previously added to the zombie
* list.
*/
static void proc_freeZombies(void)
{
Process *proc;
while (1)
{
PROC_ATOMIC(proc = (Process *)list_remHead(&zombie_list));
if (proc == NULL)
return;
if (proc->flags & PF_FREESTACK)
{
PROC_ATOMIC(heap_freemem(&proc_heap, proc->stack_base,
proc->stack_size + PROC_SIZE_WORDS * sizeof(cpu_stack_t)));
}
}
}
/**
* Enqueue a process in the zombie list.
*/
static void proc_addZombie(Process *proc)
{
Node *node;
#if CONFIG_KERN_PREEMPT
ASSERT(!proc_preemptAllowed());
#endif
#if CONFIG_KERN_PRI
node = &(proc)->link.link;
#else
node = &(proc)->link;
#endif
LIST_ASSERT_VALID(&zombie_list);
ADDTAIL(&zombie_list, node);
}
#endif /* CONFIG_KERN_HEAP */
/**
* Create a new process, starting at the provided entry point.
*
*
* \note The function
* \code
* proc_new(entry, data, stacksize, stack)
* \endcode
* is a more convenient way to create a process, as you don't have to specify
* the name.
*
* \return Process structure of new created process
* if successful, NULL otherwise.
*/
struct Process *proc_new_with_name(UNUSED_ARG(const char *, name), void (*entry)(void), iptr_t data, size_t stack_size, cpu_stack_t *stack_base)
{
Process *proc;
LOG_INFO("name=%s", name);
#if CONFIG_KERN_HEAP
bool free_stack = false;
/*
* Free up resources of a zombie process.
*
* We're implementing a kind of lazy garbage collector here for
* efficiency reasons: we can avoid to introduce overhead into another
* kernel task dedicated to free up resources (e.g., idle) and we're
* not introducing any overhead into the scheduler after a context
* switch (that would be *very* bad, because the scheduler runs with
* IRQ disabled).
*
* In this way we are able to release the memory of the zombie tasks
* without disabling IRQs and without introducing any significant
* overhead in any other kernel task.
*/
proc_freeZombies();
/* Did the caller provide a stack for us? */
if (!stack_base)
{
/* Did the caller specify the desired stack size? */
if (!stack_size)
stack_size = KERN_MINSTACKSIZE;
/* Allocate stack dinamically */
PROC_ATOMIC(stack_base =
(cpu_stack_t *)heap_allocmem(&proc_heap, stack_size));
if (stack_base == NULL)
return NULL;
free_stack = true;
}
#else // CONFIG_KERN_HEAP
/* Stack must have been provided by the user */
ASSERT2(IS_VALID_PTR(stack_base), "Invalid stack pointer. Did you forget to \
enable CONFIG_KERN_HEAP?");
ASSERT2(stack_size, "Stack size cannot be 0.");
#endif // CONFIG_KERN_HEAP
#if CONFIG_KERN_MONITOR
/*
* Fill-in the stack with a special marker to help debugging.
* On 64bit platforms, CONFIG_KERN_STACKFILLCODE is larger
* than an int, so the (int) cast is required to silence the
* warning for truncating its size.
*/
memset(stack_base, (int)CONFIG_KERN_STACKFILLCODE, stack_size);
#endif
/* Initialize the process control block */
if (CPU_STACK_GROWS_UPWARD)
{
proc = (Process *)stack_base;
proc->stack = stack_base + PROC_SIZE_WORDS;
// On some architecture stack should be aligned, so we do it.
proc->stack = (cpu_stack_t *)((uintptr_t)proc->stack + (sizeof(cpu_aligned_stack_t) - ((uintptr_t)proc->stack % sizeof(cpu_aligned_stack_t))));
if (CPU_SP_ON_EMPTY_SLOT)
proc->stack++;
}
else
{
proc = (Process *)(stack_base + stack_size / sizeof(cpu_stack_t) - PROC_SIZE_WORDS);
// On some architecture stack should be aligned, so we do it.
proc->stack = (cpu_stack_t *)((uintptr_t)proc - ((uintptr_t)proc % sizeof(cpu_aligned_stack_t)));
if (CPU_SP_ON_EMPTY_SLOT)
proc->stack--;
}
/* Ensure stack is aligned */
ASSERT((uintptr_t)proc->stack % sizeof(cpu_aligned_stack_t) == 0);
stack_size -= PROC_SIZE_WORDS * sizeof(cpu_stack_t);
proc_initStruct(proc);
proc->user_data = data;
#if CONFIG_KERN_HEAP | CONFIG_KERN_MONITOR
proc->stack_base = stack_base;
proc->stack_size = stack_size;
#if CONFIG_KERN_HEAP
if (free_stack)
proc->flags |= PF_FREESTACK;
#endif
#endif
proc->user_entry = entry;
CPU_CREATE_NEW_STACK(proc->stack);
#if CONFIG_KERN_MONITOR
monitor_add(proc, name);
#endif
/* Add to ready list */
ATOMIC(SCHED_ENQUEUE(proc));
return proc;
}
/**
* Return the name of the specified process.
*
* NULL is a legal argument and will return the name "<NULL>".
*/
const char *proc_name(struct Process *proc)
{
#if CONFIG_KERN_MONITOR
return proc ? proc->monitor.name : "<NULL>";
#else
(void)proc;
return "---";
#endif
}
/// Return the name of the currently running process
const char *proc_currentName(void)
{
return proc_name(proc_current());
}
/// Rename a process
void proc_rename(struct Process *proc, const char *name)
{
#if CONFIG_KERN_MONITOR
monitor_rename(proc, name);
#else
(void)proc; (void)name;
#endif
}
#if CONFIG_KERN_PRI
/**
* Change the scheduling priority of a process.
*
* Process piorities are signed ints, whereas a larger integer value means
* higher scheduling priority. The default priority for new processes is 0.
* The idle process runs with the lowest possible priority: INT_MIN.
*
* A process with a higher priority always preempts lower priority processes.
* Processes of equal priority share the CPU time according to a simple
* round-robin policy.
*
* As a general rule to maximize responsiveness, compute-bound processes
* should be assigned negative priorities and tight, interactive processes
* should be assigned positive priorities.
*
* To avoid interfering with system background activities such as input
* processing, application processes should remain within the range -10
* and +10.
*/
void proc_setPri(struct Process *proc, int pri)
{
#if CONFIG_KERN_PRI_INHERIT
int new_pri;
/*
* Whatever it will happen below, this is the new
* original priority of the process, i.e., the priority
* it has without taking inheritance under account.
*/
proc->orig_pri = pri;
/* If not changing anything we can just leave */
if ((new_pri = __prio_proc(proc)) == proc->link.pri)
return;
/*
* Actual process priority is the highest among its
* own priority and the one of the top-priority
* process that it is blocking (returned by
* __prio_proc()).
*/
proc->link.pri = new_pri;
#else
if (proc->link.pri == pri)
return;
proc->link.pri = pri;
#endif // CONFIG_KERN_PRI_INHERIT
if (proc != current_process)
ATOMIC(sched_reenqueue(proc));
}
#endif // CONFIG_KERN_PRI
INLINE void proc_run(void)
{
void (*entry)(void) = current_process->user_entry;
LOG_INFO("New process starting at %p", entry);
entry();
}
/**
* Entry point for all the processes.
*/
void proc_entry(void)
{
/*
* Return from a context switch assumes interrupts are disabled, so
* we need to explicitly re-enable them as soon as possible.
*/
IRQ_ENABLE;
/* Call the actual process's entry point */
proc_run();
proc_exit();
}
/**
* Terminate the current process
*/
void proc_exit(void)
{
LOG_INFO("%p:%s", current_process, proc_currentName());
#if CONFIG_KERN_MONITOR
monitor_remove(current_process);
#endif
proc_forbid();
#if CONFIG_KERN_HEAP
/*
* Set the task as zombie, its resources will be freed in proc_new() in
* a lazy way, when another process will be created.
*/
proc_addZombie(current_process);
#endif
current_process = NULL;
proc_permit();
proc_switch();
/* never reached */
ASSERT(0);
}
/**
* Call the scheduler and eventually replace the current running process.
*/
static void proc_schedule(void)
{
Process *old_process = current_process;
IRQ_ASSERT_DISABLED();
/* Poll on the ready queue for the first ready process */
LIST_ASSERT_VALID(&proc_ready_list);
while (!(current_process = (struct Process *)list_remHead(&proc_ready_list)))
{
/*
* Make sure we physically reenable interrupts here, no matter what
* the current task status is. This is important because if we
* are idle-spinning, we must allow interrupts, otherwise no
* process will ever wake up.
*
* During idle-spinning, an interrupt can occur and it may
* modify \p proc_ready_list. To ensure that compiler reload this
* variable every while cycle we call CPU_MEMORY_BARRIER.
* The memory barrier ensure that all variables used in this context
* are reloaded.
* \todo If there was a way to write sig_wait() so that it does not
* disable interrupts while waiting, there would not be any
* reason to do this.
*/
IRQ_ENABLE;
CPU_IDLE;
MEMORY_BARRIER;
IRQ_DISABLE;
}
if (CONTEXT_SWITCH_FROM_ISR())
proc_context_switch(current_process, old_process);
/* This RET resumes the execution on the new process */
LOG_INFO("resuming %p:%s\n", current_process, proc_currentName());
}
#if CONFIG_KERN_PREEMPT
/* Global preemption nesting counter */
cpu_atomic_t preempt_count;
/*
* The time sharing interval: when a process is scheduled on a CPU it gets an
* amount of CONFIG_KERN_QUANTUM clock ticks. When these ticks expires and
* preemption is enabled a new process is selected to run.
*/
int _proc_quantum;
/**
* Check if we need to schedule another task
*/
bool proc_needPreempt(void)
{
if (UNLIKELY(current_process == NULL))
return false;
if (!proc_preemptAllowed())
return false;
if (LIST_EMPTY(&proc_ready_list))
return false;
return preempt_quantum() ? prio_next() > prio_curr() :
prio_next() >= prio_curr();
}
/**
* Preempt the current task.
*/
void proc_preempt(void)
{
IRQ_ASSERT_DISABLED();
ASSERT(current_process);
/* Perform the kernel preemption */
LOG_INFO("preempting %p:%s\n", current_process, proc_currentName());
/* We are inside a IRQ context, so ATOMIC is not needed here */
SCHED_ENQUEUE(current_process);
preempt_reset_quantum();
proc_schedule();
}
#endif /* CONFIG_KERN_PREEMPT */
/* Immediately switch to a particular process */
static void proc_switchTo(Process *proc)
{
Process *old_process = current_process;
SCHED_ENQUEUE(current_process);
preempt_reset_quantum();
current_process = proc;
proc_context_switch(current_process, old_process);
}
/**
* Give the control of the CPU to another process.
*
* \note Assume the current process has been already added to a wait queue.
*
* \warning This should be considered an internal kernel function, even if it
* is allowed, usage from application code is strongly discouraged.
*/
void proc_switch(void)
{
ASSERT(proc_preemptAllowed());
ATOMIC(
preempt_reset_quantum();
proc_schedule();
);
}
/**
* Immediately wakeup a process, dispatching it to the CPU.
*/
void proc_wakeup(Process *proc)
{
ASSERT(proc_preemptAllowed());
ASSERT(current_process);
IRQ_ASSERT_DISABLED();
if (prio_proc(proc) >= prio_curr())
proc_switchTo(proc);
else
SCHED_ENQUEUE_HEAD(proc);
}
/**
* Voluntarily release the CPU.
*/
void proc_yield(void)
{
Process *proc;
/*
* Voluntary preemption while preemption is disabled is considered
* illegal, as not very useful in practice.
*
* ASSERT if it happens.
*/
ASSERT(proc_preemptAllowed());
IRQ_ASSERT_ENABLED();
IRQ_DISABLE;
proc = (struct Process *)list_remHead(&proc_ready_list);
if (proc)
proc_switchTo(proc);
IRQ_ENABLE;
}

463
bertos/kern/proc.h Normal file
View file

@ -0,0 +1,463 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2001, 2004 Develer S.r.l. (http://www.develer.com/)
* Copyright 1999, 2000, 2001, 2008 Bernie Innocenti <bernie@codewiz.org>
* -->
*
* \defgroup kern_proc Process (Threads) management
* \ingroup kern
* \{
*
* \brief BeRTOS Kernel core (Process scheduler).
*
* This is the core kernel module. It allows you to create new processes
* (which are called \b threads in other systems) and set the priority of
* each process.
*
* A process needs a work area (called \b stack) to run. To create a process,
* you need to declare a stack area, then create the process.
* You may also pass NULL for the stack area, if you have enabled kernel heap:
* in this case the stack will be automatically allocated.
*
* Example:
* \code
* PROC_DEFINE_STACK(stack1, 200);
*
* void NORETURN proc1_run(void)
* {
* while (1)
* {
* LOG_INFO("I'm alive!\n");
* timer_delay(1000);
* }
* }
*
*
* int main()
* {
* Process *p1 = proc_new(proc1_run, NULL, stack1, sizeof(stack1));
* // here the process is already running
* proc_setPri(p1, 2);
* // ...
* }
* \endcode
*
* The Process struct must be regarded as an opaque data type, do not access
* any of its members directly.
*
* The entry point function should be declared as NORETURN, because it will
* remove a warning and enable compiler optimizations.
*
* You can temporarily disable preemption calling proc_forbid(); remember
* to enable it again calling proc_permit().
*
* \note You should hardly need to manually release the CPU; however you
* can do it using the cpu_relax() function. It is illegal to release
* the CPU with preemption disabled.
*
* \author Bernie Innocenti <bernie@codewiz.org>
*
* $WIZ$ module_name = "kernel"
* $WIZ$ module_configuration = "bertos/cfg/cfg_proc.h"
* $WIZ$ module_depends = "switch_ctx"
* $WIZ$ module_supports = "not atmega103"
*/
#ifndef KERN_PROC_H
#define KERN_PROC_H
#include "cfg/cfg_proc.h"
#include "cfg/cfg_signal.h"
#include "cfg/cfg_monitor.h"
#include "sem.h"
#include <struct/list.h> // Node, PriNode
#include <cfg/compiler.h>
#include <cfg/debug.h> // ASSERT()
#include <cpu/types.h> // cpu_stack_t
#include <cpu/frame.h> // CPU_SAVED_REGS_CNT
/* The following silents warnings on nightly tests. We need to regenerate
* all the projects before this can be removed.
*/
#ifndef CONFIG_KERN_PRI_INHERIT
#define CONFIG_KERN_PRI_INHERIT 0
#endif
/*
* WARNING: struct Process is considered private, so its definition can change any time
* without notice. DO NOT RELY on any field defined here, use only the interface
* functions below.
*
* You have been warned.
*/
typedef struct Process
{
#if CONFIG_KERN_PRI
PriNode link; /**< Link Process into scheduler lists */
# if CONFIG_KERN_PRI_INHERIT
PriNode inh_link; /**< Link Process into priority inheritance lists */
List inh_list; /**< Priority inheritance list for this Process */
Semaphore *inh_blocked_by; /**< Semaphore blocking this Process */
int orig_pri; /**< Process priority without considering inheritance */
# endif
#else
Node link; /**< Link Process into scheduler lists */
#endif
cpu_stack_t *stack; /**< Per-process SP */
iptr_t user_data; /**< Custom data passed to the process */
#if CONFIG_KERN_SIGNALS
Signal sig;
#endif
#if CONFIG_KERN_HEAP
uint16_t flags; /**< Flags */
#endif
#if CONFIG_KERN_HEAP | CONFIG_KERN_MONITOR
cpu_stack_t *stack_base; /**< Base of process stack */
size_t stack_size; /**< Size of process stack */
#endif
/* The actual process entry point */
void (*user_entry)(void);
#if CONFIG_KERN_MONITOR
struct ProcMonitor
{
Node link;
const char *name;
} monitor;
#endif
} Process;
/**
* Initialize the process subsystem (kernel).
* It must be called before using any process related function.
*/
void proc_init(void);
struct Process *proc_new_with_name(const char *name, void (*entry)(void), iptr_t data, size_t stacksize, cpu_stack_t *stack);
#if !CONFIG_KERN_MONITOR
/**
* Create a new named process and schedules it for execution.
*
* When defining the stacksize take into account that you may want at least:
* \li save all the registers for each nested function call;
* \li have memory for the struct Process, which is positioned at the bottom
* of the stack;
* \li have some memory for temporary variables inside called functions.
*
* The value given by KERN_MINSTACKSIZE is rather safe to use in the first place.
*
* \param entry Function that the process will execute.
* \param data Pointer to user data.
* \param size Length of the stack.
* \param stack Pointer to the memory area to be used as a stack.
*
* \return Process structure of new created process
* if successful, NULL otherwise.
*/
#define proc_new(entry,data,size,stack) proc_new_with_name(NULL,(entry),(data),(size),(stack))
#else
#define proc_new(entry,data,size,stack) proc_new_with_name(#entry,(entry),(data),(size),(stack))
#endif
/**
* Terminate the execution of the current process.
*/
void proc_exit(void);
/*
* Public scheduling class methods.
*/
void proc_yield(void);
#if CONFIG_KERN_PREEMPT
bool proc_needPreempt(void);
void proc_preempt(void);
#else
INLINE bool proc_needPreempt(void)
{
return false;
}
INLINE void proc_preempt(void)
{
}
#endif
void proc_rename(struct Process *proc, const char *name);
const char *proc_name(struct Process *proc);
const char *proc_currentName(void);
/**
* Return a pointer to the user data of the current process.
*
* To obtain user data, just call this function inside the process. Remember to cast
* the returned pointer to the correct type.
* \return Pointer to the user data of the current process.
*/
INLINE iptr_t proc_currentUserData(void)
{
extern struct Process *current_process;
return current_process->user_data;
}
int proc_testSetup(void);
int proc_testRun(void);
int proc_testTearDown(void);
/**
* Return the context structure of the currently running process.
*
* The details of the Process structure are private to the scheduler.
* The address returned by this function is an opaque pointer that can
* be passed as an argument to other process-related functions.
*/
INLINE struct Process *proc_current(void)
{
extern struct Process *current_process;
return current_process;
}
#if CONFIG_KERN_PRI
void proc_setPri(struct Process *proc, int pri);
#else
INLINE void proc_setPri(UNUSED_ARG(struct Process *,proc), UNUSED_ARG(int, pri))
{
}
#endif
#if CONFIG_KERN_PREEMPT
/**
* Disable preemptive task switching.
*
* The scheduler maintains a global nesting counter. Task switching is
* effectively re-enabled only when the number of calls to proc_permit()
* matches the number of calls to proc_forbid().
*
* \note Calling functions that could sleep while task switching is disabled
* is dangerous and unsupported.
*
* \note proc_permit() expands inline to 1-2 asm instructions, so it's a
* very efficient locking primitive in simple but performance-critical
* situations. In all other cases, semaphores offer a more flexible and
* fine-grained locking primitive.
*
* \sa proc_permit()
*/
INLINE void proc_forbid(void)
{
extern cpu_atomic_t preempt_count;
/*
* We don't need to protect the counter against other processes.
* The reason why is a bit subtle.
*
* If a process gets here, preempt_forbid_cnt can be either 0,
* or != 0. In the latter case, preemption is already disabled
* and no concurrency issues can occur.
*
* In the former case, we could be preempted just after reading the
* value 0 from memory, and a concurrent process might, in fact,
* bump the value of preempt_forbid_cnt under our nose!
*
* BUT: if this ever happens, then we won't get another chance to
* run until the other process calls proc_permit() to re-enable
* preemption. At this point, the value of preempt_forbid_cnt
* must be back to 0, and thus what we had originally read from
* memory happens to be valid.
*
* No matter how hard you think about it, and how complicated you
* make your scenario, the above holds true as long as
* "preempt_forbid_cnt != 0" means that no task switching is
* possible.
*/
++preempt_count;
/*
* Make sure preempt_count is flushed to memory so the preemption
* softirq will see the correct value from now on.
*/
MEMORY_BARRIER;
}
/**
* Re-enable preemptive task switching.
*
* \sa proc_forbid()
*/
INLINE void proc_permit(void)
{
extern cpu_atomic_t preempt_count;
/*
* This is to ensure any global state changed by the process gets
* flushed to memory before task switching is re-enabled.
*/
MEMORY_BARRIER;
/* No need to protect against interrupts here. */
ASSERT(preempt_count > 0);
--preempt_count;
/*
* This ensures preempt_count is flushed to memory immediately so the
* preemption interrupt sees the correct value.
*/
MEMORY_BARRIER;
}
/**
* \return true if preemptive task switching is allowed.
* \note This accessor is needed because preempt_count
* must be absoultely private.
*/
INLINE bool proc_preemptAllowed(void)
{
extern cpu_atomic_t preempt_count;
return (preempt_count == 0);
}
#else /* CONFIG_KERN_PREEMPT */
#define proc_forbid() /* NOP */
#define proc_permit() /* NOP */
#define proc_preemptAllowed() (true)
#endif /* CONFIG_KERN_PREEMPT */
/** Deprecated, use the proc_preemptAllowed() macro. */
#define proc_allowed() proc_preemptAllowed()
/**
* Execute a block of \a CODE atomically with respect to task scheduling.
*/
#define PROC_ATOMIC(CODE) \
do { \
proc_forbid(); \
CODE; \
proc_permit(); \
} while(0)
/**
* Default stack size for each thread, in bytes.
*
* The goal here is to allow a minimal task to save all of its
* registers twice, plus push a maximum of 32 variables on the
* stack. We add also struct Process size since we save it into the process'
* stack.
*
* The actual size computed by the default formula greatly depends on what
* options are active and on the architecture.
*
* Note that on most 16bit architectures, interrupts will also
* run on the stack of the currently running process. Nested
* interrupts will greatly increases the amount of stack space
* required per process. Use irqmanager to minimize stack
* usage.
*/
#if (ARCH & ARCH_EMUL)
/* We need a large stack because system libraries are bloated */
#define KERN_MINSTACKSIZE 65536
#else
#if CONFIG_KERN_PREEMPT
/*
* A preemptible kernel needs a larger stack compared to the
* cooperative case. A task can be interrupted anytime in each
* node of the call graph, at any level of depth. This may
* result in a higher stack consumption, to call the ISR, save
* the current user context and to execute the kernel
* preemption routines implemented as ISR prologue and
* epilogue. All these calls are nested into the process stack.
*
* So, to reduce the risk of stack overflow/underflow problems
* add a x2 to the portion stack reserved to the user process.
*/
#define KERN_MINSTACKSIZE \
(sizeof(Process) + CPU_SAVED_REGS_CNT * 2 * sizeof(cpu_stack_t) \
+ 32 * sizeof(int) * 2)
#else
#define KERN_MINSTACKSIZE \
(sizeof(Process) + CPU_SAVED_REGS_CNT * 2 * sizeof(cpu_stack_t) \
+ 32 * sizeof(int))
#endif /* CONFIG_KERN_PREEMPT */
#endif
#ifndef CONFIG_KERN_MINSTACKSIZE
/* For backward compatibility */
#define CONFIG_KERN_MINSTACKSIZE KERN_MINSTACKSIZE
#else
#warning FIXME: This macro is deprecated, use KERN_MINSTACKSIZE instead
#endif
/**
* Utility macro to allocate a stack of size \a size.
*
* This macro define a static stack for one process and do
* check if given stack size is enough to run process.
* \note If you plan to use kprintf() and similar functions, you will need
* at least KERN_MINSTACKSIZE * 2 bytes.
*
* \param name Variable name for the stack.
* \param size Stack size in bytes. It must be at least KERN_MINSTACKSIZE.
*/
#define PROC_DEFINE_STACK(name, size) \
cpu_stack_t name[((size) + sizeof(cpu_stack_t) - 1) / sizeof(cpu_stack_t)]; \
STATIC_ASSERT((size) >= KERN_MINSTACKSIZE);
/* Memory fill codes to help debugging */
#if CONFIG_KERN_MONITOR
#include <cpu/types.h>
#if (SIZEOF_CPUSTACK_T == 1)
/* 8bit cpu_stack_t */
#define CONFIG_KERN_STACKFILLCODE 0xA5
#define CONFIG_KERN_MEMFILLCODE 0xDB
#elif (SIZEOF_CPUSTACK_T == 2)
/* 16bit cpu_stack_t */
#define CONFIG_KERN_STACKFILLCODE 0xA5A5
#define CONFIG_KERN_MEMFILLCODE 0xDBDB
#elif (SIZEOF_CPUSTACK_T == 4)
/* 32bit cpu_stack_t */
#define CONFIG_KERN_STACKFILLCODE 0xA5A5A5A5UL
#define CONFIG_KERN_MEMFILLCODE 0xDBDBDBDBUL
#elif (SIZEOF_CPUSTACK_T == 8)
/* 64bit cpu_stack_t */
#define CONFIG_KERN_STACKFILLCODE 0xA5A5A5A5A5A5A5A5ULL
#define CONFIG_KERN_MEMFILLCODE 0xDBDBDBDBDBDBDBDBULL
#else
#error No cpu_stack_t size supported!
#endif
#endif
/** \} */ //defgroup kern_proc
#endif /* KERN_PROC_H */

229
bertos/kern/proc_p.h Normal file
View file

@ -0,0 +1,229 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2001, 2004 Develer S.r.l. (http://www.develer.com/)
* Copyright 1999, 2000, 2001 Bernie Innocenti <bernie@codewiz.org>
*
* -->
*
* \brief Internal scheduler structures and definitions for processes.
*
* \author Bernie Innocenti <bernie@codewiz.org>
*/
#ifndef KERN_PROC_P_H
#define KERN_PROC_P_H
#include "cfg/cfg_proc.h"
#include "cfg/cfg_monitor.h"
#include <cfg/compiler.h>
#include <cpu/types.h> /* for cpu_stack_t */
#include <cpu/irq.h> // IRQ_ASSERT_DISABLED()
#include <kern/proc.h> // struct Process
#ifndef asm_switch_context
/**
* CPU dependent context switching routines.
*
* Saving and restoring the context on the stack is done by a CPU-dependent
* support routine which usually needs to be written in assembly.
*/
EXTERN_C void asm_switch_context(cpu_stack_t **new_sp, cpu_stack_t **save_sp);
#endif
/**
* \name Flags for Process.flags.
* \{
*/
#define PF_FREESTACK BV(0) /**< Free the stack when process dies */
/*\}*/
/** Track running processes. */
extern REGISTER Process *current_process;
/**
* Track ready processes.
*
* Access to this list must be performed with interrupts disabled
*/
extern REGISTER List proc_ready_list;
#if CONFIG_KERN_PRI
# if CONFIG_KERN_PRI_INHERIT
#define __prio_orig(proc) (proc->orig_pri)
#define __prio_inh(proc) (LIST_EMPTY(&(proc)->inh_list) ? INT_MIN : \
((PriNode *)LIST_HEAD(&proc->inh_list))->pri)
#define __prio_proc(proc) (__prio_inh(proc) > __prio_orig(proc) ? \
__prio_inh(proc) : __prio_orig(proc))
# endif
#define prio_next() (LIST_EMPTY(&proc_ready_list) ? INT_MIN : \
((PriNode *)LIST_HEAD(&proc_ready_list))->pri)
#define prio_proc(proc) (proc->link.pri)
#define prio_curr() prio_proc(current_process)
#define SCHED_ENQUEUE_INTERNAL(proc) \
LIST_ENQUEUE(&proc_ready_list, &(proc)->link)
#define SCHED_ENQUEUE_HEAD_INTERNAL(proc) \
LIST_ENQUEUE_HEAD(&proc_ready_list, &(proc)->link)
#else
#define prio_next() 0
#define prio_proc(proc) 0
#define prio_curr() 0
#define SCHED_ENQUEUE_INTERNAL(proc) ADDTAIL(&proc_ready_list, &(proc)->link)
#define SCHED_ENQUEUE_HEAD_INTERNAL(proc) ADDHEAD(&proc_ready_list, &(proc)->link)
#endif
/**
* Enqueue a process in the ready list.
*
* Always use this macro to instert a process in the ready list, as its
* might vary to implement a different scheduling algorithms.
*
* \note Access to the scheduler ready list must be performed with
* interrupts disabled.
*/
#define SCHED_ENQUEUE(proc) do { \
IRQ_ASSERT_DISABLED(); \
LIST_ASSERT_VALID(&proc_ready_list); \
SCHED_ENQUEUE_INTERNAL(proc); \
} while (0)
#define SCHED_ENQUEUE_HEAD(proc) do { \
IRQ_ASSERT_DISABLED(); \
LIST_ASSERT_VALID(&proc_ready_list); \
SCHED_ENQUEUE_HEAD_INTERNAL(proc); \
} while (0)
#if CONFIG_KERN_PRI
/**
* Changes the priority of an already enqueued process.
*
* Searches and removes the process from the ready list, then uses LIST_ENQUEUE(()
* to insert again to fix priority.
*
* No action is performed for processes that aren't in the ready list, eg. in semaphore queues.
*
* \note Performance could be improved with a different implementation of priority list.
*/
INLINE void sched_reenqueue(struct Process *proc)
{
IRQ_ASSERT_DISABLED();
LIST_ASSERT_VALID(&proc_ready_list);
Node *n;
PriNode *pos = NULL;
FOREACH_NODE(n, &proc_ready_list)
{
if (n == &proc->link.link)
{
pos = (PriNode *)n;
break;
}
}
// only remove and enqueue again if process is already in the ready list
// otherwise leave it alone
if (pos)
{
REMOVE(&proc->link.link);
LIST_ENQUEUE(&proc_ready_list, &proc->link);
}
}
#endif //CONFIG_KERN_PRI
/* Process trampoline */
void proc_entry(void);
/* Schedule another process *without* adding the current one to the ready list. */
void proc_switch(void);
/* Immediately schedule a particular process bypassing the scheduler. */
void proc_wakeup(Process *proc);
/* Initialize a scheduler class. */
void proc_schedInit(void);
#if CONFIG_KERN_MONITOR
/** Initialize the monitor */
void monitor_init(void);
/** Register a process into the monitor */
void monitor_add(Process *proc, const char *name);
/** Unregister a process from the monitor */
void monitor_remove(Process *proc);
/** Rename a process */
void monitor_rename(Process *proc, const char *name);
#endif /* CONFIG_KERN_MONITOR */
/*
* Quantum related macros are used in the
* timer module and must be empty when
* kernel is disabled.
*/
#if (CONFIG_KERN && CONFIG_KERN_PREEMPT)
INLINE int preempt_quantum(void)
{
extern int _proc_quantum;
return _proc_quantum;
}
INLINE void proc_decQuantum(void)
{
extern int _proc_quantum;
if (_proc_quantum > 0)
_proc_quantum--;
}
INLINE void preempt_reset_quantum(void)
{
extern int _proc_quantum;
_proc_quantum = CONFIG_KERN_QUANTUM;
}
#else /* !(CONFIG_KERN && CONFIG_KERN_PREEMPT) */
INLINE int preempt_quantum(void)
{
return 0;
}
INLINE void proc_decQuantum(void)
{
}
INLINE void preempt_reset_quantum(void)
{
}
#endif /* (CONFIG_KERN && CONFIG_KERN_PREEMPT) */
#endif /* KERN_PROC_P_H */

406
bertos/kern/proc_test.c Normal file
View file

@ -0,0 +1,406 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2009 Develer S.r.l. (http://www.develer.com/)
* -->
*
*
* \brief Test kernel preemption.
*
* This testcase spawns TASKS parallel threads that runs for TIME seconds. They
* continuously spin updating a global counter (one counter for each thread).
*
* At exit each thread checks if the others have been che chance to update
* their own counter. If not, it means the preemption didn't occur and the
* testcase returns an error message.
*
* Otherwise, if all the threads have been able to update their own counter it
* means preemption successfully occurs, since there is no active sleep inside
* each thread's implementation.
*
* \author Andrea Righi <arighi@develer.com>
*
* $test$: cp bertos/cfg/cfg_proc.h $cfgdir/
* $test$: echo "#undef CONFIG_KERN" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN 1" >> $cfgdir/cfg_proc.h
* $test$: echo "#undef CONFIG_KERN_PRI" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN_PRI 1" >> $cfgdir/cfg_proc.h
* $test$: echo "#undef CONFIG_KERN_PREEMPT" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN_PREEMPT 1" >> $cfgdir/cfg_proc.h
* $test$: echo "#undef CONFIG_KERN_HEAP" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN_HEAP 1" >> $cfgdir/cfg_proc.h
* $test$: echo "#undef CONFIG_KERN_HEAP_SIZE" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN_HEAP_SIZE 2097152L" >> $cfgdir/cfg_proc.h
* $test$: cp bertos/cfg/cfg_monitor.h $cfgdir/
* $test$: sed -i "s/CONFIG_KERN_MONITOR 0/CONFIG_KERN_MONITOR 1/" $cfgdir/cfg_monitor.h
* $test$: cp bertos/cfg/cfg_signal.h $cfgdir/
* $test$: echo "#undef CONFIG_KERN_SIGNALS" >> $cfgdir/cfg_signal.h
* $test$: echo "#define CONFIG_KERN_SIGNALS 1" >> $cfgdir/cfg_signal.h
*
*/
#include <stdio.h> // sprintf
#include <string.h> // memset
#include <kern/proc.h>
#include <kern/irq.h>
#include <kern/monitor.h>
#include <drv/timer.h>
#include <cfg/test.h>
#include <cfg/cfg_proc.h>
enum
{
TEST_OK = 1,
TEST_FAIL = 2,
};
/* Number of tasks to spawn */
#define TASKS 8
static char name[TASKS][32];
static unsigned int done[TASKS];
static cpu_atomic_t barrier[TASKS];
static cpu_atomic_t main_barrier;
/* Base time delay for processes using timer_delay() */
#define DELAY 5
// Define process stacks for test.
#define WORKER_STACK_SIZE KERN_MINSTACKSIZE * 3
#if CONFIG_KERN_HEAP
#define WORKER_STACK(id) NULL
#else /* !CONFIG_KERN_HEAP */
static cpu_stack_t worker_stack[TASKS][(WORKER_STACK_SIZE +
sizeof(cpu_stack_t) - 1) / sizeof(cpu_stack_t)];
#define WORKER_STACK(id) (&worker_stack[id][0])
#endif /* CONFIG_KERN_HEAP */
static int prime_numbers[] =
{
1, 3, 5, 7, 11, 13, 17, 19,
23, 29, 31, 37, 41, 43, 47, 53,
};
STATIC_ASSERT(TASKS <= countof(prime_numbers));
#if CONFIG_KERN_PREEMPT
/* Time to run each preemptible thread (in seconds) */
#define TIME 10
static unsigned long preempt_counter[TASKS];
static unsigned int preempt_done[TASKS];
#endif
static void cleanup(void)
{
#if CONFIG_KERN_PREEMPT
// Clear shared data (this is needed when this testcase is embedded in
// the demo application).
memset(preempt_counter, 0, sizeof(preempt_counter));
memset(preempt_done, 0, sizeof(preempt_done));
#endif /* CONFIG_KERN_PREEMPT */
memset(done, 0, sizeof(done));
memset(barrier, 0, sizeof(barrier));
main_barrier = 0;
}
static void worker(void)
{
ssize_t pid = (ssize_t)proc_currentUserData();
long tot = prime_numbers[pid - 1];
unsigned int my_count = 0;
int i;
barrier[pid - 1] = 1;
/* Synchronize on the main barrier */
while (!main_barrier)
proc_yield();
for (i = 0; i < tot; i++)
{
my_count++;
PROC_ATOMIC(kprintf("> %s[%zd] running\n", __func__, pid));
timer_delay(tot * DELAY);
}
done[pid - 1] = 1;
PROC_ATOMIC(kprintf("> %s[%zd] completed\n", __func__, pid));
}
static int worker_test(void)
{
ssize_t i;
// Init the test processes
cleanup();
kputs("Run Proc test..\n");
for (i = 0; i < TASKS; i++)
{
name[i][0] = '\0';
snprintf(&name[i][0], sizeof(name[i]), "worker_%zd", i + 1);
name[i][sizeof(name[i]) - 1] = '\0';
proc_new_with_name(name[i], worker, (iptr_t)(i + 1),
WORKER_STACK_SIZE, WORKER_STACK(i));
}
/* Synchronize on start */
while (1)
{
for (i = 0; i < TASKS; i++)
if (!barrier[i])
break;
if (i == TASKS)
break;
proc_yield();
}
main_barrier = 1;
MEMORY_BARRIER;
kputs("> Main: Processes started\n");
while (1)
{
for (i = 0; i < TASKS; i++)
{
if (!done[i])
break;
}
if (i == TASKS)
break;
monitor_report();
timer_delay(93);
}
kputs("> Main: process test finished..ok!\n");
return 0;
}
#if CONFIG_KERN_PREEMPT
static void preempt_worker(void)
{
ssize_t pid = (ssize_t)proc_currentUserData();
unsigned long *my_count = &preempt_counter[pid - 1];
ticks_t start, stop;
int i;
barrier[pid - 1] = 1;
/* Synchronize on the main barrier */
while (!main_barrier)
proc_yield();
PROC_ATOMIC(kprintf("> %s[%zd] running\n", __func__, pid));
start = timer_clock();
stop = ms_to_ticks(TIME * 1000);
while (timer_clock() - start < stop)
{
IRQ_ASSERT_ENABLED();
(*my_count)++;
/* be sure to wrap to a value different than 0 */
if (UNLIKELY(*my_count == (unsigned int)~0))
*my_count = 1;
}
PROC_ATOMIC(kprintf("> %s[%zd] completed: (counter = %lu)\n",
__func__, pid, *my_count));
for (i = 0; i < TASKS; i++)
if (!preempt_counter[i])
{
preempt_done[pid - 1] = TEST_FAIL;
return;
}
preempt_done[pid - 1] = TEST_OK;
}
static int preempt_worker_test(void)
{
unsigned long score = 0;
ssize_t i;
// Init the test processes
cleanup();
kputs("Run Preemption test..\n");
for (i = 0; i < TASKS; i++)
{
name[i][0] = '\0';
snprintf(&name[i][0], sizeof(name[i]),
"preempt_worker_%zd", i + 1);
name[i][sizeof(name[i]) - 1] = '\0';
proc_new_with_name(name[i], preempt_worker, (iptr_t)(i + 1),
WORKER_STACK_SIZE, WORKER_STACK(i));
}
kputs("> Main: Processes created\n");
/* Synchronize on start */
while (1)
{
for (i = 0; i < TASKS; i++)
if (!barrier[i])
break;
if (i == TASKS)
break;
proc_yield();
}
/* Now all threads have been created, start them all */
main_barrier = 1;
MEMORY_BARRIER;
kputs("> Main: Processes started\n");
while (1)
{
for (i = 0; i < TASKS; i++)
{
if (!preempt_done[i])
break;
else if (preempt_done[i] == TEST_FAIL)
{
kputs("> Main: process test finished..fail!\n");
return -1;
}
}
if (i == TASKS)
break;
monitor_report();
timer_delay(1000);
}
for (i = 0; i < TASKS; i++)
score += preempt_counter[i];
kputs("> Main: process test finished..ok!\n");
kprintf("> Score: %lu\n", score);
return 0;
}
#endif /* CONFIG_KERN_PREEMPT */
#if CONFIG_KERN_SIGNALS & CONFIG_KERN_PRI
// Define params to test priority
#define PROC_PRI_TEST(num) static void proc_pri_test##num(void) \
{ \
struct Process *main_proc = (struct Process *) proc_currentUserData(); \
kputs("> Process: " #num "\n"); \
sig_send(main_proc, SIG_USER##num); \
}
// Default priority is 0
#define PROC_PRI_TEST_INIT(num, proc) \
do { \
struct Process *p = proc_new(proc_pri_test##num, (proc), \
WORKER_STACK_SIZE, \
WORKER_STACK(num)); \
proc_setPri(p, num + 1); \
} while (0)
PROC_PRI_TEST(0)
PROC_PRI_TEST(1)
PROC_PRI_TEST(2)
static int prio_worker_test(void)
{
struct Process *curr = proc_current();
int orig_pri = curr->link.pri;
int ret = 0;
// test process priority
// main process must have the higher priority to check signals received
proc_setPri(proc_current(), 10);
kputs("Run Priority test..\n");
// the order in which the processes are created is important!
PROC_PRI_TEST_INIT(0, curr);
PROC_PRI_TEST_INIT(1, curr);
PROC_PRI_TEST_INIT(2, curr);
// signals must be: USER2, 1, 0 in order
sigmask_t signals = sig_wait(SIG_USER0 | SIG_USER1 | SIG_USER2);
if (!(signals & SIG_USER2))
{
ret = -1;
goto out;
}
signals = sig_wait(SIG_USER0 | SIG_USER1 | SIG_USER2);
if (!(signals & SIG_USER1))
{
ret = -1;
goto out;
}
signals = sig_wait(SIG_USER0 | SIG_USER1 | SIG_USER2);
if (!(signals & SIG_USER0))
{
ret = -1;
goto out;
}
// All processes must have quit by now, but just in case...
signals = sig_waitTimeout(SIG_USER0 | SIG_USER1 | SIG_USER2, 200);
if (signals & (SIG_USER0 | SIG_USER1 | SIG_USER2))
{
ret = -1;
goto out;
}
if (signals & SIG_TIMEOUT)
{
kputs("Priority test successfull.\n");
}
out:
proc_setPri(proc_current(), orig_pri);
if (ret != 0)
kputs("Priority test failed.\n");
return ret;
}
#endif /* CONFIG_KERN_SIGNALS & CONFIG_KERN_PRI */
/**
* Process scheduling test
*/
int proc_testRun(void)
{
/* Start tests */
worker_test();
#if CONFIG_KERN_PREEMPT
preempt_worker_test();
#endif /* CONFIG_KERN_PREEMPT */
#if CONFIG_KERN_SIGNALS & CONFIG_KERN_PRI
prio_worker_test();
#endif /* CONFIG_KERN_SIGNALS & CONFIG_KERN_PRI */
return 0;
}
int proc_testSetup(void)
{
kdbg_init();
kprintf("Init Timer..");
timer_init();
kprintf("Done.\n");
kprintf("Init Process..");
proc_init();
kprintf("Done.\n");
return 0;
}
int proc_testTearDown(void)
{
kputs("TearDown Process test.\n");
return 0;
}
TEST_MAIN(proc);

View file

@ -0,0 +1,65 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2009 Develer S.r.l. (http://www.develer.com/)
* -->
*
*
* \brief Test kernel preemption.
*
* This testcase spawns TASKS parallel threads that runs for TIME seconds. They
* continuously spin updating a global counter (one counter for each thread).
*
* At exit each thread checks if the others have been che chance to update
* their own counter. If not, it means the preemption didn't occur and the
* testcase returns an error message.
*
* Otherwise, if all the threads have been able to update their own counter it
* means preemption successfully occurs, since there is no active sleep inside
* each thread's implementation.
*
* \author Andrea Righi <arighi@develer.com>
*
* $test$: cp bertos/cfg/cfg_proc.h $cfgdir/
* $test$: echo "#undef CONFIG_KERN" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN 1" >> $cfgdir/cfg_proc.h
* $test$: echo "#undef CONFIG_KERN_HEAP " >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN_HEAP 1" >> $cfgdir/cfg_proc.h
* $test$: echo "#undef CONFIG_KERN_HEAP_SIZE" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN_HEAP_SIZE 2097152L" >> $cfgdir/cfg_proc.h
* $test$: cp bertos/cfg/cfg_monitor.h $cfgdir/
* $test$: sed -i "s/CONFIG_KERN_MONITOR 0/CONFIG_KERN_MONITOR 1/" $cfgdir/cfg_monitor.h
* $test$: cp bertos/cfg/cfg_signal.h $cfgdir/
* $test$: echo "#undef CONFIG_KERN_SIGNALS" >> $cfgdir/cfg_signal.h
* $test$: echo "#define CONFIG_KERN_SIGNALS 1" >> $cfgdir/cfg_signal.h
*
* notest: all
*
*/
#include "../proc_test.c"

View file

@ -0,0 +1,48 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2004, 2008 Develer S.r.l. (http://www.develer.com/)
* Copyright 1999, 2000, 2001 Bernie Innocenti <bernie@codewiz.org>
* -->
*
* \brief Message test.
*
*
* \author Daniele Basile <asterix@develer.com>
*
* $test$: cp bertos/cfg/cfg_proc.h $cfgdir/
* $test$: echo "#undef CONFIG_KERN" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN 1" >> $cfgdir/cfg_proc.h
* $test$: cp bertos/cfg/cfg_signal.h $cfgdir/
* $test$: echo "#undef CONFIG_KERN_SIGNALS" >> $cfgdir/cfg_signal.h
* $test$: echo "#define CONFIG_KERN_SIGNALS 1" >> $cfgdir/cfg_signal.h
*
* notest: all
*/
#include "../msg_test.c"

View file

@ -0,0 +1,67 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2009 Develer S.r.l. (http://www.develer.com/)
* -->
*
*
* \brief Test kernel preemption.
*
* This testcase spawns TASKS parallel threads that runs for TIME seconds. They
* continuously spin updating a global counter (one counter for each thread).
*
* At exit each thread checks if the others have been che chance to update
* their own counter. If not, it means the preemption didn't occur and the
* testcase returns an error message.
*
* Otherwise, if all the threads have been able to update their own counter it
* means preemption successfully occurs, since there is no active sleep inside
* each thread's implementation.
*
* \author Andrea Righi <arighi@develer.com>
*
* $test$: cp bertos/cfg/cfg_proc.h $cfgdir/
* $test$: echo "#undef CONFIG_KERN" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN 1" >> $cfgdir/cfg_proc.h
* $test$: echo "#undef CONFIG_KERN_PRI" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN_PRI 1" >> $cfgdir/cfg_proc.h
* $test$: echo "#undef CONFIG_KERN_HEAP " >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN_HEAP 1" >> $cfgdir/cfg_proc.h
* $test$: echo "#undef CONFIG_KERN_HEAP_SIZE" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN_HEAP_SIZE 2097152L" >> $cfgdir/cfg_proc.h
* $test$: cp bertos/cfg/cfg_monitor.h $cfgdir/
* $test$: sed -i "s/CONFIG_KERN_MONITOR 0/CONFIG_KERN_MONITOR 1/" $cfgdir/cfg_monitor.h
* $test$: cp bertos/cfg/cfg_signal.h $cfgdir/
* $test$: echo "#undef CONFIG_KERN_SIGNALS" >> $cfgdir/cfg_signal.h
* $test$: echo "#define CONFIG_KERN_SIGNALS 1" >> $cfgdir/cfg_signal.h
*
* notest: all
*
*/
#include "../proc_test.c"

View file

@ -0,0 +1,63 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2009 Develer S.r.l. (http://www.develer.com/)
* -->
*
*
* \brief Test kernel preemption.
*
* This testcase spawns TASKS parallel threads that runs for TIME seconds. They
* continuously spin updating a global counter (one counter for each thread).
*
* At exit each thread checks if the others have been che chance to update
* their own counter. If not, it means the preemption didn't occur and the
* testcase returns an error message.
*
* Otherwise, if all the threads have been able to update their own counter it
* means preemption successfully occurs, since there is no active sleep inside
* each thread's implementation.
*
* \author Andrea Righi <arighi@develer.com>
*
* $test$: cp bertos/cfg/cfg_proc.h $cfgdir/
* $test$: echo "#undef CONFIG_KERN" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN 1" >> $cfgdir/cfg_proc.h
* $test$: echo "#undef CONFIG_KERN_PRI" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN_PRI 1" >> $cfgdir/cfg_proc.h
* $test$: cp bertos/cfg/cfg_monitor.h $cfgdir/
* $test$: sed -i "s/CONFIG_KERN_MONITOR 0/CONFIG_KERN_MONITOR 1/" $cfgdir/cfg_monitor.h
* $test$: cp bertos/cfg/cfg_signal.h $cfgdir/
* $test$: echo "#undef CONFIG_KERN_SIGNALS" >> $cfgdir/cfg_signal.h
* $test$: echo "#define CONFIG_KERN_SIGNALS 1" >> $cfgdir/cfg_signal.h
*
* notest: all
*
*/
#include "../proc_test.c"

View file

@ -0,0 +1,48 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2008 Develer S.r.l. (http://www.develer.com/)
* -->
*
* \brief Semaphore test.
*
*
* \author Daniele Basile <asterix@develer.com>
* \author Stefano Fedrigo <aleph@develer.com>
*
* $test$: cp bertos/cfg/cfg_proc.h $cfgdir/
* $test$: echo "#undef CONFIG_KERN" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN 1" >> $cfgdir/cfg_proc.h
* $test$: cp bertos/cfg/cfg_sem.h $cfgdir/
* $test$: echo "#undef CONFIG_KERN_SEMAPHORES" >> $cfgdir/cfg_sem.h
* $test$: echo "#define CONFIG_KERN_SEMAPHORES 1" >> $cfgdir/cfg_sem.h
*
* notest:all
*/
#include "../sem_test.c"

View file

@ -0,0 +1,48 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2004, 2008 Develer S.r.l. (http://www.develer.com/)
* Copyright 1999, 2000, 2001 Bernie Innocenti <bernie@codewiz.org>
* -->
*
* \brief Signals test.
*
*
* \author Daniele Basile <asterix@develer.com>
*
* $test$: cp bertos/cfg/cfg_proc.h $cfgdir/
* $test$: echo "#undef CONFIG_KERN" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN 1" >> $cfgdir/cfg_proc.h
* $test$: cp bertos/cfg/cfg_signal.h $cfgdir/
* $test$: echo "#undef CONFIG_KERN_SIGNALS" >> $cfgdir/cfg_signal.h
* $test$: echo "#define CONFIG_KERN_SIGNALS 1" >> $cfgdir/cfg_signal.h
*
* notest: all
*/
#include "../signal_test.c"

View file

@ -0,0 +1,60 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2009 Develer S.r.l. (http://www.develer.com/)
* -->
*
*
* \brief Test kernel preemption.
*
* This testcase spawns TASKS parallel threads that runs for TIME seconds. They
* continuously spin updating a global counter (one counter for each thread).
*
* At exit each thread checks if the others have been che chance to update
* their own counter. If not, it means the preemption didn't occur and the
* testcase returns an error message.
*
* Otherwise, if all the threads have been able to update their own counter it
* means preemption successfully occurs, since there is no active sleep inside
* each thread's implementation.
*
* \author Andrea Righi <arighi@develer.com>
*
* $test$: cp bertos/cfg/cfg_proc.h $cfgdir/
* $test$: echo "#undef CONFIG_KERN" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN 1" >> $cfgdir/cfg_proc.h
* $test$: cp bertos/cfg/cfg_monitor.h $cfgdir/
* $test$: sed -i "s/CONFIG_KERN_MONITOR 0/CONFIG_KERN_MONITOR 1/" $cfgdir/cfg_monitor.h
* $test$: cp bertos/cfg/cfg_signal.h $cfgdir/
* $test$: echo "#undef CONFIG_KERN_SIGNALS" >> $cfgdir/cfg_signal.h
* $test$: echo "#define CONFIG_KERN_SIGNALS 1" >> $cfgdir/cfg_signal.h
*
* notest: all
*/
#include "../proc_test.c"

View file

@ -0,0 +1,66 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2009 Develer S.r.l. (http://www.develer.com/)
* -->
*
*
* \brief Test kernel preemption.
*
* This testcase spawns TASKS parallel threads that runs for TIME seconds. They
* continuously spin updating a global counter (one counter for each thread).
*
* At exit each thread checks if the others have been che chance to update
* their own counter. If not, it means the preemption didn't occur and the
* testcase returns an error message.
*
* Otherwise, if all the threads have been able to update their own counter it
* means preemption successfully occurs, since there is no active sleep inside
* each thread's implementation.
*
* \author Andrea Righi <arighi@develer.com>
*
* $test$: cp bertos/cfg/cfg_proc.h $cfgdir/
* $test$: echo "#undef CONFIG_KERN" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN 1" >> $cfgdir/cfg_proc.h
* $test$: echo "#undef CONFIG_KERN_PREEMPT" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN_PREEMPT 1" >> $cfgdir/cfg_proc.h
* $test$: echo "#undef CONFIG_KERN_HEAP " >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN_HEAP 1" >> $cfgdir/cfg_proc.h
* $test$: echo "#undef CONFIG_KERN_HEAP_SIZE" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN_HEAP_SIZE 2097152L" >> $cfgdir/cfg_proc.h
* $test$: cp bertos/cfg/cfg_monitor.h $cfgdir/
* $test$: sed -i "s/CONFIG_KERN_MONITOR 0/CONFIG_KERN_MONITOR 1/" $cfgdir/cfg_monitor.h
* $test$: cp bertos/cfg/cfg_signal.h $cfgdir/
* $test$: echo "#undef CONFIG_KERN_SIGNALS" >> $cfgdir/cfg_signal.h
* $test$: echo "#define CONFIG_KERN_SIGNALS 1" >> $cfgdir/cfg_signal.h
*
* notest: all
*/
#include "../proc_test.c"

View file

@ -0,0 +1,50 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2004, 2008 Develer S.r.l. (http://www.develer.com/)
* Copyright 1999, 2000, 2001 Bernie Innocenti <bernie@codewiz.org>
* -->
*
* \brief Message test.
*
*
* \author Daniele Basile <asterix@develer.com>
*
* $test$: cp bertos/cfg/cfg_proc.h $cfgdir/
* $test$: echo "#undef CONFIG_KERN" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN 1" >> $cfgdir/cfg_proc.h
* $test$: echo "#undef CONFIG_KERN_PREEMPT" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN_PREEMPT 1" >> $cfgdir/cfg_proc.h
* $test$: cp bertos/cfg/cfg_signal.h $cfgdir/
* $test$: echo "#undef CONFIG_KERN_SIGNALS" >> $cfgdir/cfg_signal.h
* $test$: echo "#define CONFIG_KERN_SIGNALS 1" >> $cfgdir/cfg_signal.h
*
* notest:all
*/
#include "../msg_test.c"

View file

@ -0,0 +1,68 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2009 Develer S.r.l. (http://www.develer.com/)
* -->
*
*
* \brief Test kernel preemption.
*
* This testcase spawns TASKS parallel threads that runs for TIME seconds. They
* continuously spin updating a global counter (one counter for each thread).
*
* At exit each thread checks if the others have been che chance to update
* their own counter. If not, it means the preemption didn't occur and the
* testcase returns an error message.
*
* Otherwise, if all the threads have been able to update their own counter it
* means preemption successfully occurs, since there is no active sleep inside
* each thread's implementation.
*
* \author Andrea Righi <arighi@develer.com>
*
* $test$: cp bertos/cfg/cfg_proc.h $cfgdir/
* $test$: echo "#undef CONFIG_KERN" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN 1" >> $cfgdir/cfg_proc.h
* $test$: echo "#undef CONFIG_KERN_PRI" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN_PRI 1" >> $cfgdir/cfg_proc.h
* $test$: echo "#undef CONFIG_KERN_PREEMPT" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN_PREEMPT 1" >> $cfgdir/cfg_proc.h
* $test$: echo "#undef CONFIG_KERN_HEAP " >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN_HEAP 1" >> $cfgdir/cfg_proc.h
* $test$: echo "#undef CONFIG_KERN_HEAP_SIZE" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN_HEAP_SIZE 2097152L" >> $cfgdir/cfg_proc.h
* $test$: cp bertos/cfg/cfg_monitor.h $cfgdir/
* $test$: sed -i "s/CONFIG_KERN_MONITOR 0/CONFIG_KERN_MONITOR 1/" $cfgdir/cfg_monitor.h
* $test$: cp bertos/cfg/cfg_signal.h $cfgdir/
* $test$: echo "#undef CONFIG_KERN_SIGNALS" >> $cfgdir/cfg_signal.h
* $test$: echo "#define CONFIG_KERN_SIGNALS 1" >> $cfgdir/cfg_signal.h
*
* notest: all
*/
#include "../proc_test.c"

View file

@ -0,0 +1,64 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2009 Develer S.r.l. (http://www.develer.com/)
* -->
*
*
* \brief Test kernel preemption.
*
* This testcase spawns TASKS parallel threads that runs for TIME seconds. They
* continuously spin updating a global counter (one counter for each thread).
*
* At exit each thread checks if the others have been che chance to update
* their own counter. If not, it means the preemption didn't occur and the
* testcase returns an error message.
*
* Otherwise, if all the threads have been able to update their own counter it
* means preemption successfully occurs, since there is no active sleep inside
* each thread's implementation.
*
* \author Andrea Righi <arighi@develer.com>
*
* $test$: cp bertos/cfg/cfg_proc.h $cfgdir/
* $test$: echo "#undef CONFIG_KERN" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN 1" >> $cfgdir/cfg_proc.h
* $test$: echo "#undef CONFIG_KERN_PRI" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN_PRI 1" >> $cfgdir/cfg_proc.h
* $test$: echo "#undef CONFIG_KERN_PREEMPT" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN_PREEMPT 1" >> $cfgdir/cfg_proc.h
* $test$: cp bertos/cfg/cfg_monitor.h $cfgdir/
* $test$: sed -i "s/CONFIG_KERN_MONITOR 0/CONFIG_KERN_MONITOR 1/" $cfgdir/cfg_monitor.h
* $test$: cp bertos/cfg/cfg_signal.h $cfgdir/
* $test$: echo "#undef CONFIG_KERN_SIGNALS" >> $cfgdir/cfg_signal.h
* $test$: echo "#define CONFIG_KERN_SIGNALS 1" >> $cfgdir/cfg_signal.h
*
* notest: all
*/
#include "../proc_test.c"

View file

@ -0,0 +1,50 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2008 Develer S.r.l. (http://www.develer.com/)
* -->
*
* \brief Semaphore test.
*
*
* \author Daniele Basile <asterix@develer.com>
* \author Stefano Fedrigo <aleph@develer.com>
*
* $test$: cp bertos/cfg/cfg_proc.h $cfgdir/
* $test$: echo "#undef CONFIG_KERN" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN 1" >> $cfgdir/cfg_proc.h
* $test$: echo "#undef CONFIG_KERN_PREEMPT" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN_PREEMPT 1" >> $cfgdir/cfg_proc.h
* $test$: cp bertos/cfg/cfg_sem.h $cfgdir/
* $test$: echo "#undef CONFIG_KERN_SEMAPHORES" >> $cfgdir/cfg_sem.h
* $test$: echo "#define CONFIG_KERN_SEMAPHORES 1" >> $cfgdir/cfg_sem.h
*
* notest:all
*/
#include "../sem_test.c"

View file

@ -0,0 +1,50 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2004, 2008 Develer S.r.l. (http://www.develer.com/)
* Copyright 1999, 2000, 2001 Bernie Innocenti <bernie@codewiz.org>
* -->
*
* \brief Signals test.
*
*
* \author Daniele Basile <asterix@develer.com>
*
* $test$: cp bertos/cfg/cfg_proc.h $cfgdir/
* $test$: echo "#undef CONFIG_KERN" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN 1" >> $cfgdir/cfg_proc.h
* $test$: echo "#undef CONFIG_KERN_PREEMPT" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN_PREEMPT 1" >> $cfgdir/cfg_proc.h
* $test$: cp bertos/cfg/cfg_signal.h $cfgdir/
* $test$: echo "#undef CONFIG_KERN_SIGNALS" >> $cfgdir/cfg_signal.h
* $test$: echo "#define CONFIG_KERN_SIGNALS 1" >> $cfgdir/cfg_signal.h
*
* notest: all
*/
#include "../signal_test.c"

View file

@ -0,0 +1,62 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2009 Develer S.r.l. (http://www.develer.com/)
* -->
*
*
* \brief Test kernel preemption.
*
* This testcase spawns TASKS parallel threads that runs for TIME seconds. They
* continuously spin updating a global counter (one counter for each thread).
*
* At exit each thread checks if the others have been che chance to update
* their own counter. If not, it means the preemption didn't occur and the
* testcase returns an error message.
*
* Otherwise, if all the threads have been able to update their own counter it
* means preemption successfully occurs, since there is no active sleep inside
* each thread's implementation.
*
* \author Andrea Righi <arighi@develer.com>
*
* $test$: cp bertos/cfg/cfg_proc.h $cfgdir/
* $test$: echo "#undef CONFIG_KERN" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN 1" >> $cfgdir/cfg_proc.h
* $test$: echo "#undef CONFIG_KERN_PREEMPT" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN_PREEMPT 1" >> $cfgdir/cfg_proc.h
* $test$: cp bertos/cfg/cfg_monitor.h $cfgdir/
* $test$: sed -i "s/CONFIG_KERN_MONITOR 0/CONFIG_KERN_MONITOR 1/" $cfgdir/cfg_monitor.h
* $test$: cp bertos/cfg/cfg_signal.h $cfgdir/
* $test$: echo "#undef CONFIG_KERN_SIGNALS" >> $cfgdir/cfg_signal.h
* $test$: echo "#define CONFIG_KERN_SIGNALS 1" >> $cfgdir/cfg_signal.h
*
* notest: all
*/
#include "../proc_test.c"

306
bertos/kern/sem.c Normal file
View file

@ -0,0 +1,306 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2001, 2004 Develer S.r.l. (http://www.develer.com/)
* Copyright 1999, 2000, 2001 Bernie Innocenti <bernie@codewiz.org>
* -->
*
* \brief Semaphore based synchronization services.
*
* \author Bernie Innocenti <bernie@codewiz.org>
*/
#include "sem.h"
#include <cfg/debug.h>
#include <cpu/irq.h> // ASSERT_IRQ_DISABLED()
#include <kern/proc.h>
#include <kern/proc_p.h>
#include <kern/signal.h>
INLINE void sem_verify(struct Semaphore *s)
{
(void)s;
ASSERT(s);
LIST_ASSERT_VALID(&s->wait_queue);
ASSERT(s->nest_count >= 0);
ASSERT(s->nest_count < 128); // heuristic max
}
#if CONFIG_KERN_PRI && CONFIG_KERN_PRI_INHERIT
#define proc_updatePri(proc) (proc_setPri(proc, (proc)->orig_pri))
/**
* Priority inheritance update algorithm.
*
* The algorithm checks and boosts the priority of the semaphore's
* current owner and also processes in that block the owner, which
* form a chain of blocking processes.
*
* Note that the priority of a process in the chain of blocked
* processes is always greater or equal than the priority of a process
* before in the chain. See the diagram below:
* P1 --. S1 ---> P2 --. S2 ---> P3
* prio_proc(P2) >= prio_proc(P1) always.
*/
INLINE void pri_inheritBlock(Semaphore *s)
{
Process *owner = s->owner;
/*
* Enqueue the blocking process in the owner's inheritance
* list. Notice that such process might have inherited its
* current priority from someone else.
*/
current_process->inh_link.pri = __prio_proc(current_process);
LIST_ENQUEUE(&owner->inh_list, &current_process->inh_link);
current_process->inh_blocked_by = s;
/*
* As long as a process has the power of boosting the priority
* of its lock owner...
*/
while (current_process->inh_link.pri > prio_proc(owner)) {
Process *p = owner;
/* Boost the priority of the owner */
proc_updatePri(p);
/* If the owner is not blocked, we're done */
if (!p->inh_blocked_by)
break;
/*
* Otherwise update the position of the owner
* (which is `p' at each round!) in the inheritance
* list it lies in and set up `owner' for the
* next round.
*/
REMOVE(&p->inh_link.link);
p->inh_link.pri = prio_proc(p);
owner = p->inh_blocked_by->owner;
LIST_ENQUEUE(&owner->inh_list, &p->inh_link);
}
}
/**
* Priority inheritance unblock algorithm.
*
* Pass the priority inheritance list from the current owner to the
* process that will take ownership of the semaphore next, potentially
* boosting its priority.
*
* \param proc The process that will take ownership of the semaphore.
*/
INLINE void pri_inheritUnblock(Semaphore *s, Process *proc)
{
Process *owner = s->owner;
Node *n, *temp;
Process *p;
/*
* This process has nothing more to do on a priority
* inheritance list.
*/
REMOVE(&proc->inh_link.link);
proc->inh_blocked_by = NULL;
/*
* Each process in the former owner's priority inheritance
* list that is blocked on 's' needs to be removed from
* there and added to the priority inheritance list of
* this process, since it's going to be the new owner for
* that semaphore.
*/
FOREACH_NODE_SAFE(n, temp, &owner->inh_list) {
p = containerof(n, Process, inh_link.link);
/* Ensures only the processes blocked on 's' are affected! */
if (p->inh_blocked_by == s) {
REMOVE(&p->inh_link.link);
LIST_ENQUEUE(&proc->inh_list, &p->inh_link);
/* And again, update the priority of the new owner */
if (p->inh_link.pri > prio_proc(proc))
proc_updatePri(proc);
}
}
proc_updatePri(owner);
}
#else
INLINE void pri_inheritBlock(UNUSED_ARG(Semaphore *, s))
{
}
INLINE void pri_inheritUnblock(UNUSED_ARG(Semaphore *, s), UNUSED_ARG(Process *, proc))
{
}
#endif /* CONFIG_KERN_PRI_INHERIT */
/**
* \brief Initialize a Semaphore structure.
*/
void sem_init(struct Semaphore *s)
{
LIST_INIT(&s->wait_queue);
s->owner = NULL;
s->nest_count = 0;
}
/**
* \brief Attempt to lock a semaphore without waiting.
*
* \return true in case of success, false if the semaphore
* was already locked by someone else.
*
* \note each call to sem_attempt() must be matched by a
* call to sem_release().
*
* \see sem_obtain() sem_release()
*/
bool sem_attempt(struct Semaphore *s)
{
bool result = false;
proc_forbid();
sem_verify(s);
if ((!s->owner) || (s->owner == current_process))
{
s->owner = current_process;
s->nest_count++;
result = true;
}
proc_permit();
return result;
}
/**
* \brief Lock a semaphore.
*
* If the semaphore is already owned by another process, the caller
* process will be enqueued into the waiting list and sleep until
* the semaphore is available.
*
* \note Each call to sem_obtain() must be matched by a
* call to sem_release().
*
* \note This routine is optimized for highest speed in
* the most common case: the semaphore is free or locked
* by the calling process itself. Rearranging this code
* is probably a bad idea.
*
* \sa sem_release() sem_attempt()
*/
void sem_obtain(struct Semaphore *s)
{
proc_forbid();
sem_verify(s);
/* Is the semaphore already locked by another process? */
if (UNLIKELY(s->owner && (s->owner != current_process)))
{
/* Append calling process to the wait queue */
ADDTAIL(&s->wait_queue, (Node *)current_process);
/* Trigger priority inheritance logic, if enabled */
pri_inheritBlock(s);
/*
* We will wake up only when the current owner calls
* sem_release(). Then, the semaphore will already
* be locked for us.
*/
proc_permit();
proc_switch();
}
else
{
ASSERT(LIST_EMPTY(&s->wait_queue));
/* The semaphore was free: lock it */
s->owner = current_process;
s->nest_count++;
proc_permit();
}
}
/**
* \brief Release a lock on a previously locked semaphore.
*
* If the nesting count of the semaphore reaches zero,
* the next process waiting for it will be awaken.
*
* \note This routine is optimized for highest speed in
* the most common case: the semaphore has been locked just
* once and nobody else was waiting for it. Rearranging
* this code is probably a bad idea.
*
* \sa sem_obtain() sem_attempt()
*/
void sem_release(struct Semaphore *s)
{
Process *proc = NULL;
proc_forbid();
sem_verify(s);
ASSERT(s->owner == current_process);
/*
* Decrement nesting count and check if the semaphore
* has been fully unlocked.
*/
if (--s->nest_count == 0)
{
/* Give semaphore to the first applicant, if any */
if (UNLIKELY((proc = (Process *)list_remHead(&s->wait_queue))))
{
/* Undo the effects of priority inheritance, if enabled */
pri_inheritUnblock(s, proc);
s->nest_count = 1;
s->owner = proc;
} else {
/* Disown semaphore */
s->owner = NULL;
}
}
proc_permit();
if (proc)
ATOMIC(proc_wakeup(proc));
}

81
bertos/kern/sem.h Normal file
View file

@ -0,0 +1,81 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2001,2004,2005,2006,2007 Develer S.r.l. (http://www.develer.com/)
* Copyright 1999,2000,2001 Bernie Innocenti <bernie@codewiz.org>
*
* -->
*
* \defgroup kern_sem Mutually exclusive semaphores
* \ingroup kern
* \{
* \brief Mutually exclusive semaphores.
* Shared locking not supported in this implementation.
*
*
* \author Bernie Innocenti <bernie@codewiz.org>
*
* $WIZ$ module_name = "semaphores"
* $WIZ$ module_depends = "kernel"
* $WIZ$ module_configuration = "bertos/cfg/cfg_sem.h"
*/
#ifndef KERN_SEM_H
#define KERN_SEM_H
#include <cfg/compiler.h>
#include <struct/list.h>
/* Fwd decl */
struct Process;
typedef struct Semaphore
{
struct Process *owner;
List wait_queue;
int nest_count;
} Semaphore;
/**
* \name Process synchronization services
* \{
*/
void sem_init(struct Semaphore *s);
bool sem_attempt(struct Semaphore *s);
void sem_obtain(struct Semaphore *s);
void sem_release(struct Semaphore *s);
/* \} */
/* \} */ //defgroup kern_sem
int sem_testRun(void);
int sem_testSetup(void);
int sem_testTearDown(void);
#endif /* KERN_SEM_H */

395
bertos/kern/sem_test.c Normal file
View file

@ -0,0 +1,395 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2008 Develer S.r.l. (http://www.develer.com/)
* -->
*
* \brief Semaphore test.
*
* For testing priority inversion (avoidance) a set of processes
* interacting among each others by means of two semaphores are
* disturbed by an unrelated process, i.e., a process not using
* any semaphore at all.
*
* In case of priority inversion, high priority processes
* are affected (delayed!) by such process, even it has lower
* priority, because of semaphores. On the other hand, when priority
* inheritance is enabled, non interacting low priority processes
* can't affect the execution of high priority ones.
*
* It all can be seen looking at the finishing time of the various
* processes involved in sem_inv_test (logged).
*
* Notice that priority inheritance makes sense iff priorities
* exist, so the whole test depends on CONFIG_KERN_PRI.
*
* \author Daniele Basile <asterix@develer.com>
* \author Stefano Fedrigo <aleph@develer.com>
* \author Dario Faggioli <raistlin@linux.it>
*
* $test$: cp bertos/cfg/cfg_proc.h $cfgdir/
* $test$: echo "#undef CONFIG_KERN" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN 1" >> $cfgdir/cfg_proc.h
* $test$: echo "#undef CONFIG_KERN_PRI" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN_PRI 1" >> $cfgdir/cfg_proc.h
* $test$: echo "#undef CONFIG_KERN_PRI_INHERIT" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN_PRI_INHERIT 1" >> $cfgdir/cfg_proc.h
* $test$: cp bertos/cfg/cfg_sem.h $cfgdir/
* $test$: echo "#undef CONFIG_KERN_SEMAPHORES" >> $cfgdir/cfg_sem.h
* $test$: echo "#define CONFIG_KERN_SEMAPHORES 1" >> $cfgdir/cfg_sem.h
*/
#include <cfg/debug.h>
#include <cfg/test.h>
#include <kern/sem.h>
#include <kern/proc.h>
#include <kern/irq.h>
#include <drv/timer.h>
// Global settings for the serialization test.
#define MAX_GLOBAL_COUNT 1024
#define TEST_TIME_OUT_MS 6000
#define DELAY 5
// Settings for the test processes (serialization test).
//Process 1
#define INC_PROC_T1 1
#define DELAY_PROC_T1 INC_PROC_T1*DELAY
//Process 2
#define INC_PROC_T2 3
#define DELAY_PROC_T2 INC_PROC_T2*DELAY
//Process 3
#define INC_PROC_T3 5
#define DELAY_PROC_T3 INC_PROC_T3*DELAY
//Process 4
#define INC_PROC_T4 7
#define DELAY_PROC_T4 INC_PROC_T4*DELAY
//Process 5
#define INC_PROC_T5 11
#define DELAY_PROC_T5 INC_PROC_T5*DELAY
//Process 6
#define INC_PROC_T6 13
#define DELAY_PROC_T6 INC_PROC_T6*DELAY
//Process 7
#define INC_PROC_T7 17
#define DELAY_PROC_T7 INC_PROC_T7*DELAY
//Process 8
#define INC_PROC_T8 19
#define DELAY_PROC_T8 INC_PROC_T8*DELAY
Semaphore sem;
unsigned int global_count = 0;
/*
* These macros generate the code needed to create the test process functions.
*/
#define PROC_TEST(num) static void proc_semTest##num(void) \
{ \
unsigned int local_count = 0; \
\
for (int i = 0; i < INC_PROC_T##num; ++i) \
{ \
sem_obtain(&sem); \
kprintf("> test%d: Obtain semaphore.\n", num); \
local_count = global_count; \
kprintf("> test%d: Read global count [%d]\n", num, local_count); \
timer_delay(DELAY_PROC_T##num); \
local_count += INC_PROC_T##num; \
global_count = local_count; \
kprintf("> test%d: Update count g[%d] l[%d]\n", num, global_count, local_count); \
sem_release(&sem); \
kprintf("> test%d: Relase semaphore.\n", num); \
} \
} \
#define PROC_TEST_STACK(num) PROC_DEFINE_STACK(proc_sem_test##num##_stack, KERN_MINSTACKSIZE * 2)
#define PROC_TEST_INIT(num) proc_new(proc_semTest##num, NULL, sizeof(proc_sem_test##num##_stack), proc_sem_test##num##_stack);
// Define processes for the serialization test.
PROC_TEST(1)
PROC_TEST(2)
PROC_TEST(3)
PROC_TEST(4)
PROC_TEST(5)
PROC_TEST(6)
PROC_TEST(7)
PROC_TEST(8)
#if CONFIG_KERN_PRI
// Global settings for the priority inversion test.
// 0.5 secs, enough for seeing the effects
#define BASETIME 500
Semaphore s1, s2;
unsigned int loops = 0; // For counting iterations
int finishing_time[8];
typedef enum ProcType {NONE, S1, S2, S1S2} ProcType;
/*
* Macros for the processes of the priority inversion test.
*/
#define PROC_INV_TEST(num) static void proc_semInvTest##num(void) \
{ \
ProcType p_type = (ProcType)((ssize_t) proc_currentUserData()); \
int mult = p_type == NONE ? 5 : 1; \
unsigned int i, local_count = 0; \
ticks_t start; \
\
kprintf("> test%d(%d): Start.\n", num, proc_current()->link.pri); \
finishing_time[num-1] = timer_clock(); \
\
if (p_type == S1 || p_type == S1S2) { \
kprintf("> test%d(prio=%d): Obtain %p..\n", num, \
proc_current()->link.pri, &s1); \
sem_obtain(&s1); \
kprintf("> test%d(prio=%d): Obtained %p.\n", num, \
proc_current()->link.pri, &s1); \
} \
if (p_type == S2 || p_type == S1S2) { \
kprintf("> test%d(prio=%d): Obtain %p..\n", num, \
proc_current()->link.pri, &s2); \
sem_obtain(&s2); \
kprintf("> test%d(prio=%d): Obtained %p.\n", num, \
proc_current()->link.pri, &s2); \
} \
\
start = timer_clock(); \
for (i = 0; i < loops * mult && (((unsigned)timer_clock()-start) <= loops*mult); i++) { \
local_count++; \
} \
\
sem_obtain(&sem); \
global_count += local_count; \
kprintf("> test%d(prio=%d): global_count=%u..\n", num, \
proc_current()->link.pri, global_count); \
sem_release(&sem); \
\
if (p_type == S2 || p_type == S1S2) { \
kprintf("> test%d(prio=%d): Release %p..\n", num, \
proc_current()->link.pri, &s2); \
sem_release(&s2); \
kprintf("> test%d(prio=%d): %p Released.\n", num, \
proc_current()->link.pri, &s2); \
} \
if (p_type == S1 || p_type == S1S2) { \
kprintf("> test%d(prio=%d): Release %p..\n", num, \
proc_current()->link.pri, &s1); \
sem_release(&s1); \
kprintf("> test%d(prio=%d): %p Released.\n", num, \
proc_current()->link.pri, &s1); \
} \
\
finishing_time[num-1] = timer_clock() - finishing_time[num-1]; \
kprintf("> test%d(prio=%d): Exit.\n", num, proc_current()->link.pri); \
} \
#define PROC_INV_TEST_INIT(num, pri, type) \
do { \
struct Process *p; \
\
timer_delay(10); \
p = proc_new(proc_semInvTest##num, \
((void*)type), sizeof(proc_sem_test##num##_stack), \
proc_sem_test##num##_stack); \
proc_setPri(p, pri); \
} while (0) \
// Define processes for the priority inversion test.
PROC_INV_TEST(1)
PROC_INV_TEST(2)
PROC_INV_TEST(3)
PROC_INV_TEST(4)
PROC_INV_TEST(5)
PROC_INV_TEST(6)
PROC_INV_TEST(7)
PROC_INV_TEST(8)
#endif /* CONFIG_KERN_PRI */
// Define process stacks for both of the tests.
PROC_TEST_STACK(1)
PROC_TEST_STACK(2)
PROC_TEST_STACK(3)
PROC_TEST_STACK(4)
PROC_TEST_STACK(5)
PROC_TEST_STACK(6)
PROC_TEST_STACK(7)
PROC_TEST_STACK(8)
static int sem_ser_test(void)
{
ticks_t start_time = timer_clock();
sem_init(&sem);
global_count = 0;
kprintf("Run semaphore serialization test..\n");
// Initialize the processes.
PROC_TEST_INIT(1)
PROC_TEST_INIT(2)
PROC_TEST_INIT(3)
PROC_TEST_INIT(4)
PROC_TEST_INIT(5)
PROC_TEST_INIT(6)
PROC_TEST_INIT(7)
PROC_TEST_INIT(8)
kputs("> Main: Processes created\n");
/*
* Wait until all processes exit, if something goes wrong we return an
* error after timeout_ms.
*/
while((timer_clock() - start_time) < ms_to_ticks(TEST_TIME_OUT_MS))
{
if (sem_attempt(&sem))
{
kputs("> Main: Check if test has finished..\n");
if(global_count == MAX_GLOBAL_COUNT)
{
kputs("> Main: Test Finished..Ok!\n");
return 0;
}
sem_release(&sem);
kputs("> Main: Test is still running..\n");
}
proc_yield();
}
kputs("Semaphore serialization test failed..\n");
return -1;
}
#if CONFIG_KERN_PRI
static int sem_inv_test(void)
{
int i, orig_pri = proc_current()->link.pri;
ticks_t fake, start_time;
sem_init(&sem);
global_count = 0;
loops = 0;
sem_init(&s1);
sem_init(&s2);
kputs("> Main: calibration for the busy wait cycle..\n");
proc_setPri(proc_current(), 10);
fake = start_time = timer_clock();
while ((fake - start_time) < ms_to_ticks(BASETIME)) {
fake = timer_clock();
loops++;
}
kprintf("> Main: calibration done, %dms equals to %u cycles!\n", BASETIME, loops);
kputs("> Main: Run Priority Inversion test...\n");
// Will take s2
PROC_INV_TEST_INIT(1, 2, S2);
// 2 will block on s2; 3 will take s2 and still block on s2
PROC_INV_TEST_INIT(2, 3, S2);
PROC_INV_TEST_INIT(3, 4, S1S2);
// Will block on s1, nothing happens..
PROC_INV_TEST_INIT(4, 5, S1);
// No semaphore, without PI this will delay everyone!
PROC_INV_TEST_INIT(5, 6, NONE);
// Will block on s1 and boost
PROC_INV_TEST_INIT(6, 7, S1);
PROC_INV_TEST_INIT(7, 8, S1);
PROC_INV_TEST_INIT(8, 9, S1);
// All processes created, let them run.
proc_setPri(proc_current(), orig_pri);
while ((timer_clock() - start_time) < ms_to_ticks(TEST_TIME_OUT_MS*2)) {
if (sem_attempt(&sem)) {
if (global_count >= loops*7 + loops*5) {
for (i = 0; i < 8; i++)
kprintf("> Main: I-O latency of %d = %ldms\n", i+1, (long) ms_to_ticks(finishing_time[i]));
kputs("> Main: Test Finished..Ok!\n");
return 0;
}
sem_release(&sem);
}
proc_yield();
}
kputs("> Main: Priority Inversion Test failed..\n");
return -1;
}
#else
void sem_inv_test(void)
{
}
#endif /* CONFIG_KERN_PRI */
/**
* Run semaphore test
*/
int sem_testRun(void)
{
/* Start tests */
sem_ser_test(); // Serialization
sem_inv_test(); // Priority Inversion
return 0;
}
int sem_testSetup(void)
{
kdbg_init();
kprintf("Init Timer..");
timer_init();
kprintf("Done.\n");
kprintf("Init Process..");
proc_init();
kprintf("Done.\n");
return 0;
}
int sem_testTearDown(void)
{
kputs("TearDown Semaphore test.\n");
return 0;
}
TEST_MAIN(sem);

262
bertos/kern/signal.c Normal file
View file

@ -0,0 +1,262 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2004, 2008 Develer S.r.l. (http://www.develer.com/)
* Copyright 1999, 2000, 2001 Bernie Innocenti <bernie@codewiz.org>
* -->
*
* \brief IPC signals implementation.
*
* Signals are a low-level IPC primitive. A process receives a signal
* when some external event has happened. Like interrupt requests,
* signals do not carry any additional information. If processing a
* specific event requires additional data, the process must obtain it
* through some other mechanism.
*
* Despite the name, one shouldn't confuse these signals with POSIX
* signals. POSIX signals are usually executed synchronously, like
* software interrupts.
*
* Signals are very low overhead. Using them exclusively to wait
* for multiple asynchronous events results in very simple dispatch
* logic with low processor and resource usage.
*
* The "event" module is a higher-level interface that can optionally
* deliver signals to processes. Messages provide even higher-level
* IPC services built on signals. Semaphore arbitration is also
* implemented using signals.
*
* In this implementation, each process has a limited set of signal
* bits (usually 32) and can wait for multiple signals at the same
* time using sig_wait(). Signals can also be polled using sig_check(),
* but a process spinning on its signals usually defeats their purpose
* of providing a multitasking-friendly infrastructure for event-driven
* applications.
*
* Signals are like flags: they are either active or inactive. After an
* external event has delivered a particular signal, it remains raised until
* the process acknowledges it using either sig_wait() or sig_check().
* Counting signals is not a reliable way to count how many times a
* particular event has occurred, because the same signal may be
* delivered twice before the process can notice.
*
* Signals can be delivered synchronously via sig_send() or asynchronously via
* sig_post().
*
* In the synchronous case the process is awakened if it was waiting for any
* signal and immediately dispatched for execution via a direct context switch,
* if its priority is greater than the running process.
*
* <pre>
* - Synchronous-signal delivery:
*
* [P1]____sig_send()____proc_wakeup()____[P2]
* </pre>
*
* In the asynchronous case, the process is scheduled for execution as a
* consequence of the delivery, but it will be dispatched by the scheduler as
* usual, according to the scheduling policy.
*
* <pre>
* - Asynchronous-signal delivery:
*
* [P1]____sig_post()____[P1]____proc_schedule()____[P2]
* </pre>
*
* In this way, any execution context, including an interrupt handler, can
* deliver a signal to a process. However, synchronous signal delivery from a
* non-sleepable context (like an interrupt handler) is forbidden in order to
* avoid potential deadlock conditions. Instead, sig_post() can be used from
* any context, expecially from interrupt context or when the preemption is
* disabled.
*
* Multiple independent signals may be delivered at once with a single
* invocation of sig_send() or sig_post(), although this is rarely useful.
*
* \section signal_allocation Signal Allocation
*
* There's no hardcoded mapping of specific events to signal bits.
* The meaning of a particular signal bit is defined by an agreement
* between the delivering entity and the receiving process.
* For instance, a terminal driver may be designed to deliver
* a signal bit called SIG_INT when it reads the CTRL-C sequence
* from the keyboard, and a process may react to it by quitting.
*
* \section sig_single SIG_SINGLE
*
* The SIG_SINGLE bit is reserved as a convenient shortcut in those
* simple scenarios where a process needs to wait on just one event
* synchronously. By using SIG_SINGLE, there's no need to allocate
* a specific signal from the free pool. The constraints for safely
* accessing SIG_SINGLE are:
* - The process MUST sig_wait() exclusively on SIG_SINGLE
* - SIG_SIGNAL MUST NOT be left pending after use (sig_wait() will reset
* it automatically)
* - Do not sleep between starting the asynchronous task that will fire
* SIG_SINGLE, and the call to sig_wait().
* - Do not call system functions that may implicitly sleep, such as
* timer_delayTicks().
*
* \author Bernie Innocenti <bernie@codewiz.org>
*/
#include "signal.h"
#include "cfg/cfg_timer.h"
#include <cfg/debug.h>
#include <cfg/depend.h>
#include <cpu/irq.h>
#include <kern/proc.h>
#include <kern/proc_p.h>
#if CONFIG_KERN_SIGNALS
// Check config dependencies
CONFIG_DEPEND(CONFIG_KERN_SIGNALS, CONFIG_KERN);
sigmask_t sig_waitSignal(Signal *s, sigmask_t sigs)
{
sigmask_t result;
/* Sleeping with IRQs disabled or preemption forbidden is illegal */
IRQ_ASSERT_ENABLED();
ASSERT(proc_preemptAllowed());
/*
* This is subtle: there's a race condition where a concurrent process
* or an interrupt may call sig_send()/sig_post() to set a bit in
* Process.sig_recv just after we have checked for it, but before we've
* set Process.sig_wait to let them know we want to be awaken.
*
* In this case, we'd deadlock with the signal bit already set and the
* process never being reinserted into the ready list.
*/
IRQ_DISABLE;
/* Loop until we get at least one of the signals */
while (!(result = s->recv & sigs))
{
/*
* Tell "them" that we want to be awaken when any of these
* signals arrives.
*/
s->wait = sigs;
/* Go to sleep and proc_switch() to another process. */
proc_switch();
/*
* When we come back here, the wait mask must have been
* cleared by someone through sig_send()/sig_post(), and at
* least one of the signals we were expecting must have been
* delivered to us.
*/
ASSERT(!s->wait);
ASSERT(s->recv & sigs);
}
/* Signals found: clear them and return */
s->recv &= ~sigs;
IRQ_ENABLE;
return result;
}
#if CONFIG_TIMER_EVENTS
#include <drv/timer.h>
sigmask_t sig_waitTimeoutSignal(Signal *s, sigmask_t sigs, ticks_t timeout,
Hook func, iptr_t data)
{
Timer t;
sigmask_t res;
cpu_flags_t flags;
ASSERT(!sig_checkSignal(s, SIG_TIMEOUT));
ASSERT(!(sigs & SIG_TIMEOUT));
/* IRQ are needed to run timer */
ASSERT(IRQ_ENABLED());
if (func)
timer_setSoftint(&t, func, data);
else
timer_set_event_signal(&t, proc_current(), SIG_TIMEOUT);
timer_setDelay(&t, timeout);
timer_add(&t);
res = sig_waitSignal(s, SIG_TIMEOUT | sigs);
IRQ_SAVE_DISABLE(flags);
/* Remove timer if sigs occur before timer signal */
if (!(res & SIG_TIMEOUT) && !sig_checkSignal(s, SIG_TIMEOUT))
timer_abort(&t);
IRQ_RESTORE(flags);
return res;
}
#endif // CONFIG_TIMER_EVENTS
INLINE void __sig_signal(Signal *s, Process *proc, sigmask_t sigs, bool wakeup)
{
cpu_flags_t flags;
IRQ_SAVE_DISABLE(flags);
/* Set the signals */
s->recv |= sigs;
/* Check if process needs to be awoken */
if (s->recv & s->wait)
{
ASSERT(proc != current_process);
s->wait = 0;
if (wakeup)
proc_wakeup(proc);
else
SCHED_ENQUEUE_HEAD(proc);
}
IRQ_RESTORE(flags);
}
void sig_sendSignal(Signal *s, Process *proc, sigmask_t sigs)
{
ASSERT_USER_CONTEXT();
IRQ_ASSERT_ENABLED();
ASSERT(proc_preemptAllowed());
__sig_signal(s, proc, sigs, true);
}
void sig_postSignal(Signal *s, Process *proc, sigmask_t sigs)
{
__sig_signal(s, proc, sigs, false);
}
#endif /* CONFIG_KERN_SIGNALS */

193
bertos/kern/signal.h Normal file
View file

@ -0,0 +1,193 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2004 Develer S.r.l. (http://www.develer.com/)
* Copyright 1999,2000,2001 Bernie Innocenti <bernie@codewiz.org>
*
* -->
*
* \defgroup kern_signal Kernel signals
* \ingroup kern
* \{
*
* \brief Signal module for IPC.
*
*
* \author Bernie Innocenti <bernie@codewiz.org>
*
* $WIZ$ module_name = "signal"
* $WIZ$ module_depends = "kernel", "timer"
* $WIZ$ module_configuration = "bertos/cfg/cfg_signal.h"
*/
#ifndef KERN_SIGNAL_H
#define KERN_SIGNAL_H
#include <cfg/compiler.h>
#include <cfg/macros.h> // BV()
#include <cpu/irq.h>
#include <kern/proc.h>
#if CONFIG_KERN_SIGNALS
INLINE sigmask_t __sig_checkSignal(Signal *s, sigmask_t sigs)
{
sigmask_t result;
result = s->recv & sigs;
s->recv &= ~sigs;
return result;
}
/**
* Check if any of the signals in \a sigs has occurred and clear them.
*
* \return the signals that have occurred.
*/
INLINE sigmask_t sig_checkSignal(Signal *s, sigmask_t sigs)
{
cpu_flags_t flags;
sigmask_t result;
IRQ_SAVE_DISABLE(flags);
result = __sig_checkSignal(s, sigs);
IRQ_RESTORE(flags);
return result;
}
/**
* Check if any of the signals in \a sigs has occurred and clear them.
*
* \return the signals that have occurred.
*/
INLINE sigmask_t sig_check(sigmask_t sigs)
{
Process *proc = proc_current();
return sig_checkSignal(&proc->sig, sigs);
}
void sig_sendSignal(Signal *s, Process *proc, sigmask_t sig);
/**
* Send the signals \a sigs to the process \a proc and immeditaly dispatch it
* for execution.
*
* The process will be awoken if it was waiting for any of them and immediately
* dispatched for execution.
*
* \note This function can't be called from IRQ context, use sig_post()
* instead.
*/
INLINE void sig_send(Process *proc, sigmask_t sig)
{
sig_sendSignal(&proc->sig, proc, sig);
}
void sig_postSignal(Signal *s, Process *proc, sigmask_t sig);
/**
* Send the signals \a sigs to the process \a proc.
* The process will be awoken if it was waiting for any of them.
*
* \note This call is interrupt safe.
*/
INLINE void sig_post(Process *proc, sigmask_t sig)
{
sig_postSignal(&proc->sig, proc, sig);
}
/*
* XXX: this is provided for backword compatibility, consider to make this
* deprecated for the future.
*/
INLINE void sig_signal(Process *proc, sigmask_t sig)
{
sig_postSignal(&proc->sig, proc, sig);
}
sigmask_t sig_waitSignal(Signal *s, sigmask_t sigs);
/**
* Sleep until any of the signals in \a sigs occurs.
*
* \return the signal(s) that have awoken the process.
*/
INLINE sigmask_t sig_wait(sigmask_t sigs)
{
Process *proc = proc_current();
return sig_waitSignal(&proc->sig, sigs);
}
sigmask_t sig_waitTimeoutSignal(Signal *s, sigmask_t sigs, ticks_t timeout,
Hook func, iptr_t data);
/**
* Sleep until any of the signals in \a sigs or \a timeout ticks elapse.
* If the timeout elapse a SIG_TIMEOUT is added to the received signal(s).
* \return the signal(s) that have awoken the process.
* \note Caller must check return value to check which signal awoke the process.
*/
INLINE sigmask_t sig_waitTimeout(sigmask_t sigs, ticks_t timeout)
{
Process *proc = proc_current();
return sig_waitTimeoutSignal(&proc->sig, sigs, timeout,
NULL, NULL);
}
#endif /* CONFIG_KERN_SIGNALS */
int signal_testRun(void);
int signal_testSetup(void);
int signal_testTearDown(void);
/**
* \name Signal definitions
* \{
*/
#define SIG_USER0 BV(0) /**< Free for user usage */
#define SIG_USER1 BV(1) /**< Free for user usage */
#define SIG_USER2 BV(2) /**< Free for user usage */
#define SIG_USER3 BV(3) /**< Free for user usage */
#define SIG_SINGLE BV(4) /**< Used to wait for a single event */
#define SIG_SYSTEM5 BV(5) /**< Reserved for internal system use */
#define SIG_SYSTEM6 BV(6) /**< Reserved for internal system use */
#define SIG_TIMEOUT BV(7) /**< Reserved for timeout use */
/**
* Max number of signals that can be used by drivers or user applications.
*/
#define SIG_USER_MAX SIG_SINGLE
/*\}*/
/* \} */ //defgroup kern_signal
#endif /* KERN_SIGNAL_H */

189
bertos/kern/signal_test.c Normal file
View file

@ -0,0 +1,189 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2004, 2008 Develer S.r.l. (http://www.develer.com/)
* Copyright 1999, 2000, 2001 Bernie Innocenti <bernie@codewiz.org>
* -->
*
* \brief Signals test.
*
*
* \author Daniele Basile <asterix@develer.com>
*
* $test$: cp bertos/cfg/cfg_proc.h $cfgdir/
* $test$: echo "#undef CONFIG_KERN" >> $cfgdir/cfg_proc.h
* $test$: echo "#define CONFIG_KERN 1" >> $cfgdir/cfg_proc.h
* $test$: cp bertos/cfg/cfg_signal.h $cfgdir/
* $test$: echo "#undef CONFIG_KERN_SIGNALS" >> $cfgdir/cfg_signal.h
* $test$: echo "#define CONFIG_KERN_SIGNALS 1" >> $cfgdir/cfg_signal.h
*/
#include "cfg/cfg_timer.h"
#include <cfg/debug.h>
#include <cfg/test.h>
#include <kern/signal.h>
#include <kern/proc.h>
#include <kern/irq.h>
#include <drv/timer.h>
// Set mask with all signal that we want to test.
int test_signal[] = {
SIG_USER0,
SIG_USER1,
SIG_USER2,
SIG_USER3,
SIG_TIMEOUT,
SIG_SYSTEM5,
SIG_SYSTEM6,
SIG_SINGLE
};
// Current signal to send
int count = 0;
sigmask_t sig_to_master;
sigmask_t sig_to_slave;
/*
* These macros generate the code needed to create the test process functions.
*/
#define PROC_TEST_SLAVE(index, signal) \
static void proc_signalTest##index(void) \
{ \
kputs("> Slave [" #index "]: Wait signal [" #signal "]\n"); \
sig_wait(signal); \
kputs("> Slave [" #index "]: send signal [" #signal "]\n"); \
sig_send(proc_currentUserData(), signal); \
}
#define MAIN_CHECK_SIGNAL(index, slave) \
do { \
kprintf("> Main: send signal [%d]\n", test_signal[index]); \
sig_send(slave, test_signal[index]); \
kprintf("> Main: wait signal [%d]\n", test_signal[index]); \
sig_wait(test_signal[index]); \
count++; \
} while(0) \
#if CONFIG_KERN_HEAP
#define PROC_TEST_SLAVE_INIT(index, master_process) proc_new(proc_signalTest##index, master_process, KERN_MINSTACKSIZE * 2, NULL)
#else
#define PROC_TEST_SLAVE_STACK(index) PROC_DEFINE_STACK(proc_signal_test##index##_stack, KERN_MINSTACKSIZE * 2);
#define PROC_TEST_SLAVE_INIT(index, master_process) proc_new(proc_signalTest##index, master_process, sizeof(proc_signal_test##index##_stack), proc_signal_test##index##_stack)
PROC_TEST_SLAVE_STACK(0)
PROC_TEST_SLAVE_STACK(1)
PROC_TEST_SLAVE_STACK(2)
PROC_TEST_SLAVE_STACK(3)
PROC_TEST_SLAVE_STACK(4)
PROC_TEST_SLAVE_STACK(5)
PROC_TEST_SLAVE_STACK(6)
PROC_TEST_SLAVE_STACK(7)
#endif
// Generate the code for signal test.
PROC_TEST_SLAVE(0, SIG_USER0)
PROC_TEST_SLAVE(1, SIG_USER1)
PROC_TEST_SLAVE(2, SIG_USER2)
PROC_TEST_SLAVE(3, SIG_USER3)
PROC_TEST_SLAVE(4, SIG_TIMEOUT)
PROC_TEST_SLAVE(5, SIG_SYSTEM5)
PROC_TEST_SLAVE(6, SIG_SYSTEM6)
PROC_TEST_SLAVE(7, SIG_SINGLE)
/**
* Run signal test
*/
int signal_testRun(void)
{
struct Process *main_process = proc_current();
struct Process *slave_0;
struct Process *slave_1;
struct Process *slave_2;
struct Process *slave_3;
struct Process *slave_4;
struct Process *slave_5;
struct Process *slave_6;
struct Process *slave_7;
kprintf("Run Signal test..\n");
slave_0 = PROC_TEST_SLAVE_INIT(0, main_process);
slave_1 = PROC_TEST_SLAVE_INIT(1, main_process);
slave_2 = PROC_TEST_SLAVE_INIT(2, main_process);
slave_3 = PROC_TEST_SLAVE_INIT(3, main_process);
slave_4 = PROC_TEST_SLAVE_INIT(4, main_process);
slave_5 = PROC_TEST_SLAVE_INIT(5, main_process);
slave_6 = PROC_TEST_SLAVE_INIT(6, main_process);
slave_7 = PROC_TEST_SLAVE_INIT(7, main_process);
MAIN_CHECK_SIGNAL(0, slave_0);
MAIN_CHECK_SIGNAL(1, slave_1);
MAIN_CHECK_SIGNAL(2, slave_2);
MAIN_CHECK_SIGNAL(3, slave_3);
MAIN_CHECK_SIGNAL(4, slave_4);
MAIN_CHECK_SIGNAL(5, slave_5);
MAIN_CHECK_SIGNAL(6, slave_6);
MAIN_CHECK_SIGNAL(7, slave_7);
if(count == countof(test_signal))
{
kprintf("Signal test finished..ok!\n");
return 0;
}
kprintf("Signal test finished..fail!\n");
return -1;
}
int signal_testSetup(void)
{
kdbg_init();
kprintf("Init Timer..");
timer_init();
kprintf("Done.\n");
kprintf("Init Process..");
proc_init();
kprintf("Done.\n");
return 0;
}
int signal_testTearDown(void)
{
kputs("TearDown Signal test.\n");
return 0;
}
TEST_MAIN(signal);