genode/repos/dde_linux/src/include/lx_emul/impl/internal/task.h

256 lines
5.7 KiB
C++

/*
* \brief Lx::Task represents a cooperatively scheduled thread of control
* \author Sebastian Sumpf
* \author Josef Soentgen
* \author Norman Feske
* \date 2014-10-10
*/
/*
* Copyright (C) 2014 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _LX_EMUL__IMPL__INTERNAL__TASK_H_
#define _LX_EMUL__IMPL__INTERNAL__TASK_H_
/* libc includes */
#include <setjmp.h>
/* Genode includes */
#include <base/sleep.h>
/* Linux emulation environment includes */
#include <lx_emul/impl/internal/list.h>
#include <lx_emul/impl/internal/arch_execute.h>
namespace Lx {
class Scheduler;
class Task;
}
/**
* Allows pseudo-parallel execution of functions
*/
class Lx::Task : public Lx::List<Lx::Task>::Element
{
public:
/**
* TODO generalize - higher is more important
*/
enum Priority { PRIORITY_0, PRIORITY_1, PRIORITY_2, PRIORITY_3 };
/**
* Runtime state
*
* INIT
* |
* [run]
* v
* BLOCKED <--[block]--- RUNNING ---[mutex_block]--> MUTEX_BLOCKED
* --[unblock]-> <-[mutex_unblock]--
*
* Transitions between BLOCKED and MUTEX_BLOCKED are not possible.
*/
enum State { STATE_INIT, STATE_RUNNING, STATE_BLOCKED, STATE_MUTEX_BLOCKED, STATE_WAIT_BLOCKED };
/**
* List element type
*/
typedef Lx::List_element<Lx::Task> List_element;
/**
* List type
*/
typedef Lx::List<List_element> List;
private:
bool verbose = true;
State _state = STATE_INIT;
/* sub-classes may overwrite the runnable condition */
virtual bool _runnable() const
{
switch (_state) {
case STATE_INIT: return true;
case STATE_RUNNING: return true;
case STATE_BLOCKED: return false;
case STATE_MUTEX_BLOCKED: return false;
case STATE_WAIT_BLOCKED: return false;
}
PERR("state %d not handled by switch", _state);
Genode::sleep_forever();
}
void *_stack = nullptr; /* stack pointer */
jmp_buf _env; /* execution state */
jmp_buf _saved_env; /* saved state of thread calling run() */
Priority _priority;
Scheduler &_scheduler; /* scheduler this task is attached to */
void (*_func)(void *); /* function to call*/
void *_arg; /* argument for function */
char const *_name; /* name of task */
List_element _mutex_le { this }; /* list element for mutex_blocked state */
List *_wait_list { 0 };
List_element _wait_le { this };
bool _wait_le_enqueued { false };
inline void _deinit();
public:
inline Task(void (*func)(void*), void *arg, char const *name,
Priority priority, Scheduler &scheduler);
virtual ~Task() { _deinit(); }
State state() const { return _state; }
Priority priority() const { return _priority; }
void wait_enqueue(List *list)
{
if (_wait_le_enqueued) {
PERR("%p already queued in %p", this, _wait_list);
Genode::sleep_forever();
}
_wait_le_enqueued = true;
_wait_list = list;
_wait_list->append(&_wait_le);
}
void wait_dequeue(List *list)
{
if (!_wait_le_enqueued) {
PERR("%p not queued", this);
Genode::sleep_forever();
}
if (_wait_list != list) {
PERR("especially not in list %p", list);
Genode::sleep_forever();
}
_wait_list->remove(&_wait_le);
_wait_list = 0;
_wait_le_enqueued = false;
}
/*******************************
** Runtime state transitions **
*******************************/
void block()
{
if (_state == STATE_RUNNING) {
_state = STATE_BLOCKED;
}
}
void unblock()
{
if (_state == STATE_BLOCKED) {
_state = STATE_RUNNING;
}
}
void mutex_block(List *list)
{
if (_state == STATE_RUNNING) {
_state = STATE_MUTEX_BLOCKED;
list->append(&_mutex_le);
}
}
void mutex_unblock(List *list)
{
if (_state == STATE_MUTEX_BLOCKED) {
_state = STATE_RUNNING;
list->remove(&_mutex_le);
}
}
/**
* Run task until next preemption point
*
* \return true if run, false if not runnable
*/
bool run()
{
if (!_runnable())
return false;
/*
* Save the execution environment. The scheduled task returns to this point
* after execution, i.e., at the next preemption point.
*/
if (_setjmp(_saved_env))
return true;
if (_state == STATE_INIT) {
/* setup execution environment and call task's function */
_state = STATE_RUNNING;
Genode::Thread_base *th = Genode::Thread_base::myself();
enum { STACK_SIZE = 32 * 1024 }; /* FIXME make stack size configurable */
_stack = th->alloc_secondary_stack(_name, STACK_SIZE);
/* switch stack and call '_func(_arg)' */
arch_execute(_stack, (void *)_func, _arg);
} else {
/* restore execution environment */
_longjmp(_env, 1);
}
/* never reached */
PERR("Unexpected return of Task");
Genode::sleep_forever();
}
/**
* Request scheduling (of other tasks)
*
* Note, this task may not be blocked when calling schedule() depending
* on the use case.
*/
void schedule()
{
/*
* Save the execution environment. The task will resume from here on next
* schedule.
*/
if (_setjmp(_env))
return;
/* return to thread calling run() */
_longjmp(_saved_env, 1);
}
/**
* Shortcut to enter blocking state and request scheduling
*/
void block_and_schedule()
{
block();
schedule();
}
/**
* Return the name of the task (mainly for debugging purposes)
*/
char const *name() { return _name; }
};
#endif /* _LX_EMUL__IMPL__INTERNAL__TASK_H_ */