# HG changeset patch # User Paul Boddie # Date 1680903321 -7200 # Node ID 542bf2c53298f7169269ab1e21caca8bb37f2861 # Parent 84e50bfc0d76919ae52486a78d7679021a8854ac# Parent 06eb2b9ef9e7e23fa2b7ec866db098bd6a74ba85 Merged concurrent changes. diff -r 84e50bfc0d76 -r 542bf2c53298 libexec/include/exec/elf.h --- a/libexec/include/exec/elf.h Fri Apr 07 23:34:59 2023 +0200 +++ b/libexec/include/exec/elf.h Fri Apr 07 23:35:21 2023 +0200 @@ -66,6 +66,7 @@ protected: HEADER *_header; Segment **_segments = NULL; + unsigned int _nsegments; public: explicit PayloadVariant(HEADER *header); diff -r 84e50bfc0d76 -r 542bf2c53298 libexec/include/exec/process_creating.h --- a/libexec/include/exec/process_creating.h Fri Apr 07 23:34:59 2023 +0200 +++ b/libexec/include/exec/process_creating.h Fri Apr 07 23:35:21 2023 +0200 @@ -76,8 +76,6 @@ long init_external_pager(l4_cap_idx_t *pager); - long init_process_monitor(l4_cap_idx_t *monitor); - long configure_task(); long allocate_internal_pager(); @@ -90,10 +88,14 @@ long start_program(l4_cap_idx_t monitor, int argc, const char *argv[]); + long _start(int argc, const char *argv[], l4_cap_idx_t process); + public: explicit ProcessCreating(const char *rm_filename, file_t *rm_file); - virtual long start(int argc, const char *argv[], l4_cap_idx_t *process); + virtual long init_process_monitor(l4_cap_idx_t *monitor); + + virtual long start(int argc, const char *argv[], l4_cap_idx_t process); }; /* vim: tabstop=2 expandtab shiftwidth=2 diff -r 84e50bfc0d76 -r 542bf2c53298 libexec/include/exec/process_creator_resource.h --- a/libexec/include/exec/process_creator_resource.h Fri Apr 07 23:34:59 2023 +0200 +++ b/libexec/include/exec/process_creator_resource.h Fri Apr 07 23:35:21 2023 +0200 @@ -47,9 +47,11 @@ void *interface() { return static_cast(this); } - /* Process creator interface methods. */ + /* Local operations. */ - virtual long start(int argc, const char *argv[], l4_cap_idx_t *process); + virtual long init_process(l4_cap_idx_t *process); + + virtual long start(int argc, const char *argv[], l4_cap_idx_t process); /* Opener interface methods. */ diff -r 84e50bfc0d76 -r 542bf2c53298 libexec/lib/src/elf.cc --- a/libexec/lib/src/elf.cc Fri Apr 07 23:34:59 2023 +0200 +++ b/libexec/lib/src/elf.cc Fri Apr 07 23:35:21 2023 +0200 @@ -100,6 +100,11 @@ PayloadVariant::PayloadVariant(HEADER *header) : _header(header) { + /* Record the number of segments since various methods depend on it, but the + header data may be unavailable when it is used, upon destruction, for + example. */ + + _nsegments = _header->e_phnum; } /* Specific payload destruction. */ @@ -111,6 +116,8 @@ { for (unsigned int i = 0; i < segments(); i++) delete _segments[i]; + + delete _segments; } } @@ -135,7 +142,7 @@ template unsigned int PayloadVariant::segments() { - return _header->e_phnum; + return _nsegments; } template diff -r 84e50bfc0d76 -r 542bf2c53298 libexec/lib/src/process_creating.cc --- a/libexec/lib/src/process_creating.cc Fri Apr 07 23:34:59 2023 +0200 +++ b/libexec/lib/src/process_creating.cc Fri Apr 07 23:35:21 2023 +0200 @@ -86,6 +86,7 @@ long ProcessCreating::init_external_pager(l4_cap_idx_t *pager) { _exec_pager = new ExternalPager(0, 10 * L4_PAGESIZE); + _exec_pager->set_monitor(_monitor); /* Initialise pager regions for the region mapper. */ @@ -119,13 +120,7 @@ /* Start the monitor in a separate thread. */ - long err = ResourceServer(_monitor).start_thread(monitor); - - if (err) - return err; - - _exec_pager->set_monitor(_monitor); - return L4_EOK; + return ResourceServer(_monitor).start_thread(monitor); } /* Configure the environment for the task. */ @@ -369,10 +364,8 @@ program arguments, returning a reference to the process monitor as an object for interacting with the process. */ -long ProcessCreating::start(int argc, const char *argv[], l4_cap_idx_t *process) +long ProcessCreating::_start(int argc, const char *argv[], l4_cap_idx_t process) { - std::lock_guard guard(_lock); - /* Open the program file, handling any error conditions. If successfully opened, it will be closed when the process terminates. */ @@ -405,10 +398,6 @@ if (err) return err; - err = init_process_monitor(process); - if (err) - return err; - err = configure_task(); if (err) return err; @@ -421,7 +410,7 @@ if (err) return err; - err = start_program(*process, argc, argv); + err = start_program(process, argc, argv); if (err) return err; @@ -444,5 +433,21 @@ return L4_EOK; } +/* Start the given program, notifying the process monitor upon any error. */ + +long ProcessCreating::start(int argc, const char *argv[], l4_cap_idx_t process) +{ + std::lock_guard guard(_lock); + + long err = _start(argc, argv, process); + + /* Communicate the error using the signal value. */ + + if (err) + _monitor->notify_all(NOTIFY_TASK_ERROR, (notify_values_t) {0, err}); + + return err; +} + /* vim: tabstop=2 expandtab shiftwidth=2 */ diff -r 84e50bfc0d76 -r 542bf2c53298 libexec/lib/src/process_creator_context_resource.cc --- a/libexec/lib/src/process_creator_context_resource.cc Fri Apr 07 23:34:59 2023 +0200 +++ b/libexec/lib/src/process_creator_context_resource.cc Fri Apr 07 23:35:21 2023 +0200 @@ -67,9 +67,27 @@ printf("argv[%d] = %s\n", i, argv[i]); } - /* Start the new process, obtaining a reference to it. */ + /* Obtain a reference to a new process and send the reference to the client. + This must be done in advance of actually starting the program since the + program may complete before the client gets the reference. */ + + long err = _creator->init_process(process); + + if (!err) + err = complete_ProcessCreatorContext_start(*process); + + /* Attempt to communicate any error from these activities. */ - return _creator->start(argc, argv, process); + if (err) + return err; + + /* Attempt to actually start the process. Errors cannot be communicated as a + reply, so a notification is sent via the process monitor instead by the + process creator. */ + + _creator->start(argc, argv, *process); + + return IPC_MESSAGE_SENT; } /* vim: tabstop=4 expandtab shiftwidth=4 diff -r 84e50bfc0d76 -r 542bf2c53298 libexec/lib/src/process_creator_resource.cc --- a/libexec/lib/src/process_creator_resource.cc Fri Apr 07 23:34:59 2023 +0200 +++ b/libexec/lib/src/process_creator_resource.cc Fri Apr 07 23:35:21 2023 +0200 @@ -48,9 +48,14 @@ /* ProcessCreator interface methods. */ +long ProcessCreatorResource::init_process(l4_cap_idx_t *process) +{ + return _creating.init_process_monitor(process); +} + /* Start the new process, obtaining a reference to it. */ -long ProcessCreatorResource::start(int argc, const char *argv[], l4_cap_idx_t *process) +long ProcessCreatorResource::start(int argc, const char *argv[], l4_cap_idx_t process) { return _creating.start(argc, argv, process); } diff -r 84e50bfc0d76 -r 542bf2c53298 libfsclient/include/fsclient/process.h --- a/libfsclient/include/fsclient/process.h Fri Apr 07 23:34:59 2023 +0200 +++ b/libfsclient/include/fsclient/process.h Fri Apr 07 23:35:21 2023 +0200 @@ -58,8 +58,9 @@ process_t *process_new(); void process_close(process_t *process); +long process_error(process_t *process); void process_init(process_t *process); -long process_start(process_t *process, int argc, char *argv[]); +long process_start(process_t *process, int argc, const char *argv[]); /* Notification support. */ diff -r 84e50bfc0d76 -r 542bf2c53298 libfsclient/lib/src/file.cc --- a/libfsclient/lib/src/file.cc Fri Apr 07 23:34:59 2023 +0200 +++ b/libfsclient/lib/src/file.cc Fri Apr 07 23:35:21 2023 +0200 @@ -95,7 +95,9 @@ /* Initialise the notifiable section of the structure. */ file->notifiable.notifications = 0; + file->notifiable.pending_notifications = 0; file->notifiable.base = (notifiable_base_t *) file; + file->notifiable.handler = NULL; } @@ -634,7 +636,6 @@ void file_notify_close(file_notifier_t *notifier) { - notifier->obj->stop(); delete notifier->obj; delete notifier; } @@ -677,8 +678,7 @@ long file_notify_wait_file(file_t *file, file_notifier_t *notifier) { - SpecificObjectNotifier *specific_notifier = dynamic_cast(notifier->obj); - long err = specific_notifier->wait_object(file_notifiable(file)); + long err = notifier->obj->wait_object(file_notifiable(file)); /* Unsubscribe if a closure notification has been received. */ @@ -692,9 +692,8 @@ long file_notify_wait_files(file_t **file, file_notifier_t *notifier) { - GeneralObjectNotifier *general_notifier = dynamic_cast(notifier->obj); notifiable_t *notifiable; - long err = general_notifier->wait(¬ifiable); + long err = notifier->obj->wait(¬ifiable); *file = (file_t *) notifiable->base; diff -r 84e50bfc0d76 -r 542bf2c53298 libfsclient/lib/src/process.cc --- a/libfsclient/lib/src/process.cc Fri Apr 07 23:34:59 2023 +0200 +++ b/libfsclient/lib/src/process.cc Fri Apr 07 23:35:21 2023 +0200 @@ -34,6 +34,17 @@ +/* Utility functions. */ + +static bool _process_terminated(process_t *process) +{ + return ((process->notifiable.notifications & NOTIFY_TASK_SIGNAL) && + (process->notifiable.values.sig == 0)) || + (process->notifiable.notifications & NOTIFY_TASK_ERROR); +} + + + /* Create a new process object. */ process_t *process_new() @@ -60,17 +71,34 @@ process_init(process); } +/* Return any process initiation error. */ + +long process_error(process_t *process) +{ + if (process->notifiable.notifications & NOTIFY_TASK_ALL) + return process->notifiable.values.val; + else + return L4_EOK; +} + /* Initialise the given process structure. */ void process_init(process_t *process) { process->ref = L4_INVALID_CAP; + + /* Initialise the notifiable section of the structure. */ + + process->notifiable.notifications = 0; + process->notifiable.pending_notifications = 0; + process->notifiable.base = (notifiable_base_t *) process; + process->notifiable.handler = NULL; } /* Start a process using the given arguments. NOTE: This does not yet obtain input/output pipes. */ -long process_start(process_t *process, int argc, char *argv[]) +long process_start(process_t *process, int argc, const char *argv[]) { l4_cap_idx_t server = l4re_env_get_cap(ENV_PROCESS_SERVER_NAME); @@ -106,6 +134,7 @@ /* Initialise the notifiable section of the structure. */ process->notifiable.notifications = 0; + process->notifiable.pending_notifications = 0; process->notifiable.base = (notifiable_base_t *) process; /* Close the context, although a separate mechanism could permit contexts to @@ -152,7 +181,6 @@ void process_notify_close(process_notifier_t *notifier) { - notifier->obj->stop(); delete notifier->obj; delete notifier; } @@ -195,17 +223,12 @@ long process_notify_wait_process(process_t *process, process_notifier_t *notifier) { - SpecificObjectNotifier *specific_notifier = dynamic_cast(notifier->obj); - long err = specific_notifier->wait_object(process_notifiable(process)); + long err = notifier->obj->wait_object(process_notifiable(process)); /* Unsubscribe if a termination notification has been received. */ - if (!err && (process->notifiable.notifications & NOTIFY_TASK_SIGNAL) && - (process->notifiable.values.sig == 0)) - { + if (!err && _process_terminated(process)) process_notify_unsubscribe(process, notifier); - process_close(process); - } return err; } @@ -214,20 +237,15 @@ long process_notify_wait_processes(process_t **process, process_notifier_t *notifier) { - GeneralObjectNotifier *general_notifier = dynamic_cast(notifier->obj); notifiable_t *notifiable; - long err = general_notifier->wait(¬ifiable); + long err = notifier->obj->wait(¬ifiable); *process = (process_t *) notifiable->base; /* Unsubscribe if a termination notification has been received. */ - if (!err && ((*process)->notifiable.notifications & NOTIFY_TASK_SIGNAL) && - ((*process)->notifiable.values.sig == 0)) - { + if (!err && _process_terminated(*process)) process_notify_unsubscribe(*process, notifier); - process_close(*process); - } return err; } diff -r 84e50bfc0d76 -r 542bf2c53298 libnotifier/include/notifier/notifier.h --- a/libnotifier/include/notifier/notifier.h Fri Apr 07 23:34:59 2023 +0200 +++ b/libnotifier/include/notifier/notifier.h Fri Apr 07 23:35:21 2023 +0200 @@ -23,8 +23,8 @@ #include #include -#include #include +#include #include #include @@ -34,36 +34,10 @@ -/* Object-specific notification details. */ - -class ObjectNotificationState -{ -public: - /* Synchronisation primitives for state access and notification. */ - - std::mutex lock; - std::condition_variable condition; - - /* Pending notifications for monitored objects. */ - - notify_flags_t pending_notifications = 0; - notify_values_t pending_values = NOTIFY_VALUES_NULL; - - /* Endpoints associated with monitored objects. */ - - l4_cap_idx_t endpoint = L4_INVALID_CAP; - - bool is_null() { return l4_is_invalid_cap(endpoint); } -}; - - - /* Collection types. */ -typedef std::map ObjectNotificationStates; -typedef std::map ObjectStateLocks; - -typedef std::vector ServerConfigs; +typedef std::list NotifiableObjectQueue; +typedef std::set NotifiableObjects; @@ -72,102 +46,43 @@ class ObjectNotifier { protected: - /* General state access locking. */ - - std::mutex _state_lock; - - /* Object-specific state locking. */ + /* Locking for the affected queue and monitored object registry. */ - ObjectStateLocks _object_locks; - - /* Notification state. */ - - ObjectNotificationStates _state; + std::mutex _affected_lock, _monitored_lock; + std::condition_variable _condition; - /* Notifier resource details. */ - - ServerConfigs _configs; - bool _started = false; - l4_cap_idx_t _irq = L4_INVALID_CAP; + /* Objects affected by notifications. */ - /* Convenience method to access object state. */ - - virtual ObjectNotificationState &object_state(notifiable_t *object, bool create); - - /* Helper methods. */ - - virtual bool _transfer(ObjectNotificationState &state, notifiable_t *object); + NotifiableObjectQueue _queued; + NotifiableObjects _affected, _monitored; public: virtual ~ObjectNotifier(); /* Local operations. */ - virtual long start(); - - virtual void stop(); - virtual long subscribe(notifiable_t *object, notify_flags_t flags); virtual long unsubscribe(notifiable_t *object); - virtual long remove_endpoint(notifiable_t *object, l4_cap_idx_t endpoint); + virtual void update(notifiable_t *object); + + virtual long wait(notifiable_t **object); + + virtual long wait_object(notifiable_t *object); /* Helper methods. */ - virtual void notify(notifiable_t *object, notify_flags_t flags, - notify_values_t values) = 0; + virtual void notify(notifiable_t *object); + + virtual void release(notifiable_t *object); }; -/* An object monitoring notifications for a collection of different objects. */ - -class GeneralObjectNotifier : public ObjectNotifier -{ -protected: - /* Locking to protect pending notification members and to coordinate access - to notifications. */ - - std::mutex _general_lock; - - /* General lock synchronisation. */ - - std::condition_variable _general_condition; - - /* Objects affected by notifications. */ - - std::list _affected; - - /* Helper methods. */ - - virtual bool _retrieve(notifiable_t **object); - - virtual bool _retrieve_for_object(notifiable_t *object); +/* Collection types. */ -public: - virtual long wait(notifiable_t **object); - - /* Helper methods. */ - - virtual void notify(notifiable_t *object, notify_flags_t flags, - notify_values_t values); -}; - - - -/* An object monitoring notifications for specific objects. */ - -class SpecificObjectNotifier : public ObjectNotifier -{ -public: - virtual long wait_object(notifiable_t *object); - - /* Helper methods. */ - - virtual void notify(notifiable_t *object, notify_flags_t flags, - notify_values_t values); -}; +typedef std::set ObjectNotifiers; @@ -176,19 +91,35 @@ class NotifierResource : public Notifier, public Resource { protected: - ObjectNotifier *_notifier; + /* Locking for the resource. */ + + std::mutex _lock; + std::condition_variable _condition; + + /* Notifiers and the monitored object. */ + + ObjectNotifiers _notifiers; notifiable_t *_object; + /* Utility methods. */ + + virtual void _notify(); + + virtual void _release(); + public: - explicit NotifierResource(ObjectNotifier *notifier, notifiable_t *object) - : _notifier(notifier), _object(object) + l4_cap_idx_t endpoint = L4_INVALID_CAP; + + explicit NotifierResource(notifiable_t *object) + : _object(object) { } - explicit NotifierResource() - : _notifier(NULL), _object(NULL) - { - } + virtual ~NotifierResource(); + + /* Resource methods. */ + + virtual void close(); /* Server details. */ @@ -200,14 +131,20 @@ /* Notifier methods. */ virtual long notify(notify_flags_t flags, notify_values_t values); + + /* Local operations. */ + + virtual long add(ObjectNotifier *notifier, notify_flags_t flags); + + virtual long remove(ObjectNotifier *notifier); }; /* Helper functions. */ -SpecificObjectNotifier *notifier_get_task_notifier(); +ObjectNotifier *notifier_get_task_notifier(); -GeneralObjectNotifier *notifier_get_local_notifier(); +ObjectNotifier *notifier_get_local_notifier(); // vim: tabstop=4 expandtab shiftwidth=4 diff -r 84e50bfc0d76 -r 542bf2c53298 libnotifier/lib/src/notifier.cc --- a/libnotifier/lib/src/notifier.cc Fri Apr 07 23:34:59 2023 +0200 +++ b/libnotifier/lib/src/notifier.cc Fri Apr 07 23:35:21 2023 +0200 @@ -19,11 +19,6 @@ * Boston, MA 02110-1301, USA */ -#include -#include - -#include - #include #include #include @@ -34,47 +29,35 @@ -/* Null notification state. */ - -static ObjectNotificationState _null_state; - - - /* Lock protecting per-task notifier access. */ -static std::mutex _lock; +static std::mutex _task_lock; /* Per-task storage for specific waiting operations. */ -static SpecificObjectNotifier *_notifier = NULL; +static ObjectNotifier *_notifier = NULL; /* Return the per-task notifier for object-specific waiting operations. */ -SpecificObjectNotifier *notifier_get_task_notifier() +ObjectNotifier *notifier_get_task_notifier() { - std::lock_guard guard(_lock); + std::lock_guard guard(_task_lock); /* Start any new notifier. */ if (_notifier == NULL) - { - _notifier = new SpecificObjectNotifier; - _notifier->start(); - } + _notifier = new ObjectNotifier; return _notifier; } /* Return a local notifier for general object waiting operations. */ -GeneralObjectNotifier *notifier_get_local_notifier() +ObjectNotifier *notifier_get_local_notifier() { - GeneralObjectNotifier *notifier = new GeneralObjectNotifier; - - notifier->start(); - return notifier; + return new ObjectNotifier; } @@ -83,358 +66,217 @@ ObjectNotifier::~ObjectNotifier() { - stop(); + /* Remove this notifier from the individual notifier resources. */ + + NotifiableObjects::iterator it; - ServerConfigs::iterator it; + for (it = _monitored.begin(); it != _monitored.end(); it++) + { + notifiable_t *object = *it; - for (it = _configs.begin(); it != _configs.end(); it++) - delete *it; + if (object->handler != NULL) + { + NotifierResource *resource = reinterpret_cast(object->handler); + resource->remove(this); + } + } - _configs.clear(); + _monitored.clear(); /* Handle deletion of the special task notifier. */ + std::lock_guard guard(_task_lock); + if (this == _notifier) _notifier = NULL; } - - -/* Start listening for notifications. */ - -long ObjectNotifier::start() -{ - if (_started) - return L4_EOK; - - /* Create a new thread to serve a "null" resource. This resource is not used - for notifications but merely for control purposes. */ - - NotifierResource *notifier = new NotifierResource; - ResourceServer server(notifier); - long err = server.start_thread(true, false); - - if (err) - return err; - - _configs.push_back(server.config()); - _started = true; - - /* Retain the IRQ created for the server for control purposes. */ - - _irq = server.config()->irq; - - return L4_EOK; -} - +/* Handle a deletion event for an object. */ - -/* Stop the notifier. */ - -void ObjectNotifier::stop() +void ObjectNotifier::release(notifiable_t *object) { - if (l4_is_valid_cap(_irq)) - { - l4_irq_trigger(_irq); - _irq = L4_INVALID_CAP; - } -} - - + std::lock_guard guard(_monitored_lock); -/* Return notification state for the given object or null state if no record - existed for the object. */ - -ObjectNotificationState &ObjectNotifier::object_state(notifiable_t *object, bool create) -{ - ObjectNotificationStates::iterator it = _state.find(object); - - if (it == _state.end()) - { - if (create) - return _state[object]; - else - return _null_state; - } - - return it->second; + _monitored.erase(object); + object->handler = NULL; } /* Subscribe to notification events on an object. */ long ObjectNotifier::subscribe(notifiable_t *object, notify_flags_t flags) { - /* Acquire the lock for state lookup. */ - - std::unique_lock state_guard(_state_lock); + std::lock_guard guard(_monitored_lock); - /* Obtain potentially new state for the object. */ + /* Ensure that a handler resource is available for object notifications. */ - ObjectNotificationState &state = object_state(object, true); + NotifierResource *resource; - if (state.is_null()) - { - /* Serve the new object in the notifier thread. */ + if (object->handler != NULL) + resource = reinterpret_cast(object->handler); + + /* Create a resource if none is recorded. */ - NotifierResource *resource = new NotifierResource(this, object); + else + { + resource = new NotifierResource(object); ResourceServer server(resource); - long err = server.start_in_thread(_configs.front()->thread); + long err = server.start_thread(&resource->endpoint); if (err) return err; - _configs.push_back(server.config()); - state.endpoint = server.config()->server; + object->handler = resource; } - /* Forbid repeated subscription. - NOTE: Could instead rely on being unsubscribed, releasing the existing - endpoint. */ + /* Record the object. */ - else - return -L4_EEXIST; + _monitored.insert(object); - /* Allow this object to be re-entered. This may occur because the subscribe - operation can cause deferred notifications to be sent back to the - subscribed notifier and to this object. */ - - state_guard.unlock(); + /* Record this notifier to get general notifications. */ - /* Subscribe, sending the notification endpoint via the principal reference - for the object. */ - - client_Notification notify(object->base->ref); - - return notify.subscribe(state.endpoint, flags); + return resource->add(this, flags); } /* Unsubscribe from notification events on an object. */ long ObjectNotifier::unsubscribe(notifiable_t *object) { - /* Acquire the lock for state lookup. */ + std::lock_guard guard(_monitored_lock); + + if (object->handler == NULL) + return L4_EOK; + + NotifierResource *resource = reinterpret_cast(object->handler); - std::unique_lock state_guard(_state_lock); + _monitored.erase(object); + object->handler = NULL; + return resource->remove(this); +} + +/* Handle a notification event for an object. */ - ObjectNotificationState &state = object_state(object, false); +void ObjectNotifier::notify(notifiable_t *object) +{ + /* Enter critical section to access the queue. */ + + std::unique_lock guard(_affected_lock); + + /* Ensure that a queue entry exists for the object. */ - if (state.is_null()) - return -L4_ENOENT; + NotifiableObjects::iterator it = _affected.find(object); - /* Unsubscribe via the notification interface. */ + if (it == _affected.end()) + { + _queued.push_back(object); + _affected.insert(object); + } + + /* Notify all waiting callers that at least one notification is available. */ - client_Notification notify(object->base->ref); + _condition.notify_all(); +} + +/* Update the notifiable object when being notified. */ - notify.unsubscribe(); +void ObjectNotifier::update(notifiable_t *object) +{ + object->notifications = object->pending_notifications; + object->values = object->pending_values; - return remove_endpoint(object, state.endpoint); + object->pending_notifications = 0; } -/* Remove a notification endpoint for an object. */ +/* Wait for notification events on objects, returning each object that has a + notification registered for it. */ + +long ObjectNotifier::wait(notifiable_t **object) +{ + /* Enter critical section to access the queue. */ + + std::unique_lock guard(_affected_lock); + + while (1) + { + /* With pending notifications, return the first object. */ -long ObjectNotifier::remove_endpoint(notifiable_t *object, l4_cap_idx_t endpoint) -{ - if (l4_is_invalid_cap(endpoint)) - return -L4_EINVAL; + if (!_affected.empty()) + { + *object = _queued.front(); + _queued.pop_front(); + _affected.erase(*object); + break; + } + + /* Otherwise, wait for notifications. */ + + _condition.wait(guard); + } + + /* Synchronise the notification flags and values. */ + + update(*object); + return L4_EOK; +} + +/* Wait for notification events on a specific object. */ - ipc_cap_free_um(endpoint); +long ObjectNotifier::wait_object(notifiable_t *object) +{ + /* Enter critical section to access the queue. */ + + std::unique_lock guard(_affected_lock); - _state.erase(object); + while (1) + { + /* With pending notifications, find any for the specified object. */ + + NotifiableObjects::iterator it = _affected.find(object); + + if (it != _affected.end()) + { + /* Remove the object from the queue. */ + + NotifiableObjectQueue::iterator itq; - /* Remove the lock for updating object state. */ + for (itq = _queued.begin(); itq != _queued.end(); itq++) + { + if (*itq == object) + { + _queued.erase(itq); + break; + } + } - _object_locks.erase(object); + _affected.erase(it); + break; + } + + /* Otherwise, wait for notifications. */ + _condition.wait(guard); + } + + /* Synchronise the notification flags and values. */ + + update(object); return L4_EOK; } -/* Handle a notification event for an object. */ - -void GeneralObjectNotifier::notify(notifiable_t *object, notify_flags_t flags, - notify_values_t values) -{ - /* Enter critical section for the notifier (affecting all objects). */ - - std::unique_lock general_guard(_general_lock); - - /* Acquire the lock for state lookup. */ - - std::unique_lock state_guard(_state_lock); - - ObjectNotificationState &state = object_state(object, false); - - if (state.is_null()) - return; - - /* Acquire the lock for the object state itself. */ - - std::unique_lock object_guard(state.lock); - - /* Record flags and note previous flags. */ - - notify_flags_t recorded = state.pending_notifications; - - state.pending_notifications |= flags; - state.pending_values = values; - - /* Add an object queue entry for any objects without previous notifications. */ - - if (!recorded) - _affected.push_back(object); - - /* Notify any waiting caller. */ - - _general_condition.notify_one(); -} +/* Virtual destructor required for base class instance reference deletion. */ -void SpecificObjectNotifier::notify(notifiable_t *object, notify_flags_t flags, - notify_values_t values) +NotifierResource::~NotifierResource() { - /* Acquire the lock for state lookup. */ - - std::unique_lock state_guard(_state_lock); - - ObjectNotificationState &state = object_state(object, false); - - if (state.is_null()) - return; - - /* Acquire the lock for the object state itself. */ - - std::unique_lock object_guard(state.lock); - - state.pending_notifications |= flags; - state.pending_values = values; - - /* Notify any waiting caller. */ - - state.condition.notify_one(); -} - - - -/* Transfer pending notifications to the given object. This must be called with - a lock acquired on the object notification state. */ - -bool ObjectNotifier::_transfer(ObjectNotificationState &state, notifiable_t *object) -{ - notify_flags_t recorded = state.pending_notifications; - - if (recorded) - { - object->notifications = recorded; - object->values = state.pending_values; - state.pending_notifications = 0; - return true; - } - - return false; } - - -/* Obtain object state and transfer notifications. */ - -bool GeneralObjectNotifier::_retrieve_for_object(notifiable_t *object) -{ - /* Acquire the lock for state lookup. */ - - std::unique_lock state_guard(_state_lock); - - ObjectNotificationState &state = object_state(object, false); - - if (state.is_null()) - return false; - - /* Acquire the lock for the object state itself, then release the state lock. */ - - std::unique_lock object_guard(state.lock); - - state_guard.unlock(); - - /* Call generic method to transfer notifications, if possible. */ - - return _transfer(state, object); -} - -/* Obtain queued objects until one is found that still has events recorded for - it. This must be called with the notifier's general lock acquired. */ - -bool GeneralObjectNotifier::_retrieve(notifiable_t **object) -{ - while (!_affected.empty()) - { - *object = _affected.front(); - _affected.pop_front(); - - if (_retrieve_for_object(*object)) - return true; - } - - return false; -} - - - -/* Wait for notification events on objects. */ - -long GeneralObjectNotifier::wait(notifiable_t **object) -{ - std::unique_lock general_guard(_general_lock); +/* Handle the release of the resource. */ - while (1) - { - /* With pending notifications, update the first object and exit. */ - - if (_retrieve(object)) - break; - - /* Otherwise, wait for notifications. */ - - _general_condition.wait(general_guard); - } - - return L4_EOK; -} - -/* Wait for notifications from a single object. */ - -long SpecificObjectNotifier::wait_object(notifiable_t *object) +void NotifierResource::close() { - /* Acquire the lock for reading object state. */ - - std::unique_lock state_guard(_state_lock); - - ObjectNotificationState &state = object_state(object, false); - - if (state.is_null()) - return -L4_EINVAL; - - /* Acquire the lock for the object state itself, then release the state lock. */ - - std::unique_lock object_guard(state.lock); - - state_guard.unlock(); - - while (1) - { - /* With pending notifications, update the object and exit. */ - - if (_transfer(state, object)) - break; - - /* Otherwise, wait for notifications. */ - - state.condition.wait(object_guard); - } - - return L4_EOK; + _release(); } - - /* Object-specific resource methods. */ ipc_server_default_config_type NotifierResource::config() @@ -442,14 +284,85 @@ return config_Notifier; } + + +/* Registration of notifiers. */ + +long NotifierResource::add(ObjectNotifier *notifier, notify_flags_t flags) +{ + std::lock_guard guard(_lock); + + bool is_new = _notifiers.empty(); + + _notifiers.insert(notifier); + + /* Subscribe, sending the notification endpoint via the principal reference + for the object. */ + + if (!is_new) + return L4_EOK; + + client_Notification notify(_object->base->ref); + + return notify.subscribe(endpoint, flags); +} + +long NotifierResource::remove(ObjectNotifier *notifier) +{ + std::lock_guard guard(_lock); + + _notifiers.erase(notifier); + + /* Unsubscribe via the notification interface. */ + + if (!_notifiers.empty()) + return L4_EOK; + + client_Notification notify(_object->base->ref); + + return notify.unsubscribe(); +} + + + /* Register a notification received by an object-specific resource. */ long NotifierResource::notify(notify_flags_t flags, notify_values_t values) { - if (_notifier != NULL) - _notifier->notify(_object, flags, values); + /* Update the notifiable object. */ + _object->pending_notifications |= flags; + _object->pending_values = values; + + _notify(); return L4_EOK; } -// vim: tabstop=2 expandtab shiftwidth=2 +void NotifierResource::_notify() +{ + /* Enter critical section for the resource. */ + + std::lock_guard guard(_lock); + + /* Register the notification with all notifier objects. */ + + ObjectNotifiers::iterator it; + + for (it = _notifiers.begin(); it != _notifiers.end(); it++) + (*it)->notify(_object); +} + +void NotifierResource::_release() +{ + /* Enter critical section for the resource. */ + + std::lock_guard guard(_lock); + + ObjectNotifiers::iterator it; + + for (it = _notifiers.begin(); it != _notifiers.end(); it++) + (*it)->release(_object); +} + +/* vim: tabstop=2 expandtab shiftwidth=2 +*/ diff -r 84e50bfc0d76 -r 542bf2c53298 libsystypes/idl/region_mapper.idl --- a/libsystypes/idl/region_mapper.idl Fri Apr 07 23:34:59 2023 +0200 +++ b/libsystypes/idl/region_mapper.idl Fri Apr 07 23:35:21 2023 +0200 @@ -15,11 +15,17 @@ in address_t offset, in unsigned char align); /* Detach memory from the task, searching from the given address for a region - of the given size. Return the start address, region size and dataspace. */ + of the given size. Return the start address, region size and dataspace. + Note that the dataspace is returned as an index, not an actual capability. + + See: pkg/l4re-core/l4re/include/rm + See: pkg/l4re-core/l4sys/include/cxx/ipc_iface + + This is not obvious from the L4Re definitions and the IPC type system. */ [opcode(1)] void detach(in address_t addr, in address_t size, in map_flags_t flags, out address_t start, - out address_t rsize, out cap ds); + out address_t rsize, out l4_cap_idx_t ds); /* Find a region starting from the given address and having the given size. Return the start address, region size and dataspace. */ diff -r 84e50bfc0d76 -r 542bf2c53298 libsystypes/include/systypes/base.h --- a/libsystypes/include/systypes/base.h Fri Apr 07 23:34:59 2023 +0200 +++ b/libsystypes/include/systypes/base.h Fri Apr 07 23:35:21 2023 +0200 @@ -49,7 +49,8 @@ typedef struct { - unsigned long sig, val; /* signal-specific values */ + unsigned long sig; /* signal number */ + long val; /* signal-specific value */ } notify_values_t; @@ -62,6 +63,11 @@ NOTIFY_PEER_CLOSED = 0x004, /* closing files and pipes */ NOTIFY_FILE_OPENED = 0x008, /* opening files in directories */ NOTIFY_TASK_SIGNAL = 0x100, /* signal from task */ + NOTIFY_TASK_ERROR = 0x200, /* error when creating task */ + + /* Combinations of flags. */ + + NOTIFY_TASK_ALL = 0x300, }; /* Notifiable object types. */ @@ -76,7 +82,10 @@ { notifiable_base_t *base; /* access to the specific object */ notify_flags_t notifications; /* essential notifications */ + notify_flags_t pending_notifications; notify_values_t values; /* signal-specific values */ + notify_values_t pending_values; + void *handler; /* associated notification handler */ } notifiable_t; diff -r 84e50bfc0d76 -r 542bf2c53298 test_files/programs/clip.c --- a/test_files/programs/clip.c Fri Apr 07 23:34:59 2023 +0200 +++ b/test_files/programs/clip.c Fri Apr 07 23:35:21 2023 +0200 @@ -135,14 +135,22 @@ i = 1; while (i < startline) + { if (!readline(file, &i, &start, &end)) - break; + { + printf("EOF error at line %d.\n", i); + return 1; + } + } while (i < startline + numlines) { fwrite(start, sizeof(char), end - start, stdout); if (!readline(file, &i, &start, &end)) - break; + { + printf("EOF error at line %d.\n", i); + return 1; + } } fputs("\n\n", stdout); diff -r 84e50bfc0d76 -r 542bf2c53298 tests/dstest_exec.cc --- a/tests/dstest_exec.cc Fri Apr 07 23:34:59 2023 +0200 +++ b/tests/dstest_exec.cc Fri Apr 07 23:35:21 2023 +0200 @@ -28,6 +28,62 @@ +static long test_process(int argc, const char *argv[]) +{ + /* Obtain the common notifier. */ + + process_notifier_t *notifier = process_notify_task(); + + /* Create a new process structure. */ + + process_t process; + + process_init(&process); + + /* Start the process. */ + + long err = process_start(&process, argc, argv); + + if (err) + { + printf("Could not start process: %s\n", l4sys_errtostr(err)); + return err; + } + + printf("Finished program initiation.\n"); + + /* Subscribe to the process for notifications. */ + + err = process_notify_subscribe(&process, NOTIFY_TASK_ALL, notifier); + + if (err) + { + printf("Could not subscribe to process: %s\n", l4sys_errtostr(err)); + return err; + } + + /* Wait for a signal from the process. */ + + err = process_notify_wait_process(&process, notifier); + + if (err) + { + printf("Could not wait for process: %s\n", l4sys_errtostr(err)); + return err; + } + + notify_flags_t flags = process_notifications(&process); + notify_values_t values = process_notification_values(&process); + + printf("End process (flags %" pFMTnotify_flags "x values: %ld, %ld)\n", flags, values.sig, values.val); + + err = process_error(&process); + process_close(&process); + return err; +} + + + int main(int argc, char *argv[]) { long err; @@ -42,52 +98,30 @@ process_notifier_t *notifier = process_notify_task(); - /* Create a new process structure. */ - - process_t process; + /* Start a process for a non-existent program. */ - process_init(&process); + printf("Start non-existent program...\n"); - printf("Start process...\n"); + const char *bad_argv[] = {"non_existent_program"}; - /* Start a process for the given program. */ - - err = process_start(&process, argc - 1, argv + 1); + err = test_process(1, bad_argv); - if (err) + if (!err) { - printf("Could not start process: %s\n", l4sys_errtostr(err)); + printf("Non-existent program was apparently successfully started.\n"); return 1; } - printf("Finished program initiation.\n"); + printf("Non-existent program result: %s\n", l4sys_errtostr(err)); + + /* Start a process for the given program. */ - /* Subscribe to the process for notifications. */ + printf("Start process...\n"); - err = process_notify_subscribe(&process, NOTIFY_TASK_SIGNAL, notifier); + err = test_process(argc - 1, (const char **) argv + 1); if (err) - { - printf("Could not subscribe to process: %s\n", l4sys_errtostr(err)); return 1; - } - - /* Wait for a signal from the process. */ - - err = process_notify_wait_process(&process, notifier); - - if (err) - { - printf("Could not wait for process: %s\n", l4sys_errtostr(err)); - return 1; - } - - notify_flags_t flags = process_notifications(&process); - notify_values_t values = process_notification_values(&process); - - process_close(&process); - - printf("End process (flags %" pFMTnotify_flags "x values: %ld, %ld)\n", flags, values.sig, values.val); process_notify_close(notifier); diff -r 84e50bfc0d76 -r 542bf2c53298 tests/dstest_exec_many.cc --- a/tests/dstest_exec_many.cc Fri Apr 07 23:34:59 2023 +0200 +++ b/tests/dstest_exec_many.cc Fri Apr 07 23:35:21 2023 +0200 @@ -57,7 +57,7 @@ /* Start a process for the given program. */ - err = process_start(&process, argc - 2, argv + 2); + err = process_start(&process, argc - 2, (const char **) argv + 2); if (err) { @@ -69,7 +69,7 @@ /* Subscribe to the process for notifications. */ - err = process_notify_subscribe(&process, NOTIFY_TASK_SIGNAL, notifier); + err = process_notify_subscribe(&process, NOTIFY_TASK_ALL, notifier); if (err) { @@ -90,9 +90,9 @@ notify_flags_t flags = process_notifications(&process); notify_values_t values = process_notification_values(&process); - process_close(&process); + printf("[%d/%d] End process (flags %" pFMTnotify_flags "x values: %ld, %ld)\n", i, num_processes, flags, values.sig, values.val); - printf("[%d/%d] End process (flags %" pFMTnotify_flags "x values: %ld, %ld)\n", i, num_processes, flags, values.sig, values.val); + process_close(&process); } process_notify_close(notifier); diff -r 84e50bfc0d76 -r 542bf2c53298 tests/dstest_file_monitor.cc --- a/tests/dstest_file_monitor.cc Fri Apr 07 23:34:59 2023 +0200 +++ b/tests/dstest_file_monitor.cc Fri Apr 07 23:35:21 2023 +0200 @@ -56,6 +56,7 @@ { sprintf(buffer, "%s/file%02d", filename, i); + printf("Open file %d.\n", i); client_open(buffer, O_CREAT | O_RDWR); sleep(1); } @@ -65,21 +66,13 @@ static void monitor_files(file_t *directory) { - long err = client_subscribe(directory, NOTIFY_FILE_OPENED, client_notifier_task()); - - if (err) - { - printf("Could not subscribe to events on directory.\n"); - return; - } - int expected; for (expected = FILES_TO_OPEN; expected; expected--) { /* Wait for notification of content. */ - err = client_wait_file(directory, client_notifier_task()); + long err = client_wait_file(directory, client_notifier_task()); if (err) { @@ -116,6 +109,14 @@ return 1; } + long err = client_subscribe(directory, NOTIFY_FILE_OPENED, client_notifier_task()); + + if (err) + { + printf("Could not subscribe to events on directory.\n"); + return 1; + } + /* Schedule threads. */ std::thread *activities[2]; @@ -127,6 +128,9 @@ activities[i]->join(); printf("Expected %d 'file opened' notifications.\n", FILES_TO_OPEN); + + client_close(directory); + printf("End of test.\n"); return 0; } diff -r 84e50bfc0d76 -r 542bf2c53298 tests/dstest_pipe_client.cc --- a/tests/dstest_pipe_client.cc Fri Apr 07 23:34:59 2023 +0200 +++ b/tests/dstest_pipe_client.cc Fri Apr 07 23:35:21 2023 +0200 @@ -154,8 +154,8 @@ for (int p = 0; p < 2; p++) { - printf("Data received for pipe #%d: " FMToffsetd "\n", p, totals[p]); - printf("Data shown for pipe #%d: %s\n", p, totals[p] == TO_TRANSFER * 26 * NUMBER_OF_ITERATIONS ? "True" : "False"); + printf("Data received for pipe #%d: " FMToffsetd "\n", p + 1, totals[p]); + printf("Data shown for pipe #%d: %s\n", p + 1, totals[p] == TO_TRANSFER * 26 * NUMBER_OF_ITERATIONS ? "True" : "False"); } }