1.1 --- a/libfsclient/lib/src/notifier.cc Mon Mar 20 14:52:03 2023 +0100
1.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
1.3 @@ -1,458 +0,0 @@
1.4 -/*
1.5 - * Generic object event notification support.
1.6 - *
1.7 - * Copyright (C) 2021, 2022, 2023 Paul Boddie <paul@boddie.org.uk>
1.8 - *
1.9 - * This program is free software; you can redistribute it and/or
1.10 - * modify it under the terms of the GNU General Public License as
1.11 - * published by the Free Software Foundation; either version 2 of
1.12 - * the License, or (at your option) any later version.
1.13 - *
1.14 - * This program is distributed in the hope that it will be useful,
1.15 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.16 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.17 - * GNU General Public License for more details.
1.18 - *
1.19 - * You should have received a copy of the GNU General Public License
1.20 - * along with this program; if not, write to the Free Software
1.21 - * Foundation, Inc., 51 Franklin Street, Fifth Floor,
1.22 - * Boston, MA 02110-1301, USA
1.23 - */
1.24 -
1.25 -#include <map>
1.26 -#include <mutex>
1.27 -
1.28 -#include <ipc/cap_alloc.h>
1.29 -#include <ipc/server.h>
1.30 -
1.31 -#include <pthread.h>
1.32 -#include <pthread-l4.h>
1.33 -
1.34 -#include "notification_client.h"
1.35 -#include "notifier.h"
1.36 -#include "notifier_interface.h"
1.37 -
1.38 -
1.39 -
1.40 -/* Null notification state. */
1.41 -
1.42 -static ObjectNotificationState _null_state;
1.43 -
1.44 -
1.45 -
1.46 -/* Lock protecting per-task notifier access. */
1.47 -
1.48 -static std::mutex _lock;
1.49 -
1.50 -/* Per-task storage for specific waiting operations. */
1.51 -
1.52 -static SpecificObjectNotifier *_notifier = NULL;
1.53 -
1.54 -
1.55 -
1.56 -/* Return the per-task notifier for object-specific waiting operations. */
1.57 -
1.58 -SpecificObjectNotifier *notifier_get_task_notifier()
1.59 -{
1.60 - std::lock_guard<std::mutex> guard(_lock);
1.61 -
1.62 - /* Start any new notifier. */
1.63 -
1.64 - if (_notifier == NULL)
1.65 - {
1.66 - _notifier = new SpecificObjectNotifier;
1.67 - _notifier->start();
1.68 - }
1.69 -
1.70 - return _notifier;
1.71 -}
1.72 -
1.73 -/* Return a local notifier for general object waiting operations. */
1.74 -
1.75 -GeneralObjectNotifier *notifier_get_local_notifier()
1.76 -{
1.77 - GeneralObjectNotifier *notifier = new GeneralObjectNotifier;
1.78 -
1.79 - notifier->start();
1.80 - return notifier;
1.81 -}
1.82 -
1.83 -
1.84 -
1.85 -/* Invoke the mainloop in a thread. */
1.86 -
1.87 -static void *notifier_mainloop(void *data)
1.88 -{
1.89 - ObjectNotifier *notifier = reinterpret_cast<ObjectNotifier *>(data);
1.90 -
1.91 - notifier->mainloop();
1.92 - return 0;
1.93 -}
1.94 -
1.95 -
1.96 -
1.97 -/* Virtual destructor required for base class instance reference deletion. */
1.98 -
1.99 -ObjectNotifier::~ObjectNotifier()
1.100 -{
1.101 -}
1.102 -
1.103 -/* Listen for notifications. */
1.104 -
1.105 -void ObjectNotifier::mainloop()
1.106 -{
1.107 - ipc_message_t msg;
1.108 - l4_umword_t label;
1.109 -
1.110 - while (1)
1.111 - {
1.112 - ipc_message_wait(&msg, &label);
1.113 -
1.114 - /* Clear lower label bits. */
1.115 -
1.116 - label = label & ~3UL;
1.117 -
1.118 - /* Ignore erroneous messages. */
1.119 -
1.120 - if (l4_ipc_error(msg.tag, l4_utcb()))
1.121 - continue;
1.122 -
1.123 - /* Interpret gate labels as notifiable objects. */
1.124 -
1.125 - notifiable_t *object = (notifiable_t *) label;
1.126 -
1.127 - /* Obtain message details. */
1.128 -
1.129 - ipc_message_open(&msg);
1.130 -
1.131 - struct in_words_Notifier_notify *in_words = (struct in_words_Notifier_notify *) ipc_message_get_word_address(&msg, 0);
1.132 -
1.133 - /* Reply to notifications. */
1.134 -
1.135 - ipc_message_reply(&msg);
1.136 - ipc_message_discard(&msg);
1.137 -
1.138 - /* Register the notification. */
1.139 -
1.140 - _notify(object, in_words->flags, in_words->values);
1.141 - }
1.142 -
1.143 - ipc_message_free(&msg);
1.144 -}
1.145 -
1.146 -/* Start listening for notifications. */
1.147 -
1.148 -long ObjectNotifier::start()
1.149 -{
1.150 - if (_started)
1.151 - return L4_EOK;
1.152 -
1.153 - pthread_t thread;
1.154 - pthread_attr_t attr;
1.155 - long err;
1.156 -
1.157 - pthread_attr_init(&attr);
1.158 - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1.159 -
1.160 - err = pthread_create(&thread, &attr, notifier_mainloop, this);
1.161 - if (err)
1.162 - return err;
1.163 -
1.164 - _thread = pthread_l4_cap(thread);
1.165 - _started = true;
1.166 -
1.167 - return L4_EOK;
1.168 -}
1.169 -
1.170 -
1.171 -
1.172 -/* Return notification state for the given object or null state if no record
1.173 - existed for the object. */
1.174 -
1.175 -ObjectNotificationState &ObjectNotifier::object_state(notifiable_t *object, bool create)
1.176 -{
1.177 - ObjectNotificationStates::iterator it = _state.find(object);
1.178 -
1.179 - if (it == _state.end())
1.180 - {
1.181 - if (create)
1.182 - return _state[object];
1.183 - else
1.184 - return _null_state;
1.185 - }
1.186 -
1.187 - return it->second;
1.188 -}
1.189 -
1.190 -/* Subscribe to notification events on an object. */
1.191 -
1.192 -long ObjectNotifier::subscribe(notifiable_t *object, notify_flags_t flags)
1.193 -{
1.194 - l4_cap_idx_t endpoint;
1.195 - long err = get_endpoint(object, &endpoint, true);
1.196 -
1.197 - if (err)
1.198 - return err;
1.199 -
1.200 - /* Subscribe, sending the notification endpoint via the principal reference
1.201 - for the object. */
1.202 -
1.203 - client_Notification notify(object->base->ref);
1.204 -
1.205 - return notify.subscribe(endpoint, flags);
1.206 -}
1.207 -
1.208 -/* Unsubscribe from notification events on an object. */
1.209 -
1.210 -long ObjectNotifier::unsubscribe(notifiable_t *object)
1.211 -{
1.212 - l4_cap_idx_t endpoint;
1.213 - long err = get_endpoint(object, &endpoint, false);
1.214 -
1.215 - if (err)
1.216 - return err;
1.217 -
1.218 - /* Unsubscribe via the notification interface. */
1.219 -
1.220 - client_Notification notify(object->base->ref);
1.221 -
1.222 - notify.unsubscribe(endpoint);
1.223 -
1.224 - return remove_endpoint(object, endpoint);
1.225 -}
1.226 -
1.227 -/* Obtain a notification endpoint for an object. */
1.228 -
1.229 -long ObjectNotifier::get_endpoint(notifiable_t *object, l4_cap_idx_t *endpoint, bool create)
1.230 -{
1.231 - /* Acquire the lock for state lookup. */
1.232 -
1.233 - std::unique_lock<std::mutex> state_guard(_state_lock);
1.234 -
1.235 - ObjectNotificationState &state = object_state(object, create);
1.236 -
1.237 - /* Create a notification endpoint, if necessary. */
1.238 -
1.239 - if (state.is_null())
1.240 - {
1.241 - if (create)
1.242 - {
1.243 - long err = ipc_server_new_for_thread(&state.endpoint, object, _thread);
1.244 -
1.245 - if (err)
1.246 - return err;
1.247 - }
1.248 - else
1.249 - return -L4_ENOENT;
1.250 - }
1.251 -
1.252 - *endpoint = state.endpoint;
1.253 - return L4_EOK;
1.254 -}
1.255 -
1.256 -/* Remove a notification endpoint for an object. */
1.257 -
1.258 -long ObjectNotifier::remove_endpoint(notifiable_t *object, l4_cap_idx_t endpoint)
1.259 -{
1.260 - if (l4_is_invalid_cap(endpoint))
1.261 - return -L4_EINVAL;
1.262 -
1.263 - ipc_cap_free_um(endpoint);
1.264 -
1.265 - _state.erase(object);
1.266 -
1.267 - /* Remove the lock for updating object state. */
1.268 -
1.269 - _object_locks.erase(object);
1.270 -
1.271 - return L4_EOK;
1.272 -}
1.273 -
1.274 -
1.275 -
1.276 -/* Handle a notification event for an object. Ideally, this would be invoked by
1.277 - the generic server dispatch mechanism, with the gate label being interpreted
1.278 - and provided as the first parameter. */
1.279 -
1.280 -void GeneralObjectNotifier::_notify(notifiable_t *object, notify_flags_t flags,
1.281 - notify_values_t values)
1.282 -{
1.283 - /* Enter critical section for the notifier (affecting all objects). */
1.284 -
1.285 - std::unique_lock<std::mutex> general_guard(_general_lock);
1.286 -
1.287 - /* Acquire the lock for state lookup. */
1.288 -
1.289 - std::unique_lock<std::mutex> state_guard(_state_lock);
1.290 -
1.291 - ObjectNotificationState &state = object_state(object, false);
1.292 -
1.293 - if (state.is_null())
1.294 - return;
1.295 -
1.296 - /* Acquire the lock for the object state itself. */
1.297 -
1.298 - std::unique_lock<std::mutex> object_guard(state.lock);
1.299 -
1.300 - /* Record flags and note previous flags. */
1.301 -
1.302 - notify_flags_t recorded = state.pending_notifications;
1.303 -
1.304 - state.pending_notifications |= flags;
1.305 - state.pending_values = values;
1.306 -
1.307 - /* Add an object queue entry for any objects without previous notifications. */
1.308 -
1.309 - if (!recorded)
1.310 - _affected.push_back(object);
1.311 -
1.312 - /* Notify any waiting caller. */
1.313 -
1.314 - _general_condition.notify_one();
1.315 -}
1.316 -
1.317 -void SpecificObjectNotifier::_notify(notifiable_t *object, notify_flags_t flags,
1.318 - notify_values_t values)
1.319 -{
1.320 - /* Acquire the lock for state lookup. */
1.321 -
1.322 - std::unique_lock<std::mutex> state_guard(_state_lock);
1.323 -
1.324 - ObjectNotificationState &state = object_state(object, false);
1.325 -
1.326 - if (state.is_null())
1.327 - return;
1.328 -
1.329 - /* Acquire the lock for the object state itself. */
1.330 -
1.331 - std::unique_lock<std::mutex> object_guard(state.lock);
1.332 -
1.333 - state.pending_notifications |= flags;
1.334 - state.pending_values = values;
1.335 -
1.336 - /* Notify any waiting caller. */
1.337 -
1.338 - state.condition.notify_one();
1.339 -}
1.340 -
1.341 -
1.342 -
1.343 -/* Transfer pending notifications to the given object. This must be called with
1.344 - a lock acquired on the object notification state. */
1.345 -
1.346 -bool ObjectNotifier::_transfer(ObjectNotificationState &state, notifiable_t *object)
1.347 -{
1.348 - notify_flags_t recorded = state.pending_notifications;
1.349 -
1.350 - if (recorded)
1.351 - {
1.352 - object->notifications = recorded;
1.353 - object->values = state.pending_values;
1.354 - state.pending_notifications = 0;
1.355 - return true;
1.356 - }
1.357 -
1.358 - return false;
1.359 -}
1.360 -
1.361 -
1.362 -
1.363 -/* Obtain object state and transfer notifications. */
1.364 -
1.365 -bool GeneralObjectNotifier::_retrieve_for_object(notifiable_t *object)
1.366 -{
1.367 - /* Acquire the lock for state lookup. */
1.368 -
1.369 - std::unique_lock<std::mutex> state_guard(_state_lock);
1.370 -
1.371 - ObjectNotificationState &state = object_state(object, false);
1.372 -
1.373 - if (state.is_null())
1.374 - return false;
1.375 -
1.376 - /* Acquire the lock for the object state itself, then release the state lock. */
1.377 -
1.378 - std::unique_lock<std::mutex> object_guard(state.lock);
1.379 -
1.380 - state_guard.unlock();
1.381 -
1.382 - /* Call generic method to transfer notifications, if possible. */
1.383 -
1.384 - return _transfer(state, object);
1.385 -}
1.386 -
1.387 -/* Obtain queued objects until one is found that still has events recorded for
1.388 - it. This must be called with the notifier's general lock acquired. */
1.389 -
1.390 -bool GeneralObjectNotifier::_retrieve(notifiable_t **object)
1.391 -{
1.392 - while (!_affected.empty())
1.393 - {
1.394 - *object = _affected.front();
1.395 - _affected.pop_front();
1.396 -
1.397 - if (_retrieve_for_object(*object))
1.398 - return true;
1.399 - }
1.400 -
1.401 - return false;
1.402 -}
1.403 -
1.404 -
1.405 -
1.406 -/* Wait for notification events on objects. */
1.407 -
1.408 -long GeneralObjectNotifier::wait(notifiable_t **object)
1.409 -{
1.410 - std::unique_lock<std::mutex> general_guard(_general_lock);
1.411 -
1.412 - while (1)
1.413 - {
1.414 - /* With pending notifications, update the first object and exit. */
1.415 -
1.416 - if (_retrieve(object))
1.417 - break;
1.418 -
1.419 - /* Otherwise, wait for notifications. */
1.420 -
1.421 - _general_condition.wait(general_guard);
1.422 - }
1.423 -
1.424 - return L4_EOK;
1.425 -}
1.426 -
1.427 -/* Wait for notifications from a single object. */
1.428 -
1.429 -long SpecificObjectNotifier::wait_object(notifiable_t *object)
1.430 -{
1.431 - /* Acquire the lock for reading object state. */
1.432 -
1.433 - std::unique_lock<std::mutex> state_guard(_state_lock);
1.434 -
1.435 - ObjectNotificationState &state = object_state(object, false);
1.436 -
1.437 - if (state.is_null())
1.438 - return -L4_EINVAL;
1.439 -
1.440 - /* Acquire the lock for the object state itself, then release the state lock. */
1.441 -
1.442 - std::unique_lock<std::mutex> object_guard(state.lock);
1.443 -
1.444 - state_guard.unlock();
1.445 -
1.446 - while (1)
1.447 - {
1.448 - /* With pending notifications, update the object and exit. */
1.449 -
1.450 - if (_transfer(state, object))
1.451 - break;
1.452 -
1.453 - /* Otherwise, wait for notifications. */
1.454 -
1.455 - state.condition.wait(object_guard);
1.456 - }
1.457 -
1.458 - return L4_EOK;
1.459 -}
1.460 -
1.461 -// vim: tabstop=2 expandtab shiftwidth=2