os/slave.h: Remove use of global allocator

This patch eliminates the need for a global allocator by passing the
parent-service registry as argument to the 'Slave::Policy' constructor.

Fixes #2269
This commit is contained in:
Norman Feske 2017-02-01 14:04:53 +01:00
parent c0d61858c3
commit f8349b5bc7
9 changed files with 148 additions and 170 deletions

View File

@ -16,6 +16,7 @@
/* Genode includes */ /* Genode includes */
#include <base/lock.h> #include <base/lock.h>
#include <os/static_parent_services.h>
#include <os/slave.h> #include <os/slave.h>
#include <report_session/connection.h> #include <report_session/connection.h>
#include <rom_session/connection.h> #include <rom_session/connection.h>
@ -25,7 +26,14 @@ class Report_rom_slave : public Genode::Noncopyable
{ {
private: private:
class Policy : public Genode::Slave::Policy class Policy
:
private Genode::Static_parent_services<Genode::Rom_session,
Genode::Cpu_session,
Genode::Pd_session,
Genode::Ram_session,
Genode::Log_session>,
public Genode::Slave::Policy
{ {
private: private:
@ -35,14 +43,6 @@ class Report_rom_slave : public Genode::Noncopyable
protected: protected:
char const **_permitted_services() const
{
static char const *permitted_services[] = {
"ROM", "CPU", "PD", "RAM", "LOG", 0 };
return permitted_services;
};
static Name _name() { return "report_rom"; } static Name _name() { return "report_rom"; }
static Genode::size_t _quota() { return 1024*1024; } static Genode::size_t _quota() { return 1024*1024; }
@ -53,7 +53,7 @@ class Report_rom_slave : public Genode::Noncopyable
Genode::Ram_session_capability ram, Genode::Ram_session_capability ram,
const char *config) const char *config)
: :
Genode::Slave::Policy(_name(), _name(), ep, rm, ram, _quota()) Genode::Slave::Policy(_name(), _name(), *this, ep, rm, ram, _quota())
{ {
if (config) if (config)
configure(config); configure(config);

View File

@ -15,6 +15,7 @@
#define _MENU_VIEW_SLAVE_H_ #define _MENU_VIEW_SLAVE_H_
/* Genode includes */ /* Genode includes */
#include <os/static_parent_services.h>
#include <os/slave.h> #include <os/slave.h>
#include <nitpicker_session/nitpicker_session.h> #include <nitpicker_session/nitpicker_session.h>
@ -35,7 +36,15 @@ class Launcher::Menu_view_slave
private: private:
class Policy : public Genode::Slave::Policy class Policy
:
private Genode::Static_parent_services<Genode::Cpu_session,
Genode::Pd_session,
Genode::Ram_session,
Genode::Rom_session,
Genode::Log_session,
Timer::Session>,
public Genode::Slave::Policy
{ {
private: private:
@ -45,16 +54,6 @@ class Launcher::Menu_view_slave
Position _position; Position _position;
protected:
char const **_permitted_services() const
{
static char const *permitted_services[] = {
"CPU", "PD", "RAM", "ROM", "LOG", "Timer", 0 };
return permitted_services;
};
private: private:
void _configure(Position pos) void _configure(Position pos)
@ -87,7 +86,7 @@ class Launcher::Menu_view_slave
Capability<Report::Session> hover_report_session, Capability<Report::Session> hover_report_session,
Position position) Position position)
: :
Genode::Slave::Policy(_name(), _name(), ep, rm, ram, _quota()), Genode::Slave::Policy(_name(), _name(), *this, ep, rm, ram, _quota()),
_nitpicker(nitpicker_session), _nitpicker(nitpicker_session),
_dialog_rom(dialog_rom_session), _dialog_rom(dialog_rom_session),
_hover_report(hover_report_session), _hover_report(hover_report_session),

View File

@ -15,6 +15,7 @@
#define _NIT_FADER_SLAVE_H_ #define _NIT_FADER_SLAVE_H_
/* Genode includes */ /* Genode includes */
#include <os/static_parent_services.h>
#include <os/slave.h> #include <os/slave.h>
#include <nitpicker_session/nitpicker_session.h> #include <nitpicker_session/nitpicker_session.h>
@ -28,7 +29,15 @@ class Launcher::Nit_fader_slave
{ {
private: private:
class Policy : public Slave::Policy class Policy
:
private Genode::Static_parent_services<Genode::Ram_session,
Genode::Cpu_session,
Genode::Pd_session,
Genode::Rom_session,
Genode::Log_session,
Timer::Session>,
public Slave::Policy
{ {
private: private:
@ -36,14 +45,6 @@ class Launcher::Nit_fader_slave
protected: protected:
char const **_permitted_services() const
{
static char const *permitted_services[] = {
"RAM", "CPU", "PD", "ROM", "LOG", "Timer", 0 };
return permitted_services;
};
static Name _name() { return "nit_fader"; } static Name _name() { return "nit_fader"; }
static size_t _quota() { return 2*1024*1024; } static size_t _quota() { return 2*1024*1024; }
@ -54,7 +55,7 @@ class Launcher::Nit_fader_slave
Ram_session_capability ram, Ram_session_capability ram,
Genode::Service &nitpicker_service) Genode::Service &nitpicker_service)
: :
Genode::Slave::Policy(_name(), _name(), ep, rm, ram, _quota()), Genode::Slave::Policy(_name(), _name(), *this, ep, rm, ram, _quota()),
_nitpicker_service(nitpicker_service) _nitpicker_service(nitpicker_service)
{ {
visible(false); visible(false);

View File

@ -23,6 +23,9 @@
/* Genode includes */ /* Genode includes */
#include <input/component.h> #include <input/component.h>
#include <timer_session/timer_session.h>
#include <audio_out_session/audio_out_session.h>
#include <os/static_parent_services.h>
#include <os/slave.h> #include <os/slave.h>
/* local includes */ /* local includes */
@ -36,7 +39,16 @@ class Avplay_slave : public QObject
private: private:
class Policy : public Genode::Slave::Policy class Policy
:
private Genode::Static_parent_services<Genode::Cpu_session,
Genode::Log_session,
Genode::Pd_session,
Genode::Ram_session,
Genode::Rom_session,
Timer::Session,
Audio_out::Session>,
public Genode::Slave::Policy
{ {
private: private:
@ -94,16 +106,6 @@ class Avplay_slave : public QObject
static Genode::size_t _quota() { return 32*1024*1024; } static Genode::size_t _quota() { return 32*1024*1024; }
static Name _name() { return "avplay"; } static Name _name() { return "avplay"; }
protected:
const char **_permitted_services() const override
{
static const char *permitted_services[] = {
"CPU", "LOG", "PD", "RAM", "RM", "ROM", "Timer", "Audio_out", 0 };
return permitted_services;
};
public: public:
Policy(Genode::Rpc_entrypoint &entrypoint, Policy(Genode::Rpc_entrypoint &entrypoint,
@ -112,12 +114,13 @@ class Avplay_slave : public QObject
Input_service &input_service, Input_service &input_service,
Framebuffer_service_factory &framebuffer_service_factory, Framebuffer_service_factory &framebuffer_service_factory,
char const *mediafile) char const *mediafile)
: Genode::Slave::Policy(_name(), _name(), entrypoint, rm, ram, :
_quota()), Genode::Slave::Policy(_name(), _name(), *this, entrypoint,
_input_service(input_service), rm, ram, _quota()),
_framebuffer_service_factory(framebuffer_service_factory), _input_service(input_service),
_mediafile(mediafile), _framebuffer_service_factory(framebuffer_service_factory),
_sdl_audio_volume(100) _mediafile(mediafile),
_sdl_audio_volume(100)
{ {
configure(_config()); configure(_config());
} }

View File

@ -16,7 +16,9 @@
/* Genode includes */ /* Genode includes */
#include <base/service.h> #include <base/service.h>
#include <os/static_parent_services.h>
#include <os/slave.h> #include <os/slave.h>
#include <timer_session/timer_session.h>
/* local includes */ /* local includes */
#include "framebuffer_service_factory.h" #include "framebuffer_service_factory.h"
@ -26,22 +28,20 @@ class Filter_framebuffer_slave
{ {
private: private:
class Policy : public Genode::Slave::Policy class Policy
:
private Genode::Static_parent_services<Genode::Cpu_session,
Genode::Log_session,
Genode::Pd_session,
Genode::Ram_session,
Genode::Rom_session,
Timer::Session>,
public Genode::Slave::Policy
{ {
private: private:
Framebuffer_service_factory &_framebuffer_service_factory; Framebuffer_service_factory &_framebuffer_service_factory;
protected:
const char **_permitted_services() const
{
static const char *permitted_services[] = {
"CPU", "LOG", "PD", "RAM", "RM", "ROM", "Timer", 0 };
return permitted_services;
};
public: public:
Policy(Genode::Rpc_entrypoint &entrypoint, Policy(Genode::Rpc_entrypoint &entrypoint,
@ -50,11 +50,13 @@ class Filter_framebuffer_slave
Name const &name, Name const &name,
size_t quota, size_t quota,
Framebuffer_service_factory &framebuffer_service_factory) Framebuffer_service_factory &framebuffer_service_factory)
: Genode::Slave::Policy(name, name, entrypoint, rm, ram, quota), :
_framebuffer_service_factory(framebuffer_service_factory) { } Genode::Slave::Policy(name, name, *this, entrypoint, rm, ram, quota),
_framebuffer_service_factory(framebuffer_service_factory)
{ }
Genode::Service &resolve_session_request(Genode::Service::Name const &service_name, Genode::Service &resolve_session_request(Genode::Service::Name const &service_name,
Genode::Session_state::Args const &args) override Genode::Session_state::Args const &args) override
{ {
if (service_name == "Framebuffer") if (service_name == "Framebuffer")
return _framebuffer_service_factory.create(args); return _framebuffer_service_factory.create(args);

View File

@ -47,18 +47,9 @@ class Genode::Slave::Policy : public Child_policy
typedef Child_policy::Name Name; typedef Child_policy::Name Name;
typedef Session_label Label; typedef Session_label Label;
protected:
typedef Registered<Genode::Parent_service> Parent_service; typedef Registered<Genode::Parent_service> Parent_service;
typedef Registry<Parent_service> Parent_services; typedef Registry<Parent_service> Parent_services;
/**
* Return white list of services the slave is permitted to use
*
* The list is terminated via a null pointer.
*/
virtual char const **_permitted_services() const = 0;
private: private:
Label const _label; Label const _label;
@ -66,20 +57,10 @@ class Genode::Slave::Policy : public Child_policy
Ram_session_client _ram; Ram_session_client _ram;
Genode::Parent_service _binary_service; Genode::Parent_service _binary_service;
size_t _ram_quota; size_t _ram_quota;
Parent_services _parent_services; Parent_services &_parent_services;
Rpc_entrypoint &_ep; Rpc_entrypoint &_ep;
Child_policy_dynamic_rom_file _config_policy; Child_policy_dynamic_rom_file _config_policy;
Session_requester _session_requester;
bool _service_permitted(Service::Name const &service_name) const
{
for (const char **s = _permitted_services(); *s; ++s)
if (service_name == *s)
return true;
return false;
}
Session_requester _session_requester;
public: public:
@ -98,6 +79,7 @@ class Genode::Slave::Policy : public Child_policy
*/ */
Policy(Label const &label, Policy(Label const &label,
Name const &binary_name, Name const &binary_name,
Parent_services &parent_services,
Rpc_entrypoint &ep, Rpc_entrypoint &ep,
Region_map &rm, Region_map &rm,
Ram_session_capability ram_cap, Ram_session_capability ram_cap,
@ -105,7 +87,7 @@ class Genode::Slave::Policy : public Child_policy
: :
_label(label), _binary_name(binary_name), _ram(ram_cap), _label(label), _binary_name(binary_name), _ram(ram_cap),
_binary_service(Rom_session::service_name()), _binary_service(Rom_session::service_name()),
_ram_quota(ram_quota), _ep(ep), _ram_quota(ram_quota), _parent_services(parent_services), _ep(ep),
_config_policy("config", _ep, &_ram), _config_policy("config", _ep, &_ram),
_session_requester(ep, _ram, rm) _session_requester(ep, _ram, rm)
{ {
@ -163,21 +145,17 @@ class Genode::Slave::Policy : public Child_policy
if (rom_name == "session_requests") return _session_requester.service(); if (rom_name == "session_requests") return _session_requester.service();
} }
if (!_service_permitted(service_name)) {
error(name(), ": illegal session request of "
"service \"", service_name, "\" (", args, ")");
throw Parent::Service_denied();
}
/* fill parent service registry on demand */ /* fill parent service registry on demand */
Parent_service *service = nullptr; Parent_service *service = nullptr;
_parent_services.for_each([&] (Parent_service &s) { _parent_services.for_each([&] (Parent_service &s) {
if (!service && s.name() == service_name) if (!service && s.name() == service_name)
service = &s; }); service = &s; });
if (!service) if (!service) {
service = new (env()->heap()) error(name(), ": illegal session request of "
Parent_service(_parent_services, service_name); "service \"", service_name, "\" (", args, ")");
throw Parent::Service_denied();
}
return *service; return *service;
} }

View File

@ -13,23 +13,22 @@
#pragma once #pragma once
#include <os/static_parent_services.h>
#include <os/slave.h> #include <os/slave.h>
enum { STACK_SIZE = 4 * sizeof(void *) * 1024 }; enum { STACK_SIZE = 4 * sizeof(void *) * 1024 };
namespace Platform { class Device_pd_policy; } namespace Platform { class Device_pd_policy; }
class Platform::Device_pd_policy : public Genode::Slave::Policy class Platform::Device_pd_policy
:
private Genode::Static_parent_services<Genode::Ram_session,
Genode::Pd_session,
Genode::Cpu_session,
Genode::Log_session,
Genode::Rom_session>,
public Genode::Slave::Policy
{ {
protected:
char const **_permitted_services() const override
{
static char const *permitted_services[] = {
"RAM", "PD", "CPU", "LOG", "ROM", 0 };
return permitted_services;
};
public: public:
Device_pd_policy(Genode::Rpc_entrypoint &slave_ep, Device_pd_policy(Genode::Rpc_entrypoint &slave_ep,
@ -38,9 +37,8 @@ class Platform::Device_pd_policy : public Genode::Slave::Policy
Genode::size_t ram_quota, Genode::size_t ram_quota,
Genode::Session_label const &label) Genode::Session_label const &label)
: :
Genode::Slave::Policy(label, "device_pd", slave_ep, local_rm, Genode::Slave::Policy(label, "device_pd", *this, slave_ep, local_rm,
ram_ref_cap, ram_quota) ram_ref_cap, ram_quota)
{ } { }
}; };

View File

@ -13,6 +13,7 @@
/* Genode includes */ /* Genode includes */
#include <base/component.h> #include <base/component.h>
#include <os/static_parent_services.h>
#include <os/slave.h> #include <os/slave.h>
#include <timer_session/connection.h> #include <timer_session/connection.h>
@ -26,20 +27,16 @@ namespace Test {
} }
struct Test::Policy : Genode::Slave::Policy struct Test::Policy
:
private Static_parent_services<Cpu_session, Ram_session, Rom_session,
Pd_session, Log_session>,
public Slave::Policy
{ {
const char **_permitted_services() const Policy(Env &env, Name const &name)
{
static const char *permitted_services[] = {
"CPU", "RAM", "ROM", "PD", "LOG", 0 };
return permitted_services;
};
Policy(Genode::Env &env, Name const &name)
: :
Genode::Slave::Policy(name, name, env.ep().rpc_ep(), env.rm(), Slave::Policy(name, name, *this, env.ep().rpc_ep(), env.rm(),
env.ram_session_cap(), 1024*1024) env.ram_session_cap(), 1024*1024)
{ } { }
}; };

View File

@ -32,8 +32,15 @@
#include <base/log.h> #include <base/log.h>
#include <base/signal.h> #include <base/signal.h>
#include <timer_session/connection.h> #include <timer_session/connection.h>
#include <os/static_parent_services.h>
#include <os/slave.h> #include <os/slave.h>
namespace Test {
class Child;
class Parent;
using namespace Genode;
}
/**************** /****************
** Child role ** ** Child role **
@ -43,21 +50,19 @@
* The child eats more and more RAM. However, when receiving a yield request, * The child eats more and more RAM. However, when receiving a yield request,
* it releases the requested amount of resources. * it releases the requested amount of resources.
*/ */
class Child class Test::Child
{ {
private: private:
typedef Genode::size_t size_t; struct Ram_chunk : List<Ram_chunk>::Element
struct Ram_chunk : Genode::List<Ram_chunk>::Element
{ {
Genode::Env &env; Env &env;
size_t const size; size_t const size;
Genode::Ram_dataspace_capability ds_cap; Ram_dataspace_capability ds_cap;
Ram_chunk(Genode::Env &env, size_t size) Ram_chunk(Env &env, size_t size)
: :
env(env),size(size), ds_cap(env.ram().alloc(size)) env(env),size(size), ds_cap(env.ram().alloc(size))
{ } { }
@ -65,14 +70,14 @@ class Child
~Ram_chunk() { env.ram().free(ds_cap); } ~Ram_chunk() { env.ram().free(ds_cap); }
}; };
Genode::Env &_env; Env &_env;
Genode::Heap _heap { _env.ram(), _env.rm() }; Heap _heap { _env.ram(), _env.rm() };
bool const _expand; bool const _expand;
Genode::List<Ram_chunk> _ram_chunks; List<Ram_chunk> _ram_chunks;
Timer::Connection _timer { _env }; Timer::Connection _timer { _env };
Genode::Signal_handler<Child> _periodic_timeout_handler; Signal_handler<Child> _periodic_timeout_handler;
Genode::Signal_handler<Child> _yield_handler; Signal_handler<Child> _yield_handler;
unsigned long const _period_ms; unsigned long const _period_ms;
void _handle_periodic_timeout(); void _handle_periodic_timeout();
void _handle_yield(); void _handle_yield();
@ -84,19 +89,19 @@ class Child
public: public:
Child(Genode::Env &, Genode::Xml_node); Child(Env &, Xml_node);
void main(); void main();
}; };
void Child::_handle_periodic_timeout() void Test::Child::_handle_periodic_timeout()
{ {
size_t const chunk_size = 1024*1024; size_t const chunk_size = 1024*1024;
if (_env.ram().avail() < chunk_size) { if (_env.ram().avail() < chunk_size) {
if (_expand) { if (_expand) {
Genode::log("quota consumed, request additional resources"); log("quota consumed, request additional resources");
/* /*
* The attempt to allocate RAM will result in a resource request to * The attempt to allocate RAM will result in a resource request to
@ -105,7 +110,7 @@ void Child::_handle_periodic_timeout()
*/ */
} else { } else {
Genode::log("consumed all of our quota, stop allocating"); log("consumed all of our quota, stop allocating");
return; return;
} }
} }
@ -113,18 +118,16 @@ void Child::_handle_periodic_timeout()
/* perform allocation and remember chunk in list */ /* perform allocation and remember chunk in list */
_ram_chunks.insert(new (_heap) Ram_chunk(_env, chunk_size)); _ram_chunks.insert(new (_heap) Ram_chunk(_env, chunk_size));
Genode::log("allocated chunk of ", chunk_size / 1024, " KiB"); log("allocated chunk of ", chunk_size / 1024, " KiB");
_schedule_next_timeout(); _schedule_next_timeout();
} }
void Child::_handle_yield() void Test::Child::_handle_yield()
{ {
using namespace Genode;
/* request yield request arguments */ /* request yield request arguments */
Parent::Resource_args const args = _env.parent().yield_request(); Genode::Parent::Resource_args const args = _env.parent().yield_request();
log("yield request: ", args.string()); log("yield request: ", args.string());
@ -156,7 +159,7 @@ void Child::_handle_yield()
} }
Child::Child(Genode::Env &env, Genode::Xml_node config) Test::Child::Child(Env &env, Xml_node config)
: :
_env(env), _env(env),
_expand(config.attribute_value("expand", false)), _expand(config.attribute_value("expand", false)),
@ -182,22 +185,20 @@ Child::Child(Genode::Env &env, Genode::Xml_node config)
* The parent grants resource requests as long as it has free resources. * The parent grants resource requests as long as it has free resources.
* Once in a while, it politely requests the child to yield resources. * Once in a while, it politely requests the child to yield resources.
*/ */
class Parent class Test::Parent
{ {
private: private:
Genode::Env &_env; Env &_env;
typedef Genode::size_t size_t;
Timer::Connection _timer { _env }; Timer::Connection _timer { _env };
Genode::Lock _yield_blockade; Lock _yield_blockade;
void _print_status() void _print_status()
{ {
Genode::log("quota: ", _child.ram().quota() / 1024, " KiB " log("quota: ", _child.ram().quota() / 1024, " KiB "
"used: ", _child.ram().used() / 1024, " KiB"); "used: ", _child.ram().used() / 1024, " KiB");
} }
size_t _used_ram_prior_yield = 0; size_t _used_ram_prior_yield = 0;
@ -214,7 +215,7 @@ class Parent
void _schedule_one_second_timeout() void _schedule_one_second_timeout()
{ {
Genode::log("wait ", _wait_cnt, "/", _wait_secs); log("wait ", _wait_cnt, "/", _wait_secs);
_timer.trigger_once(1000*1000); _timer.trigger_once(1000*1000);
} }
@ -230,7 +231,7 @@ class Parent
/* remember quantum of resources used by the child */ /* remember quantum of resources used by the child */
_used_ram_prior_yield = _child.ram().used(); _used_ram_prior_yield = _child.ram().used();
Genode::log("request yield (ram prior yield: ", _used_ram_prior_yield); log("request yield (ram prior yield: ", _used_ram_prior_yield);
/* issue yield request */ /* issue yield request */
Genode::Parent::Resource_args yield_args("ram_quota=5M"); Genode::Parent::Resource_args yield_args("ram_quota=5M");
@ -252,7 +253,7 @@ class Parent
void _yield_response() void _yield_response()
{ {
Genode::log("got yield response"); log("got yield response");
_state = YIELD_GOT_RESPONSE; _state = YIELD_GOT_RESPONSE;
_print_status(); _print_status();
@ -260,43 +261,42 @@ class Parent
/* validate that the amount of yielded resources matches the request */ /* validate that the amount of yielded resources matches the request */
size_t const used_after_yield = _child.ram().used(); size_t const used_after_yield = _child.ram().used();
if (used_after_yield + 5*1024*1024 > _used_ram_prior_yield) { if (used_after_yield + 5*1024*1024 > _used_ram_prior_yield) {
Genode::error("child has not yielded enough resources"); error("child has not yielded enough resources");
throw Insufficient_yield(); throw Insufficient_yield();
} }
if (_cnt-- > 0) { if (_cnt-- > 0) {
_init(); _init();
} else { } else {
Genode::log("--- test-resource_yield finished ---"); log("--- test-resource_yield finished ---");
_env.parent().exit(0); _env.parent().exit(0);
} }
} }
Genode::Signal_handler<Parent> _timeout_handler { Signal_handler<Parent> _timeout_handler {
_env.ep(), *this, &Parent::_handle_timeout }; _env.ep(), *this, &Parent::_handle_timeout };
struct Policy : Genode::Slave::Policy struct Policy
:
private Static_parent_services<Ram_session, Pd_session,
Cpu_session, Rom_session,
Log_session, Timer::Session>,
public Slave::Policy
{ {
Parent &_parent; Parent &_parent;
enum { SLAVE_QUOTA = 10*1024*1024 }; enum { SLAVE_QUOTA = 10*1024*1024 };
char const **_permitted_services() const override
{
static char const *services[] = { "RAM", "PD", "CPU", "ROM", "LOG", "Timer" };
return services;
}
void yield_response() override void yield_response() override
{ {
_parent._yield_response(); _parent._yield_response();
} }
Policy(Parent &parent, Genode::Env &env) Policy(Parent &parent, Env &env)
: :
Genode::Slave::Policy(Label("child"), "test-resource_yield", Slave::Policy(Label("child"), "test-resource_yield",
env.ep().rpc_ep(), env.rm(), *this, env.ep().rpc_ep(), env.rm(),
env.ram_session_cap(), SLAVE_QUOTA), env.ram_session_cap(), SLAVE_QUOTA),
_parent(parent) _parent(parent)
{ {
configure("<config child=\"yes\" />"); configure("<config child=\"yes\" />");
@ -314,7 +314,7 @@ class Parent
/** /**
* Constructor * Constructor
*/ */
Parent(Genode::Env &env) : _env(env) Parent(Env &env) : _env(env)
{ {
_timer.sigh(_timeout_handler); _timer.sigh(_timeout_handler);
_init(); _init();
@ -339,9 +339,9 @@ void Component::construct(Genode::Env &env)
if (is_child) { if (is_child) {
log("--- test-resource_yield child role started ---"); log("--- test-resource_yield child role started ---");
static ::Child child(env, config.xml()); static Test::Child child(env, config.xml());
} else { } else {
log("--- test-resource_yield parent role started ---"); log("--- test-resource_yield parent role started ---");
static ::Parent parent(env); static Test::Parent parent(env);
} }
} }