base: redesign object pool using lambda interface

Instead of returning pointers to locked objects via a lookup function,
the new object pool implementation restricts object access to
functors resp. lambda expressions that are applied to the objects
within the pool itself.

Fix #884
Fix #1658
This commit is contained in:
Stefan Kalkowski
2015-08-10 13:34:16 +02:00
committed by Christian Helmuth
parent 555835c95b
commit 458b4d6fc4
66 changed files with 1616 additions and 1709 deletions

View File

@ -15,35 +15,34 @@ using namespace Genode;
void Cpu_session_component::thread_id(Thread_capability thread_cap, int pid, int tid)
{
Object_pool<Cpu_thread_component>::Guard
thread(_thread_ep->lookup_and_lock(thread_cap));
if (!thread) return;
thread->platform_thread()->thread_id(pid, tid);
_thread_ep->apply(thread_cap, [&] (Cpu_thread_component *thread) {
if (thread) thread->platform_thread()->thread_id(pid, tid); });
}
Untyped_capability Cpu_session_component::server_sd(Thread_capability thread_cap)
{
Object_pool<Cpu_thread_component>::Guard
thread(_thread_ep->lookup_and_lock(thread_cap));
if (!thread) return Untyped_capability();
auto lambda = [] (Cpu_thread_component *thread) {
if (!thread) return Untyped_capability();
enum { DUMMY_LOCAL_NAME = 0 };
typedef Native_capability::Dst Dst;
return Untyped_capability(Dst(thread->platform_thread()->server_sd()),
DUMMY_LOCAL_NAME);
enum { DUMMY_LOCAL_NAME = 0 };
typedef Native_capability::Dst Dst;
return Untyped_capability(Dst(thread->platform_thread()->server_sd()),
DUMMY_LOCAL_NAME);
};
return _thread_ep->apply(thread_cap, lambda);
}
Untyped_capability Cpu_session_component::client_sd(Thread_capability thread_cap)
{
Object_pool<Cpu_thread_component>::Guard
thread(_thread_ep->lookup_and_lock(thread_cap));
if (!thread) return Untyped_capability();
auto lambda = [] (Cpu_thread_component *thread) {
if (!thread) return Untyped_capability();
enum { DUMMY_LOCAL_NAME = 0 };
typedef Native_capability::Dst Dst;
return Untyped_capability(Dst(thread->platform_thread()->client_sd()),
DUMMY_LOCAL_NAME);
enum { DUMMY_LOCAL_NAME = 0 };
typedef Native_capability::Dst Dst;
return Untyped_capability(Dst(thread->platform_thread()->client_sd()),
DUMMY_LOCAL_NAME);
};
return _thread_ep->apply(thread_cap, lambda);
}

View File

@ -40,16 +40,15 @@ namespace Genode {
*/
Thread_capability thread_cap() { return _thread_cap; } const
void thread_cap(Thread_capability cap) { _thread_cap = cap; }
/* required by lookup_and_lock, provided by Object_pool::Entry normally */
void release() { }
};
struct Pager_entrypoint
{
Pager_entrypoint(Cap_session *) { }
Pager_object *lookup_and_lock(Pager_capability) { return 0; }
template <typename FUNC>
auto apply(Pager_capability, FUNC f) -> decltype(f(nullptr)) {
return f(nullptr); }
};
}

View File

@ -24,6 +24,7 @@
namespace Genode {
class Dataspace_component;
class Pd_session_component : public Rpc_object<Linux_pd_session, Pd_session_component>
{
private:
@ -39,6 +40,8 @@ namespace Genode {
Parent_capability _parent;
Rpc_entrypoint *_ds_ep;
void _start(Dataspace_component *ds);
public:
/**

View File

@ -305,6 +305,98 @@ static const char *get_env(const char *key)
** PD session interface **
**************************/
void Pd_session_component::_start(Dataspace_component *ds)
{
const char *tmp_filename = "temporary_executable_elf_dataspace_file_for_execve";
if (!ds) {
PERR("could not lookup binary, aborted PD startup");
return; /* XXX reflect error to client */
}
/* we need 's' on stack to make it an lvalue with an lvalue member we use the pointer to */
Linux_dataspace::Filename s = ds->fname();
const char *filename = s.buf;
/*
* In order to be executable via 'execve', a program must be represented as
* a file on the Linux file system. However, this is not the case for a
* plain RAM dataspace that contains an ELF image. In this case, we copy
* the dataspace content into a temporary file whose path is passed to
* 'execve()'.
*/
if (strcmp(filename, "") == 0) {
filename = tmp_filename;
int tmp_binary_fd = lx_open(filename, O_CREAT | O_EXCL | O_WRONLY, S_IRWXU);
if (tmp_binary_fd < 0) {
PERR("Could not create file '%s'", filename);
return; /* XXX reflect error to client */
}
char buf[4096];
int num_bytes = 0;
while ((num_bytes = lx_read(ds->fd().dst().socket, buf, sizeof(buf))) != 0)
lx_write(tmp_binary_fd, buf, num_bytes);
lx_close(tmp_binary_fd);
}
/* pass parent capability as environment variable to the child */
enum { ENV_STR_LEN = 256 };
static char envbuf[5][ENV_STR_LEN];
Genode::snprintf(envbuf[1], ENV_STR_LEN, "parent_local_name=%lu",
_parent.local_name());
Genode::snprintf(envbuf[2], ENV_STR_LEN, "DISPLAY=%s",
get_env("DISPLAY"));
Genode::snprintf(envbuf[3], ENV_STR_LEN, "HOME=%s",
get_env("HOME"));
Genode::snprintf(envbuf[4], ENV_STR_LEN, "LD_LIBRARY_PATH=%s",
get_env("LD_LIBRARY_PATH"));
char *env[] = { &envbuf[0][0], &envbuf[1][0], &envbuf[2][0],
&envbuf[3][0], &envbuf[4][0], 0 };
/* prefix name of Linux program (helps killing some zombies) */
char const *prefix = "[Genode] ";
char pname_buf[sizeof(_label) + sizeof(prefix)];
snprintf(pname_buf, sizeof(pname_buf), "%s%s", prefix, _label);
char *argv_buf[2];
argv_buf[0] = pname_buf;
argv_buf[1] = 0;
/*
* We cannot create the new process via 'fork()' because all our used
* memory including stack memory is backed by dataspaces, which had been
* mapped with the 'MAP_SHARED' flag. Therefore, after being created, the
* new process starts using the stack with the same physical memory pages
* as used by parent process. This would ultimately lead to stack
* corruption. To prevent both processes from concurrently accessing the
* same stack, we pause the execution of the parent until the child calls
* 'execve'. From then on, the child has its private memory layout. The
* desired behaviour is normally provided by 'vfork' but we use the more
* modern 'clone' call for this purpose.
*/
enum { STACK_SIZE = 4096 };
static char stack[STACK_SIZE]; /* initial stack used by the child until
calling 'execve' */
/*
* Argument frame as passed to 'clone'. Because, we can only pass a single
* pointer, all arguments are embedded within the 'execve_args' struct.
*/
Execve_args arg(filename, _root, argv_buf, env, _uid, _gid,
_parent.dst().socket);
_pid = lx_create_process((int (*)(void *))_exec_child,
stack + STACK_SIZE - sizeof(umword_t), &arg);
if (strcmp(filename, tmp_filename) == 0)
lx_unlink(filename);
}
Pd_session_component::Pd_session_component(Rpc_entrypoint * ep,
Allocator * md_alloc,
const char * args)
@ -366,94 +458,7 @@ int Pd_session_component::assign_parent(Parent_capability parent)
void Pd_session_component::start(Capability<Dataspace> binary)
{
const char *tmp_filename = "temporary_executable_elf_dataspace_file_for_execve";
/* lookup binary dataspace */
Object_pool<Dataspace_component>::Guard ds(_ds_ep->lookup_and_lock(binary));
if (!ds) {
PERR("could not lookup binary, aborted PD startup");
return; /* XXX reflect error to client */
}
/* we need 's' on stack to make it an lvalue with an lvalue member we use the pointer to */
Linux_dataspace::Filename s = ds->fname();
const char *filename = s.buf;
/*
* In order to be executable via 'execve', a program must be represented as
* a file on the Linux file system. However, this is not the case for a
* plain RAM dataspace that contains an ELF image. In this case, we copy
* the dataspace content into a temporary file whose path is passed to
* 'execve()'.
*/
if (strcmp(filename, "") == 0) {
filename = tmp_filename;
int tmp_binary_fd = lx_open(filename, O_CREAT | O_EXCL | O_WRONLY, S_IRWXU);
if (tmp_binary_fd < 0) {
PERR("Could not create file '%s'", filename);
return; /* XXX reflect error to client */
}
char buf[4096];
int num_bytes = 0;
while ((num_bytes = lx_read(ds->fd().dst().socket, buf, sizeof(buf))) != 0)
lx_write(tmp_binary_fd, buf, num_bytes);
lx_close(tmp_binary_fd);
}
/* pass parent capability as environment variable to the child */
enum { ENV_STR_LEN = 256 };
static char envbuf[5][ENV_STR_LEN];
Genode::snprintf(envbuf[1], ENV_STR_LEN, "parent_local_name=%lu",
_parent.local_name());
Genode::snprintf(envbuf[2], ENV_STR_LEN, "DISPLAY=%s",
get_env("DISPLAY"));
Genode::snprintf(envbuf[3], ENV_STR_LEN, "HOME=%s",
get_env("HOME"));
Genode::snprintf(envbuf[4], ENV_STR_LEN, "LD_LIBRARY_PATH=%s",
get_env("LD_LIBRARY_PATH"));
char *env[] = { &envbuf[0][0], &envbuf[1][0], &envbuf[2][0],
&envbuf[3][0], &envbuf[4][0], 0 };
/* prefix name of Linux program (helps killing some zombies) */
char const *prefix = "[Genode] ";
char pname_buf[sizeof(_label) + sizeof(prefix)];
snprintf(pname_buf, sizeof(pname_buf), "%s%s", prefix, _label);
char *argv_buf[2];
argv_buf[0] = pname_buf;
argv_buf[1] = 0;
/*
* We cannot create the new process via 'fork()' because all our used
* memory including stack memory is backed by dataspaces, which had been
* mapped with the 'MAP_SHARED' flag. Therefore, after being created, the
* new process starts using the stack with the same physical memory pages
* as used by parent process. This would ultimately lead to stack
* corruption. To prevent both processes from concurrently accessing the
* same stack, we pause the execution of the parent until the child calls
* 'execve'. From then on, the child has its private memory layout. The
* desired behaviour is normally provided by 'vfork' but we use the more
* modern 'clone' call for this purpose.
*/
enum { STACK_SIZE = 4096 };
static char stack[STACK_SIZE]; /* initial stack used by the child until
calling 'execve' */
/*
* Argument frame as passed to 'clone'. Because, we can only pass a single
* pointer, all arguments are embedded within the 'execve_args' struct.
*/
Execve_args arg(filename, _root, argv_buf, env, _uid, _gid,
_parent.dst().socket);
_pid = lx_create_process((int (*)(void *))_exec_child,
stack + STACK_SIZE - sizeof(umword_t), &arg);
if (strcmp(filename, tmp_filename) == 0)
lx_unlink(filename);
_ds_ep->apply(binary, [&] (Dataspace_component *ds) {
_start(ds); });
};

View File

@ -193,10 +193,8 @@ Platform_env_base::Rm_session_mmap::_dataspace_size(Capability<Dataspace> ds_cap
}
/* use local function call if called from the entrypoint */
Object_pool<Rpc_object_base>::Guard
ds_rpc(core_env()->entrypoint()->lookup_and_lock(ds_cap));
Dataspace * ds = dynamic_cast<Dataspace *>(&*ds_rpc);
return ds ? ds->size() : 0;
return core_env()->entrypoint()->apply(ds_cap, [] (Dataspace *ds) {
return ds ? ds->size() : 0; });
}
@ -212,10 +210,6 @@ int Platform_env_base::Rm_session_mmap::_dataspace_fd(Capability<Dataspace> ds_c
Capability<Linux_dataspace> lx_ds_cap = static_cap_cast<Linux_dataspace>(ds_cap);
Object_pool<Rpc_object_base>::Guard
ds_rpc(core_env()->entrypoint()->lookup_and_lock(lx_ds_cap));
Linux_dataspace * ds = dynamic_cast<Linux_dataspace *>(&*ds_rpc);
/*
* Return a duplicate of the dataspace file descriptor, which will be freed
* immediately after mmap'ing the file (see 'Rm_session_mmap').
@ -225,7 +219,8 @@ int Platform_env_base::Rm_session_mmap::_dataspace_fd(Capability<Dataspace> ds_c
* socket descriptor during the RPC handling). When later destroying the
* dataspace, the descriptor would unexpectedly be closed again.
*/
return ds ? lx_dup(ds->fd().dst().socket) : -1;
return core_env()->entrypoint()->apply(lx_ds_cap, [] (Linux_dataspace *ds) {
return ds ? lx_dup(ds->fd().dst().socket) : -1; });
}
@ -239,9 +234,6 @@ bool Platform_env_base::Rm_session_mmap::_dataspace_writable(Dataspace_capabilit
return writable;
}
Object_pool<Rpc_object_base>::Guard
ds_rpc(core_env()->entrypoint()->lookup_and_lock(ds_cap));
Dataspace * ds = dynamic_cast<Dataspace *>(&*ds_rpc);
return ds ? ds->writable() : false;
return core_env()->entrypoint()->apply(ds_cap, [] (Dataspace *ds) {
return ds ? ds->writable() : false; });
}