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 <ipc/cap_alloc.h> 23 #include <ipc/server.h> 24 #include <resource/resource_server.h> 25 26 #include "notification_client.h" 27 #include "notifier.h" 28 #include "notifier_server.h" 29 30 31 32 /* Lock protecting per-task notifier access. */ 33 34 static std::mutex _task_lock; 35 36 /* Per-task storage for specific waiting operations. */ 37 38 static ObjectNotifier *_notifier = NULL; 39 40 41 42 /* Return the per-task notifier for object-specific waiting operations. */ 43 44 ObjectNotifier *notifier_get_task_notifier() 45 { 46 std::lock_guard<std::mutex> guard(_task_lock); 47 48 /* Start any new notifier. */ 49 50 if (_notifier == NULL) 51 _notifier = new ObjectNotifier; 52 53 return _notifier; 54 } 55 56 /* Return a local notifier for general object waiting operations. */ 57 58 ObjectNotifier *notifier_get_local_notifier() 59 { 60 return new ObjectNotifier; 61 } 62 63 64 65 /* Virtual destructor required for base class instance reference deletion. */ 66 67 ObjectNotifier::~ObjectNotifier() 68 { 69 /* Remove this notifier from the individual notifier resources. */ 70 71 NotifiableObjects::iterator it; 72 73 for (it = _monitored.begin(); it != _monitored.end(); it++) 74 { 75 notifiable_t *object = *it; 76 77 if (object->handler != NULL) 78 { 79 NotifierResource *resource = reinterpret_cast<NotifierResource *>(object->handler); 80 resource->remove(this); 81 } 82 } 83 84 _monitored.clear(); 85 86 /* Handle deletion of the special task notifier. */ 87 88 std::lock_guard<std::mutex> guard(_task_lock); 89 90 if (this == _notifier) 91 _notifier = NULL; 92 } 93 94 /* Handle a deletion event for an object. */ 95 96 void ObjectNotifier::release(notifiable_t *object) 97 { 98 std::lock_guard<std::mutex> guard(_monitored_lock); 99 100 _monitored.erase(object); 101 object->handler = NULL; 102 } 103 104 /* Subscribe to notification events on an object. */ 105 106 long ObjectNotifier::subscribe(notifiable_t *object, notify_flags_t flags) 107 { 108 std::lock_guard<std::mutex> guard(_monitored_lock); 109 110 /* Ensure that a handler resource is available for object notifications. */ 111 112 NotifierResource *resource; 113 114 if (object->handler != NULL) 115 resource = reinterpret_cast<NotifierResource *>(object->handler); 116 117 /* Create a resource if none is recorded. */ 118 119 else 120 { 121 resource = new NotifierResource(object); 122 ResourceServer server(resource); 123 long err = server.start_thread(&resource->endpoint); 124 125 if (err) 126 return err; 127 128 object->handler = resource; 129 } 130 131 /* Record the object. */ 132 133 _monitored.insert(object); 134 135 /* Record this notifier to get general notifications. */ 136 137 return resource->add(this, flags); 138 } 139 140 /* Unsubscribe from notification events on an object. */ 141 142 long ObjectNotifier::unsubscribe(notifiable_t *object) 143 { 144 std::lock_guard<std::mutex> guard(_monitored_lock); 145 146 if (object->handler == NULL) 147 return L4_EOK; 148 149 NotifierResource *resource = reinterpret_cast<NotifierResource *>(object->handler); 150 151 _monitored.erase(object); 152 object->handler = NULL; 153 return resource->remove(this); 154 } 155 156 /* Handle a notification event for an object. */ 157 158 void ObjectNotifier::notify(notifiable_t *object) 159 { 160 /* Enter critical section to access the queue. */ 161 162 std::unique_lock<std::mutex> guard(_affected_lock); 163 164 /* Ensure that a queue entry exists for the object. */ 165 166 NotifiableObjects::iterator it = _affected.find(object); 167 168 if (it == _affected.end()) 169 { 170 _queued.push_back(object); 171 _affected.insert(object); 172 } 173 174 /* Notify all waiting callers that at least one notification is available. */ 175 176 _condition.notify_all(); 177 } 178 179 /* Update the notifiable object when being notified. */ 180 181 void ObjectNotifier::update(notifiable_t *object) 182 { 183 object->notifications = object->pending_notifications; 184 object->values = object->pending_values; 185 186 object->pending_notifications = 0; 187 } 188 189 /* Wait for notification events on objects, returning each object that has a 190 notification registered for it. */ 191 192 long ObjectNotifier::wait(notifiable_t **object) 193 { 194 /* Enter critical section to access the queue. */ 195 196 std::unique_lock<std::mutex> guard(_affected_lock); 197 198 while (1) 199 { 200 /* With pending notifications, return the first object. */ 201 202 if (!_affected.empty()) 203 { 204 *object = _queued.front(); 205 _queued.pop_front(); 206 _affected.erase(*object); 207 break; 208 } 209 210 /* Otherwise, wait for notifications. */ 211 212 _condition.wait(guard); 213 } 214 215 /* Synchronise the notification flags and values. */ 216 217 update(*object); 218 return L4_EOK; 219 } 220 221 /* Wait for notification events on a specific object. */ 222 223 long ObjectNotifier::wait_object(notifiable_t *object) 224 { 225 /* Enter critical section to access the queue. */ 226 227 std::unique_lock<std::mutex> guard(_affected_lock); 228 229 while (1) 230 { 231 /* With pending notifications, find any for the specified object. */ 232 233 NotifiableObjects::iterator it = _affected.find(object); 234 235 if (it != _affected.end()) 236 { 237 /* Remove the object from the queue. */ 238 239 NotifiableObjectQueue::iterator itq; 240 241 for (itq = _queued.begin(); itq != _queued.end(); itq++) 242 { 243 if (*itq == object) 244 { 245 _queued.erase(itq); 246 break; 247 } 248 } 249 250 _affected.erase(it); 251 break; 252 } 253 254 /* Otherwise, wait for notifications. */ 255 256 _condition.wait(guard); 257 } 258 259 /* Synchronise the notification flags and values. */ 260 261 update(object); 262 return L4_EOK; 263 } 264 265 266 267 /* Virtual destructor required for base class instance reference deletion. */ 268 269 NotifierResource::~NotifierResource() 270 { 271 } 272 273 /* Handle the release of the resource. */ 274 275 void NotifierResource::close() 276 { 277 _release(); 278 } 279 280 /* Object-specific resource methods. */ 281 282 ipc_server_default_config_type NotifierResource::config() 283 { 284 return config_Notifier; 285 } 286 287 288 289 /* Registration of notifiers. */ 290 291 long NotifierResource::add(ObjectNotifier *notifier, notify_flags_t flags) 292 { 293 std::lock_guard<std::mutex> guard(_lock); 294 295 bool is_new = _notifiers.empty(); 296 297 _notifiers.insert(notifier); 298 299 /* Subscribe, sending the notification endpoint via the principal reference 300 for the object. */ 301 302 if (!is_new) 303 return L4_EOK; 304 305 client_Notification notify(_object->base->ref); 306 307 return notify.subscribe(endpoint, flags); 308 } 309 310 long NotifierResource::remove(ObjectNotifier *notifier) 311 { 312 std::lock_guard<std::mutex> guard(_lock); 313 314 _notifiers.erase(notifier); 315 316 /* Unsubscribe via the notification interface. */ 317 318 if (!_notifiers.empty()) 319 return L4_EOK; 320 321 client_Notification notify(_object->base->ref); 322 323 return notify.unsubscribe(); 324 } 325 326 327 328 /* Register a notification received by an object-specific resource. */ 329 330 long NotifierResource::notify(notify_flags_t flags, notify_values_t values) 331 { 332 /* Update the notifiable object. */ 333 334 _object->pending_notifications |= flags; 335 _object->pending_values = values; 336 337 _notify(); 338 return L4_EOK; 339 } 340 341 void NotifierResource::_notify() 342 { 343 /* Enter critical section for the resource. */ 344 345 std::lock_guard<std::mutex> guard(_lock); 346 347 /* Register the notification with all notifier objects. */ 348 349 ObjectNotifiers::iterator it; 350 351 for (it = _notifiers.begin(); it != _notifiers.end(); it++) 352 (*it)->notify(_object); 353 } 354 355 void NotifierResource::_release() 356 { 357 /* Enter critical section for the resource. */ 358 359 std::lock_guard<std::mutex> guard(_lock); 360 361 ObjectNotifiers::iterator it; 362 363 for (it = _notifiers.begin(); it != _notifiers.end(); it++) 364 (*it)->release(_object); 365 } 366 367 /* vim: tabstop=2 expandtab shiftwidth=2 368 */