/* * \brief Service management framework * \author Norman Feske * \date 2006-07-12 */ /* * Copyright (C) 2006-2013 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 _INCLUDE__BASE__SERVICE_H_ #define _INCLUDE__BASE__SERVICE_H_ #include #include #include #include #include namespace Genode { /** * Client role * * A client is someone who applies for a service. If the service is not * available yet, we enqueue the client into a wait queue and wake him up * as soon as the requested service gets available. */ class Client : public List::Element { private: Cancelable_lock _service_apply_lock; const char *_apply_for; public: /** * Constructor */ Client(): _service_apply_lock(Lock::LOCKED), _apply_for(0) { } virtual ~Client() { } /** * Set/Request service name that we are currently applying for */ void apply_for(const char *apply_for) { _apply_for = apply_for; } const char *apply_for() { return _apply_for; } /** * Service wait queue support */ void sleep() { _service_apply_lock.lock(); } void wakeup() { _service_apply_lock.unlock(); } }; /** * Server role * * A server is a process that provides one or multiple services. For the * most part, this class is used as an opaque key to represent the server * role. */ class Server { private: Ram_session_capability _ram; public: /** * Constructor * * \param ram RAM session capability of the server process used, * for quota transfers from/to the server */ Server(Ram_session_capability ram): _ram(ram) { } /** * Return RAM session capability of the server process */ Ram_session_capability ram_session_cap() const { return _ram; } }; class Service : public List::Element { public: enum { MAX_NAME_LEN = 32 }; private: char _name[MAX_NAME_LEN]; public: /********************* ** Exception types ** *********************/ class Invalid_args { }; class Unavailable { }; class Quota_exceeded { }; /** * Constructor * * \param name service name */ Service(const char *name) { strncpy(_name, name, sizeof(_name)); } virtual ~Service() { } /** * Return service name */ const char *name() const { return _name; } /** * Create session * * \param args session-construction arguments * \param affinity preferred CPU affinity of session * * \throw Invalid_args * \throw Unavailable * \throw Quota_exceeded */ virtual Session_capability session(char const *args, Affinity const &affinity) = 0; /** * Extend resource donation to an existing session */ virtual void upgrade(Session_capability session, const char *args) = 0; /** * Close session */ virtual void close(Session_capability /*session*/) { } /** * Return server providing the service */ virtual Server *server() const { return 0; } /** * Return the RAM session to be used for trading resources */ Ram_session_capability ram_session_cap() { if (server()) return server()->ram_session_cap(); return Ram_session_capability(); } }; /** * Representation of a locally implemented service */ class Local_service : public Service { private: Root *_root; public: Local_service(const char *name, Root *root) : Service(name), _root(root) { } Session_capability session(const char *args, Affinity const &affinity) { try { return _root->session(args, affinity); } catch (Root::Invalid_args) { throw Invalid_args(); } catch (Root::Unavailable) { throw Unavailable(); } catch (Root::Quota_exceeded) { throw Quota_exceeded(); } } void upgrade(Session_capability session, const char *args) { _root->upgrade(session, args); } void close(Session_capability session) { _root->close(session); } }; /** * Representation of a service provided by our parent */ class Parent_service : public Service { public: Parent_service(const char *name) : Service(name) { } Session_capability session(const char *args, Affinity const &affinity) { try { return env()->parent()->session(name(), args, affinity); } catch (Parent::Unavailable) { PWRN("parent has no service \"%s\"", name()); throw Unavailable(); } catch (Parent::Quota_exceeded) { throw Quota_exceeded(); } } void upgrade(Session_capability session, const char *args) { env()->parent()->upgrade(session, args); } void close(Session_capability session) { env()->parent()->close(session); } }; /** * Representation of a service that is implemented in a child */ class Child_service : public Service { private: Root_capability _root_cap; Root_client _root; Server *_server; public: /** * Constructor * * \param name name of service * \param root capability to root interface * \param server server process providing the service */ Child_service(const char *name, Root_capability root, Server *server) : Service(name), _root_cap(root), _root(root), _server(server) { } Server *server() const { return _server; } Session_capability session(const char *args, Affinity const &affinity) { if (!_root_cap.valid()) throw Unavailable(); try { return _root.session(args, affinity); } catch (Root::Invalid_args) { throw Invalid_args(); } catch (Root::Unavailable) { throw Unavailable(); } catch (Root::Quota_exceeded) { throw Quota_exceeded(); } } void upgrade(Session_capability sc, const char *args) { if (!_root_cap.valid()) throw Unavailable(); try { _root.upgrade(sc, args); } catch (Root::Invalid_args) { throw Invalid_args(); } catch (Root::Unavailable) { throw Unavailable(); } catch (Root::Quota_exceeded) { throw Quota_exceeded(); } } void close(Session_capability sc) { _root.close(sc); } }; /** * Container for holding service representations */ class Service_registry { protected: Lock _service_wait_queue_lock; List _service_wait_queue; List _services; public: /** * Probe for service with specified name * * \param name service name * \param server server providing the service, * default (0) for any server */ Service *find(const char *name, Server *server = 0) { if (!name) return 0; Lock::Guard lock_guard(_service_wait_queue_lock); for (Service *s = _services.first(); s; s = s->next()) if (strcmp(s->name(), name) == 0 && (!server || s->server() == server)) return s; return 0; } /** * Check if service name is ambiguous * * \return true if the same service is provided multiple * times */ bool is_ambiguous(const char *name) { Lock::Guard lock_guard(_service_wait_queue_lock); /* count number of services with the specified name */ unsigned cnt = 0; for (Service *s = _services.first(); s; s = s->next()) cnt += (strcmp(s->name(), name) == 0); return cnt > 1; } /** * Return first service provided by specified server */ Service *find_by_server(Server *server) { Lock::Guard lock_guard(_service_wait_queue_lock); for (Service *s = _services.first(); s; s = s->next()) if (s->server() == server) return s; return 0; } /** * Wait for service * * This function is called by the clients's thread * when requesting a session creation. It blocks * if the requested service is not available. * * \return service structure that matches the request or * 0 if the waiting was canceled. */ Service *wait_for_service(const char *name, Client *client, const char *client_name) { Service *service; client->apply_for(name); _service_wait_queue_lock.lock(); _service_wait_queue.insert(client); _service_wait_queue_lock.unlock(); do { service = find(name); /* * The service that we are seeking is not available today. * Lets sleep a night over it. */ if (!service) { printf("%s: service %s not yet available - sleeping\n", client_name, name); try { client->sleep(); printf("%s: service %s got available\n", client_name, name); } catch (Blocking_canceled) { printf("%s: cancel waiting for service\n", client_name); break; } } } while (!service); /* we got what we needed, stop applying */ _service_wait_queue_lock.lock(); _service_wait_queue.remove(client); _service_wait_queue_lock.unlock(); client->apply_for(0); return service; } /** * Register service * * This function is called by the server's thread. */ void insert(Service *service) { /* make new service known */ _services.insert(service); /* wake up applicants waiting for the service */ Lock::Guard lock_guard(_service_wait_queue_lock); for (Client *c = _service_wait_queue.first(); c; c = c->next()) if (strcmp(service->name(), c->apply_for()) == 0) c->wakeup(); } /** * Unregister service */ void remove(Service *service) { _services.remove(service); } /** * Unregister all services */ void remove_all() { while (_services.first()) remove(_services.first()); } }; } #endif /* _INCLUDE__BASE__SERVICE_H_ */