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 } 102 103 /* Subscribe to notification events on an object. */ 104 105 long ObjectNotifier::subscribe(notifiable_t *object, notify_flags_t flags) 106 { 107 std::lock_guard<std::mutex> guard(_monitored_lock); 108 109 /* Ensure that a handler resource is available for object notifications. */ 110 111 NotifierResource *resource; 112 113 if (object->handler != NULL) 114 resource = reinterpret_cast<NotifierResource *>(object->handler); 115 116 /* Create a resource if none is recorded. */ 117 118 else 119 { 120 resource = new NotifierResource(object); 121 ResourceServer server(resource); 122 long err = server.start_thread(&resource->endpoint); 123 124 if (err) 125 return err; 126 127 object->handler = resource; 128 } 129 130 /* Record the object. */ 131 132 _monitored.insert(object); 133 134 /* Record this notifier to get general notifications. */ 135 136 return resource->add(this, flags); 137 } 138 139 /* Unsubscribe from notification events on an object. */ 140 141 long ObjectNotifier::unsubscribe(notifiable_t *object) 142 { 143 std::lock_guard<std::mutex> guard(_monitored_lock); 144 145 if (object->handler == NULL) 146 return L4_EOK; 147 148 NotifierResource *resource = reinterpret_cast<NotifierResource *>(object->handler); 149 150 _monitored.erase(object); 151 return resource->remove(this); 152 } 153 154 /* Handle a notification event for an object. */ 155 156 void ObjectNotifier::notify(notifiable_t *object) 157 { 158 /* Enter critical section to access the queue. */ 159 160 std::lock_guard<std::mutex> guard(_affected_lock); 161 162 /* Ensure that a queue entry exists for the object. */ 163 164 NotifiableObjects::iterator it = _affected.find(object); 165 166 if (it == _affected.end()) 167 { 168 _queued.push_back(object); 169 _affected.insert(object); 170 } 171 172 /* Notify any waiting caller that at least one notification is available. */ 173 174 _condition.notify_one(); 175 } 176 177 /* Wait for notification events on objects, returning each object that has a 178 notification registered for it. */ 179 180 long ObjectNotifier::wait(notifiable_t **object) 181 { 182 /* Enter critical section to access the queue. */ 183 184 std::unique_lock<std::mutex> guard(_affected_lock); 185 186 while (1) 187 { 188 /* With pending notifications, return the first object. */ 189 190 if (!_affected.empty()) 191 { 192 *object = _queued.front(); 193 _queued.pop_front(); 194 _affected.erase(*object); 195 break; 196 } 197 198 /* Otherwise, wait for notifications. */ 199 200 _condition.wait(guard); 201 } 202 203 return L4_EOK; 204 } 205 206 /* Wait for notification events on a specific object. */ 207 208 long ObjectNotifier::wait_object(notifiable_t *object) 209 { 210 if (object->handler == NULL) 211 return -L4_EINVAL; 212 213 reinterpret_cast<NotifierResource *>(object->handler)->wait(); 214 return L4_EOK; 215 } 216 217 218 219 /* Virtual destructor required for base class instance reference deletion. */ 220 221 NotifierResource::~NotifierResource() 222 { 223 } 224 225 /* Handle the release of the resource. */ 226 227 void NotifierResource::close() 228 { 229 _release(); 230 _object->handler = NULL; 231 } 232 233 /* Object-specific resource methods. */ 234 235 ipc_server_default_config_type NotifierResource::config() 236 { 237 return config_Notifier; 238 } 239 240 241 242 /* Registration of notifiers. */ 243 244 long NotifierResource::add(ObjectNotifier *notifier, notify_flags_t flags) 245 { 246 std::lock_guard<std::mutex> guard(_lock); 247 248 bool is_new = _notifiers.empty(); 249 250 _notifiers.insert(notifier); 251 252 /* Subscribe, sending the notification endpoint via the principal reference 253 for the object. */ 254 255 if (!is_new) 256 return L4_EOK; 257 258 client_Notification notify(_object->base->ref); 259 260 return notify.subscribe(endpoint, flags); 261 } 262 263 long NotifierResource::remove(ObjectNotifier *notifier) 264 { 265 std::lock_guard<std::mutex> guard(_lock); 266 267 _notifiers.erase(notifier); 268 269 /* Unsubscribe via the notification interface. */ 270 271 if (!_notifiers.empty()) 272 return L4_EOK; 273 274 client_Notification notify(_object->base->ref); 275 276 return notify.unsubscribe(); 277 } 278 279 280 281 /* Register a notification received by an object-specific resource. */ 282 283 long NotifierResource::notify(notify_flags_t flags, notify_values_t values) 284 { 285 /* Update the notifiable object. */ 286 287 _object->notifications |= flags; 288 _object->values = values; 289 290 _notify(); 291 return L4_EOK; 292 } 293 294 void NotifierResource::_notify() 295 { 296 /* Enter critical section for the resource. */ 297 298 std::lock_guard<std::mutex> guard(_lock); 299 300 /* Record a pending notification which persists if nothing is waiting. */ 301 302 _pending = true; 303 304 /* Notify any party waiting specifically on this object. */ 305 306 _condition.notify_one(); 307 308 /* Register the notification with all notifier objects. */ 309 310 ObjectNotifiers::iterator it; 311 312 for (it = _notifiers.begin(); it != _notifiers.end(); it++) 313 (*it)->notify(_object); 314 } 315 316 void NotifierResource::_release() 317 { 318 /* Enter critical section for the resource. */ 319 320 std::lock_guard<std::mutex> guard(_lock); 321 322 ObjectNotifiers::iterator it; 323 324 for (it = _notifiers.begin(); it != _notifiers.end(); it++) 325 (*it)->release(_object); 326 } 327 328 /* Wait for notification events on a specific object. */ 329 330 long NotifierResource::wait() 331 { 332 /* Enter critical section for the resource. */ 333 334 std::unique_lock<std::mutex> guard(_lock); 335 336 /* Wait for the notification condition. */ 337 338 if (!_pending) 339 _condition.wait(guard); 340 341 _pending = false; 342 return L4_EOK; 343 } 344 345 /* vim: tabstop=2 expandtab shiftwidth=2 346 */