1.1 --- a/libnotifier/lib/src/notifier.cc Wed Mar 29 00:32:20 2023 +0200
1.2 +++ b/libnotifier/lib/src/notifier.cc Mon Apr 03 00:35:34 2023 +0200
1.3 @@ -19,11 +19,6 @@
1.4 * Boston, MA 02110-1301, USA
1.5 */
1.6
1.7 -#include <map>
1.8 -#include <mutex>
1.9 -
1.10 -#include <l4/sys/irq.h>
1.11 -
1.12 #include <ipc/cap_alloc.h>
1.13 #include <ipc/server.h>
1.14 #include <resource/resource_server.h>
1.15 @@ -34,47 +29,35 @@
1.16
1.17
1.18
1.19 -/* Null notification state. */
1.20 -
1.21 -static ObjectNotificationState _null_state;
1.22 -
1.23 -
1.24 -
1.25 /* Lock protecting per-task notifier access. */
1.26
1.27 -static std::mutex _lock;
1.28 +static std::mutex _task_lock;
1.29
1.30 /* Per-task storage for specific waiting operations. */
1.31
1.32 -static SpecificObjectNotifier *_notifier = NULL;
1.33 +static ObjectNotifier *_notifier = NULL;
1.34
1.35
1.36
1.37 /* Return the per-task notifier for object-specific waiting operations. */
1.38
1.39 -SpecificObjectNotifier *notifier_get_task_notifier()
1.40 +ObjectNotifier *notifier_get_task_notifier()
1.41 {
1.42 - std::lock_guard<std::mutex> guard(_lock);
1.43 + std::lock_guard<std::mutex> guard(_task_lock);
1.44
1.45 /* Start any new notifier. */
1.46
1.47 if (_notifier == NULL)
1.48 - {
1.49 - _notifier = new SpecificObjectNotifier;
1.50 - _notifier->start();
1.51 - }
1.52 + _notifier = new ObjectNotifier;
1.53
1.54 return _notifier;
1.55 }
1.56
1.57 /* Return a local notifier for general object waiting operations. */
1.58
1.59 -GeneralObjectNotifier *notifier_get_local_notifier()
1.60 +ObjectNotifier *notifier_get_local_notifier()
1.61 {
1.62 - GeneralObjectNotifier *notifier = new GeneralObjectNotifier;
1.63 -
1.64 - notifier->start();
1.65 - return notifier;
1.66 + return new ObjectNotifier;
1.67 }
1.68
1.69
1.70 @@ -83,358 +66,170 @@
1.71
1.72 ObjectNotifier::~ObjectNotifier()
1.73 {
1.74 - stop();
1.75 + /* Remove this notifier from the individual notifier resources. */
1.76 +
1.77 + NotifiableObjects::iterator it;
1.78
1.79 - ServerConfigs::iterator it;
1.80 + for (it = _monitored.begin(); it != _monitored.end(); it++)
1.81 + {
1.82 + notifiable_t *object = *it;
1.83
1.84 - for (it = _configs.begin(); it != _configs.end(); it++)
1.85 - delete *it;
1.86 + if (object->handler != NULL)
1.87 + {
1.88 + NotifierResource *resource = reinterpret_cast<NotifierResource *>(object->handler);
1.89 + resource->remove(this);
1.90 + }
1.91 + }
1.92
1.93 - _configs.clear();
1.94 + _monitored.clear();
1.95
1.96 /* Handle deletion of the special task notifier. */
1.97
1.98 + std::lock_guard<std::mutex> guard(_task_lock);
1.99 +
1.100 if (this == _notifier)
1.101 _notifier = NULL;
1.102 }
1.103
1.104 -
1.105 -
1.106 -/* Start listening for notifications. */
1.107 -
1.108 -long ObjectNotifier::start()
1.109 -{
1.110 - if (_started)
1.111 - return L4_EOK;
1.112 -
1.113 - /* Create a new thread to serve a "null" resource. This resource is not used
1.114 - for notifications but merely for control purposes. */
1.115 -
1.116 - NotifierResource *notifier = new NotifierResource;
1.117 - ResourceServer server(notifier);
1.118 - long err = server.start_thread(true, false);
1.119 -
1.120 - if (err)
1.121 - return err;
1.122 -
1.123 - _configs.push_back(server.config());
1.124 - _started = true;
1.125 -
1.126 - /* Retain the IRQ created for the server for control purposes. */
1.127 -
1.128 - _irq = server.config()->irq;
1.129 -
1.130 - return L4_EOK;
1.131 -}
1.132 -
1.133 +/* Handle a deletion event for an object. */
1.134
1.135 -
1.136 -/* Stop the notifier. */
1.137 -
1.138 -void ObjectNotifier::stop()
1.139 +void ObjectNotifier::release(notifiable_t *object)
1.140 {
1.141 - if (l4_is_valid_cap(_irq))
1.142 - {
1.143 - l4_irq_trigger(_irq);
1.144 - _irq = L4_INVALID_CAP;
1.145 - }
1.146 -}
1.147 -
1.148 -
1.149 + std::lock_guard<std::mutex> guard(_monitored_lock);
1.150
1.151 -/* Return notification state for the given object or null state if no record
1.152 - existed for the object. */
1.153 -
1.154 -ObjectNotificationState &ObjectNotifier::object_state(notifiable_t *object, bool create)
1.155 -{
1.156 - ObjectNotificationStates::iterator it = _state.find(object);
1.157 -
1.158 - if (it == _state.end())
1.159 - {
1.160 - if (create)
1.161 - return _state[object];
1.162 - else
1.163 - return _null_state;
1.164 - }
1.165 -
1.166 - return it->second;
1.167 + _monitored.erase(object);
1.168 }
1.169
1.170 /* Subscribe to notification events on an object. */
1.171
1.172 long ObjectNotifier::subscribe(notifiable_t *object, notify_flags_t flags)
1.173 {
1.174 - /* Acquire the lock for state lookup. */
1.175 -
1.176 - std::unique_lock<std::mutex> state_guard(_state_lock);
1.177 + std::lock_guard<std::mutex> guard(_monitored_lock);
1.178
1.179 - /* Obtain potentially new state for the object. */
1.180 + /* Ensure that a handler resource is available for object notifications. */
1.181
1.182 - ObjectNotificationState &state = object_state(object, true);
1.183 + NotifierResource *resource;
1.184
1.185 - if (state.is_null())
1.186 - {
1.187 - /* Serve the new object in the notifier thread. */
1.188 + if (object->handler != NULL)
1.189 + resource = reinterpret_cast<NotifierResource *>(object->handler);
1.190 +
1.191 + /* Create a resource if none is recorded. */
1.192
1.193 - NotifierResource *resource = new NotifierResource(this, object);
1.194 + else
1.195 + {
1.196 + resource = new NotifierResource(object);
1.197 ResourceServer server(resource);
1.198 - long err = server.start_in_thread(_configs.front()->thread);
1.199 + long err = server.start_thread(&resource->endpoint);
1.200
1.201 if (err)
1.202 return err;
1.203
1.204 - _configs.push_back(server.config());
1.205 - state.endpoint = server.config()->server;
1.206 + object->handler = resource;
1.207 }
1.208
1.209 - /* Forbid repeated subscription.
1.210 - NOTE: Could instead rely on being unsubscribed, releasing the existing
1.211 - endpoint. */
1.212 + /* Record the object. */
1.213
1.214 - else
1.215 - return -L4_EEXIST;
1.216 + _monitored.insert(object);
1.217
1.218 - /* Allow this object to be re-entered. This may occur because the subscribe
1.219 - operation can cause deferred notifications to be sent back to the
1.220 - subscribed notifier and to this object. */
1.221 -
1.222 - state_guard.unlock();
1.223 + /* Record this notifier to get general notifications. */
1.224
1.225 - /* Subscribe, sending the notification endpoint via the principal reference
1.226 - for the object. */
1.227 -
1.228 - client_Notification notify(object->base->ref);
1.229 -
1.230 - return notify.subscribe(state.endpoint, flags);
1.231 + return resource->add(this, flags);
1.232 }
1.233
1.234 /* Unsubscribe from notification events on an object. */
1.235
1.236 long ObjectNotifier::unsubscribe(notifiable_t *object)
1.237 {
1.238 - /* Acquire the lock for state lookup. */
1.239 + std::lock_guard<std::mutex> guard(_monitored_lock);
1.240 +
1.241 + if (object->handler == NULL)
1.242 + return L4_EOK;
1.243
1.244 - std::unique_lock<std::mutex> state_guard(_state_lock);
1.245 + NotifierResource *resource = reinterpret_cast<NotifierResource *>(object->handler);
1.246
1.247 - ObjectNotificationState &state = object_state(object, false);
1.248 + _monitored.erase(object);
1.249 + return resource->remove(this);
1.250 +}
1.251 +
1.252 +/* Handle a notification event for an object. */
1.253
1.254 - if (state.is_null())
1.255 - return -L4_ENOENT;
1.256 +void ObjectNotifier::notify(notifiable_t *object)
1.257 +{
1.258 + /* Enter critical section to access the queue. */
1.259
1.260 - /* Unsubscribe via the notification interface. */
1.261 + std::lock_guard<std::mutex> guard(_affected_lock);
1.262 +
1.263 + /* Ensure that a queue entry exists for the object. */
1.264 +
1.265 + NotifiableObjects::iterator it = _affected.find(object);
1.266
1.267 - client_Notification notify(object->base->ref);
1.268 + if (it == _affected.end())
1.269 + {
1.270 + _queued.push_back(object);
1.271 + _affected.insert(object);
1.272 + }
1.273
1.274 - notify.unsubscribe();
1.275 + /* Notify any waiting caller that at least one notification is available. */
1.276
1.277 - return remove_endpoint(object, state.endpoint);
1.278 + _condition.notify_one();
1.279 }
1.280
1.281 -/* Remove a notification endpoint for an object. */
1.282 +/* Wait for notification events on objects, returning each object that has a
1.283 + notification registered for it. */
1.284 +
1.285 +long ObjectNotifier::wait(notifiable_t **object)
1.286 +{
1.287 + /* Enter critical section to access the queue. */
1.288 +
1.289 + std::unique_lock<std::mutex> guard(_affected_lock);
1.290 +
1.291 + while (1)
1.292 + {
1.293 + /* With pending notifications, return the first object. */
1.294
1.295 -long ObjectNotifier::remove_endpoint(notifiable_t *object, l4_cap_idx_t endpoint)
1.296 + if (!_affected.empty())
1.297 + {
1.298 + *object = _queued.front();
1.299 + _queued.pop_front();
1.300 + _affected.erase(*object);
1.301 + break;
1.302 + }
1.303 +
1.304 + /* Otherwise, wait for notifications. */
1.305 +
1.306 + _condition.wait(guard);
1.307 + }
1.308 +
1.309 + return L4_EOK;
1.310 +}
1.311 +
1.312 +/* Wait for notification events on a specific object. */
1.313 +
1.314 +long ObjectNotifier::wait_object(notifiable_t *object)
1.315 {
1.316 - if (l4_is_invalid_cap(endpoint))
1.317 + if (object->handler == NULL)
1.318 return -L4_EINVAL;
1.319
1.320 - ipc_cap_free_um(endpoint);
1.321 -
1.322 - _state.erase(object);
1.323 -
1.324 - /* Remove the lock for updating object state. */
1.325 -
1.326 - _object_locks.erase(object);
1.327 -
1.328 + reinterpret_cast<NotifierResource *>(object->handler)->wait();
1.329 return L4_EOK;
1.330 }
1.331
1.332
1.333
1.334 -/* Handle a notification event for an object. */
1.335 -
1.336 -void GeneralObjectNotifier::notify(notifiable_t *object, notify_flags_t flags,
1.337 - notify_values_t values)
1.338 -{
1.339 - /* Enter critical section for the notifier (affecting all objects). */
1.340 -
1.341 - std::unique_lock<std::mutex> general_guard(_general_lock);
1.342 -
1.343 - /* Acquire the lock for state lookup. */
1.344 -
1.345 - std::unique_lock<std::mutex> state_guard(_state_lock);
1.346 -
1.347 - ObjectNotificationState &state = object_state(object, false);
1.348 -
1.349 - if (state.is_null())
1.350 - return;
1.351 -
1.352 - /* Acquire the lock for the object state itself. */
1.353 -
1.354 - std::unique_lock<std::mutex> object_guard(state.lock);
1.355 -
1.356 - /* Record flags and note previous flags. */
1.357 -
1.358 - notify_flags_t recorded = state.pending_notifications;
1.359 -
1.360 - state.pending_notifications |= flags;
1.361 - state.pending_values = values;
1.362 -
1.363 - /* Add an object queue entry for any objects without previous notifications. */
1.364 -
1.365 - if (!recorded)
1.366 - _affected.push_back(object);
1.367 -
1.368 - /* Notify any waiting caller. */
1.369 -
1.370 - _general_condition.notify_one();
1.371 -}
1.372 +/* Virtual destructor required for base class instance reference deletion. */
1.373
1.374 -void SpecificObjectNotifier::notify(notifiable_t *object, notify_flags_t flags,
1.375 - notify_values_t values)
1.376 +NotifierResource::~NotifierResource()
1.377 {
1.378 - /* Acquire the lock for state lookup. */
1.379 -
1.380 - std::unique_lock<std::mutex> state_guard(_state_lock);
1.381 -
1.382 - ObjectNotificationState &state = object_state(object, false);
1.383 -
1.384 - if (state.is_null())
1.385 - return;
1.386 -
1.387 - /* Acquire the lock for the object state itself. */
1.388 -
1.389 - std::unique_lock<std::mutex> object_guard(state.lock);
1.390 -
1.391 - state.pending_notifications |= flags;
1.392 - state.pending_values = values;
1.393 -
1.394 - /* Notify any waiting caller. */
1.395 -
1.396 - state.condition.notify_one();
1.397 -}
1.398 -
1.399 -
1.400 -
1.401 -/* Transfer pending notifications to the given object. This must be called with
1.402 - a lock acquired on the object notification state. */
1.403 -
1.404 -bool ObjectNotifier::_transfer(ObjectNotificationState &state, notifiable_t *object)
1.405 -{
1.406 - notify_flags_t recorded = state.pending_notifications;
1.407 -
1.408 - if (recorded)
1.409 - {
1.410 - object->notifications = recorded;
1.411 - object->values = state.pending_values;
1.412 - state.pending_notifications = 0;
1.413 - return true;
1.414 - }
1.415 -
1.416 - return false;
1.417 }
1.418
1.419 -
1.420 -
1.421 -/* Obtain object state and transfer notifications. */
1.422 -
1.423 -bool GeneralObjectNotifier::_retrieve_for_object(notifiable_t *object)
1.424 -{
1.425 - /* Acquire the lock for state lookup. */
1.426 -
1.427 - std::unique_lock<std::mutex> state_guard(_state_lock);
1.428 -
1.429 - ObjectNotificationState &state = object_state(object, false);
1.430 -
1.431 - if (state.is_null())
1.432 - return false;
1.433 -
1.434 - /* Acquire the lock for the object state itself, then release the state lock. */
1.435 -
1.436 - std::unique_lock<std::mutex> object_guard(state.lock);
1.437 -
1.438 - state_guard.unlock();
1.439 -
1.440 - /* Call generic method to transfer notifications, if possible. */
1.441 -
1.442 - return _transfer(state, object);
1.443 -}
1.444 -
1.445 -/* Obtain queued objects until one is found that still has events recorded for
1.446 - it. This must be called with the notifier's general lock acquired. */
1.447 -
1.448 -bool GeneralObjectNotifier::_retrieve(notifiable_t **object)
1.449 -{
1.450 - while (!_affected.empty())
1.451 - {
1.452 - *object = _affected.front();
1.453 - _affected.pop_front();
1.454 -
1.455 - if (_retrieve_for_object(*object))
1.456 - return true;
1.457 - }
1.458 -
1.459 - return false;
1.460 -}
1.461 -
1.462 -
1.463 -
1.464 -/* Wait for notification events on objects. */
1.465 -
1.466 -long GeneralObjectNotifier::wait(notifiable_t **object)
1.467 -{
1.468 - std::unique_lock<std::mutex> general_guard(_general_lock);
1.469 +/* Handle the release of the resource. */
1.470
1.471 - while (1)
1.472 - {
1.473 - /* With pending notifications, update the first object and exit. */
1.474 -
1.475 - if (_retrieve(object))
1.476 - break;
1.477 -
1.478 - /* Otherwise, wait for notifications. */
1.479 -
1.480 - _general_condition.wait(general_guard);
1.481 - }
1.482 -
1.483 - return L4_EOK;
1.484 -}
1.485 -
1.486 -/* Wait for notifications from a single object. */
1.487 -
1.488 -long SpecificObjectNotifier::wait_object(notifiable_t *object)
1.489 +void NotifierResource::close()
1.490 {
1.491 - /* Acquire the lock for reading object state. */
1.492 -
1.493 - std::unique_lock<std::mutex> state_guard(_state_lock);
1.494 -
1.495 - ObjectNotificationState &state = object_state(object, false);
1.496 -
1.497 - if (state.is_null())
1.498 - return -L4_EINVAL;
1.499 -
1.500 - /* Acquire the lock for the object state itself, then release the state lock. */
1.501 -
1.502 - std::unique_lock<std::mutex> object_guard(state.lock);
1.503 -
1.504 - state_guard.unlock();
1.505 -
1.506 - while (1)
1.507 - {
1.508 - /* With pending notifications, update the object and exit. */
1.509 -
1.510 - if (_transfer(state, object))
1.511 - break;
1.512 -
1.513 - /* Otherwise, wait for notifications. */
1.514 -
1.515 - state.condition.wait(object_guard);
1.516 - }
1.517 -
1.518 - return L4_EOK;
1.519 + _release();
1.520 + _object->handler = NULL;
1.521 }
1.522
1.523 -
1.524 -
1.525 /* Object-specific resource methods. */
1.526
1.527 ipc_server_default_config_type NotifierResource::config()
1.528 @@ -442,14 +237,110 @@
1.529 return config_Notifier;
1.530 }
1.531
1.532 +
1.533 +
1.534 +/* Registration of notifiers. */
1.535 +
1.536 +long NotifierResource::add(ObjectNotifier *notifier, notify_flags_t flags)
1.537 +{
1.538 + std::lock_guard<std::mutex> guard(_lock);
1.539 +
1.540 + bool is_new = _notifiers.empty();
1.541 +
1.542 + _notifiers.insert(notifier);
1.543 +
1.544 + /* Subscribe, sending the notification endpoint via the principal reference
1.545 + for the object. */
1.546 +
1.547 + if (!is_new)
1.548 + return L4_EOK;
1.549 +
1.550 + client_Notification notify(_object->base->ref);
1.551 +
1.552 + return notify.subscribe(endpoint, flags);
1.553 +}
1.554 +
1.555 +long NotifierResource::remove(ObjectNotifier *notifier)
1.556 +{
1.557 + std::lock_guard<std::mutex> guard(_lock);
1.558 +
1.559 + _notifiers.erase(notifier);
1.560 +
1.561 + /* Unsubscribe via the notification interface. */
1.562 +
1.563 + if (!_notifiers.empty())
1.564 + return L4_EOK;
1.565 +
1.566 + client_Notification notify(_object->base->ref);
1.567 +
1.568 + return notify.unsubscribe();
1.569 +}
1.570 +
1.571 +
1.572 +
1.573 /* Register a notification received by an object-specific resource. */
1.574
1.575 long NotifierResource::notify(notify_flags_t flags, notify_values_t values)
1.576 {
1.577 - if (_notifier != NULL)
1.578 - _notifier->notify(_object, flags, values);
1.579 + /* Update the notifiable object. */
1.580
1.581 + _object->notifications |= flags;
1.582 + _object->values = values;
1.583 +
1.584 + _notify();
1.585 return L4_EOK;
1.586 }
1.587
1.588 -// vim: tabstop=2 expandtab shiftwidth=2
1.589 +void NotifierResource::_notify()
1.590 +{
1.591 + /* Enter critical section for the resource. */
1.592 +
1.593 + std::lock_guard<std::mutex> guard(_lock);
1.594 +
1.595 + /* Record a pending notification which persists if nothing is waiting. */
1.596 +
1.597 + _pending = true;
1.598 +
1.599 + /* Notify any party waiting specifically on this object. */
1.600 +
1.601 + _condition.notify_one();
1.602 +
1.603 + /* Register the notification with all notifier objects. */
1.604 +
1.605 + ObjectNotifiers::iterator it;
1.606 +
1.607 + for (it = _notifiers.begin(); it != _notifiers.end(); it++)
1.608 + (*it)->notify(_object);
1.609 +}
1.610 +
1.611 +void NotifierResource::_release()
1.612 +{
1.613 + /* Enter critical section for the resource. */
1.614 +
1.615 + std::lock_guard<std::mutex> guard(_lock);
1.616 +
1.617 + ObjectNotifiers::iterator it;
1.618 +
1.619 + for (it = _notifiers.begin(); it != _notifiers.end(); it++)
1.620 + (*it)->release(_object);
1.621 +}
1.622 +
1.623 +/* Wait for notification events on a specific object. */
1.624 +
1.625 +long NotifierResource::wait()
1.626 +{
1.627 + /* Enter critical section for the resource. */
1.628 +
1.629 + std::unique_lock<std::mutex> guard(_lock);
1.630 +
1.631 + /* Wait for the notification condition. */
1.632 +
1.633 + if (!_pending)
1.634 + _condition.wait(guard);
1.635 +
1.636 + _pending = false;
1.637 + return L4_EOK;
1.638 +}
1.639 +
1.640 +/* vim: tabstop=2 expandtab shiftwidth=2
1.641 +*/