1 /* 2 * Generic object event notification support. 3 * 4 * Copyright (C) 2021, 2022, 2023 Paul Boddie <paul@boddie.org.uk> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 of 9 * the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA 20 */ 21 22 #include <map> 23 #include <mutex> 24 25 #include <ipc/cap_alloc.h> 26 #include <ipc/server.h> 27 28 #include <pthread.h> 29 #include <pthread-l4.h> 30 31 #include "notification_client.h" 32 #include "notifier.h" 33 #include "notifier_interface.h" 34 35 36 37 /* Null notification state. */ 38 39 static ObjectNotificationState _null_state; 40 41 42 43 /* Lock protecting per-task notifier access. */ 44 45 static std::mutex _lock; 46 47 /* Per-task storage for specific waiting operations. */ 48 49 static SpecificObjectNotifier *_notifier = NULL; 50 51 52 53 /* Return the per-task notifier for object-specific waiting operations. */ 54 55 SpecificObjectNotifier *notifier_get_task_notifier() 56 { 57 std::lock_guard<std::mutex> guard(_lock); 58 59 /* Start any new notifier. */ 60 61 if (_notifier == NULL) 62 { 63 _notifier = new SpecificObjectNotifier; 64 _notifier->start(); 65 } 66 67 return _notifier; 68 } 69 70 /* Return a local notifier for general object waiting operations. */ 71 72 GeneralObjectNotifier *notifier_get_local_notifier() 73 { 74 GeneralObjectNotifier *notifier = new GeneralObjectNotifier; 75 76 notifier->start(); 77 return notifier; 78 } 79 80 81 82 /* Invoke the mainloop in a thread. */ 83 84 static void *notifier_mainloop(void *data) 85 { 86 ObjectNotifier *notifier = reinterpret_cast<ObjectNotifier *>(data); 87 88 notifier->mainloop(); 89 return 0; 90 } 91 92 93 94 /* Virtual destructor required for base class instance reference deletion. */ 95 96 ObjectNotifier::~ObjectNotifier() 97 { 98 } 99 100 /* Listen for notifications. */ 101 102 void ObjectNotifier::mainloop() 103 { 104 ipc_message_t msg; 105 l4_umword_t label; 106 107 while (1) 108 { 109 ipc_message_wait(&msg, &label); 110 111 /* Clear lower label bits. */ 112 113 label = label & ~3UL; 114 115 /* Ignore erroneous messages. */ 116 117 if (l4_ipc_error(msg.tag, l4_utcb())) 118 continue; 119 120 /* Interpret gate labels as notifiable objects. */ 121 122 notifiable_t *object = (notifiable_t *) label; 123 124 /* Obtain message details. */ 125 126 ipc_message_open(&msg); 127 128 struct in_words_Notifier_notify *in_words = (struct in_words_Notifier_notify *) ipc_message_get_word_address(&msg, 0); 129 130 /* Reply to notifications. */ 131 132 ipc_message_reply(&msg); 133 ipc_message_discard(&msg); 134 135 /* Register the notification. */ 136 137 _notify(object, in_words->flags, in_words->values); 138 } 139 140 ipc_message_free(&msg); 141 } 142 143 /* Start listening for notifications. */ 144 145 long ObjectNotifier::start() 146 { 147 if (_started) 148 return L4_EOK; 149 150 pthread_t thread; 151 pthread_attr_t attr; 152 long err; 153 154 pthread_attr_init(&attr); 155 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 156 157 err = pthread_create(&thread, &attr, notifier_mainloop, this); 158 if (err) 159 return err; 160 161 _thread = pthread_l4_cap(thread); 162 _started = true; 163 164 return L4_EOK; 165 } 166 167 168 169 /* Return notification state for the given object or null state if no record 170 existed for the object. */ 171 172 ObjectNotificationState &ObjectNotifier::object_state(notifiable_t *object, bool create) 173 { 174 ObjectNotificationStates::iterator it = _state.find(object); 175 176 if (it == _state.end()) 177 { 178 if (create) 179 return _state[object]; 180 else 181 return _null_state; 182 } 183 184 return it->second; 185 } 186 187 /* Subscribe to notification events on an object. */ 188 189 long ObjectNotifier::subscribe(notifiable_t *object, notify_flags_t flags) 190 { 191 l4_cap_idx_t endpoint; 192 long err = get_endpoint(object, &endpoint, true); 193 194 if (err) 195 return err; 196 197 /* Subscribe, sending the notification endpoint via the principal reference 198 for the object. */ 199 200 client_Notification notify(object->base->ref); 201 202 return notify.subscribe(endpoint, flags); 203 } 204 205 /* Unsubscribe from notification events on an object. */ 206 207 long ObjectNotifier::unsubscribe(notifiable_t *object) 208 { 209 l4_cap_idx_t endpoint; 210 long err = get_endpoint(object, &endpoint, false); 211 212 if (err) 213 return err; 214 215 /* Unsubscribe via the notification interface. */ 216 217 client_Notification notify(object->base->ref); 218 219 notify.unsubscribe(); 220 221 return remove_endpoint(object, endpoint); 222 } 223 224 /* Obtain a notification endpoint for an object. */ 225 226 long ObjectNotifier::get_endpoint(notifiable_t *object, l4_cap_idx_t *endpoint, bool create) 227 { 228 /* Acquire the lock for state lookup. */ 229 230 std::unique_lock<std::mutex> state_guard(_state_lock); 231 232 ObjectNotificationState &state = object_state(object, create); 233 234 /* Create a notification endpoint, if necessary. */ 235 236 if (state.is_null()) 237 { 238 if (create) 239 { 240 long err = ipc_server_new_for_thread(&state.endpoint, object, _thread); 241 242 if (err) 243 return err; 244 } 245 else 246 return -L4_ENOENT; 247 } 248 249 *endpoint = state.endpoint; 250 return L4_EOK; 251 } 252 253 /* Remove a notification endpoint for an object. */ 254 255 long ObjectNotifier::remove_endpoint(notifiable_t *object, l4_cap_idx_t endpoint) 256 { 257 if (l4_is_invalid_cap(endpoint)) 258 return -L4_EINVAL; 259 260 ipc_cap_free_um(endpoint); 261 262 _state.erase(object); 263 264 /* Remove the lock for updating object state. */ 265 266 _object_locks.erase(object); 267 268 return L4_EOK; 269 } 270 271 272 273 /* Handle a notification event for an object. Ideally, this would be invoked by 274 the generic server dispatch mechanism, with the gate label being interpreted 275 and provided as the first parameter. */ 276 277 void GeneralObjectNotifier::_notify(notifiable_t *object, notify_flags_t flags, 278 notify_values_t values) 279 { 280 /* Enter critical section for the notifier (affecting all objects). */ 281 282 std::unique_lock<std::mutex> general_guard(_general_lock); 283 284 /* Acquire the lock for state lookup. */ 285 286 std::unique_lock<std::mutex> state_guard(_state_lock); 287 288 ObjectNotificationState &state = object_state(object, false); 289 290 if (state.is_null()) 291 return; 292 293 /* Acquire the lock for the object state itself. */ 294 295 std::unique_lock<std::mutex> object_guard(state.lock); 296 297 /* Record flags and note previous flags. */ 298 299 notify_flags_t recorded = state.pending_notifications; 300 301 state.pending_notifications |= flags; 302 state.pending_values = values; 303 304 /* Add an object queue entry for any objects without previous notifications. */ 305 306 if (!recorded) 307 _affected.push_back(object); 308 309 /* Notify any waiting caller. */ 310 311 _general_condition.notify_one(); 312 } 313 314 void SpecificObjectNotifier::_notify(notifiable_t *object, notify_flags_t flags, 315 notify_values_t values) 316 { 317 /* Acquire the lock for state lookup. */ 318 319 std::unique_lock<std::mutex> state_guard(_state_lock); 320 321 ObjectNotificationState &state = object_state(object, false); 322 323 if (state.is_null()) 324 return; 325 326 /* Acquire the lock for the object state itself. */ 327 328 std::unique_lock<std::mutex> object_guard(state.lock); 329 330 state.pending_notifications |= flags; 331 state.pending_values = values; 332 333 /* Notify any waiting caller. */ 334 335 state.condition.notify_one(); 336 } 337 338 339 340 /* Transfer pending notifications to the given object. This must be called with 341 a lock acquired on the object notification state. */ 342 343 bool ObjectNotifier::_transfer(ObjectNotificationState &state, notifiable_t *object) 344 { 345 notify_flags_t recorded = state.pending_notifications; 346 347 if (recorded) 348 { 349 object->notifications = recorded; 350 object->values = state.pending_values; 351 state.pending_notifications = 0; 352 return true; 353 } 354 355 return false; 356 } 357 358 359 360 /* Obtain object state and transfer notifications. */ 361 362 bool GeneralObjectNotifier::_retrieve_for_object(notifiable_t *object) 363 { 364 /* Acquire the lock for state lookup. */ 365 366 std::unique_lock<std::mutex> state_guard(_state_lock); 367 368 ObjectNotificationState &state = object_state(object, false); 369 370 if (state.is_null()) 371 return false; 372 373 /* Acquire the lock for the object state itself, then release the state lock. */ 374 375 std::unique_lock<std::mutex> object_guard(state.lock); 376 377 state_guard.unlock(); 378 379 /* Call generic method to transfer notifications, if possible. */ 380 381 return _transfer(state, object); 382 } 383 384 /* Obtain queued objects until one is found that still has events recorded for 385 it. This must be called with the notifier's general lock acquired. */ 386 387 bool GeneralObjectNotifier::_retrieve(notifiable_t **object) 388 { 389 while (!_affected.empty()) 390 { 391 *object = _affected.front(); 392 _affected.pop_front(); 393 394 if (_retrieve_for_object(*object)) 395 return true; 396 } 397 398 return false; 399 } 400 401 402 403 /* Wait for notification events on objects. */ 404 405 long GeneralObjectNotifier::wait(notifiable_t **object) 406 { 407 std::unique_lock<std::mutex> general_guard(_general_lock); 408 409 while (1) 410 { 411 /* With pending notifications, update the first object and exit. */ 412 413 if (_retrieve(object)) 414 break; 415 416 /* Otherwise, wait for notifications. */ 417 418 _general_condition.wait(general_guard); 419 } 420 421 return L4_EOK; 422 } 423 424 /* Wait for notifications from a single object. */ 425 426 long SpecificObjectNotifier::wait_object(notifiable_t *object) 427 { 428 /* Acquire the lock for reading object state. */ 429 430 std::unique_lock<std::mutex> state_guard(_state_lock); 431 432 ObjectNotificationState &state = object_state(object, false); 433 434 if (state.is_null()) 435 return -L4_EINVAL; 436 437 /* Acquire the lock for the object state itself, then release the state lock. */ 438 439 std::unique_lock<std::mutex> object_guard(state.lock); 440 441 state_guard.unlock(); 442 443 while (1) 444 { 445 /* With pending notifications, update the object and exit. */ 446 447 if (_transfer(state, object)) 448 break; 449 450 /* Otherwise, wait for notifications. */ 451 452 state.condition.wait(object_guard); 453 } 454 455 return L4_EOK; 456 } 457 458 // vim: tabstop=2 expandtab shiftwidth=2