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 /* Acquire the lock for state lookup. */ 192 193 std::unique_lock<std::mutex> state_guard(_state_lock); 194 195 ObjectNotificationState &state = object_state(object, true); 196 long err; 197 198 /* Create a notification endpoint, if necessary. */ 199 200 if (state.is_null()) 201 { 202 err = ipc_server_new_for_thread(&state.endpoint, object, _thread); 203 204 if (err) 205 return err; 206 } 207 208 /* Subscribe, sending the notification endpoint. */ 209 210 client_Notification notify(object->base->ref); 211 212 err = notify.subscribe(state.endpoint, flags); 213 214 if (err) 215 { 216 ipc_cap_free_um(state.endpoint); 217 _state.erase(object); 218 return err; 219 } 220 221 return L4_EOK; 222 } 223 224 /* Unsubscribe from notification events on an object. */ 225 226 long ObjectNotifier::unsubscribe(notifiable_t *object) 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, false); 233 234 if (state.is_null()) 235 return -L4_EINVAL; 236 237 /* Unsubscribe via the notification interface. */ 238 239 client_Notification notify(object->base->ref); 240 241 notify.unsubscribe(); 242 243 ipc_cap_free_um(state.endpoint); 244 245 _state.erase(object); 246 247 /* Remove the lock for updating object state. */ 248 249 _object_locks.erase(object); 250 251 return L4_EOK; 252 } 253 254 255 256 /* Handle a notification event for an object. Ideally, this would be invoked by 257 the generic server dispatch mechanism, with the gate label being interpreted 258 and provided as the first parameter. */ 259 260 void GeneralObjectNotifier::_notify(notifiable_t *object, notify_flags_t flags, 261 notify_values_t values) 262 { 263 /* Enter critical section for the notifier (affecting all objects). */ 264 265 std::unique_lock<std::mutex> general_guard(_general_lock); 266 267 /* Acquire the lock for state lookup. */ 268 269 std::unique_lock<std::mutex> state_guard(_state_lock); 270 271 ObjectNotificationState &state = object_state(object, false); 272 273 if (state.is_null()) 274 return; 275 276 /* Acquire the lock for the object state itself. */ 277 278 std::unique_lock<std::mutex> object_guard(state.lock); 279 280 /* Record flags and note previous flags. */ 281 282 notify_flags_t recorded = state.pending_notifications; 283 284 state.pending_notifications |= flags; 285 state.pending_values = values; 286 287 /* Add an object queue entry for any objects without previous notifications. */ 288 289 if (!recorded) 290 _affected.push_back(object); 291 292 /* Notify any waiting caller. */ 293 294 _general_condition.notify_one(); 295 } 296 297 void SpecificObjectNotifier::_notify(notifiable_t *object, notify_flags_t flags, 298 notify_values_t values) 299 { 300 /* Acquire the lock for state lookup. */ 301 302 std::unique_lock<std::mutex> state_guard(_state_lock); 303 304 ObjectNotificationState &state = object_state(object, false); 305 306 if (state.is_null()) 307 return; 308 309 /* Acquire the lock for the object state itself. */ 310 311 std::unique_lock<std::mutex> object_guard(state.lock); 312 313 state.pending_notifications |= flags; 314 state.pending_values = values; 315 316 /* Notify any waiting caller. */ 317 318 state.condition.notify_one(); 319 } 320 321 322 323 /* Transfer pending notifications to the given object. This must be called with 324 a lock acquired on the object notification state. */ 325 326 bool ObjectNotifier::_transfer(ObjectNotificationState &state, notifiable_t *object) 327 { 328 notify_flags_t recorded = state.pending_notifications; 329 330 if (recorded) 331 { 332 object->notifications = recorded; 333 object->values = state.pending_values; 334 state.pending_notifications = 0; 335 return true; 336 } 337 338 return false; 339 } 340 341 342 343 /* Obtain object state and transfer notifications. */ 344 345 bool GeneralObjectNotifier::_retrieve_for_object(notifiable_t *object) 346 { 347 /* Acquire the lock for state lookup. */ 348 349 std::unique_lock<std::mutex> state_guard(_state_lock); 350 351 ObjectNotificationState &state = object_state(object, false); 352 353 if (state.is_null()) 354 return false; 355 356 /* Acquire the lock for the object state itself, then release the state lock. */ 357 358 std::unique_lock<std::mutex> object_guard(state.lock); 359 360 state_guard.unlock(); 361 362 /* Call generic method to transfer notifications, if possible. */ 363 364 return _transfer(state, object); 365 } 366 367 /* Obtain queued objects until one is found that still has events recorded for 368 it. This must be called with the notifier's general lock acquired. */ 369 370 bool GeneralObjectNotifier::_retrieve(notifiable_t **object) 371 { 372 while (!_affected.empty()) 373 { 374 *object = _affected.front(); 375 _affected.pop_front(); 376 377 if (_retrieve_for_object(*object)) 378 return true; 379 } 380 381 return false; 382 } 383 384 385 386 /* Wait for notification events on objects. */ 387 388 long GeneralObjectNotifier::wait(notifiable_t **object) 389 { 390 std::unique_lock<std::mutex> general_guard(_general_lock); 391 392 while (1) 393 { 394 /* With pending notifications, update the first object and exit. */ 395 396 if (_retrieve(object)) 397 break; 398 399 /* Otherwise, wait for notifications. */ 400 401 _general_condition.wait(general_guard); 402 } 403 404 return L4_EOK; 405 } 406 407 /* Wait for notifications from a single object. */ 408 409 long SpecificObjectNotifier::wait_object(notifiable_t *object) 410 { 411 /* Acquire the lock for reading object state. */ 412 413 std::unique_lock<std::mutex> state_guard(_state_lock); 414 415 ObjectNotificationState &state = object_state(object, false); 416 417 if (state.is_null()) 418 return -L4_EINVAL; 419 420 /* Acquire the lock for the object state itself, then release the state lock. */ 421 422 std::unique_lock<std::mutex> object_guard(state.lock); 423 424 state_guard.unlock(); 425 426 while (1) 427 { 428 /* With pending notifications, update the object and exit. */ 429 430 if (_transfer(state, object)) 431 break; 432 433 /* Otherwise, wait for notifications. */ 434 435 state.condition.wait(object_guard); 436 } 437 438 return L4_EOK; 439 } 440 441 // vim: tabstop=2 expandtab shiftwidth=2